aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Lukacs Berki <lberki@google.com>2015-03-27 14:08:38 +0000
committerGravatar Ulf Adams <ulfjack@google.com>2015-03-30 12:17:41 +0000
commitf5e23c61ad53f209bea51607cd598062453c0b8a (patch)
treed4751aeca79d96e72b6163c0d5528f13427d783c
parentb50215e23100a3030d4e5a3eeeca9e74e65e1757 (diff)
Make Python rules in Bazel actually work.
In particular: add a BazelPythonSemantics implementation and the respective RuleConfiguredTarget factories, rule class definitions and hook them up with BazelRuleClassProvider. Add implicit dependencies (2to3 is just a stub script for now that always fails) and a tiny exampe. -- MOS_MIGRATED_REVID=89691827
-rw-r--r--examples/py_native/BUILD10
-rw-r--r--examples/py_native/bin.py4
-rw-r--r--examples/py_native/lib.py5
-rw-r--r--src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java13
-rw-r--r--src/main/java/com/google/devtools/build/lib/bazel/rules/python/BazelPyBinary.java28
-rw-r--r--src/main/java/com/google/devtools/build/lib/bazel/rules/python/BazelPyBinaryRule.java101
-rw-r--r--src/main/java/com/google/devtools/build/lib/bazel/rules/python/BazelPyLibrary.java28
-rw-r--r--src/main/java/com/google/devtools/build/lib/bazel/rules/python/BazelPyLibraryRule.java76
-rw-r--r--src/main/java/com/google/devtools/build/lib/bazel/rules/python/BazelPyRuleClasses.java180
-rw-r--r--src/main/java/com/google/devtools/build/lib/bazel/rules/python/BazelPythonSemantics.java74
-rw-r--r--src/main/java/com/google/devtools/build/lib/bazel/rules/python/stub_template.txt5
-rwxr-xr-xtools/python/2to3.sh3
-rw-r--r--tools/python/BUILD6
13 files changed, 532 insertions, 1 deletions
diff --git a/examples/py_native/BUILD b/examples/py_native/BUILD
new file mode 100644
index 0000000000..3d8de94251
--- /dev/null
+++ b/examples/py_native/BUILD
@@ -0,0 +1,10 @@
+py_binary(
+ name = "bin",
+ srcs = ["bin.py"],
+ deps = [":lib"],
+)
+
+py_library(
+ name = "lib",
+ srcs = ["lib.py"],
+)
diff --git a/examples/py_native/bin.py b/examples/py_native/bin.py
new file mode 100644
index 0000000000..f79379a237
--- /dev/null
+++ b/examples/py_native/bin.py
@@ -0,0 +1,4 @@
+"""A tiny example binary for the native Python rules of Bazel."""
+from examples.py_native.lib import GetNumber
+
+print "The number is %d" % GetNumber()
diff --git a/examples/py_native/lib.py b/examples/py_native/lib.py
new file mode 100644
index 0000000000..44522e0fe9
--- /dev/null
+++ b/examples/py_native/lib.py
@@ -0,0 +1,5 @@
+"""A tiny example binary for the native Python rules of Bazel."""
+
+
+def GetNumber():
+ return 42
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java
index 909eec8b20..4ee294d211 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java
@@ -44,6 +44,9 @@ import com.google.devtools.build.lib.bazel.rules.java.BazelJavaPluginRule;
import com.google.devtools.build.lib.bazel.rules.java.BazelJavaRuleClasses;
import com.google.devtools.build.lib.bazel.rules.java.BazelJavaTestRule;
import com.google.devtools.build.lib.bazel.rules.objc.BazelIosTestRule;
+import com.google.devtools.build.lib.bazel.rules.python.BazelPyBinaryRule;
+import com.google.devtools.build.lib.bazel.rules.python.BazelPyLibraryRule;
+import com.google.devtools.build.lib.bazel.rules.python.BazelPyRuleClasses;
import com.google.devtools.build.lib.bazel.rules.sh.BazelShBinaryRule;
import com.google.devtools.build.lib.bazel.rules.sh.BazelShLibraryRule;
import com.google.devtools.build.lib.bazel.rules.sh.BazelShRuleClasses;
@@ -88,6 +91,8 @@ import com.google.devtools.build.lib.rules.objc.ObjcOptionsRule;
import com.google.devtools.build.lib.rules.objc.ObjcProtoLibraryRule;
import com.google.devtools.build.lib.rules.objc.ObjcRuleClasses;
import com.google.devtools.build.lib.rules.objc.ObjcXcodeprojRule;
+import com.google.devtools.build.lib.rules.python.PythonConfigurationLoader;
+import com.google.devtools.build.lib.rules.python.PythonOptions;
import com.google.devtools.build.lib.rules.workspace.BindRule;
import com.google.devtools.build.lib.syntax.Label;
import com.google.devtools.build.lib.syntax.SkylarkType;
@@ -171,6 +176,7 @@ public class BazelRuleClassProvider {
BuildConfiguration.Options.class,
CppOptions.class,
JavaOptions.class,
+ PythonOptions.class,
ObjcCommandLineOptions.class
);
@@ -225,10 +231,14 @@ public class BazelRuleClassProvider {
builder.addRuleDefinition(new BazelCppRuleClasses.CcBinaryBaseRule());
builder.addRuleDefinition(new BazelCppRuleClasses.CcBinaryRule());
builder.addRuleDefinition(new BazelCppRuleClasses.CcTestRule());
-
builder.addRuleDefinition(new BazelCppRuleClasses.CcLibraryBaseRule());
builder.addRuleDefinition(new BazelCppRuleClasses.CcLibraryRule());
+ builder.addRuleDefinition(new BazelPyRuleClasses.PyBaseRule());
+ builder.addRuleDefinition(new BazelPyRuleClasses.PyBinaryBaseRule());
+ builder.addRuleDefinition(new BazelPyLibraryRule());
+ builder.addRuleDefinition(new BazelPyBinaryRule());
+
builder.addWorkspaceFile(BazelJavaRuleClasses.getDefaultWorkspace());
builder.addRuleDefinition(new BazelJavaRuleClasses.BaseJavaBinaryRule());
builder.addRuleDefinition(new BazelJavaRuleClasses.IjarBaseRule());
@@ -286,6 +296,7 @@ public class BazelRuleClassProvider {
builder.addConfigurationFragment(new BazelConfiguration.Loader());
builder.addConfigurationFragment(new CppConfigurationLoader(
Functions.<String>identity()));
+ builder.addConfigurationFragment(new PythonConfigurationLoader(Functions.<String>identity()));
builder.addConfigurationFragment(new JvmConfigurationLoader(JAVA_CPU_SUPPLIER));
builder.addConfigurationFragment(new JavaConfigurationLoader(JAVA_CPU_SUPPLIER));
builder.addConfigurationFragment(new ObjcConfigurationLoader());
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/python/BazelPyBinary.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/python/BazelPyBinary.java
new file mode 100644
index 0000000000..3186791265
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/python/BazelPyBinary.java
@@ -0,0 +1,28 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.devtools.build.lib.bazel.rules.python;
+
+import com.google.devtools.build.lib.rules.python.PyBinary;
+import com.google.devtools.build.lib.rules.python.PythonSemantics;
+
+/**
+ * Implementation of the {@code py_binary} rule for Bazel.
+ */
+public class BazelPyBinary extends PyBinary {
+ @Override
+ protected PythonSemantics createSemantics() {
+ return new BazelPythonSemantics();
+ }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/python/BazelPyBinaryRule.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/python/BazelPyBinaryRule.java
new file mode 100644
index 0000000000..6d6b2fd1a5
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/python/BazelPyBinaryRule.java
@@ -0,0 +1,101 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.devtools.build.lib.bazel.rules.python;
+
+import com.google.devtools.build.lib.analysis.RuleDefinition;
+import com.google.devtools.build.lib.analysis.RuleDefinitionEnvironment;
+import com.google.devtools.build.lib.bazel.rules.BazelBaseRuleClasses;
+import com.google.devtools.build.lib.bazel.rules.python.BazelPyRuleClasses.PyBinaryBaseRule;
+import com.google.devtools.build.lib.packages.RuleClass;
+
+/**
+ * Rule definition for the {@code py_binary} rule.
+ */
+public final class BazelPyBinaryRule implements RuleDefinition {
+ @Override
+ public RuleClass build(RuleClass.Builder builder, RuleDefinitionEnvironment env) {
+ /* <!-- #BLAZE_RULE(py_binary).NAME -->
+ <br/>If <code>main</code> is unspecified, this should be the same as the name
+ of the source file that is the main entry point of the application,
+ minus the extension. For example, if your entry point is called
+ <code>main.py</code>, then your name should be <code>main</code>.
+ <!-- #END_BLAZE_RULE.NAME --> */
+ return builder
+ .build();
+ }
+
+ @Override
+ public Metadata getMetadata() {
+ return RuleDefinition.Metadata.builder()
+ .name("py_binary")
+ .ancestors(PyBinaryBaseRule.class, BazelBaseRuleClasses.BinaryBaseRule.class)
+ .factoryClass(BazelPyBinary.class)
+ .build();
+ }
+}
+
+/*<!-- #BLAZE_RULE (NAME = py_binary, TYPE = BINARY, FAMILY = Python) -->
+
+${ATTRIBUTE_SIGNATURE}
+
+<p>
+ A <code>py_binary</code> is an executable Python program consisting
+ of a collection of <code>.py</code> source files (possibly belonging
+ to other <code>py_library</code> rules), a <code>*.runfiles</code>
+ directory tree containing all the code and data needed by the
+ program at run-time, and a stub script that starts up the program with
+ the correct initial environment and data.
+</p>
+
+${IMPLICIT_OUTPUTS}
+
+${ATTRIBUTE_DEFINITION}
+
+<h4 id="py_binary_examples">Examples</h4>
+
+<pre class="code">
+py_binary(
+ name = "foo",
+ srcs = ["foo.py"],
+ data = [":transform"], # a cc_binary which we invoke at run time
+ deps = [
+ "//pyglib",
+ ":foolib", # a py_library
+ ],
+)
+</pre>
+
+<p>If you want to run a <code>py_binary</code> from within another binary or
+ test (for example, running a python binary to set up some mock resource from
+ within a java_test) then the correct approach is to make the other binary or
+ test depend on the <code>py_binary</code> in its data section. The other
+ binary can then locate the <code>py_binary</code> relative to the source
+ directory.
+</p>
+
+<pre class="code">
+py_binary(
+ name = "test_main",
+ srcs = ["test_main.py"],
+ deps = [":testlib"],
+)
+
+java_library(
+ name = "testing",
+ srcs = glob(["*.java"]),
+ data = [":test_main"]
+)
+</pre>
+<!-- #END_BLAZE_RULE -->*/
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/python/BazelPyLibrary.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/python/BazelPyLibrary.java
new file mode 100644
index 0000000000..5cd9b8fbb3
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/python/BazelPyLibrary.java
@@ -0,0 +1,28 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.devtools.build.lib.bazel.rules.python;
+
+import com.google.devtools.build.lib.rules.python.PyLibrary;
+import com.google.devtools.build.lib.rules.python.PythonSemantics;
+
+/**
+ * Implementation of the {@code py_library} rule for Bazel.
+ */
+public class BazelPyLibrary extends PyLibrary {
+ @Override
+ protected PythonSemantics createSemantics() {
+ return new BazelPythonSemantics();
+ }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/python/BazelPyLibraryRule.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/python/BazelPyLibraryRule.java
new file mode 100644
index 0000000000..bc508960b2
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/python/BazelPyLibraryRule.java
@@ -0,0 +1,76 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.devtools.build.lib.bazel.rules.python;
+
+import static com.google.devtools.build.lib.packages.Attribute.attr;
+import static com.google.devtools.build.lib.packages.Type.LABEL_LIST;
+
+import com.google.devtools.build.lib.analysis.RuleDefinition;
+import com.google.devtools.build.lib.analysis.RuleDefinitionEnvironment;
+import com.google.devtools.build.lib.bazel.rules.python.BazelPyRuleClasses.PyBaseRule;
+import com.google.devtools.build.lib.packages.RuleClass;
+
+/**
+ * Rule definition for the {@code py_library} rule.
+ */
+public final class BazelPyLibraryRule implements RuleDefinition {
+ @Override
+ public RuleClass build(RuleClass.Builder builder, RuleDefinitionEnvironment env) {
+ return builder
+ /* <!-- #BLAZE_RULE(py_library).ATTRIBUTE(deps) -->
+ The list of other libraries to be linked in to the library target.
+ ${SYNOPSIS}
+ See general comments about <code>deps</code>
+ at <a href="#common-attributes">Attributes common to all build rules</a>.
+ In practice, these arguments are treated like those in <code>srcs</code>;
+ you may move items between these lists willy-nilly. It's probably more
+ readable to keep your <code>.py</code> files in your <code>srcs</code>.
+ <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
+
+ /* <!-- #BLAZE_RULE(py_library).ATTRIBUTE(data) -->
+ The list of files needed by this library at runtime.
+ ${SYNOPSIS}
+ See general comments about <code>data</code>
+ at <a href="#common-attributes">Attributes common to all build rules</a>.
+ <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
+
+ /* <!-- #BLAZE_RULE(py_library).ATTRIBUTE(srcs) -->
+ The list of source files that are processed to create the target.
+ ${SYNOPSIS}
+ This includes all your checked-in code and any generated source files.
+ <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
+ .add(attr("srcs", LABEL_LIST)
+ .direct_compile_time_input()
+ .allowedFileTypes(BazelPyRuleClasses.PYTHON_SOURCE))
+ .build();
+ }
+
+ @Override
+ public Metadata getMetadata() {
+ return RuleDefinition.Metadata.builder()
+ .name("py_library")
+ .ancestors(PyBaseRule.class)
+ .factoryClass(BazelPyLibrary.class)
+ .build();
+ }
+}
+
+/*<!-- #BLAZE_RULE (NAME = py_library, TYPE = LIBRARY, FAMILY = Python) -->
+
+${ATTRIBUTE_SIGNATURE}
+
+${ATTRIBUTE_DEFINITION}
+
+<!-- #END_BLAZE_RULE -->*/
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/python/BazelPyRuleClasses.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/python/BazelPyRuleClasses.java
new file mode 100644
index 0000000000..019170c88f
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/python/BazelPyRuleClasses.java
@@ -0,0 +1,180 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.devtools.build.lib.bazel.rules.python;
+
+import static com.google.devtools.build.lib.packages.Attribute.ConfigurationTransition.HOST;
+import static com.google.devtools.build.lib.packages.Attribute.attr;
+import static com.google.devtools.build.lib.packages.Type.LABEL;
+import static com.google.devtools.build.lib.packages.Type.LABEL_LIST;
+import static com.google.devtools.build.lib.packages.Type.STRING;
+import static com.google.devtools.build.lib.packages.Type.TRISTATE;
+
+import com.google.devtools.build.lib.analysis.BaseRuleClasses;
+import com.google.devtools.build.lib.analysis.RuleDefinition;
+import com.google.devtools.build.lib.analysis.RuleDefinitionEnvironment;
+import com.google.devtools.build.lib.bazel.rules.cpp.BazelCppRuleClasses;
+import com.google.devtools.build.lib.packages.RuleClass;
+import com.google.devtools.build.lib.packages.RuleClass.Builder.RuleClassType;
+import com.google.devtools.build.lib.packages.TriState;
+import com.google.devtools.build.lib.rules.python.PyRuleClasses;
+import com.google.devtools.build.lib.rules.python.PythonVersion;
+import com.google.devtools.build.lib.util.FileType;
+
+/**
+ * Bazel-specific rule definitions for Python rules.
+ */
+public final class BazelPyRuleClasses {
+ public static final FileType PYTHON_SOURCE = FileType.of(".py");
+
+ public static final String[] ALLOWED_RULES_IN_DEPS = new String[] {
+ "py_binary",
+ "py_library",
+ };
+
+ /**
+ * Base class for Python rule definitions.
+ */
+ public static final class PyBaseRule implements RuleDefinition {
+ @Override
+ public RuleClass build(RuleClass.Builder builder, RuleDefinitionEnvironment env) {
+ return builder
+ /* <!-- #BLAZE_RULE($base_py).ATTRIBUTE(deps) -->
+ The list of other libraries to be linked in to the binary target.
+ ${SYNOPSIS}
+ See general comments about <code>deps</code>
+ at <a href="#common-attributes">Attributes common to all build rules</a>.
+ These can be
+ <a href="#py_binary"><code>py_binary</code></a> rules,
+ <a href="#py_library"><code>py_library</code></a> rules or
+ <a href="#cc_library"><code>cc_library</code></a> rules,
+ <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
+ .override(builder.copy("deps")
+ .allowedRuleClasses(ALLOWED_RULES_IN_DEPS)
+ .allowedFileTypes())
+ /* <!-- #BLAZE_RULE($base_py).ATTRIBUTE(srcs_version) -->
+ A string specifying the Python major version(s) that the <code>.py</code> source
+ files listed in the <code>srcs</code> of this rule are compatible with.
+ ${SYNOPSIS}
+ Valid values are:<br/>
+ <code>"PY2ONLY"</code> -
+ Python 2 code that is <b>not</b> suitable for <code>2to3</code> conversion.<br/>
+ <code>"PY2"</code> -
+ Python 2 code that is expected to work when run through <code>2to3</code>.<br/>
+ <code>"PY2AND3"</code> -
+ Code that is compatible with both Python 2 and 3 without
+ <code>2to3</code> conversion.<br/>
+ <code>"PY3"</code> -
+ Python 3 code that will not run on Python 2.<br/>
+ <br/>
+ <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
+ .add(attr("srcs_version", STRING)
+ .value(PythonVersion.defaultValue().toString()))
+ // do not depend on lib2to3:2to3 rule, because it creates circular dependencies
+ // 2to3 is itself written in Python and depends on many libraries.
+ .add(attr("$python2to3", LABEL).cfg(HOST).exec()
+ .value(env.getLabel("//tools/python:2to3")))
+ .setPreferredDependencyPredicate(PyRuleClasses.PYTHON_SOURCE)
+ .build();
+ }
+
+ @Override
+ public Metadata getMetadata() {
+ return RuleDefinition.Metadata.builder()
+ .name("$base_py")
+ .type(RuleClassType.ABSTRACT)
+ .ancestors(BaseRuleClasses.RuleBase.class)
+ .build();
+ }
+ }
+
+ /**
+ * Base class for Python rule definitions that produce binaries.
+ */
+ public static final class PyBinaryBaseRule implements RuleDefinition {
+ @Override
+ public RuleClass build(RuleClass.Builder builder, final RuleDefinitionEnvironment env) {
+ return builder
+ /* <!-- #BLAZE_RULE($base_py_binary).ATTRIBUTE(data) -->
+ The list of files needed by this binary at runtime.
+ ${SYNOPSIS}
+ See general comments about <code>data</code>
+ at <a href="#common-attributes">Attributes common to all build rules</a>.
+ Also see the <a href="#py_library.data"><code>data</code></a> argument of
+ the <a href="#py_library"><code>py_library</code></a> rule for details.
+ <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
+
+ /* <!-- #BLAZE_RULE($base_py_binary).ATTRIBUTE(main) -->
+ The name of the source file that is the main entry point of the application.
+ ${SYNOPSIS}
+ This file must also be listed in <code>srcs</code>. If left unspecified,
+ <code>name</code> is used instead (see above). If <code>name</code> does not
+ match any filename in <code>srcs</code>, <code>main</code> must be specified.
+ <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
+ .add(attr("main", LABEL).allowedFileTypes(PYTHON_SOURCE))
+ /* <!-- #BLAZE_RULE($base_py_binary).ATTRIBUTE(default_python_version) -->
+ A string specifying the default Python major version to use when building this binary and
+ all of its <code>deps</code>.
+ ${SYNOPSIS}
+ Valid values are <code>"PY2"</code> (default) or <code>"PY3"</code>.
+ Python 3 support is experimental.
+ <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
+ .add(attr("default_python_version", STRING)
+ .value(PythonVersion.defaultValue().toString())
+ .nonconfigurable("read by PythonUtils.getNewPythonVersion, which doesn't have access"
+ + " to configuration keys"))
+ /* <!-- #BLAZE_RULE($base_py_binary).ATTRIBUTE(srcs) -->
+ The list of source files that are processed to create the target.
+ ${SYNOPSIS}
+ This includes all your checked-in code and any
+ generated source files. The line between <code>srcs</code> and
+ <code>deps</code> is loose. The <code>.py</code> files
+ probably belong in <code>srcs</code> and library targets probably belong
+ in <code>deps</code>, but don't worry about it too much.
+ <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
+ .add(attr("srcs", LABEL_LIST)
+ .mandatory()
+ .allowedFileTypes(PYTHON_SOURCE)
+ .direct_compile_time_input()
+ .allowedFileTypes(BazelPyRuleClasses.PYTHON_SOURCE))
+ /* <!-- #BLAZE_RULE($base_py_binary).ATTRIBUTE(stamp) -->
+ Enable link stamping.
+ ${SYNOPSIS}
+ Whether to encode build information into the binary. Possible values:
+ <ul>
+ <li><code>stamp = 1</code>: Stamp the build information into the
+ binary. Stamped binaries are only rebuilt when their dependencies
+ change. Use this if there are tests that depend on the build
+ information.</li>
+ <li><code>stamp = 0</code>: Always replace build information by constant
+ values. This gives good build result caching.</li>
+ <li><code>stamp = -1</code>: Embedding of build information is controlled
+ by the <a href="blaze-user-manual.html#flag--stamp">--[no]stamp</a> Blaze
+ flag.</li>
+ </ul>
+ <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
+ .add(attr("stamp", TRISTATE).value(TriState.AUTO))
+ .build();
+ }
+
+ @Override
+ public Metadata getMetadata() {
+ return RuleDefinition.Metadata.builder()
+ .name("$base_py_binary")
+ .type(RuleClassType.ABSTRACT)
+ .ancestors(PyBaseRule.class, BazelCppRuleClasses.CcLinkingRule.class)
+ .build();
+ }
+ }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/python/BazelPythonSemantics.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/python/BazelPythonSemantics.java
new file mode 100644
index 0000000000..3b249010c1
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/python/BazelPythonSemantics.java
@@ -0,0 +1,74 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.devtools.build.lib.bazel.rules.python;
+
+import com.google.common.collect.ImmutableList;
+import com.google.devtools.build.lib.analysis.RuleContext;
+import com.google.devtools.build.lib.analysis.Runfiles.Builder;
+import com.google.devtools.build.lib.analysis.RunfilesSupport;
+import com.google.devtools.build.lib.analysis.actions.TemplateExpansionAction;
+import com.google.devtools.build.lib.analysis.actions.TemplateExpansionAction.Substitution;
+import com.google.devtools.build.lib.analysis.actions.TemplateExpansionAction.Template;
+import com.google.devtools.build.lib.rules.cpp.CcLinkParamsStore;
+import com.google.devtools.build.lib.rules.python.PyCommon;
+import com.google.devtools.build.lib.rules.python.PythonSemantics;
+import com.google.devtools.build.lib.rules.test.InstrumentedFilesCollector.InstrumentationSpec;
+import com.google.devtools.build.lib.util.FileTypeSet;
+
+/**
+ * Functionality specific to the Python rules in Bazel.
+ */
+public class BazelPythonSemantics implements PythonSemantics {
+ private static final Template STUB_TEMPLATE =
+ Template.forResource(BazelPythonSemantics.class, "stub_template.txt");
+ public static final InstrumentationSpec PYTHON_COLLECTION_SPEC = new InstrumentationSpec(
+ FileTypeSet.of(BazelPyRuleClasses.PYTHON_SOURCE),
+ "srcs", "deps", "data");
+
+ @Override
+ public void validate(RuleContext ruleContext, PyCommon common) {
+ }
+
+ @Override
+ public void collectRunfilesForBinary(RuleContext ruleContext, Builder builder, PyCommon common) {
+ }
+
+ @Override
+ public void collectDefaultRunfilesForBinary(RuleContext ruleContext, Builder builder) {
+ }
+
+ @Override
+ public InstrumentationSpec getCoverageInstrumentationSpec() {
+ return PYTHON_COLLECTION_SPEC;
+ }
+
+ @Override
+ public void createExecutable(RuleContext ruleContext, PyCommon common,
+ CcLinkParamsStore ccLinkParamsStore) {
+ String main = common.determineMainExecutableSource();
+
+ ruleContext.registerAction(new TemplateExpansionAction(
+ ruleContext.getActionOwner(),
+ common.getExecutable(),
+ STUB_TEMPLATE,
+ ImmutableList.of(Substitution.of("%main%", main)),
+ true));
+ }
+
+ @Override
+ public void postInitBinary(RuleContext ruleContext, RunfilesSupport runfilesSupport,
+ PyCommon common) {
+ }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/python/stub_template.txt b/src/main/java/com/google/devtools/build/lib/bazel/rules/python/stub_template.txt
new file mode 100644
index 0000000000..c285517673
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/python/stub_template.txt
@@ -0,0 +1,5 @@
+#!/bin/bash -eu
+
+STUBPATH=$(python -c "import os.path; print os.path.realpath('$0');")
+export PYTHONPATH=$STUBPATH.runfiles
+python ${PYTHONPATH}/%main%
diff --git a/tools/python/2to3.sh b/tools/python/2to3.sh
new file mode 100755
index 0000000000..d87f29eecf
--- /dev/null
+++ b/tools/python/2to3.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+exit 1
diff --git a/tools/python/BUILD b/tools/python/BUILD
new file mode 100644
index 0000000000..f489fe1d94
--- /dev/null
+++ b/tools/python/BUILD
@@ -0,0 +1,6 @@
+package(default_visibility = ["//visibility:public"])
+
+sh_binary(
+ name = "2to3",
+ srcs = ["2to3.sh"],
+)