Make Python rules require the new Python toolchain type
This makes py_binary / py_test require the new Python toolchain type (@bazel_tools//tools/python:toolchain_type). It does *not* make the rules actually use Python runtimes obtained via the toolchain; that happens in a follow-up CL.
A default toolchain is defined and registered automatically in order to prevent analysis of Python rules from failing frivolously. This default is simply a stub that fails at execution time, but a follow-up CL will change it to autodetect the Python interpreter at runtime. Registration of the toolchain occurs via a WORKSPACE suffix file, so it happens automatically for every workspace.
Note that workspace suffixes are disabled in BazelAnalysisMock#createRuleClassProvider, so we need to add it to the analysis mock's boilerplate workspace content. Tests that want to register a specific toolchain (and have it take precedence over the boilerplate) should use --extra_toolchains=... in the configuration, rather than manipulate the WORKSPACE file.
tools/python/BUILD is refactored into tools/python/BUILD.tools so that the newly added toolchain definitions can be bootstrapped with Bazel 0.23, which does not have the required PyRuntimeInfo provider type.
As a drive-by cleanup, the ":py_interpreter" attribute is pushed down from PyBaseRule to PyBinaryBaseRule.
Work toward #7375.
RELNOTES: None
PiperOrigin-RevId: 236897042
diff --git a/scripts/bootstrap/compile.sh b/scripts/bootstrap/compile.sh
index 3dfceac..8a890eb 100755
--- a/scripts/bootstrap/compile.sh
+++ b/scripts/bootstrap/compile.sh
@@ -255,8 +255,13 @@
link_file "${PWD}/tools/java/runfiles/Util.java" "${BAZEL_TOOLS_REPO}/tools/java/runfiles/Util.java"
link_file "${PWD}/tools/java/runfiles/BUILD.tools" "${BAZEL_TOOLS_REPO}/tools/java/runfiles/BUILD"
+ # Create @bazel_tools/tools/python/BUILD
+ mkdir -p ${BAZEL_TOOLS_REPO}/tools/python
+ link_file "${PWD}/tools/python/BUILD.tools" "${BAZEL_TOOLS_REPO}/tools/python/BUILD"
+
# Create the rest of @bazel_tools//tools/...
link_children "${PWD}" tools/cpp "${BAZEL_TOOLS_REPO}"
+ link_children "${PWD}" tools/python "${BAZEL_TOOLS_REPO}"
link_children "${PWD}" tools "${BAZEL_TOOLS_REPO}"
# The BUILD file needed for @remote_java_tools.
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 dea4426..b20b2cb 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
@@ -353,6 +353,13 @@
builder.addRuleDefinition(new PyRuntimeRule());
builder.addSkylarkBootstrap(new PyBootstrap(PyInfo.PROVIDER, PyRuntimeInfo.PROVIDER));
+
+ try {
+ builder.addWorkspaceFileSuffix(
+ ResourceFileLoader.loadResource(BazelPyBinaryRule.class, "python.WORKSPACE"));
+ } catch (IOException e) {
+ throw new IllegalStateException(e);
+ }
}
@Override
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
index 3610423..2bc2a56 100644
--- 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
@@ -129,10 +129,6 @@
attr("srcs_version", STRING)
.value(PythonVersion.DEFAULT_SRCS_VALUE.toString())
.allowedValues(new AllowedValueSet(PythonVersion.SRCS_STRINGS)))
- // TODO(brandjon): Consider adding to py_interpreter a .mandatoryNativeProviders() of
- // PyRuntimeInfoProvider. (Add a test case to PythonConfigurationTest for violations of
- // this requirement.) Probably moot now that this is going to be replaced by toolchains.
- .add(attr(":py_interpreter", LABEL).value(PY_INTERPRETER))
// do not depend on lib2to3:2to3 rule, because it creates circular dependencies
// 2to3 is itself written in Python and depends on many libraries.
.add(
@@ -253,6 +249,11 @@
</ul>
<!-- #END_BLAZE_RULE.ATTRIBUTE --> */
.add(attr("stamp", TRISTATE).value(TriState.AUTO))
+ // TODO(brandjon): Consider adding to py_interpreter a .mandatoryNativeProviders() of
+ // PyRuntimeInfoProvider. (Add a test case to PythonConfigurationTest for violations of
+ // this requirement.) Probably moot now that this is going to be replaced by toolchains.
+ .add(attr(":py_interpreter", LABEL).value(PY_INTERPRETER))
+ .addRequiredToolchains(env.getToolsLabel("//tools/python:toolchain_type"))
.build();
}
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/python/python.WORKSPACE b/src/main/java/com/google/devtools/build/lib/bazel/rules/python/python.WORKSPACE
new file mode 100644
index 0000000..baf03b5
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/python/python.WORKSPACE
@@ -0,0 +1 @@
+register_toolchains("@bazel_tools//tools/python:all")
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/mock/BazelAnalysisMock.java b/src/test/java/com/google/devtools/build/lib/analysis/mock/BazelAnalysisMock.java
index bf12b84..10960aa 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/mock/BazelAnalysisMock.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/mock/BazelAnalysisMock.java
@@ -78,6 +78,7 @@
"bind(name = 'android/sdk', actual='@bazel_tools//tools/android:sdk')",
"register_toolchains('@bazel_tools//tools/cpp:all')",
"register_toolchains('@bazel_tools//tools/jdk:all')",
+ "register_toolchains('@bazel_tools//tools/python:all')",
"local_repository(name = 'local_config_platform', path = '"
+ localConfigPlatformWorkspace
+ "')"));
diff --git a/src/test/java/com/google/devtools/build/lib/packages/util/BazelMockPythonSupport.java b/src/test/java/com/google/devtools/build/lib/packages/util/BazelMockPythonSupport.java
index c1884e0..224bcb4 100644
--- a/src/test/java/com/google/devtools/build/lib/packages/util/BazelMockPythonSupport.java
+++ b/src/test/java/com/google/devtools/build/lib/packages/util/BazelMockPythonSupport.java
@@ -38,6 +38,7 @@
TestConstants.TOOLS_REPOSITORY_SCRATCH + "tools/python/BUILD",
"package(default_visibility=['//visibility:public'])",
"load(':python_version.bzl', 'define_python_version_flag')",
+ "load('//tools/python:toolchain.bzl', 'py_runtime_pair')",
"define_python_version_flag(",
" name = 'python_version',",
")",
@@ -52,6 +53,12 @@
"toolchain_type(name = 'toolchain_type')",
"constraint_setting(name = 'py2_interpreter_path')",
"constraint_setting(name = 'py3_interpreter_path')",
+ "py_runtime_pair(name = 'dummy_py_runtime_pair')",
+ "toolchain(",
+ " name = 'dummy_toolchain',",
+ " toolchain = ':dummy_py_runtime_pair',",
+ " toolchain_type = ':toolchain_type',",
+ ")",
"exports_files(['precompile.py'])",
"sh_binary(name='2to3', srcs=['2to3.sh'])");
}
diff --git a/src/test/java/com/google/devtools/build/lib/rules/python/PythonToolchainTest.java b/src/test/java/com/google/devtools/build/lib/rules/python/PythonToolchainTest.java
index 273dc9b..dae74c9 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/python/PythonToolchainTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/python/PythonToolchainTest.java
@@ -104,8 +104,8 @@
" name = 'mytarget',",
")");
// Register the toolchain and ask for the platform.
- rewriteWorkspace("register_toolchains('//pkg:my_toolchain')");
- useConfiguration("--platforms=//platforms:my_platform");
+ useConfiguration(
+ "--platforms=//platforms:my_platform", "--extra_toolchains=//pkg:my_toolchain");
getConfiguredTarget("//pkg:mytarget");
assertContainsEvent("PY2 path: /system/python2");
diff --git a/tools/python/BUILD b/tools/python/BUILD
index e7b3dba..2bdfb3e 100644
--- a/tools/python/BUILD
+++ b/tools/python/BUILD
@@ -1,12 +1,5 @@
-load(":python_version.bzl", "define_python_version_flag")
-
package(default_visibility = ["//visibility:public"])
-sh_binary(
- name = "2to3",
- srcs = ["2to3.sh"],
-)
-
filegroup(
name = "srcs",
srcs = glob(["**"]) + [
@@ -29,60 +22,3 @@
],
visibility = ["//tools:__pkg__"],
)
-
-# This target can be used to inspect the current Python major version. To use,
-# put it in the `flag_values` attribute of a `config_setting` and test it
-# against the values "PY2" or "PY3". It will always match one or the other.
-#
-# If you do not need to test any other flags in combination with the Python
-# version, then as a convenience you may use the predefined `config_setting`s
-# `@bazel_tools//tools/python:PY2` and `@bazel_tools//tools/python:PY3`.
-#
-# Example usage:
-#
-# config_setting(
-# name = "py3_on_arm",
-# values = {"cpu": "arm"},
-# flag_values = {"@bazel_tools//tools/python:python_version": "PY3"},
-# )
-#
-# my_target(
-# ...
-# some_attr = select({
-# ":py3_on_arm": ...,
-# ...
-# }),
-# ...
-# )
-#
-# Caution: Do not `select()` on the built-in command-line flags `--force_python`
-# or `--python_version`, as they do not always reflect the true Python version
-# of the current target. `select()`-ing on them can lead to action conflicts and
-# will be disallowed.
-define_python_version_flag(
- name = "python_version",
-)
-
-config_setting(
- name = "PY2",
- flag_values = {":python_version": "PY2"},
- visibility = ["//visibility:public"],
-)
-
-config_setting(
- name = "PY3",
- flag_values = {":python_version": "PY3"},
- visibility = ["//visibility:public"],
-)
-
-# The toolchain type for Python rules. Provides a Python 2 and/or Python 3
-# runtime.
-toolchain_type(name = "toolchain_type")
-
-# A constraint_setting to use for constraints related to the location of the
-# system Python 2 interpreter on a platform.
-constraint_setting(name = "py2_interpreter_path")
-
-# A constraint_setting to use for constraints related to the location of the
-# system Python 3 interpreter on a platform.
-constraint_setting(name = "py3_interpreter_path")
diff --git a/tools/python/BUILD.tools b/tools/python/BUILD.tools
new file mode 100644
index 0000000..1efd754
--- /dev/null
+++ b/tools/python/BUILD.tools
@@ -0,0 +1,81 @@
+load(":python_version.bzl", "define_python_version_flag")
+load(":toolchain.bzl", "py_runtime_pair")
+
+package(default_visibility = ["//visibility:public"])
+
+sh_binary(
+ name = "2to3",
+ srcs = ["2to3.sh"],
+)
+
+# This target can be used to inspect the current Python major version. To use,
+# put it in the `flag_values` attribute of a `config_setting` and test it
+# against the values "PY2" or "PY3". It will always match one or the other.
+#
+# If you do not need to test any other flags in combination with the Python
+# version, then as a convenience you may use the predefined `config_setting`s
+# `@bazel_tools//tools/python:PY2` and `@bazel_tools//tools/python:PY3`.
+#
+# Example usage:
+#
+# config_setting(
+# name = "py3_on_arm",
+# values = {"cpu": "arm"},
+# flag_values = {"@bazel_tools//tools/python:python_version": "PY3"},
+# )
+#
+# my_target(
+# ...
+# some_attr = select({
+# ":py3_on_arm": ...,
+# ...
+# }),
+# ...
+# )
+#
+# Caution: Do not `select()` on the built-in command-line flags `--force_python`
+# or `--python_version`, as they do not always reflect the true Python version
+# of the current target. `select()`-ing on them can lead to action conflicts and
+# will be disallowed.
+define_python_version_flag(
+ name = "python_version",
+)
+
+config_setting(
+ name = "PY2",
+ flag_values = {":python_version": "PY2"},
+ visibility = ["//visibility:public"],
+)
+
+config_setting(
+ name = "PY3",
+ flag_values = {":python_version": "PY3"},
+ visibility = ["//visibility:public"],
+)
+
+# The toolchain type for Python rules. Provides a Python 2 and/or Python 3
+# runtime.
+toolchain_type(name = "toolchain_type")
+
+# A constraint_setting to use for constraints related to the location of the
+# system Python 2 interpreter on a platform.
+constraint_setting(name = "py2_interpreter_path")
+
+# A constraint_setting to use for constraints related to the location of the
+# system Python 3 interpreter on a platform.
+constraint_setting(name = "py3_interpreter_path")
+
+# A Python toolchain that, at execution time, attempts to detect a platform
+# runtime having the appropriate major Python version.
+
+py_runtime_pair(
+ name = "autodetecting_py_runtime_pair",
+ # TODO(brandjon): Not yet implemented. Currently this provides no runtimes,
+ # so it will just fail at analysis time if you attempt to use it.
+)
+
+toolchain(
+ name = "autodetecting_toolchain",
+ toolchain = ":autodetecting_py_runtime_pair",
+ toolchain_type = ":toolchain_type",
+)