feat: default to bootstrap script for non-windows (#2858)
This makes non-Windows use the script bootstrap by default.
It's been a couple releases without any reported issues, so it seems
ready to become
the default.
Work towards https://github.com/bazel-contrib/rules_python/issues/2156
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7d73613..8fdb7ed 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -55,6 +55,14 @@
{#v0-0-0-changed}
### Changed
+* If using the (deprecated) autodetecting/runtime_env toolchain, then the Python
+ version specified at build-time *must* match the Python version used at
+ runtime (the {obj}`--@rules_python//python/config_settings:python_version`
+ flag and the {attr}`python_version` attribute control the build-time version
+ for a target). If they don't match, dependencies won't be importable. (Such a
+ misconfiguration was unlikely to work to begin with; this is called out as an
+ FYI).
+* (rules) {obj}`--bootstrap_impl=script` is the default for non-Windows.
* (rules) On Windows, {obj}`--bootstrap_impl=system_python` is forced. This
allows setting `--bootstrap_impl=script` in bazelrc for mixed-platform
environments.
diff --git a/MODULE.bazel b/MODULE.bazel
index c649896..d0f7cc4 100644
--- a/MODULE.bazel
+++ b/MODULE.bazel
@@ -98,7 +98,12 @@
"internal_dev_deps",
dev_dependency = True,
)
-use_repo(internal_dev_deps, "buildkite_config", "wheel_for_testing")
+use_repo(
+ internal_dev_deps,
+ "buildkite_config",
+ "rules_python_runtime_env_tc_info",
+ "wheel_for_testing",
+)
# Add gazelle plugin so that we can run the gazelle example as an e2e integration
# test and include the distribution files.
diff --git a/docs/api/rules_python/python/config_settings/index.md b/docs/api/rules_python/python/config_settings/index.md
index f4618ff..ae84d40 100644
--- a/docs/api/rules_python/python/config_settings/index.md
+++ b/docs/api/rules_python/python/config_settings/index.md
@@ -245,6 +245,10 @@
::::{bzl:flag} bootstrap_impl
Determine how programs implement their startup process.
+The default for this depends on the platform:
+* Windows: `system_python` (**always** used)
+* Other: `script`
+
Values:
* `system_python`: Use a bootstrap that requires a system Python available
in order to start programs. This requires
@@ -269,6 +273,11 @@
:::{versionadded} 0.33.0
:::
+:::{versionchanged} VERSION_NEXT_FEATURE
+* The default for non-Windows changed from `system_python` to `script`.
+* On Windows, the value is forced to `system_python`.
+:::
+
::::
::::{bzl:flag} current_config
diff --git a/internal_dev_setup.bzl b/internal_dev_setup.bzl
index fc38e3f..f339080 100644
--- a/internal_dev_setup.bzl
+++ b/internal_dev_setup.bzl
@@ -24,6 +24,7 @@
load("//:version.bzl", "SUPPORTED_BAZEL_VERSIONS")
load("//python:versions.bzl", "MINOR_MAPPING", "TOOL_VERSIONS")
load("//python/private:pythons_hub.bzl", "hub_repo") # buildifier: disable=bzl-visibility
+load("//python/private:runtime_env_repo.bzl", "runtime_env_repo") # buildifier: disable=bzl-visibility
load("//python/private/pypi:deps.bzl", "pypi_deps") # buildifier: disable=bzl-visibility
def rules_python_internal_setup():
@@ -40,6 +41,8 @@
python_versions = sorted(TOOL_VERSIONS.keys()),
)
+ runtime_env_repo(name = "rules_python_runtime_env_tc_info")
+
pypi_deps()
bazel_skylib_workspace()
diff --git a/python/config_settings/BUILD.bazel b/python/config_settings/BUILD.bazel
index 24bbe66..1772a34 100644
--- a/python/config_settings/BUILD.bazel
+++ b/python/config_settings/BUILD.bazel
@@ -90,7 +90,7 @@
rp_string_flag(
name = "bootstrap_impl",
- build_setting_default = BootstrapImplFlag.SYSTEM_PYTHON,
+ build_setting_default = BootstrapImplFlag.SCRIPT,
override = select({
# Windows doesn't yet support bootstrap=script, so force disable it
":_is_windows": BootstrapImplFlag.SYSTEM_PYTHON,
diff --git a/python/private/config_settings.bzl b/python/private/config_settings.bzl
index 2cf7968..1685195 100644
--- a/python/private/config_settings.bzl
+++ b/python/private/config_settings.bzl
@@ -225,10 +225,19 @@
)
def _python_version_at_least_impl(ctx):
- at_least = tuple(ctx.attr.at_least.split("."))
- current = tuple(
- ctx.attr._major_minor[config_common.FeatureFlagInfo].value.split("."),
- )
+ flag_value = ctx.attr._major_minor[config_common.FeatureFlagInfo].value
+
+ # CI is, somehow, getting an empty string for the current flag value.
+ # How isn't clear.
+ if not flag_value:
+ return [config_common.FeatureFlagInfo(value = "no")]
+
+ current = tuple([
+ int(x)
+ for x in flag_value.split(".")
+ ])
+ at_least = tuple([int(x) for x in ctx.attr.at_least.split(".")])
+
value = "yes" if current >= at_least else "no"
return [config_common.FeatureFlagInfo(value = value)]
diff --git a/python/private/internal_dev_deps.bzl b/python/private/internal_dev_deps.bzl
index 2a3b84e..4f2cca0 100644
--- a/python/private/internal_dev_deps.bzl
+++ b/python/private/internal_dev_deps.bzl
@@ -15,6 +15,7 @@
load("@bazel_ci_rules//:rbe_repo.bzl", "rbe_preconfig")
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_file")
+load(":runtime_env_repo.bzl", "runtime_env_repo")
def _internal_dev_deps_impl(mctx):
_ = mctx # @unused
@@ -37,6 +38,7 @@
name = "buildkite_config",
toolchain = "ubuntu1804-bazel-java11",
)
+ runtime_env_repo(name = "rules_python_runtime_env_tc_info")
internal_dev_deps = module_extension(
implementation = _internal_dev_deps_impl,
diff --git a/python/private/runtime_env_repo.bzl b/python/private/runtime_env_repo.bzl
new file mode 100644
index 0000000..cade196
--- /dev/null
+++ b/python/private/runtime_env_repo.bzl
@@ -0,0 +1,41 @@
+"""Internal setup to help the runtime_env toolchain."""
+
+load("//python/private:repo_utils.bzl", "repo_utils")
+
+def _runtime_env_repo_impl(rctx):
+ pyenv = repo_utils.which_unchecked(rctx, "pyenv").binary
+ if pyenv != None:
+ pyenv_version_file = repo_utils.execute_checked(
+ rctx,
+ op = "GetPyenvVersionFile",
+ arguments = [pyenv, "version-file"],
+ ).stdout.strip()
+
+ # When pyenv is used, the version file is what decided the
+ # version used. Watch it so we compute the correct value if the
+ # user changes it.
+ rctx.watch(pyenv_version_file)
+
+ version = repo_utils.execute_checked(
+ rctx,
+ op = "GetPythonVersion",
+ arguments = [
+ "python3",
+ "-I",
+ "-c",
+ """import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")""",
+ ],
+ environment = {
+ # Prevent the user's current shell from influencing the result.
+ # This envvar won't be present when a test is run.
+ # NOTE: This should be None, but Bazel 7 doesn't support None
+ # values. Thankfully, pyenv treats empty string the same as missing.
+ "PYENV_VERSION": "",
+ },
+ ).stdout.strip()
+ rctx.file("info.bzl", "PYTHON_VERSION = '{}'\n".format(version))
+ rctx.file("BUILD.bazel", "")
+
+runtime_env_repo = repository_rule(
+ implementation = _runtime_env_repo_impl,
+)
diff --git a/python/private/runtime_env_toolchain_interpreter.sh b/python/private/runtime_env_toolchain_interpreter.sh
index 6159d4f..7b3ec59 100755
--- a/python/private/runtime_env_toolchain_interpreter.sh
+++ b/python/private/runtime_env_toolchain_interpreter.sh
@@ -68,6 +68,9 @@
;;
esac
+ if [ ! -e "$PYTHON_BIN" ]; then
+ die "ERROR: Python interpreter does not exist: $PYTHON_BIN"
+ fi
# PYTHONEXECUTABLE is also used because `exec -a` doesn't fully trick the
# pyenv wrappers.
# NOTE: The PYTHONEXECUTABLE envvar only works for non-Mac starting in Python 3.11
diff --git a/tests/runtime_env_toolchain/BUILD.bazel b/tests/runtime_env_toolchain/BUILD.bazel
index 59ca93b..ad2bd4e 100644
--- a/tests/runtime_env_toolchain/BUILD.bazel
+++ b/tests/runtime_env_toolchain/BUILD.bazel
@@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+load("@rules_python_runtime_env_tc_info//:info.bzl", "PYTHON_VERSION")
load("//tests/support:sh_py_run_test.bzl", "py_reconfig_test")
load("//tests/support:support.bzl", "CC_TOOLCHAIN")
load(":runtime_env_toolchain_tests.bzl", "runtime_env_toolchain_test_suite")
@@ -30,6 +31,9 @@
CC_TOOLCHAIN,
],
main = "toolchain_runs_test.py",
+ # With bootstrap=script, the build version must match the runtime version
+ # because the venv has the version in the lib/site-packages dir name.
+ python_version = PYTHON_VERSION,
# Our RBE has Python 3.6, which is too old for the language features
# we use now. Using the runtime-env toolchain on RBE is pretty
# questionable anyways.