diff --git a/MODULE.bazel b/MODULE.bazel index e7a09ab1c..930e175f2 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -14,7 +14,11 @@ bazel_dep(name = "bazel_skylib", version = "1.4.2") bazel_dep(name = "platforms", version = "1.0.0") bazel_dep(name = "rules_cc", version = "0.2.16") bazel_dep(name = "rules_pkg", version = "1.1.0") -bazel_dep(name = "rules_python", version = "1.9.0") + +# TODO: when bumping to 1.9.0+, replace py/private/exec_tools/defs.bzl with +# rules_python's public py_exec_tools_toolchain and PyExecToolsInfo, and switch +# EXEC_TOOLS_TOOLCHAIN back to @rules_python//python:exec_tools_toolchain_type. +bazel_dep(name = "rules_python", version = "1.0.0") bazel_dep(name = "rules_shell", version = "0.6.1") bazel_dep(name = "tar.bzl", version = "0.5.5") bazel_dep(name = "with_cfg.bzl", version = "0.14.1") @@ -190,7 +194,7 @@ use_repo(version, "bazel_features_globals", "bazel_features_version") # rules_oci and friends # rules_python and friends -bazel_dep(name = "rules_python_gazelle_plugin", version = "1.9.0", dev_dependency = IS_RELEASE) +bazel_dep(name = "rules_python_gazelle_plugin", version = "1.0.0", dev_dependency = IS_RELEASE) python = use_extension("@rules_python//python/extensions:python.bzl", "python", dev_dependency = IS_RELEASE) python.toolchain( diff --git a/e2e/MODULE.bazel b/e2e/MODULE.bazel index f974ca391..5ef04bd17 100644 --- a/e2e/MODULE.bazel +++ b/e2e/MODULE.bazel @@ -6,7 +6,7 @@ bazel_dep(name = "bazel_skylib", version = "1.4.2") bazel_dep(name = "bazel_lib", version = "3.0.0") bazel_dep(name = "rules_cc", version = "0.2.16") bazel_dep(name = "tar.bzl", version = "0.5.5") -bazel_dep(name = "rules_python", version = "1.9.0") +bazel_dep(name = "rules_python", version = "1.0.0") bazel_dep(name = "platforms", version = "1.0.0") bazel_dep(name = "toolchains_llvm_bootstrapped", version = "0.3.1") bazel_dep(name = "rules_oci", version = "2.2.7") diff --git a/py/private/exec_tools/BUILD.bazel b/py/private/exec_tools/BUILD.bazel new file mode 100644 index 000000000..e87cbb1f0 --- /dev/null +++ b/py/private/exec_tools/BUILD.bazel @@ -0,0 +1,6 @@ +load(":defs.bzl", "current_interpreter_executable") + +current_interpreter_executable( + name = "current_interpreter_executable", + visibility = ["//visibility:public"], +) diff --git a/py/private/exec_tools/defs.bzl b/py/private/exec_tools/defs.bzl new file mode 100644 index 000000000..965c6edba --- /dev/null +++ b/py/private/exec_tools/defs.bzl @@ -0,0 +1,81 @@ +# Copyright 2024 The Bazel Authors. 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. + +"""Exec-configured Python interpreter toolchain. + +Hoisted from rules_python to avoid depending on its private API and to +support rules_python >= 1.0.0 (exec_runtime was added in 1.9.0). +""" + +load("@bazel_skylib//lib:paths.bzl", "paths") +load("//py/private/toolchain:types.bzl", "PY_TOOLCHAIN") + +PyExecToolsInfo = provider( + doc = "Build tools used as part of building Python programs.", + fields = { + "exec_runtime": "PyRuntimeInfo | None: the py3_runtime from the exec interpreter.", + }, +) + +def _py_exec_tools_toolchain_impl(ctx): + exec_interpreter = ctx.attr.exec_interpreter + + exec_runtime = None + if exec_interpreter != None and platform_common.ToolchainInfo in exec_interpreter: + tc = exec_interpreter[platform_common.ToolchainInfo] + exec_runtime = getattr(tc, "py3_runtime", None) + + return [platform_common.ToolchainInfo( + exec_tools = PyExecToolsInfo( + exec_runtime = exec_runtime, + ), + )] + +py_exec_tools_toolchain = rule( + implementation = _py_exec_tools_toolchain_impl, + attrs = { + "exec_interpreter": attr.label( + default = "//py/private/exec_tools:current_interpreter_executable", + providers = [DefaultInfo, platform_common.ToolchainInfo], + cfg = "exec", + ), + }, +) + +def _current_interpreter_executable_impl(ctx): + toolchain = ctx.toolchains[PY_TOOLCHAIN] + runtime = toolchain.py3_runtime + + # Name the output after the interpreter binary so tools like pyenv that + # use $0 to re-exec work correctly. + if runtime.interpreter: + executable = ctx.actions.declare_file(runtime.interpreter.basename) + ctx.actions.symlink(output = executable, target_file = runtime.interpreter, is_executable = True) + else: + executable = ctx.actions.declare_symlink(paths.basename(runtime.interpreter_path)) + ctx.actions.symlink(output = executable, target_path = runtime.interpreter_path) + + return [ + toolchain, + DefaultInfo( + executable = executable, + runfiles = ctx.runfiles([executable], transitive_files = runtime.files), + ), + ] + +current_interpreter_executable = rule( + implementation = _current_interpreter_executable_impl, + toolchains = [PY_TOOLCHAIN], + executable = True, +) diff --git a/py/private/interpreter/repository.bzl b/py/private/interpreter/repository.bzl index 8c2494240..f070cd51b 100644 --- a/py/private/interpreter/repository.bzl +++ b/py/private/interpreter/repository.bzl @@ -113,7 +113,7 @@ def _build_file_content(major, minor, micro, python_bin, is_windows): return """\ load("@rules_python//python:py_runtime.bzl", "py_runtime") load("@rules_python//python:py_runtime_pair.bzl", "py_runtime_pair") -load("@rules_python//python/private:py_exec_tools_toolchain.bzl", "py_exec_tools_toolchain") +load("@aspect_rules_py//py/private/exec_tools:defs.bzl", "py_exec_tools_toolchain") package(default_visibility = ["//visibility:public"]) @@ -325,7 +325,7 @@ toolchain( name = "{name}_exec_tools", exec_compatible_with = {target_compatible_with}, toolchain = "@{repo}//:exec_tools_toolchain", - toolchain_type = "@rules_python//python:exec_tools_toolchain_type", + toolchain_type = "@aspect_rules_py//py/private/toolchain:exec_tools_toolchain_type", ) """.format( name = info["name"], diff --git a/py/private/toolchain/BUILD.bazel b/py/private/toolchain/BUILD.bazel index f0d036375..48a6eea65 100644 --- a/py/private/toolchain/BUILD.bazel +++ b/py/private/toolchain/BUILD.bazel @@ -36,6 +36,11 @@ toolchain_type( name = "target_exec_toolchain_type", ) +toolchain_type( + name = "exec_tools_toolchain_type", + visibility = ["//visibility:public"], +) + # This creates a toolchain instance with exec_compatible_with placement # constraints matching the target, backed by the Python toolchain already # resolved for the target. diff --git a/py/private/toolchain/types.bzl b/py/private/toolchain/types.bzl index b9092fbe3..858321038 100644 --- a/py/private/toolchain/types.bzl +++ b/py/private/toolchain/types.bzl @@ -2,6 +2,7 @@ PY_TOOLCHAIN = "@bazel_tools//tools/python:toolchain_type" SH_TOOLCHAIN = "@bazel_tools//tools/sh:toolchain_type" +EXEC_TOOLS_TOOLCHAIN = "@aspect_rules_py//py/private/toolchain:exec_tools_toolchain_type" # Toolchain type for the virtual env creation tools. SHIM_TOOLCHAIN = "@aspect_rules_py//py/private/toolchain:shim_toolchain_type" diff --git a/uv/private/whl_install/rule.bzl b/uv/private/whl_install/rule.bzl index 042886bcc..1a099e60a 100644 --- a/uv/private/whl_install/rule.bzl +++ b/uv/private/whl_install/rule.bzl @@ -2,8 +2,7 @@ """ load("@rules_python//python:defs.bzl", "PyInfo") -load("@rules_python//python/private:toolchain_types.bzl", "EXEC_TOOLS_TOOLCHAIN_TYPE") -load("//py/private/toolchain:types.bzl", "PY_TOOLCHAIN", "UNPACK_TOOLCHAIN") +load("//py/private/toolchain:types.bzl", "EXEC_TOOLS_TOOLCHAIN", "PY_TOOLCHAIN", "UNPACK_TOOLCHAIN") def _whl_install(ctx): py_toolchain = ctx.toolchains[PY_TOOLCHAIN].py3_runtime @@ -40,11 +39,11 @@ def _whl_install(ctx): transitive_inputs.append(depset(patch_files)) # Optional .pyc pre-compilation (runs after patching). - # Use the exec-configured interpreter from EXEC_TOOLS_TOOLCHAIN_TYPE so cross-arch + # Use the exec-configured interpreter from EXEC_TOOLS_TOOLCHAIN so cross-arch # builds work (the target interpreter isn't runnable on the build host). This is # safe because .pyc bytecode varies by Python version, not by architecture. if ctx.attr.compile_pyc: - exec_runtime = ctx.toolchains[EXEC_TOOLS_TOOLCHAIN_TYPE].exec_tools.exec_runtime + exec_runtime = ctx.toolchains[EXEC_TOOLS_TOOLCHAIN].exec_tools.exec_runtime arguments.add("--compile-pyc") arguments.add("--pyc-invalidation-mode", ctx.attr.pyc_invalidation_mode) arguments.add("--python", exec_runtime.interpreter) @@ -130,7 +129,7 @@ lighter weight since the toolchain's files aren't inputs. toolchains = [ PY_TOOLCHAIN, UNPACK_TOOLCHAIN, - EXEC_TOOLS_TOOLCHAIN_TYPE, + EXEC_TOOLS_TOOLCHAIN, ], provides = [ DefaultInfo,