chore: support removal of builtin providers (#2274)
The builtin providers PyInfo, PyRuntimeInfo, and PyCcLinkParamsProvider
are being removed,
which means Bazel throws an error while compiling bzl files if there is
a reference to a
top-level symbol that doesn't exist anymore. For backwards
compatibility, rules_python
consumes/produces these providers, so the symbols are used in various
places.
To fix, use `native.legacy_globals` and Bazel version detection to
conditionally emit
the symbols into `@rules_python_internal`. If they aren't present, they
are reported
as None.
This mimics equivalent functionality in bazel_features; bazel_features
isn't used because
it would require users to update their WORKSPACE to initialize some
dependencies before
rules_python can perform its initialization.
Removal of the builtin symbols is controlled by
`--incompatible_autoload_externally`
(which is in Bazel 8 and has been cherry-picked into earlier version).
If the flag is
enabled with "@rules_python" or "-@rules_python" the providers are
removed from Bazel.
---------
Co-authored-by: Richard Levasseur <rlevasseur@google.com>
diff --git a/python/config_settings/transition.bzl b/python/config_settings/transition.bzl
index 7ac41f8..a7646dc 100644
--- a/python/config_settings/transition.bzl
+++ b/python/config_settings/transition.bzl
@@ -101,12 +101,12 @@
]
if PyInfo in target:
providers.append(target[PyInfo])
- if BuiltinPyInfo in target and PyInfo != BuiltinPyInfo:
+ if BuiltinPyInfo != None and BuiltinPyInfo in target and PyInfo != BuiltinPyInfo:
providers.append(target[BuiltinPyInfo])
if PyRuntimeInfo in target:
providers.append(target[PyRuntimeInfo])
- if BuiltinPyRuntimeInfo in target and PyRuntimeInfo != BuiltinPyRuntimeInfo:
+ if BuiltinPyRuntimeInfo != None and BuiltinPyRuntimeInfo in target and PyRuntimeInfo != BuiltinPyRuntimeInfo:
providers.append(target[BuiltinPyRuntimeInfo])
return providers
diff --git a/python/private/BUILD.bazel b/python/private/BUILD.bazel
index cb0fd5c..b4084fb 100644
--- a/python/private/BUILD.bazel
+++ b/python/private/BUILD.bazel
@@ -344,7 +344,10 @@
visibility = [
"//:__subpackages__",
],
- deps = [":bazel_tools_bzl"],
+ deps = [
+ ":bazel_tools_bzl",
+ "@rules_python_internal//:rules_python_config_bzl",
+ ],
)
bzl_library(
diff --git a/python/private/common/attributes.bzl b/python/private/common/attributes.bzl
index 5e81f46..0299e85 100644
--- a/python/private/common/attributes.bzl
+++ b/python/private/common/attributes.bzl
@@ -253,6 +253,8 @@
allow_none = True,
)
+_MaybeBuiltinPyInfo = [[BuiltinPyInfo]] if BuiltinPyInfo != None else []
+
# Attributes common to rules accepting Python sources and deps.
PY_SRCS_ATTRS = union_attrs(
{
@@ -260,8 +262,7 @@
providers = [
[PyInfo],
[CcInfo],
- [BuiltinPyInfo],
- ],
+ ] + _MaybeBuiltinPyInfo,
# TODO(b/228692666): Google-specific; remove these allowances once
# the depot is cleaned up.
allow_rules = DEPS_ATTR_ALLOW_RULES,
diff --git a/python/private/common/common.bzl b/python/private/common/common.bzl
index e4cc254..99a6324 100644
--- a/python/private/common/common.bzl
+++ b/python/private/common/common.bzl
@@ -280,7 +280,7 @@
dep[BuiltinPyInfo].imports
for dep in ctx.attr.deps
if BuiltinPyInfo in dep
- ])
+ ] if BuiltinPyInfo != None else [])
def collect_runfiles(ctx, files = depset()):
"""Collects the necessary files from the rule's context.
@@ -374,7 +374,7 @@
for target in ctx.attr.deps:
# PyInfo may not be present e.g. cc_library rules.
- if PyInfo in target or BuiltinPyInfo in target:
+ if PyInfo in target or (BuiltinPyInfo != None and BuiltinPyInfo in target):
py_info.merge(_get_py_info(target))
else:
# TODO(b/228692666): Remove this once non-PyInfo targets are no
@@ -395,7 +395,7 @@
for target in ctx.attr.data:
# TODO(b/234730058): Remove checking for PyInfo in data once depot
# cleaned up.
- if PyInfo in target or BuiltinPyInfo in target:
+ if PyInfo in target or (BuiltinPyInfo != None and BuiltinPyInfo in target):
info = _get_py_info(target)
py_info.merge_uses_shared_libraries(info.uses_shared_libraries)
else:
@@ -410,7 +410,7 @@
return py_info.build(), deps_transitive_sources, py_info.build_builtin_py_info()
def _get_py_info(target):
- return target[PyInfo] if PyInfo in target else target[BuiltinPyInfo]
+ return target[PyInfo] if PyInfo in target or BuiltinPyInfo == None else target[BuiltinPyInfo]
def create_instrumented_files_info(ctx):
return _coverage_common.instrumented_files_info(
diff --git a/python/private/common/py_executable.bzl b/python/private/common/py_executable.bzl
index 1d14344..cfd9961 100644
--- a/python/private/common/py_executable.bzl
+++ b/python/private/common/py_executable.bzl
@@ -855,7 +855,7 @@
# builtin py_runtime rule or defined their own. We can't directly detect
# the type of the provider object, but the rules_python PyRuntimeInfo
# object has an extra attribute that the builtin one doesn't.
- if hasattr(py_runtime_info, "interpreter_version_info"):
+ if hasattr(py_runtime_info, "interpreter_version_info") and BuiltinPyRuntimeInfo != None:
providers.append(BuiltinPyRuntimeInfo(
interpreter_path = py_runtime_info.interpreter_path,
interpreter = py_runtime_info.interpreter,
@@ -890,7 +890,8 @@
)
providers.append(py_info)
- providers.append(builtin_py_info)
+ if builtin_py_info:
+ providers.append(builtin_py_info)
providers.append(create_output_group_info(py_info.transitive_sources, output_groups))
extra_providers = semantics.get_extra_providers(
diff --git a/python/private/common/py_library.bzl b/python/private/common/py_library.bzl
index 4423986..078626e 100644
--- a/python/private/common/py_library.bzl
+++ b/python/private/common/py_library.bzl
@@ -98,14 +98,16 @@
dependency_transitive_python_sources = deps_transitive_sources,
)
- return [
+ providers = [
DefaultInfo(files = default_outputs, runfiles = runfiles),
py_info,
- builtins_py_info,
create_instrumented_files_info(ctx),
PyCcLinkParamsProvider(cc_info = cc_info),
create_output_group_info(py_info.transitive_sources, extra_groups = {}),
]
+ if builtins_py_info:
+ providers.append(builtins_py_info)
+ return providers
_DEFAULT_PY_LIBRARY_DOC = """
A library of Python code that can be depended upon.
diff --git a/python/private/common/py_runtime_rule.bzl b/python/private/common/py_runtime_rule.bzl
index d944796..088b6ea 100644
--- a/python/private/common/py_runtime_rule.bzl
+++ b/python/private/common/py_runtime_rule.bzl
@@ -125,18 +125,20 @@
if not IS_BAZEL_7_OR_HIGHER:
builtin_py_runtime_info_kwargs.pop("bootstrap_template")
- return [
+ providers = [
PyRuntimeInfo(**py_runtime_info_kwargs),
- # Return the builtin provider for better compatibility.
- # 1. There is a legacy code path in py_binary that
- # checks for the provider when toolchains aren't used
- # 2. It makes it easier to transition from builtins to rules_python
- BuiltinPyRuntimeInfo(**builtin_py_runtime_info_kwargs),
DefaultInfo(
files = runtime_files,
runfiles = runfiles,
),
]
+ if BuiltinPyRuntimeInfo != None and BuiltinPyRuntimeInfo != PyRuntimeInfo:
+ # Return the builtin provider for better compatibility.
+ # 1. There is a legacy code path in py_binary that
+ # checks for the provider when toolchains aren't used
+ # 2. It makes it easier to transition from builtins to rules_python
+ providers.append(BuiltinPyRuntimeInfo(**builtin_py_runtime_info_kwargs))
+ return providers
# Bind to the name "py_runtime" to preserve the kind/rule_class it shows up
# as elsewhere.
diff --git a/python/private/internal_config_repo.bzl b/python/private/internal_config_repo.bzl
index c37bc35..e2fa8f6 100644
--- a/python/private/internal_config_repo.bzl
+++ b/python/private/internal_config_repo.bzl
@@ -24,6 +24,9 @@
_CONFIG_TEMPLATE = """\
config = struct(
enable_pystar = {enable_pystar},
+ BuiltinPyInfo = getattr(getattr(native, "legacy_globals", None), "PyInfo", {builtin_py_info_symbol}),
+ BuiltinPyRuntimeInfo = getattr(getattr(native, "legacy_globals", None), "PyRuntimeInfo", {builtin_py_runtime_info_symbol}),
+ BuiltinPyCcLinkParamsProvider = getattr(getattr(native, "legacy_globals", None), "PyCcLinkParamsProvider", {builtin_py_cc_link_params_provider}),
)
"""
@@ -65,8 +68,20 @@
else:
enable_pystar = False
+ if native.bazel_version.startswith("8."):
+ builtin_py_info_symbol = "None"
+ builtin_py_runtime_info_symbol = "None"
+ builtin_py_cc_link_params_provider = "None"
+ else:
+ builtin_py_info_symbol = "PyInfo"
+ builtin_py_runtime_info_symbol = "PyRuntimeInfo"
+ builtin_py_cc_link_params_provider = "PyCcLinkParamsProvider"
+
rctx.file("rules_python_config.bzl", _CONFIG_TEMPLATE.format(
enable_pystar = enable_pystar,
+ builtin_py_info_symbol = builtin_py_info_symbol,
+ builtin_py_runtime_info_symbol = builtin_py_runtime_info_symbol,
+ builtin_py_cc_link_params_provider = builtin_py_cc_link_params_provider,
))
if enable_pystar:
diff --git a/python/private/py_info.bzl b/python/private/py_info.bzl
index 97cd50b..ce56e23 100644
--- a/python/private/py_info.bzl
+++ b/python/private/py_info.bzl
@@ -118,7 +118,7 @@
)
# The "effective" PyInfo is what the canonical //python:py_info.bzl%PyInfo symbol refers to
-_EffectivePyInfo = PyInfo if config.enable_pystar else BuiltinPyInfo
+_EffectivePyInfo = PyInfo if (config.enable_pystar or BuiltinPyInfo == None) else BuiltinPyInfo
def PyInfoBuilder():
# buildifier: disable=uninitialized
@@ -206,7 +206,7 @@
def _PyInfoBuilder_merge_target(self, target):
if PyInfo in target:
self.merge(target[PyInfo])
- elif BuiltinPyInfo in target:
+ elif BuiltinPyInfo != None and BuiltinPyInfo in target:
self.merge(target[BuiltinPyInfo])
return self
@@ -234,6 +234,9 @@
)
def _PyInfoBuilder_build_builtin_py_info(self):
+ if BuiltinPyInfo == None:
+ return None
+
return BuiltinPyInfo(
has_py2_only_sources = self._has_py2_only_sources[0],
has_py3_only_sources = self._has_py3_only_sources[0],
diff --git a/python/private/py_runtime_pair_rule.bzl b/python/private/py_runtime_pair_rule.bzl
index eb91413..39f15bf 100644
--- a/python/private/py_runtime_pair_rule.bzl
+++ b/python/private/py_runtime_pair_rule.bzl
@@ -56,7 +56,7 @@
# py_binary (implemented in Java) performs a type check on the provider
# value to verify it is an instance of the Java-implemented PyRuntimeInfo
# class.
- if IS_BAZEL_7_OR_HIGHER and PyRuntimeInfo in target:
+ if (IS_BAZEL_7_OR_HIGHER and PyRuntimeInfo in target) or BuiltinPyRuntimeInfo == None:
return target[PyRuntimeInfo]
else:
return target[BuiltinPyRuntimeInfo]
@@ -70,13 +70,15 @@
return False
return ctx.fragments.py.disable_py2
+_MaybeBuiltinPyRuntimeInfo = [[BuiltinPyRuntimeInfo]] if BuiltinPyRuntimeInfo != None else []
+
py_runtime_pair = rule(
implementation = _py_runtime_pair_impl,
attrs = {
# The two runtimes are used by the py_binary at runtime, and so need to
# be built for the target platform.
"py2_runtime": attr.label(
- providers = [[PyRuntimeInfo], [BuiltinPyRuntimeInfo]],
+ providers = [[PyRuntimeInfo]] + _MaybeBuiltinPyRuntimeInfo,
cfg = "target",
doc = """\
The runtime to use for Python 2 targets. Must have `python_version` set to
@@ -84,7 +86,7 @@
""",
),
"py3_runtime": attr.label(
- providers = [[PyRuntimeInfo], [BuiltinPyRuntimeInfo]],
+ providers = [[PyRuntimeInfo]] + _MaybeBuiltinPyRuntimeInfo,
cfg = "target",
doc = """\
The runtime to use for Python 3 targets. Must have `python_version` set to
diff --git a/python/private/reexports.bzl b/python/private/reexports.bzl
index ea39ac9..e9d2ded 100644
--- a/python/private/reexports.bzl
+++ b/python/private/reexports.bzl
@@ -30,11 +30,12 @@
different name. Then we can load it from elsewhere.
"""
-# Don't use underscore prefix, since that would make the symbol local to this
-# file only. Use a non-conventional name to emphasize that this is not a public
-# symbol.
-# buildifier: disable=name-conventions
-BuiltinPyInfo = PyInfo
+load("@rules_python_internal//:rules_python_config.bzl", "config")
+# NOTE: May be None (Bazel 8 autoloading rules_python)
# buildifier: disable=name-conventions
-BuiltinPyRuntimeInfo = PyRuntimeInfo
+BuiltinPyInfo = config.BuiltinPyInfo
+
+# NOTE: May be None (Bazel 8 autoloading rules_python)
+# buildifier: disable=name-conventions
+BuiltinPyRuntimeInfo = config.BuiltinPyRuntimeInfo
diff --git a/python/py_cc_link_params_info.bzl b/python/py_cc_link_params_info.bzl
index 42d8daf..b0ad0a7 100644
--- a/python/py_cc_link_params_info.bzl
+++ b/python/py_cc_link_params_info.bzl
@@ -3,4 +3,8 @@
load("@rules_python_internal//:rules_python_config.bzl", "config")
load("//python/private/common:providers.bzl", _starlark_PyCcLinkParamsProvider = "PyCcLinkParamsProvider")
-PyCcLinkParamsInfo = _starlark_PyCcLinkParamsProvider if config.enable_pystar else PyCcLinkParamsProvider
+PyCcLinkParamsInfo = (
+ _starlark_PyCcLinkParamsProvider if (
+ config.enable_pystar or config.BuiltinPyCcLinkParamsProvider == None
+ ) else config.BuiltinPyCcLinkParamsProvider
+)
diff --git a/python/py_info.bzl b/python/py_info.bzl
index 52a66a8..5697f58 100644
--- a/python/py_info.bzl
+++ b/python/py_info.bzl
@@ -18,4 +18,4 @@
load("//python/private:py_info.bzl", _starlark_PyInfo = "PyInfo")
load("//python/private:reexports.bzl", "BuiltinPyInfo")
-PyInfo = _starlark_PyInfo if config.enable_pystar else BuiltinPyInfo
+PyInfo = _starlark_PyInfo if config.enable_pystar or BuiltinPyInfo == None else BuiltinPyInfo
diff --git a/tests/base_rules/py_executable_base_tests.bzl b/tests/base_rules/py_executable_base_tests.bzl
index 873349f..3cc6dfb 100644
--- a/tests/base_rules/py_executable_base_tests.bzl
+++ b/tests/base_rules/py_executable_base_tests.bzl
@@ -19,14 +19,13 @@
load("@rules_testing//lib:truth.bzl", "matching")
load("@rules_testing//lib:util.bzl", rt_util = "util")
load("//python:py_executable_info.bzl", "PyExecutableInfo")
+load("//python/private:reexports.bzl", "BuiltinPyRuntimeInfo") # buildifier: disable=bzl-visibility
load("//python/private:util.bzl", "IS_BAZEL_7_OR_HIGHER") # buildifier: disable=bzl-visibility
load("//tests/base_rules:base_tests.bzl", "create_base_tests")
load("//tests/base_rules:util.bzl", "WINDOWS_ATTR", pt_util = "util")
load("//tests/support:py_executable_info_subject.bzl", "PyExecutableInfoSubject")
load("//tests/support:support.bzl", "CC_TOOLCHAIN", "CROSSTOOL_TOP", "LINUX_X86_64", "WINDOWS_X86_64")
-_BuiltinPyRuntimeInfo = PyRuntimeInfo
-
_tests = []
def _test_basic_windows(name, config):
@@ -359,9 +358,10 @@
# Make sure that the rules_python loaded symbol is provided.
env.expect.that_target(target).has_provider(RulesPythonPyRuntimeInfo)
- # For compatibility during the transition, the builtin PyRuntimeInfo should
- # also be provided.
- env.expect.that_target(target).has_provider(_BuiltinPyRuntimeInfo)
+ if BuiltinPyRuntimeInfo != None:
+ # For compatibility during the transition, the builtin PyRuntimeInfo should
+ # also be provided.
+ env.expect.that_target(target).has_provider(BuiltinPyRuntimeInfo)
_tests.append(_test_py_runtime_info_provided)
diff --git a/tests/base_rules/py_info/py_info_tests.bzl b/tests/base_rules/py_info/py_info_tests.bzl
index 97c8e26..0f46d12 100644
--- a/tests/base_rules/py_info/py_info_tests.bzl
+++ b/tests/base_rules/py_info/py_info_tests.bzl
@@ -191,7 +191,8 @@
])
check(builder.build())
- check(builder.build_builtin_py_info())
+ if BuiltinPyInfo != None:
+ check(builder.build_builtin_py_info())
builder.set_has_py2_only_sources(False)
builder.set_has_py3_only_sources(False)