From fff00cd1ad1ad3baab41a54e841ad9cd8016bf93 Mon Sep 17 00:00:00 2001 From: Frank Portman Date: Mon, 12 Oct 2026 18:42:29 -0400 Subject: [PATCH 01/13] feat(rules): describe runfiles groups for rules_runfiles_group Give py_library, py_binary and py_test runfiles group describers, so packaging rules that use rules_runfiles_group (e.g. rules_img's image_from_binary) can split a binary's runfiles into layers: the runtime, one group per PyPI package, one per library and per rule target in data, and the binary's own files. Groups are collected on demand by a packager's aspect, so nothing runs otherwise. The runtime group comes from a singleton target resolving the same toolchain as the binary, so a packager builds it once per configuration. A binary whose pyc_collection differs from the configuration's, or that generates legacy __init__.py files, keeps its dependencies in its own group. Describers only see providers, so this adds PyInfo.direct_implicit_pyc_files and PyExecutableInfo.venv_runfiles. Fixes #3757 --- .bazelrc | 4 + MODULE.bazel | 1 + news/3757.added.md | 5 + python/private/BUILD.bazel | 48 ++++ python/private/py_executable.bzl | 22 +- python/private/py_library.bzl | 13 +- python/private/py_repositories.bzl | 6 + python/private/runfiles_groups.bzl | 256 ++++++++++++++++++ .../integration/whl_library/test_contents.py | 1 + tests/runfiles_groups/BUILD.bazel | 159 +++++++++++ tests/runfiles_groups/bin.txt | 1 + tests/runfiles_groups/data.txt | 1 + tests/runfiles_groups/fg.txt | 1 + tests/runfiles_groups/liba.py | 0 tests/runfiles_groups/libb.py | 0 tests/runfiles_groups/libc.py | 0 tests/runfiles_groups/main.py | 1 + tests/runfiles_groups/outer.py | 1 + .../foo_pkg-1.0.dist-info/METADATA | 3 + .../runfiles_groups/site-packages/foo_pkg.py | 0 tests/runfiles_groups/srcs_data.txt | 1 + tests/runfiles_groups/test_rules.bzl | 20 ++ 22 files changed, 533 insertions(+), 11 deletions(-) create mode 100644 news/3757.added.md create mode 100644 python/private/runfiles_groups.bzl create mode 100644 tests/runfiles_groups/BUILD.bazel create mode 100644 tests/runfiles_groups/bin.txt create mode 100644 tests/runfiles_groups/data.txt create mode 100644 tests/runfiles_groups/fg.txt create mode 100644 tests/runfiles_groups/liba.py create mode 100644 tests/runfiles_groups/libb.py create mode 100644 tests/runfiles_groups/libc.py create mode 100644 tests/runfiles_groups/main.py create mode 100644 tests/runfiles_groups/outer.py create mode 100644 tests/runfiles_groups/site-packages/foo_pkg-1.0.dist-info/METADATA create mode 100644 tests/runfiles_groups/site-packages/foo_pkg.py create mode 100644 tests/runfiles_groups/srcs_data.txt create mode 100644 tests/runfiles_groups/test_rules.bzl diff --git a/.bazelrc b/.bazelrc index a5b9664ce6..a1f14356c4 100644 --- a/.bazelrc +++ b/.bazelrc @@ -96,3 +96,7 @@ import %workspace%/specialized_configs.bazelrc import %workspace%/buildbuddy.bazelrc try-import user.bazelrc + +# TODO: Delete before merging. rules_runfiles_group's describer API isn't in the +# BCR yet; see https://github.com/bazelbuild/rules_java/pull/368. +common --registry=https://bazel-contrib.github.io/rules_runfiles_group --registry=https://bcr.bazel.build diff --git a/MODULE.bazel b/MODULE.bazel index a03eb49a01..e7cfa84467 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -9,6 +9,7 @@ bazel_dep(name = "bazel_skylib", version = "1.8.2") bazel_dep(name = "package_metadata", version = "0.0.7") bazel_dep(name = "platforms", version = "0.0.11") bazel_dep(name = "rules_cc", version = "0.2.17") +bazel_dep(name = "rules_runfiles_group", version = "99.0.0-dev.20261009.1.f6acddf") bazel_dep(name = "toml.bzl", version = "0.4.1") # Those are loaded only when using py_proto_library diff --git a/news/3757.added.md b/news/3757.added.md new file mode 100644 index 0000000000..d9629703ca --- /dev/null +++ b/news/3757.added.md @@ -0,0 +1,5 @@ +(rules) `py_binary` and `py_test` describe their runfiles groups for +[rules_runfiles_group](https://github.com/bazel-contrib/rules_runfiles_group), +so packaging rules like rules_img's `image_from_binary` can put the runtime, +each PyPI package and each library in a layer of its own +([#3757](https://github.com/bazel-contrib/rules_python/issues/3757)). diff --git a/python/private/BUILD.bazel b/python/private/BUILD.bazel index 075b77b05d..e5a5b9775a 100644 --- a/python/private/BUILD.bazel +++ b/python/private/BUILD.bazel @@ -20,6 +20,13 @@ load(":bazel_config_mode.bzl", "bazel_config_mode") load(":distribution_filegroup.bzl", "distribution_filegroup") load(":py_exec_tools_toolchain.bzl", "current_interpreter_executable") load(":py_interpreter_program.bzl", "py_interpreter_program") +load( + ":runfiles_groups.bzl", + "py_executable_runfiles_group_describer", + "py_library_runfiles_group_describer", + "python_runtime_runfiles", + "python_runtime_runfiles_group_describer", +) load(":sentinel_impl.bzl", "sentinel") load(":stamp_impl.bzl", "stamp_build_setting") load(":uncachable_version_file.bzl", "define_uncachable_version_file") @@ -849,6 +856,24 @@ bzl_library( ], ) +bzl_library( + name = "runfiles_groups", + srcs = ["runfiles_groups.bzl"], + deps = [ + ":attributes", + ":common", + ":flags", + ":py_executable", + ":py_executable_info", + ":py_info", + ":py_internal", + ":py_library", + ":reexports", + ":toolchain_types", + "@rules_runfiles_group//runfiles_group:lib", + ], +) + bzl_library( name = "runtime_env_repo", srcs = ["runtime_env_repo.bzl"], @@ -1044,3 +1069,26 @@ bzl_library( name = "visibility", srcs = ["visibility.bzl"], ) + +# Runfiles group describers of the Python rules (see runfiles_groups.bzl), the +# defaults of their _runfiles_group_describer attributes. Not actually public; +# only public because they're implicit dependencies of rules built via the rule +# builders. +py_library_runfiles_group_describer( + name = "py_library_runfiles_group_describer", + visibility = ["//visibility:public"], +) + +py_executable_runfiles_group_describer( + name = "py_executable_runfiles_group_describer", + visibility = ["//visibility:public"], +) + +python_runtime_runfiles_group_describer(name = "python_runtime_runfiles_group_describer") + +# The interpreter and standard library as one runfiles group, merged in by every +# py_binary and py_test. +python_runtime_runfiles( + name = "python_runtime_runfiles", + visibility = ["//visibility:public"], +) diff --git a/python/private/py_executable.bzl b/python/private/py_executable.bzl index e8240da894..04ba32f0b3 100644 --- a/python/private/py_executable.bzl +++ b/python/private/py_executable.bzl @@ -97,6 +97,16 @@ EXECUTABLE_ATTRS = dicts.add( "_default_to_explicit_init_py_flag": attr.label(default = "//python/config_settings:incompatible_default_to_explicit_init_py"), "_python_import_all_repositories_flag": attr.label(default = "//python/config_settings:experimental_python_import_all_repositories"), "_python_path_flag": attr.label(default = "//python/config_settings:python_path"), + # Read by rules_runfiles_group's packaging aspects; see runfiles_groups.bzl. + "_runfiles_group_attrs": attr.string_list(default = [ + "_debugger_flag", + "_runfiles_group_python_runtime", + "data", + "deps", + "srcs", + ]), + "_runfiles_group_describer": attr.label(default = "//python/private:py_executable_runfiles_group_describer"), + "_runfiles_group_python_runtime": attr.label(default = "//python/private:python_runtime_runfiles"), }, { "interpreter_args": lambda: attrb.StringList( @@ -1445,7 +1455,7 @@ def _get_runtime_details(ctx): fail("'python_path' must be an absolute path or a name to be resolved from the system PATH (e.g., 'python', 'python3').") effective_runtime = _maybe_get_runtime_from_ctx(ctx) - runtime_files = get_runtime_files(ctx, effective_runtime) + runtime_files = _get_runtime_files(ctx, effective_runtime) executable_interpreter_path = _get_interpreter_path( ctx, @@ -1467,16 +1477,18 @@ def _get_runtime_details(ctx): runfiles = ctx.runfiles(transitive_files = runtime_files), ) -def get_runtime_files(ctx, runtime): +def get_runtime_files(ctx): """Returns the runtime files an executable puts into its runfiles. Args: - ctx: {type}`ctx` the rule or aspect ctx. - runtime: {type}`PyRuntimeInfo` the runtime of the executable. + ctx: The rule ctx. The rule must use the target Python toolchain. Returns: - {type}`depset[File]` + depset[File] """ + return _get_runtime_files(ctx, _maybe_get_runtime_from_ctx(ctx)) + +def _get_runtime_files(ctx, runtime): direct = [] # List of files transitive = [] # List of depsets if runtime.interpreter: diff --git a/python/private/py_library.bzl b/python/private/py_library.bzl index 9b1e064853..b8e9548e85 100644 --- a/python/private/py_library.bzl +++ b/python/private/py_library.bzl @@ -116,6 +116,11 @@ This allows optimizing the generation of symlinks to be cheaper at analysis time "_add_srcs_to_runfiles_flag": lambda: attrb.Label( default = labels.ADD_SRCS_TO_RUNFILES, ), + # Read by rules_runfiles_group's packaging aspects; see runfiles_groups.bzl. + "_runfiles_group_attrs": lambda: attrb.StringList(default = ["data", "deps", "srcs"]), + "_runfiles_group_describer": lambda: attrb.Label( + default = "//python/private:py_library_runfiles_group_describer", + ), }, ) @@ -230,14 +235,10 @@ def get_package_and_version(data_files): allows us to get the name of the package and its version. Args: - data_files: {type}`list[File]` the files in the library's `data` - attribute. + data_files: list of File; the files in the library's `data` attribute. Returns: - {type}`tuple[str | None, str | None]` the distribution name, lowercased - with runs of `-`, `_` and `.` turned into `_` (e.g. `google_cloud_storage`), - and the normalized version; `(None, None)` without a - `.dist-info/METADATA` file. + tuple of the normalized package name and version, or `(None, None)`. """ dist_info_metadata = None for d in data_files: diff --git a/python/private/py_repositories.bzl b/python/private/py_repositories.bzl index 3489613a6c..45b883047b 100644 --- a/python/private/py_repositories.bzl +++ b/python/private/py_repositories.bzl @@ -72,6 +72,12 @@ def py_repositories(transition_settings = []): strip_prefix = "rules_cc-0.1.5", urls = ["https://github.com/bazelbuild/rules_cc/releases/download/0.1.5/rules_cc-0.1.5.tar.gz"], ) + http_archive( + name = "rules_runfiles_group", + sha256 = "bf618b9b065dc5ef7fba23e4e6b1a6d48c375a36810cba842660caa8bd18267a", + strip_prefix = "rules_runfiles_group-f6acddf050e3a0703a62ad272ff637fca26cd91c", + url = "https://github.com/bazel-contrib/rules_runfiles_group/archive/f6acddf050e3a0703a62ad272ff637fca26cd91c.tar.gz", + ) http_archive( name = "toml.bzl", sha256 = "63633c762bdb4d836add5a9a81deeeae93c4b2edbd62ac032733020b5652b90a", diff --git a/python/private/runfiles_groups.bzl b/python/private/runfiles_groups.bzl new file mode 100644 index 0000000000..61d68a984b --- /dev/null +++ b/python/private/runfiles_groups.bzl @@ -0,0 +1,256 @@ +# Copyright 2026 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. +"""Runfiles group describers of the Python rules, for rules_runfiles_group. + +A packaging rule (a container image rule, say) walks a binary with an aspect and +asks each target's describer which runfiles it adds and which attributes it +merges runfiles in from. The Python rules point their `_runfiles_group_describer` +attribute at the describers below. + +Only `//python/private:BUILD.bazel` loads this file; the rules refer to the +describers by label. With Bazel 8's autoloads, rules_python loading +rules_runfiles_group from the files that define the rules is a load cycle. + +A library's group holds what it contributes to a consuming binary: the files the +binary takes from its PyInfo, plus its plain data files. +""" + +load("@rules_runfiles_group//runfiles_group:lib.bzl", "runfiles_groups") +load(":attributes.bzl", "PycCollectionAttr") +load(":common.bzl", "is_py_source", "py_info_runfiles_files") +load(":flags.bzl", "PyiDepsToRunfilesFlag") +load(":py_executable.bzl", "get_runtime_files") +load(":py_executable_info.bzl", "PyExecutableInfo") +load(":py_info.bzl", "PyInfo") +load(":py_internal.bzl", "py_internal") +load(":py_library.bzl", "get_package_and_version") +load(":reexports.bzl", "BuiltinPyInfo") +load(":toolchain_types.bzl", TOOLCHAIN_TYPE = "TARGET_TOOLCHAIN_TYPE") + +# Stamped on every group, so Python groups stay together when a packager has to +# merge groups to fit a layer limit. +_MERGE_AFFINITY = "rules_python" + +# The interpreter and standard library, shared by every binary in a configuration. +_RUNTIME_GROUP = "rules_python#runtime" + +# Libraries from PyPI are named by package: whl_library's repository names are an +# implementation detail. +_PYPI_GROUP_PREFIX = "rules_python#pypi/" + +def _pyc_collection_enabled(ctx, pyc_collection = PycCollectionAttr.INHERIT): + # PycCollectionAttr only reads these two attributes. + return PycCollectionAttr.is_pyc_collection_enabled(struct(attr = struct( + pyc_collection = pyc_collection, + _precompile_flag = ctx.rule.attr._precompile_flag, + ))) + +def _is_file(target): + # A source file owns itself, a generated file is owned by the rule that + # generates it, and a rule target owns its own outputs. + files = target[DefaultInfo].files + if not py_internal.is_singleton_depset(files): + return False + file = files.to_list()[0] + return (file.owner == target.label) == file.is_source + +def _is_empty(runfiles): + return not (runfiles.files or runfiles.symlinks or runfiles.root_symlinks) + +def _py_info_files(target, pyc_collection_enabled): + if PyInfo in target: + return py_info_runfiles_files(target[PyInfo], pyc_collection_enabled) + if BuiltinPyInfo != None and BuiltinPyInfo in target: + return py_info_runfiles_files(target[BuiltinPyInfo], pyc_collection_enabled) + return [] + +def _kind(label): + return "first_party" if label.repo_name == "" else "third_party" + +def _own_files(target, ctx, pyc_collection_enabled): + """The files a target itself puts into a consuming binary's runfiles.""" + info = target[PyInfo] + + # Sources that aren't .py files (directories, .pyc files) are outputs, but + # never runfiles. + sources = {f: None for f in info.direct_original_sources.to_list()} + direct = [ + f + for f in target[DefaultInfo].files.to_list() + if f not in sources or is_py_source(f) + ] + transitive = [] + if pyc_collection_enabled: + transitive.append(info.direct_implicit_pyc_files) + else: + direct.extend([f for f in sources if is_py_source(f)]) + + # Plain files in `data` belong to the target's own group (and so to each + # target listing them); rule targets get groups of their own. + for dep in ctx.rule.attr.data: + if _is_file(dep): + direct.append(dep[DefaultInfo].files.to_list()[0]) + return depset(direct, transitive = transitive) + +def _dep_fallback(ctx, dep, py_outputs): + content = _py_info_files(dep, _pyc_collection_enabled(ctx)) + if py_outputs and not content: + content.append(depset([f for f in dep[DefaultInfo].files.to_list() if is_py_source(f)])) + runfiles = dep[DefaultInfo].default_runfiles + if not _is_empty(runfiles): + content.append(runfiles) + if not content: + return None + return runfiles_groups.entry(name = dep.label, content = runfiles_groups.union(ctx, content)) + +def _library_dep_fallback(ctx, dep): + # py_library adds the .py outputs of a dep without PyInfo to its own PyInfo. + return _dep_fallback(ctx, dep, py_outputs = True) + +def _executable_dep_fallback(ctx, dep): + return _dep_fallback(ctx, dep, py_outputs = False) + +def _data_fallback(ctx, dep): + # Plain files are in the merging target's own group. + default_info = dep[DefaultInfo] + if _is_file(dep) or (not default_info.files and _is_empty(default_info.data_runfiles)): + return None + content = runfiles_groups.union(ctx, [default_info.files, default_info.data_runfiles]) + return runfiles_groups.entry(name = dep.label, content = content) + +def _srcs_fallback(_ctx, dep): + runfiles = dep[DefaultInfo].default_runfiles + if _is_file(dep) or _is_empty(runfiles): + return None + return runfiles_groups.entry(name = dep.label, content = runfiles) + +def _describe_py_library(target, ctx): + pyc_collection_enabled = _pyc_collection_enabled(ctx) + content = _own_files(target, ctx, pyc_collection_enabled) + + # With pyi_deps_to_runfiles, a binary takes the files of pyi_deps from + # this target's PyInfo, but not their runfiles. + if PyiDepsToRunfilesFlag.is_enabled(struct(attr = ctx.rule.attr, var = ctx.var)): + content = depset(transitive = [content] + [ + files + for dep in ctx.rule.attr.pyi_deps + for files in _py_info_files(dep, pyc_collection_enabled) + ]) + + package, _ = get_package_and_version(ctx.rule.files.data) + if package: + name = _PYPI_GROUP_PREFIX + package + kind = "third_party" + else: + name = ctx.label + kind = _kind(ctx.label) + return runfiles_groups.node( + add = [runfiles_groups.entry( + name = name, + content = content, + kind = kind, + rank = runfiles_groups.RANK_SHARED_DEPS if kind == "third_party" else runfiles_groups.RANK_EXECUTABLE, + weight = len(ctx.rule.files.srcs) + len(ctx.rule.files.data), + merge_affinity = _MERGE_AFFINITY, + )] if content else [], + merge_from = [ + runfiles_groups.merge_from("data", fallback = _data_fallback), + runfiles_groups.merge_from("deps", fallback = _library_dep_fallback), + runfiles_groups.merge_from("srcs", fallback = _srcs_fallback), + ], + ) + +py_library_runfiles_group_describer = runfiles_groups.make_describer_rule( + describe = _describe_py_library, +) + +def _describe_py_executable(target, ctx): + info = target[PyExecutableInfo] + pyc_collection_enabled = _pyc_collection_enabled(ctx, ctx.rule.attr.pyc_collection) + content = [ + _own_files(target, ctx, pyc_collection_enabled), + depset([f for f in (info.build_data_file, info.stage2_bootstrap) if f]), + ] + content.extend([rf for rf in (info.venv_runfiles, info.venv_interpreter_runfiles) if rf]) + + # Two cases where the binary's group takes its dependencies: + # * The legacy __init__.py files: they're generated for every directory in + # the runfiles, the runtime's included. Checking for them is cheap unless + # they exist. + # * A pyc selection that differs from the configuration's, which is the one + # dependencies describe their files with. + if info.app_runfiles.empty_filenames: + content.append(info.runfiles_without_exe) + merge_from = [] + elif pyc_collection_enabled != _pyc_collection_enabled(ctx): + content.append(info.app_runfiles) + merge_from = ["_runfiles_group_python_runtime"] + else: + merge_from = [ + "_runfiles_group_python_runtime", + runfiles_groups.merge_from("_debugger_flag", fallback = _executable_dep_fallback), + runfiles_groups.merge_from("data", fallback = _data_fallback), + runfiles_groups.merge_from("deps", fallback = _executable_dep_fallback), + runfiles_groups.merge_from("srcs", fallback = _srcs_fallback), + ] + + return runfiles_groups.node( + add = [runfiles_groups.entry( + name = ctx.label, + content = runfiles_groups.union(ctx, content), + kind = _kind(ctx.label), + merge_affinity = _MERGE_AFFINITY, + )], + merge_from = merge_from, + executable_group = ctx.label, + ) + +py_executable_runfiles_group_describer = runfiles_groups.make_describer_rule( + describe = _describe_py_executable, +) + +def _describe_python_runtime(target, _ctx): + files = target[DefaultInfo].files + return runfiles_groups.node(add = [runfiles_groups.entry( + name = _RUNTIME_GROUP, + content = files, + kind = "foundation", + rank = runfiles_groups.RANK_FOUNDATION, + do_not_merge = True, + merge_affinity = _MERGE_AFFINITY, + )] if files else []) + +python_runtime_runfiles_group_describer = runfiles_groups.make_describer_rule( + describe = _describe_python_runtime, +) + +def _python_runtime_runfiles_impl(ctx): + return [DefaultInfo(files = get_runtime_files(ctx))] + +python_runtime_runfiles = rule( + implementation = _python_runtime_runfiles_impl, + doc = """\ +The runtime files a binary puts into its runfiles, as one runfiles group. + +Every binary merges in the same target, so a packager builds the group once per +configuration rather than once per binary. +""", + attrs = { + "_runfiles_group_attrs": attr.string_list(default = []), + "_runfiles_group_describer": attr.label( + default = ":python_runtime_runfiles_group_describer", + ), + }, + toolchains = [TOOLCHAIN_TYPE], +) diff --git a/tests/integration/whl_library/test_contents.py b/tests/integration/whl_library/test_contents.py index 58f13354e3..587cb48ed1 100644 --- a/tests/integration/whl_library/test_contents.py +++ b/tests/integration/whl_library/test_contents.py @@ -171,6 +171,7 @@ def test_whl_deps_ar_the_same(self): "@rules_python//python/config_settings:precompile_source_retention", "@rules_python//python/config_settings:pyi_deps_to_runfiles", "@rules_python//python/config_settings:venvs_site_packages", + "@rules_python//python/private:py_library_runfiles_group_describer", "@rules_python//python/private:sentinel", }, ) diff --git a/tests/runfiles_groups/BUILD.bazel b/tests/runfiles_groups/BUILD.bazel new file mode 100644 index 0000000000..fd27d891ac --- /dev/null +++ b/tests/runfiles_groups/BUILD.bazel @@ -0,0 +1,159 @@ +load("@rules_runfiles_group//runfiles_group:runfiles_group_analysis_test.bzl", "runfiles_group_analysis_test") +load("//python:py_binary.bzl", "py_binary") +load("//python:py_library.bzl", "py_library") +load("//python:py_test.bzl", "py_test") +load("//tests/support:py_reconfig.bzl", "py_reconfig_binary") +load(":test_rules.bzl", "outputs_and_runfiles", "outputs_only") + +outputs_only(name = "outputs_only") + +outputs_and_runfiles(name = "outputs_and_runfiles") + +genrule( + name = "generated", + outs = ["generated.txt"], + cmd = "touch $@", +) + +genrule( + name = "generated_file", + outs = ["generated_file.txt"], + cmd = "touch $@", +) + +filegroup( + name = "files", + srcs = ["fg.txt"], +) + +py_library( + name = "lib_a", + srcs = ["liba.py"], + data = [ + "data.txt", + "generated_file.txt", + ":outputs_only", + ], +) + +filegroup( + name = "srcs_with_runfiles", + srcs = ["libc.py"], + data = ["srcs_data.txt"], +) + +py_library( + name = "lib_b", + srcs = [ + "libb.py", + ":srcs_with_runfiles", + ], + data = [":outputs_and_runfiles"], + deps = [":lib_a"], +) + +# Looks like a library from a wheel: grouped by package name. +py_library( + name = "foo_pkg", + srcs = ["site-packages/foo_pkg.py"], + data = ["site-packages/foo_pkg-1.0.dist-info/METADATA"], + imports = ["site-packages"], +) + +_DATA = [ + ":files", + ":generated", + ":outputs_and_runfiles", + ":outputs_only", + "bin.txt", +] + +py_binary( + name = "bin", + srcs = ["main.py"], + data = _DATA, + main = "main.py", + deps = [ + ":foo_pkg", + ":lib_b", + ], +) + +py_test( + name = "test", + srcs = ["main.py"], + data = _DATA, + main = "main.py", + tags = ["manual"], + deps = [":lib_b"], +) + +py_binary( + name = "bin_in_data", + srcs = ["outer.py"], + data = [":bin"], + main = "outer.py", + deps = [":lib_a"], +) + +# Libraries describe their implicit pyc files. +py_reconfig_binary( + name = "bin_precompiled", + srcs = ["main.py"], + config_settings = { + "//python/config_settings:precompile": "enabled", + }, + main = "main.py", + deps = [":lib_b"], +) + +# Falls back to one group for itself and its deps, because its pyc selection +# differs from what its deps describe. +py_binary( + name = "bin_pyc_collection", + srcs = ["main.py"], + main = "main.py", + pyc_collection = "include_pyc", + deps = [":lib_b"], +) + +# Falls back to one group for itself and its deps, because the generated +# __init__.py files only exist in its own runfiles. +py_binary( + name = "bin_legacy_init", + srcs = ["main.py"], + legacy_create_init = 1, + main = "main.py", + deps = [":lib_b"], +) + +runfiles_group_analysis_test( + name = "runfiles_group_test", + binaries = [ + ":bin", + ":bin_in_data", + ":bin_legacy_init", + ":bin_precompiled", + ":bin_pyc_collection", + ":test", + ], + overlapping_group_behavior = "error", +) + +runfiles_group_analysis_test( + name = "bin_groups_test", + binaries = [":bin"], + expected_executable_group = "@@//tests/runfiles_groups:bin", + expected_group_names = [ + "rules_python#runtime", + "rules_python#pypi/foo_pkg", + "@@//tests/runfiles_groups:files", + "@@//tests/runfiles_groups:generated", + "@@//tests/runfiles_groups:lib_a", + "@@//tests/runfiles_groups:lib_b", + "@@//tests/runfiles_groups:outputs_and_runfiles", + "@@//tests/runfiles_groups:outputs_only", + "@@//tests/runfiles_groups:srcs_with_runfiles", + "@@//tests/runfiles_groups:bin", + ], +) diff --git a/tests/runfiles_groups/bin.txt b/tests/runfiles_groups/bin.txt new file mode 100644 index 0000000000..ba077a4031 --- /dev/null +++ b/tests/runfiles_groups/bin.txt @@ -0,0 +1 @@ +bin diff --git a/tests/runfiles_groups/data.txt b/tests/runfiles_groups/data.txt new file mode 100644 index 0000000000..1269488f7f --- /dev/null +++ b/tests/runfiles_groups/data.txt @@ -0,0 +1 @@ +data diff --git a/tests/runfiles_groups/fg.txt b/tests/runfiles_groups/fg.txt new file mode 100644 index 0000000000..0da04c7866 --- /dev/null +++ b/tests/runfiles_groups/fg.txt @@ -0,0 +1 @@ +fg diff --git a/tests/runfiles_groups/liba.py b/tests/runfiles_groups/liba.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/runfiles_groups/libb.py b/tests/runfiles_groups/libb.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/runfiles_groups/libc.py b/tests/runfiles_groups/libc.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/runfiles_groups/main.py b/tests/runfiles_groups/main.py new file mode 100644 index 0000000000..11b15b1a45 --- /dev/null +++ b/tests/runfiles_groups/main.py @@ -0,0 +1 @@ +print("hello") diff --git a/tests/runfiles_groups/outer.py b/tests/runfiles_groups/outer.py new file mode 100644 index 0000000000..11b15b1a45 --- /dev/null +++ b/tests/runfiles_groups/outer.py @@ -0,0 +1 @@ +print("hello") diff --git a/tests/runfiles_groups/site-packages/foo_pkg-1.0.dist-info/METADATA b/tests/runfiles_groups/site-packages/foo_pkg-1.0.dist-info/METADATA new file mode 100644 index 0000000000..a7a47b1539 --- /dev/null +++ b/tests/runfiles_groups/site-packages/foo_pkg-1.0.dist-info/METADATA @@ -0,0 +1,3 @@ +Metadata-Version: 2.1 +Name: foo-pkg +Version: 1.0 diff --git a/tests/runfiles_groups/site-packages/foo_pkg.py b/tests/runfiles_groups/site-packages/foo_pkg.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/runfiles_groups/srcs_data.txt b/tests/runfiles_groups/srcs_data.txt new file mode 100644 index 0000000000..587be6b4c3 --- /dev/null +++ b/tests/runfiles_groups/srcs_data.txt @@ -0,0 +1 @@ +x diff --git a/tests/runfiles_groups/test_rules.bzl b/tests/runfiles_groups/test_rules.bzl new file mode 100644 index 0000000000..f5e55046f4 --- /dev/null +++ b/tests/runfiles_groups/test_rules.bzl @@ -0,0 +1,20 @@ +"""Rules with shapes of `data` that the runfiles group describers must cover.""" + +def _outputs_only_impl(ctx): + files = [ctx.actions.declare_file(ctx.label.name + suffix) for suffix in (".a", ".b")] + for f in files: + ctx.actions.write(f, "") + return [DefaultInfo(files = depset(files))] + +# Default outputs, no runfiles. +outputs_only = rule(implementation = _outputs_only_impl) + +def _outputs_and_runfiles_impl(ctx): + output = ctx.actions.declare_file(ctx.label.name + ".out") + runfile = ctx.actions.declare_file(ctx.label.name + ".runfile") + ctx.actions.write(output, "") + ctx.actions.write(runfile, "") + return [DefaultInfo(files = depset([output]), runfiles = ctx.runfiles([runfile]))] + +# Default outputs and runfiles that differ. +outputs_and_runfiles = rule(implementation = _outputs_and_runfiles_impl) From 546a1d213bb30b550e71a013330f0ca963d889b4 Mon Sep 17 00:00:00 2001 From: Frank Portman Date: Mon, 19 Oct 2026 09:40:54 -0400 Subject: [PATCH 02/13] feat(rules): move runfiles groups to the toolchain-walking describer API Port to rules_runfiles_group's current API: the describers are held by _aspect_rule_hints, and the runtime group is merged in from the Python toolchain, described by py_runtime_pair. This drops the singleton python_runtime_runfiles target. Also fix two issues found testing against a real image: * A Python target in srcs (whl_library's :pkg wraps its :srcs) describes its own files, so the wrapper no longer claims them too. * With venvs_site_packages=yes, the venv's links to a wheel's files (shared libraries) go into that wheel's group instead of the binary's, so packagers keep them next to the files they point to. --- MODULE.bazel | 2 +- news/3757.added.md | 4 +- python/private/BUILD.bazel | 19 +-- python/private/py_executable.bzl | 23 ++- python/private/py_library.bzl | 14 +- python/private/py_repositories.bzl | 6 +- python/private/py_runtime_pair_rule.bzl | 5 + python/private/runfiles_groups.bzl | 138 ++++++++++++------ tests/runfiles_groups/BUILD.bazel | 33 ++++- .../site-packages/_foo_native.so | 0 10 files changed, 159 insertions(+), 85 deletions(-) create mode 100644 tests/runfiles_groups/site-packages/_foo_native.so diff --git a/MODULE.bazel b/MODULE.bazel index e7cfa84467..ddc27bd378 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -9,7 +9,7 @@ bazel_dep(name = "bazel_skylib", version = "1.8.2") bazel_dep(name = "package_metadata", version = "0.0.7") bazel_dep(name = "platforms", version = "0.0.11") bazel_dep(name = "rules_cc", version = "0.2.17") -bazel_dep(name = "rules_runfiles_group", version = "99.0.0-dev.20261009.1.f6acddf") +bazel_dep(name = "rules_runfiles_group", version = "99.0.0-dev.20261016.1.41d2544") bazel_dep(name = "toml.bzl", version = "0.4.1") # Those are loaded only when using py_proto_library diff --git a/news/3757.added.md b/news/3757.added.md index d9629703ca..dc8758af62 100644 --- a/news/3757.added.md +++ b/news/3757.added.md @@ -1,5 +1,5 @@ (rules) `py_binary` and `py_test` describe their runfiles groups for [rules_runfiles_group](https://github.com/bazel-contrib/rules_runfiles_group), so packaging rules like rules_img's `image_from_binary` can put the runtime, -each PyPI package and each library in a layer of its own -([#3757](https://github.com/bazel-contrib/rules_python/issues/3757)). +each PyPI package and each library in a layer of its own. Needs Bazel 8.8 or +later ([#3757](https://github.com/bazel-contrib/rules_python/issues/3757)). diff --git a/python/private/BUILD.bazel b/python/private/BUILD.bazel index e5a5b9775a..c80f2b5d6c 100644 --- a/python/private/BUILD.bazel +++ b/python/private/BUILD.bazel @@ -24,8 +24,7 @@ load( ":runfiles_groups.bzl", "py_executable_runfiles_group_describer", "py_library_runfiles_group_describer", - "python_runtime_runfiles", - "python_runtime_runfiles_group_describer", + "py_runtime_pair_runfiles_group_describer", ) load(":sentinel_impl.bzl", "sentinel") load(":stamp_impl.bzl", "stamp_build_setting") @@ -869,7 +868,6 @@ bzl_library( ":py_internal", ":py_library", ":reexports", - ":toolchain_types", "@rules_runfiles_group//runfiles_group:lib", ], ) @@ -1070,10 +1068,9 @@ bzl_library( srcs = ["visibility.bzl"], ) -# Runfiles group describers of the Python rules (see runfiles_groups.bzl), the -# defaults of their _runfiles_group_describer attributes. Not actually public; -# only public because they're implicit dependencies of rules built via the rule -# builders. +# Runfiles group describers of the Python rules (see runfiles_groups.bzl), held +# by their _aspect_rule_hints attributes. Not actually public; only public +# because they're implicit dependencies of rules built via the rule builders. py_library_runfiles_group_describer( name = "py_library_runfiles_group_describer", visibility = ["//visibility:public"], @@ -1084,11 +1081,7 @@ py_executable_runfiles_group_describer( visibility = ["//visibility:public"], ) -python_runtime_runfiles_group_describer(name = "python_runtime_runfiles_group_describer") - -# The interpreter and standard library as one runfiles group, merged in by every -# py_binary and py_test. -python_runtime_runfiles( - name = "python_runtime_runfiles", +py_runtime_pair_runfiles_group_describer( + name = "py_runtime_pair_runfiles_group_describer", visibility = ["//visibility:public"], ) diff --git a/python/private/py_executable.bzl b/python/private/py_executable.bzl index 04ba32f0b3..b2f8a4fa6d 100644 --- a/python/private/py_executable.bzl +++ b/python/private/py_executable.bzl @@ -98,15 +98,12 @@ EXECUTABLE_ATTRS = dicts.add( "_python_import_all_repositories_flag": attr.label(default = "//python/config_settings:experimental_python_import_all_repositories"), "_python_path_flag": attr.label(default = "//python/config_settings:python_path"), # Read by rules_runfiles_group's packaging aspects; see runfiles_groups.bzl. - "_runfiles_group_attrs": attr.string_list(default = [ - "_debugger_flag", - "_runfiles_group_python_runtime", - "data", - "deps", - "srcs", + "_aspect_rule_hints": attr.label_list(default = [ + "//python/private:py_executable_runfiles_group_describer", + # What //python:toolchain_type is an alias of. + "@bazel_tools//tools/python:toolchain_type", ]), - "_runfiles_group_describer": attr.label(default = "//python/private:py_executable_runfiles_group_describer"), - "_runfiles_group_python_runtime": attr.label(default = "//python/private:python_runtime_runfiles"), + "_runfiles_group_attrs": attr.string_list(default = ["_debugger_flag", "data", "deps", "srcs"]), }, { "interpreter_args": lambda: attrb.StringList( @@ -1455,7 +1452,7 @@ def _get_runtime_details(ctx): fail("'python_path' must be an absolute path or a name to be resolved from the system PATH (e.g., 'python', 'python3').") effective_runtime = _maybe_get_runtime_from_ctx(ctx) - runtime_files = _get_runtime_files(ctx, effective_runtime) + runtime_files = get_runtime_files(ctx, effective_runtime) executable_interpreter_path = _get_interpreter_path( ctx, @@ -1477,18 +1474,16 @@ def _get_runtime_details(ctx): runfiles = ctx.runfiles(transitive_files = runtime_files), ) -def get_runtime_files(ctx): +def get_runtime_files(ctx, runtime): """Returns the runtime files an executable puts into its runfiles. Args: - ctx: The rule ctx. The rule must use the target Python toolchain. + ctx: The rule or aspect ctx. + runtime: PyRuntimeInfo; the runtime of the executable. Returns: depset[File] """ - return _get_runtime_files(ctx, _maybe_get_runtime_from_ctx(ctx)) - -def _get_runtime_files(ctx, runtime): direct = [] # List of files transitive = [] # List of depsets if runtime.interpreter: diff --git a/python/private/py_library.bzl b/python/private/py_library.bzl index b8e9548e85..b3368f5960 100644 --- a/python/private/py_library.bzl +++ b/python/private/py_library.bzl @@ -117,10 +117,10 @@ This allows optimizing the generation of symlinks to be cheaper at analysis time default = labels.ADD_SRCS_TO_RUNFILES, ), # Read by rules_runfiles_group's packaging aspects; see runfiles_groups.bzl. - "_runfiles_group_attrs": lambda: attrb.StringList(default = ["data", "deps", "srcs"]), - "_runfiles_group_describer": lambda: attrb.Label( - default = "//python/private:py_library_runfiles_group_describer", + "_aspect_rule_hints": lambda: attrb.LabelList( + default = ["//python/private:py_library_runfiles_group_describer"], ), + "_runfiles_group_attrs": lambda: attrb.StringList(default = ["data", "deps", "srcs"]), }, ) @@ -235,10 +235,14 @@ def get_package_and_version(data_files): allows us to get the name of the package and its version. Args: - data_files: list of File; the files in the library's `data` attribute. + data_files: {type}`list[File]` the files in the library's `data` + attribute. Returns: - tuple of the normalized package name and version, or `(None, None)`. + {type}`tuple[str | None, str | None]` the distribution name, lowercased + with runs of `-`, `_` and `.` turned into `_` (e.g. `google_cloud_storage`), + and the normalized version; `(None, None)` without a + `.dist-info/METADATA` file. """ dist_info_metadata = None for d in data_files: diff --git a/python/private/py_repositories.bzl b/python/private/py_repositories.bzl index 45b883047b..6b8148e406 100644 --- a/python/private/py_repositories.bzl +++ b/python/private/py_repositories.bzl @@ -74,9 +74,9 @@ def py_repositories(transition_settings = []): ) http_archive( name = "rules_runfiles_group", - sha256 = "bf618b9b065dc5ef7fba23e4e6b1a6d48c375a36810cba842660caa8bd18267a", - strip_prefix = "rules_runfiles_group-f6acddf050e3a0703a62ad272ff637fca26cd91c", - url = "https://github.com/bazel-contrib/rules_runfiles_group/archive/f6acddf050e3a0703a62ad272ff637fca26cd91c.tar.gz", + sha256 = "2da2befdf87d85eafe8b77a9aa0dca0a700cf7ba929866f6c1b505e3389fbaf8", + strip_prefix = "rules_runfiles_group-41d2544bab5c1eee0117c9e8a992631be8e0d9d1", + url = "https://github.com/bazel-contrib/rules_runfiles_group/archive/41d2544bab5c1eee0117c9e8a992631be8e0d9d1.tar.gz", ) http_archive( name = "toml.bzl", diff --git a/python/private/py_runtime_pair_rule.bzl b/python/private/py_runtime_pair_rule.bzl index c339e0bd67..a648bbdca9 100644 --- a/python/private/py_runtime_pair_rule.bzl +++ b/python/private/py_runtime_pair_rule.bzl @@ -136,6 +136,11 @@ The runtime to use for Python 3 targets. Must have `python_version` set to "_visible_for_testing": attr.label( default = labels.VISIBLE_FOR_TESTING, ), + # Read by rules_runfiles_group's packaging aspects; see runfiles_groups.bzl. + "_aspect_rule_hints": attr.label_list( + default = ["//python/private:py_runtime_pair_runfiles_group_describer"], + ), + "_runfiles_group_attrs": attr.string_list(default = []), }, fragments = ["py"], doc = """ diff --git a/python/private/runfiles_groups.bzl b/python/private/runfiles_groups.bzl index 61d68a984b..bcca0f183c 100644 --- a/python/private/runfiles_groups.bzl +++ b/python/private/runfiles_groups.bzl @@ -15,8 +15,9 @@ A packaging rule (a container image rule, say) walks a binary with an aspect and asks each target's describer which runfiles it adds and which attributes it -merges runfiles in from. The Python rules point their `_runfiles_group_describer` -attribute at the describers below. +merges runfiles in from. The Python rules list the describers below in their +`_aspect_rule_hints` attribute. The runtime comes from the Python toolchain, +which the aspect only walks on Bazel 8.8 or later. Only `//python/private:BUILD.bazel` loads this file; the rules refer to the describers by label. With Bazel 8's autoloads, rules_python loading @@ -36,7 +37,6 @@ load(":py_info.bzl", "PyInfo") load(":py_internal.bzl", "py_internal") load(":py_library.bzl", "get_package_and_version") load(":reexports.bzl", "BuiltinPyInfo") -load(":toolchain_types.bzl", TOOLCHAIN_TYPE = "TARGET_TOOLCHAIN_TYPE") # Stamped on every group, so Python groups stay together when a packager has to # merge groups to fit a layer limit. @@ -49,6 +49,12 @@ _RUNTIME_GROUP = "rules_python#runtime" # implementation detail. _PYPI_GROUP_PREFIX = "rules_python#pypi/" +# The toolchain type //python:toolchain_type is an alias of; the aspect matches +# toolchains by the actual label. +_RUNTIME_MERGE_FROM = runfiles_groups.merge_from( + toolchain = Label("@bazel_tools//tools/python:toolchain_type"), +) + def _pyc_collection_enabled(ctx, pyc_collection = PycCollectionAttr.INHERIT): # PycCollectionAttr only reads these two attributes. return PycCollectionAttr.is_pyc_collection_enabled(struct(attr = struct( @@ -68,6 +74,9 @@ def _is_file(target): def _is_empty(runfiles): return not (runfiles.files or runfiles.symlinks or runfiles.root_symlinks) +def _has_py_info(target): + return PyInfo in target or (BuiltinPyInfo != None and BuiltinPyInfo in target) + def _py_info_files(target, pyc_collection_enabled): if PyInfo in target: return py_info_runfiles_files(target[PyInfo], pyc_collection_enabled) @@ -78,17 +87,36 @@ def _py_info_files(target, pyc_collection_enabled): def _kind(label): return "first_party" if label.repo_name == "" else "third_party" +def _pypi_entry(package, content, weight = None): + return runfiles_groups.entry( + name = _PYPI_GROUP_PREFIX + package, + content = content, + kind = "third_party", + rank = runfiles_groups.RANK_SHARED_DEPS, + weight = weight, + merge_affinity = _MERGE_AFFINITY, + ) + def _own_files(target, ctx, pyc_collection_enabled): """The files a target itself puts into a consuming binary's runfiles.""" info = target[PyInfo] + # A Python target in srcs (a wheel's :pkg wraps its :srcs) describes its + # own files. + foreign = { + f: None + for dep in ctx.rule.attr.srcs + if _has_py_info(dep) + for f in dep[DefaultInfo].files.to_list() + } + # Sources that aren't .py files (directories, .pyc files) are outputs, but # never runfiles. - sources = {f: None for f in info.direct_original_sources.to_list()} + sources = {f: None for f in info.direct_original_sources.to_list() if f not in foreign} direct = [ f for f in target[DefaultInfo].files.to_list() - if f not in sources or is_py_source(f) + if f not in foreign and (f not in sources or is_py_source(f)) ] transitive = [] if pyc_collection_enabled: @@ -129,11 +157,11 @@ def _data_fallback(ctx, dep): content = runfiles_groups.union(ctx, [default_info.files, default_info.data_runfiles]) return runfiles_groups.entry(name = dep.label, content = content) -def _srcs_fallback(_ctx, dep): - runfiles = dep[DefaultInfo].default_runfiles - if _is_file(dep) or _is_empty(runfiles): +def _srcs_fallback(ctx, dep): + # Plain files are in the merging target's own group. + if _is_file(dep): return None - return runfiles_groups.entry(name = dep.label, content = runfiles) + return _dep_fallback(ctx, dep, py_outputs = False) def _describe_py_library(target, ctx): pyc_collection_enabled = _pyc_collection_enabled(ctx) @@ -148,22 +176,22 @@ def _describe_py_library(target, ctx): for files in _py_info_files(dep, pyc_collection_enabled) ]) + weight = len(ctx.rule.files.srcs) + len(ctx.rule.files.data) package, _ = get_package_and_version(ctx.rule.files.data) if package: - name = _PYPI_GROUP_PREFIX + package - kind = "third_party" + entry = _pypi_entry(package, content, weight) else: - name = ctx.label kind = _kind(ctx.label) - return runfiles_groups.node( - add = [runfiles_groups.entry( - name = name, + entry = runfiles_groups.entry( + name = ctx.label, content = content, kind = kind, rank = runfiles_groups.RANK_SHARED_DEPS if kind == "third_party" else runfiles_groups.RANK_EXECUTABLE, - weight = len(ctx.rule.files.srcs) + len(ctx.rule.files.data), + weight = weight, merge_affinity = _MERGE_AFFINITY, - )] if content else [], + ) + return runfiles_groups.node( + add = [entry] if content else [], merge_from = [ runfiles_groups.merge_from("data", fallback = _data_fallback), runfiles_groups.merge_from("deps", fallback = _library_dep_fallback), @@ -175,6 +203,41 @@ py_library_runfiles_group_describer = runfiles_groups.make_describer_rule( describe = _describe_py_library, ) +def _venv_entries(ctx, venv_runfiles): + """Splits the venv's links to PyPI package files into those packages' groups. + + A packager that copies symlinked files can then keep each link with the + file it points to, instead of copying shared libraries into the binary's + group. + + Args: + ctx: The aspect ctx of the binary. + venv_runfiles: runfiles; PyExecutableInfo.venv_runfiles. + + Returns: + tuple of the PyPI entries and the venv runfiles left for the binary. + """ + packages = { + entry.link_to_file: entry.package + for entry in depset(transitive = [ + dep[PyInfo].venv_symlinks + for dep in ctx.rule.attr.deps + if PyInfo in dep + ]).to_list() + if entry.link_to_file and entry.package + } + by_package = {} + own_links = {} + for link in venv_runfiles.root_symlinks.to_list(): + package = packages.get(link.target_file) + links = by_package.setdefault(package, {}) if package else own_links + links[link.path] = link.target_file + entries = [ + _pypi_entry(package, ctx.runfiles(root_symlinks = links)) + for package, links in by_package.items() + ] + return entries, ctx.runfiles(transitive_files = venv_runfiles.files, root_symlinks = own_links) + def _describe_py_executable(target, ctx): info = target[PyExecutableInfo] pyc_collection_enabled = _pyc_collection_enabled(ctx, ctx.rule.attr.pyc_collection) @@ -182,7 +245,8 @@ def _describe_py_executable(target, ctx): _own_files(target, ctx, pyc_collection_enabled), depset([f for f in (info.build_data_file, info.stage2_bootstrap) if f]), ] - content.extend([rf for rf in (info.venv_runfiles, info.venv_interpreter_runfiles) if rf]) + venv_runfiles = info.venv_runfiles + add = [] # Two cases where the binary's group takes its dependencies: # * The legacy __init__.py files: they're generated for every directory in @@ -195,15 +259,18 @@ def _describe_py_executable(target, ctx): merge_from = [] elif pyc_collection_enabled != _pyc_collection_enabled(ctx): content.append(info.app_runfiles) - merge_from = ["_runfiles_group_python_runtime"] + merge_from = [_RUNTIME_MERGE_FROM] else: + if venv_runfiles: + add, venv_runfiles = _venv_entries(ctx, venv_runfiles) merge_from = [ - "_runfiles_group_python_runtime", + _RUNTIME_MERGE_FROM, runfiles_groups.merge_from("_debugger_flag", fallback = _executable_dep_fallback), runfiles_groups.merge_from("data", fallback = _data_fallback), runfiles_groups.merge_from("deps", fallback = _executable_dep_fallback), runfiles_groups.merge_from("srcs", fallback = _srcs_fallback), ] + content.extend([rf for rf in (venv_runfiles, info.venv_interpreter_runfiles) if rf]) return runfiles_groups.node( add = [runfiles_groups.entry( @@ -211,7 +278,7 @@ def _describe_py_executable(target, ctx): content = runfiles_groups.union(ctx, content), kind = _kind(ctx.label), merge_affinity = _MERGE_AFFINITY, - )], + )] + add, merge_from = merge_from, executable_group = ctx.label, ) @@ -220,8 +287,9 @@ py_executable_runfiles_group_describer = runfiles_groups.make_describer_rule( describe = _describe_py_executable, ) -def _describe_python_runtime(target, _ctx): - files = target[DefaultInfo].files +def _describe_py_runtime_pair(target, ctx): + runtime = target[platform_common.ToolchainInfo].py3_runtime + files = get_runtime_files(ctx, runtime) if runtime else depset() return runfiles_groups.node(add = [runfiles_groups.entry( name = _RUNTIME_GROUP, content = files, @@ -231,26 +299,6 @@ def _describe_python_runtime(target, _ctx): merge_affinity = _MERGE_AFFINITY, )] if files else []) -python_runtime_runfiles_group_describer = runfiles_groups.make_describer_rule( - describe = _describe_python_runtime, -) - -def _python_runtime_runfiles_impl(ctx): - return [DefaultInfo(files = get_runtime_files(ctx))] - -python_runtime_runfiles = rule( - implementation = _python_runtime_runfiles_impl, - doc = """\ -The runtime files a binary puts into its runfiles, as one runfiles group. - -Every binary merges in the same target, so a packager builds the group once per -configuration rather than once per binary. -""", - attrs = { - "_runfiles_group_attrs": attr.string_list(default = []), - "_runfiles_group_describer": attr.label( - default = ":python_runtime_runfiles_group_describer", - ), - }, - toolchains = [TOOLCHAIN_TYPE], +py_runtime_pair_runfiles_group_describer = runfiles_groups.make_describer_rule( + describe = _describe_py_runtime_pair, ) diff --git a/tests/runfiles_groups/BUILD.bazel b/tests/runfiles_groups/BUILD.bazel index fd27d891ac..f1d7a04d35 100644 --- a/tests/runfiles_groups/BUILD.bazel +++ b/tests/runfiles_groups/BUILD.bazel @@ -56,10 +56,22 @@ py_library( py_library( name = "foo_pkg", srcs = ["site-packages/foo_pkg.py"], - data = ["site-packages/foo_pkg-1.0.dist-info/METADATA"], + data = [ + "site-packages/_foo_native.so", + "site-packages/foo_pkg-1.0.dist-info/METADATA", + ], + experimental_venvs_site_packages = "//python/config_settings:venvs_site_packages", imports = ["site-packages"], ) +# Wraps foo_pkg like whl_library's :pkg wraps its :srcs; adds no group. +py_library( + name = "foo_pkg_wrapper", + srcs = [":foo_pkg"], + precompile = "disabled", + deps = [":foo_pkg"], +) + _DATA = [ ":files", ":generated", @@ -74,7 +86,7 @@ py_binary( data = _DATA, main = "main.py", deps = [ - ":foo_pkg", + ":foo_pkg_wrapper", ":lib_b", ], ) @@ -107,6 +119,22 @@ py_reconfig_binary( deps = [":lib_b"], ) +# The venv's links to foo_pkg's files go into foo_pkg's group. +py_binary( + name = "bin_venv", + srcs = ["main.py"], + config_settings = { + "//python/config_settings:venvs_site_packages": "yes", + } | select({ + "@platforms//os:windows": {}, + "//conditions:default": { + "//python/config_settings:bootstrap_impl": "script", + }, + }), + main = "main.py", + deps = [":foo_pkg_wrapper"], +) + # Falls back to one group for itself and its deps, because its pyc selection # differs from what its deps describe. py_binary( @@ -135,6 +163,7 @@ runfiles_group_analysis_test( ":bin_legacy_init", ":bin_precompiled", ":bin_pyc_collection", + ":bin_venv", ":test", ], overlapping_group_behavior = "error", diff --git a/tests/runfiles_groups/site-packages/_foo_native.so b/tests/runfiles_groups/site-packages/_foo_native.so new file mode 100644 index 0000000000..e69de29bb2 From cbc11339e27d20300d1137c4390897bdf5fd1719 Mon Sep 17 00:00:00 2001 From: Frank Portman Date: Mon, 19 Oct 2026 09:40:54 -0400 Subject: [PATCH 03/13] DO NOT MERGE: use the rules_runfiles_group dev registry in nested workspaces Revert once rules_runfiles_group is released. --- examples/bzlmod/.bazelrc | 3 + examples/multi_python_versions/.bazelrc | 3 + examples/pip_parse/.bazelrc | 3 + gazelle/.bazelrc | 3 + .../bzlmod_build_file_generation/.bazelrc | 3 + sphinxdocs/.bazelrc | 3 + .../persistent_worker/workspace/.bazelrc | 3 + tests/integration/bzlmod_lockfile/.bazelrc | 3 + .../bzlmod_lockfile/MODULE.bazel.lock | 277 ++++++++++-------- .../compile_pip_requirements/.bazelrc | 3 + .../.bazelrc | 3 + tests/integration/custom_commands/.bazelrc | 2 + tests/integration/local_toolchains/.bazelrc | 3 + tests/integration/pip_parse/.bazelrc | 3 + tests/integration/pip_parse_isolated/.bazelrc | 3 + .../py_cc_toolchain_registered/.bazelrc | 3 + tests/integration/runtime_manifests/.bazelrc | 3 + .../toolchain_target_settings/.bazelrc | 3 + tests/integration/unified_pypi/.bazelrc | 3 + tests/integration/uv_lock/.bazelrc | 3 + tests/integration/validate_test_main/.bazelrc | 2 + tests/integration/whl_library/.bazelrc | 3 + 22 files changed, 217 insertions(+), 121 deletions(-) create mode 100644 tests/integration/custom_commands/.bazelrc create mode 100644 tests/integration/validate_test_main/.bazelrc diff --git a/examples/bzlmod/.bazelrc b/examples/bzlmod/.bazelrc index 28a44a7523..aaeaa7befe 100644 --- a/examples/bzlmod/.bazelrc +++ b/examples/bzlmod/.bazelrc @@ -24,3 +24,6 @@ test --test_output=errors --enable_runfiles # Windows requires these for multi-python support: build --enable_runfiles common:bazel7.x --incompatible_python_disallow_native_rules + +# TODO: Delete before merging; see the root .bazelrc. +common --registry=https://bazel-contrib.github.io/rules_runfiles_group --registry=https://bcr.bazel.build diff --git a/examples/multi_python_versions/.bazelrc b/examples/multi_python_versions/.bazelrc index 97a973bd85..e019fc2592 100644 --- a/examples/multi_python_versions/.bazelrc +++ b/examples/multi_python_versions/.bazelrc @@ -5,3 +5,6 @@ build --enable_runfiles coverage --java_runtime_version=remotejdk_11 common:bazel7.x --incompatible_python_disallow_native_rules + +# TODO: Delete before merging; see the root .bazelrc. +common --registry=https://bazel-contrib.github.io/rules_runfiles_group --registry=https://bcr.bazel.build diff --git a/examples/pip_parse/.bazelrc b/examples/pip_parse/.bazelrc index f263a1744d..4796c06451 100644 --- a/examples/pip_parse/.bazelrc +++ b/examples/pip_parse/.bazelrc @@ -1,3 +1,6 @@ # https://docs.bazel.build/versions/main/best-practices.html#using-the-bazelrc-file try-import %workspace%/user.bazelrc common --incompatible_python_disallow_native_rules + +# TODO: Delete before merging; see the root .bazelrc. +common --registry=https://bazel-contrib.github.io/rules_runfiles_group --registry=https://bcr.bazel.build diff --git a/gazelle/.bazelrc b/gazelle/.bazelrc index c098d0c392..eee906dd3d 100644 --- a/gazelle/.bazelrc +++ b/gazelle/.bazelrc @@ -27,3 +27,6 @@ build --enable_runfiles common --noincompatible_no_implicit_file_export common:bazel7.x --incompatible_python_disallow_native_rules + +# TODO: Delete before merging; see the root .bazelrc. +common --registry=https://bazel-contrib.github.io/rules_runfiles_group --registry=https://bcr.bazel.build diff --git a/gazelle/examples/bzlmod_build_file_generation/.bazelrc b/gazelle/examples/bzlmod_build_file_generation/.bazelrc index 31097b41de..4192d2cbfa 100644 --- a/gazelle/examples/bzlmod_build_file_generation/.bazelrc +++ b/gazelle/examples/bzlmod_build_file_generation/.bazelrc @@ -14,3 +14,6 @@ common:bazel7.x --incompatible_python_disallow_native_rules # rules_python code. In the BCR presubmits, this override is removed # and the bazel_dep version of rules_python is used. common --override_module=rules_python=../../../ + +# TODO: Delete before merging; see the root .bazelrc. +common --registry=https://bazel-contrib.github.io/rules_runfiles_group --registry=https://bcr.bazel.build diff --git a/sphinxdocs/.bazelrc b/sphinxdocs/.bazelrc index 6989c2656a..094382a169 100644 --- a/sphinxdocs/.bazelrc +++ b/sphinxdocs/.bazelrc @@ -35,3 +35,6 @@ common:fast-tests --test_tag_filters=-large,-enormous,-integration-test build --config=pyrefly build:pyrefly --aspects=//tests/support/pyrefly:pyrefly.bzl%pyrefly_aspect build:pyrefly --output_groups=+pyrefly + +# TODO: Delete before merging; see the root .bazelrc. +common --registry=https://bazel-contrib.github.io/rules_runfiles_group --registry=https://bcr.bazel.build diff --git a/sphinxdocs/integration_tests/persistent_worker/workspace/.bazelrc b/sphinxdocs/integration_tests/persistent_worker/workspace/.bazelrc index f3162b9547..d0ac68f7cb 100644 --- a/sphinxdocs/integration_tests/persistent_worker/workspace/.bazelrc +++ b/sphinxdocs/integration_tests/persistent_worker/workspace/.bazelrc @@ -6,3 +6,6 @@ common --http_timeout_scaling=10.0 common --experimental_repository_downloader_retries=10 common --experimental_downloader_config=downloader_config.cfg common --lockfile_mode=off + +# TODO: Delete before merging; see the root .bazelrc. +common --registry=https://bazel-contrib.github.io/rules_runfiles_group --registry=https://bcr.bazel.build diff --git a/tests/integration/bzlmod_lockfile/.bazelrc b/tests/integration/bzlmod_lockfile/.bazelrc index 3687a11fae..1c624e47cc 100644 --- a/tests/integration/bzlmod_lockfile/.bazelrc +++ b/tests/integration/bzlmod_lockfile/.bazelrc @@ -7,3 +7,6 @@ common --experimental_isolated_extension_usages # https://docs.bazel.build/versions/main/best-practices.html#using-the-bazelrc-file try-import %workspace%/user.bazelrc + +# TODO: Delete before merging; see the root .bazelrc. +common --registry=https://bazel-contrib.github.io/rules_runfiles_group --registry=https://bcr.bazel.build diff --git a/tests/integration/bzlmod_lockfile/MODULE.bazel.lock b/tests/integration/bzlmod_lockfile/MODULE.bazel.lock index f86bf246f1..8d37f1ef55 100644 --- a/tests/integration/bzlmod_lockfile/MODULE.bazel.lock +++ b/tests/integration/bzlmod_lockfile/MODULE.bazel.lock @@ -1,6 +1,149 @@ { "lockFileVersion": 26, "registryFileHashes": { + "https://bazel-contrib.github.io/rules_runfiles_group/bazel_registry.json": "ca3d163bab055381827226140568f3bef7eaac187cebd76878e0b63e9e442356", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/abseil-cpp/20210324.2/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/abseil-cpp/20211102.0/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/abseil-cpp/20230125.1/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/abseil-cpp/20230802.0.bcr.1/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/abseil-cpp/20230802.0/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/abseil-cpp/20230802.1/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/abseil-cpp/20240116.1/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/abseil-cpp/20240116.2/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/abseil-cpp/20250127.1/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/abseil-cpp/20250512.1/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/abseil-cpp/20250814.1/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/apple_support/1.11.1/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/apple_support/1.15.1/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/apple_support/1.21.0/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/apple_support/1.21.1/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/apple_support/1.24.2/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/bazel_features/1.10.0/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/bazel_features/1.11.0/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/bazel_features/1.15.0/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/bazel_features/1.17.0/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/bazel_features/1.18.0/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/bazel_features/1.19.0/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/bazel_features/1.21.0/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/bazel_features/1.23.0/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/bazel_features/1.27.0/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/bazel_features/1.28.0/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/bazel_features/1.3.0/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/bazel_features/1.30.0/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/bazel_features/1.33.0/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/bazel_features/1.4.1/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/bazel_features/1.42.1/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/bazel_features/1.51.0/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/bazel_skylib/1.0.3/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/bazel_skylib/1.1.1/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/bazel_skylib/1.2.0/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/bazel_skylib/1.2.1/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/bazel_skylib/1.3.0/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/bazel_skylib/1.4.1/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/bazel_skylib/1.4.2/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/bazel_skylib/1.5.0/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/bazel_skylib/1.6.1/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/bazel_skylib/1.7.0/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/bazel_skylib/1.7.1/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/bazel_skylib/1.8.1/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/bazel_skylib/1.8.2/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/buildozer/8.5.1/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/google_benchmark/1.8.2/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/googletest/1.11.0/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/googletest/1.14.0.bcr.1/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/googletest/1.14.0/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/googletest/1.15.2/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/googletest/1.17.0/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/jsoncpp/1.9.5/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/jsoncpp/1.9.6/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/libpfm/4.11.0/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/nlohmann_json/3.6.1/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/package_metadata/0.0.7/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/platforms/0.0.10/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/platforms/0.0.11/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/platforms/0.0.4/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/platforms/0.0.5/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/platforms/0.0.6/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/platforms/0.0.7/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/platforms/0.0.8/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/platforms/0.0.9/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/platforms/1.0.0/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/protobuf/21.7/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/protobuf/27.0/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/protobuf/29.0-rc2/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/protobuf/29.1/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/protobuf/3.19.0/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/protobuf/32.1/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/protobuf/33.4/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/pybind11_bazel/2.11.1/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/pybind11_bazel/2.12.0/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/re2/2023-09-01/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/re2/2024-07-02.bcr.1/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/re2/2024-07-02/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_android/0.1.1/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_apple/3.16.0/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_apple/4.1.0/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_cc/0.0.1/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_cc/0.0.10/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_cc/0.0.13/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_cc/0.0.15/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_cc/0.0.16/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_cc/0.0.17/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_cc/0.0.2/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_cc/0.0.6/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_cc/0.0.8/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_cc/0.0.9/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_cc/0.1.1/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_cc/0.1.2/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_cc/0.2.0/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_cc/0.2.13/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_cc/0.2.17/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_cc/0.2.8/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_foreign_cc/0.9.0/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_fuzzing/0.5.2/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_java/4.0.0/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_java/5.3.5/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_java/6.5.2/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_java/7.10.0/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_java/7.12.2/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_java/7.2.0/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_java/7.6.1/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_java/8.6.1/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_java/9.1.0/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_jvm_external/4.4.2/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_jvm_external/5.1/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_jvm_external/5.2/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_jvm_external/6.3/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_jvm_external/6.7/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_kotlin/1.9.6/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_license/0.0.3/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_license/0.0.7/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_license/1.0.0/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_pkg/0.7.0/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_pkg/1.0.1/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_proto/4.0.0/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_proto/5.3.0-21.7/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_proto/6.0.2/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_proto/7.1.0/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_runfiles_group/99.0.0-dev.20261016.1.41d2544/MODULE.bazel": "e35f5260f613c7cd7b29a398cd34191db239ec138ba357dbfe81c3f2ce19de70", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_runfiles_group/99.0.0-dev.20261016.1.41d2544/source.json": "801750c105fd41f3ca2818e93a6cbaa55b8236ea505394adfe3596e1bfc44967", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_shell/0.2.0/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_shell/0.3.0/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_shell/0.6.1/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_swift/1.16.0/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_swift/2.1.1/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_swift/2.4.0/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_swift/3.1.2/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/stardoc/0.5.1/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/stardoc/0.5.3/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/stardoc/0.7.0/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/swift_argument_parser/1.3.1.1/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/swift_argument_parser/1.3.1.2/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/toml.bzl/0.4.1/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/upb/0.0.0-20220923-a547704/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/zlib/1.2.11/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/zlib/1.3.1.bcr.5/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/zlib/1.3.1/MODULE.bazel": "not found", "https://bcr.bazel.build/bazel_registry.json": "8a28e4aff06ee60aed2a8c281907fb8bcbf3b753c91fb5a5c57da3215d5b3497", "https://bcr.bazel.build/modules/abseil-cpp/20210324.2/MODULE.bazel": "7cd0312e064fde87c8d1cd79ba06c876bd23630c83466e9500321be55c96ace2", "https://bcr.bazel.build/modules/abseil-cpp/20211102.0/MODULE.bazel": "70390338f7a5106231d20620712f7cccb659cd0e9d073d1991c038eb9fc57589", @@ -18,7 +161,6 @@ "https://bcr.bazel.build/modules/apple_support/1.15.1/MODULE.bazel": "a0556fefca0b1bb2de8567b8827518f94db6a6e7e7d632b4c48dc5f865bc7c85", "https://bcr.bazel.build/modules/apple_support/1.21.0/MODULE.bazel": "ac1824ed5edf17dee2fdd4927ada30c9f8c3b520be1b5fd02a5da15bc10bff3e", "https://bcr.bazel.build/modules/apple_support/1.21.1/MODULE.bazel": "5809fa3efab15d1f3c3c635af6974044bac8a4919c62238cce06acee8a8c11f1", - "https://bcr.bazel.build/modules/apple_support/1.24.1/MODULE.bazel": "f46e8ddad60aef170ee92b2f3d00ef66c147ceafea68b6877cb45bd91737f5f8", "https://bcr.bazel.build/modules/apple_support/1.24.2/MODULE.bazel": "0e62471818affb9f0b26f128831d5c40b074d32e6dda5a0d3852847215a41ca4", "https://bcr.bazel.build/modules/apple_support/1.24.2/source.json": "2c22c9827093250406c5568da6c54e6fdf0ef06238def3d99c71b12feb057a8d", "https://bcr.bazel.build/modules/bazel_features/1.10.0/MODULE.bazel": "f75e8807570484a99be90abcd52b5e1f390362c258bcb73106f4544957a48101", @@ -36,8 +178,8 @@ "https://bcr.bazel.build/modules/bazel_features/1.33.0/MODULE.bazel": "8b8dc9d2a4c88609409c3191165bccec0e4cb044cd7a72ccbe826583303459f6", "https://bcr.bazel.build/modules/bazel_features/1.4.1/MODULE.bazel": "e45b6bb2350aff3e442ae1111c555e27eac1d915e77775f6fdc4b351b758b5d7", "https://bcr.bazel.build/modules/bazel_features/1.42.1/MODULE.bazel": "275a59b5406ff18c01739860aa70ad7ccb3cfb474579411decca11c93b951080", - "https://bcr.bazel.build/modules/bazel_features/1.50.0/MODULE.bazel": "2083ef9c7a469f520890483ccf8e0189d6e71e2117e7752e15e6554433d5ae3e", - "https://bcr.bazel.build/modules/bazel_features/1.50.0/source.json": "e0ee3debde2789ff56e4452e612d126925ba9ab64d4bde79c67f099d2902df9b", + "https://bcr.bazel.build/modules/bazel_features/1.51.0/MODULE.bazel": "8e1310d09db6ee2e4f19f9994e360aca941ddc32083edc8d9bdc19c83c94cee4", + "https://bcr.bazel.build/modules/bazel_features/1.51.0/source.json": "d5af1f1748d2b4ace5f04087030c0a1cfd93f5be6bdf0b5c1beb24ed0fd24955", "https://bcr.bazel.build/modules/bazel_skylib/1.0.3/MODULE.bazel": "bcb0fd896384802d1ad283b4e4eb4d718eebd8cb820b0a2c3a347fb971afd9d8", "https://bcr.bazel.build/modules/bazel_skylib/1.1.1/MODULE.bazel": "1add3e7d93ff2e6998f9e118022c84d163917d912f5afafb3058e3d2f1545b5e", "https://bcr.bazel.build/modules/bazel_skylib/1.2.0/MODULE.bazel": "44fe84260e454ed94ad326352a698422dbe372b21a1ac9f3eab76eb531223686", @@ -67,7 +209,6 @@ "https://bcr.bazel.build/modules/libpfm/4.11.0/MODULE.bazel": "45061ff025b301940f1e30d2c16bea596c25b176c8b6b3087e92615adbd52902", "https://bcr.bazel.build/modules/nlohmann_json/3.6.1/MODULE.bazel": "6f7b417dcc794d9add9e556673ad25cb3ba835224290f4f848f8e2db1e1fca74", "https://bcr.bazel.build/modules/nlohmann_json/3.6.1/source.json": "f448c6e8963fdfa7eb831457df83ad63d3d6355018f6574fb017e8169deb43a9", - "https://bcr.bazel.build/modules/package_metadata/0.0.3/MODULE.bazel": "77890552ecea9e284b5424c9de827a58099348763a4359e975c359a83d4faa83", "https://bcr.bazel.build/modules/package_metadata/0.0.7/MODULE.bazel": "7adb03933fc8401f495800cf4eafcff0edc6da0ff55c7db223ef69d19f689486", "https://bcr.bazel.build/modules/package_metadata/0.0.7/source.json": "50639625e937b56115012674c797cca7a05a96b4878c87d803c13dc2b31de8a0", "https://bcr.bazel.build/modules/platforms/0.0.10/MODULE.bazel": "8cb8efaf200bdeb2150d93e162c40f388529a25852b332cec879373771e48ed5", @@ -79,8 +220,7 @@ "https://bcr.bazel.build/modules/platforms/0.0.8/MODULE.bazel": "9f142c03e348f6d263719f5074b21ef3adf0b139ee4c5133e2aa35664da9eb2d", "https://bcr.bazel.build/modules/platforms/0.0.9/MODULE.bazel": "4a87a60c927b56ddd67db50c89acaa62f4ce2a1d2149ccb63ffd871d5ce29ebc", "https://bcr.bazel.build/modules/platforms/1.0.0/MODULE.bazel": "f05feb42b48f1b3c225e4ccf351f367be0371411a803198ec34a389fb22aa580", - "https://bcr.bazel.build/modules/platforms/1.1.0/MODULE.bazel": "1c0c09f5bdcf4b3f924720d2478a3711cb39f4977019ca5988685e5b7e18b3d2", - "https://bcr.bazel.build/modules/platforms/1.1.0/source.json": "fcf351c47596c939140ab0d333dfdd08ed1ea6ce33c2fe70c12493a301cf1344", + "https://bcr.bazel.build/modules/platforms/1.0.0/source.json": "f4ff1fd412e0246fd38c82328eb209130ead81d62dcd5a9e40910f867f733d96", "https://bcr.bazel.build/modules/protobuf/21.7/MODULE.bazel": "a5a29bb89544f9b97edce05642fac225a808b5b7be74038ea3640fae2f8e66a7", "https://bcr.bazel.build/modules/protobuf/27.0/MODULE.bazel": "7873b60be88844a0a1d8f80b9d5d20cfbd8495a689b8763e76c6372998d3f64c", "https://bcr.bazel.build/modules/protobuf/29.0-rc2/MODULE.bazel": "6241d35983510143049943fc0d57937937122baf1b287862f9dc8590fc4c37df", @@ -117,7 +257,6 @@ "https://bcr.bazel.build/modules/rules_cc/0.2.13/MODULE.bazel": "eecdd666eda6be16a8d9dc15e44b5c75133405e820f620a234acc4b1fdc5aa37", "https://bcr.bazel.build/modules/rules_cc/0.2.17/MODULE.bazel": "1849602c86cb60da8613d2de887f9566a6d354a6df6d7009f9d04a14402f9a84", "https://bcr.bazel.build/modules/rules_cc/0.2.17/source.json": "3832f45d145354049137c0090df04629d9c2b5493dc5c2bf46f1834040133a07", - "https://bcr.bazel.build/modules/rules_cc/0.2.4/MODULE.bazel": "1ff1223dfd24f3ecf8f028446d4a27608aa43c3f41e346d22838a4223980b8cc", "https://bcr.bazel.build/modules/rules_cc/0.2.8/MODULE.bazel": "f1df20f0bf22c28192a794f29b501ee2018fa37a3862a1a2132ae2940a23a642", "https://bcr.bazel.build/modules/rules_foreign_cc/0.9.0/MODULE.bazel": "c9e8c682bf75b0e7c704166d79b599f93b72cfca5ad7477df596947891feeef6", "https://bcr.bazel.build/modules/rules_fuzzing/0.5.2/MODULE.bazel": "40c97d1144356f52905566c55811f13b299453a14ac7769dfba2ac38192337a8", @@ -151,8 +290,6 @@ "https://bcr.bazel.build/modules/rules_proto/6.0.2/MODULE.bazel": "ce916b775a62b90b61888052a416ccdda405212b6aaeb39522f7dc53431a5e73", "https://bcr.bazel.build/modules/rules_proto/7.1.0/MODULE.bazel": "002d62d9108f75bb807cd56245d45648f38275cb3a99dcd45dfb864c5d74cb96", "https://bcr.bazel.build/modules/rules_proto/7.1.0/source.json": "39f89066c12c24097854e8f57ab8558929f9c8d474d34b2c00ac04630ad8940e", - "https://bcr.bazel.build/modules/rules_rust/0.73.0/MODULE.bazel": "25e3b077128612754c4add1b4c90d20a6be06566b623dee6e32038d0e8f93062", - "https://bcr.bazel.build/modules/rules_rust/0.73.0/source.json": "8eeb3d9ba7c57916b63887a651e8f84c2f68b7243af9e712d728c2a0b7882255", "https://bcr.bazel.build/modules/rules_shell/0.2.0/MODULE.bazel": "fda8a652ab3c7d8fee214de05e7a9916d8b28082234e8d2c0094505c5268ed3c", "https://bcr.bazel.build/modules/rules_shell/0.3.0/MODULE.bazel": "de4402cd12f4cc8fda2354fce179fdb068c0b9ca1ec2d2b17b3e21b24c1a937b", "https://bcr.bazel.build/modules/rules_shell/0.6.1/MODULE.bazel": "72e76b0eea4e81611ef5452aa82b3da34caca0c8b7b5c0c9584338aa93bae26b", @@ -259,8 +396,8 @@ }, "@@rules_python+//python/uv:uv.bzl%uv": { "general": { - "bzlTransitiveDigest": "a11TI1DxtsKzJJnFDIAhAoVNhub4iqECM+h0qTqOT68=", - "usagesDigest": "UgEeJTkNLce2eABymcbLBebdHpF0D8qrT6A6PSESotA=", + "bzlTransitiveDigest": "mkSJjS3IKYKgnN+NNEXte2ylbT3HbgQHO3Wb0v1mHPU=", + "usagesDigest": "4oeBVCEzXt49RrVnpRaNDbydAxQ9Yw+5jX6Xm82siGM=", "recordedInputs": [ "REPO_MAPPING:rules_python+,bazel_tools bazel_tools", "REPO_MAPPING:rules_python+,platforms platforms" @@ -286,108 +423,6 @@ } } } - }, - "@@rules_rust+//crate_universe/private:internal_extensions.bzl%cu_nr": { - "general": { - "bzlTransitiveDigest": "C59iXZ0yL27grwDeD63yBucdmChxaRa5m/NvLxalsyQ=", - "usagesDigest": "+1w4EnEfZLFpf8Ak18jhmCWsOTE75Noy2i16whyXYWs=", - "recordedInputs": [ - "REPO_MAPPING:apple_support+,bazel_skylib bazel_skylib+", - "REPO_MAPPING:bazel_features+,bazel_features_globals bazel_features++version_extension+bazel_features_globals", - "REPO_MAPPING:bazel_features+,bazel_features_version bazel_features++version_extension+bazel_features_version", - "REPO_MAPPING:rules_cc+,bazel_skylib bazel_skylib+", - "REPO_MAPPING:rules_cc+,bazel_tools bazel_tools", - "REPO_MAPPING:rules_cc+,cc_compatibility_proxy rules_cc++compatibility_proxy+cc_compatibility_proxy", - "REPO_MAPPING:rules_cc+,platforms platforms", - "REPO_MAPPING:rules_cc+,rules_cc rules_cc+", - "REPO_MAPPING:rules_cc++compatibility_proxy+cc_compatibility_proxy,rules_cc rules_cc+", - "REPO_MAPPING:rules_rust+,apple_support apple_support+", - "REPO_MAPPING:rules_rust+,bazel_features bazel_features+", - "REPO_MAPPING:rules_rust+,bazel_skylib bazel_skylib+", - "REPO_MAPPING:rules_rust+,bazel_tools bazel_tools", - "REPO_MAPPING:rules_rust+,cui rules_rust++cu+cui", - "REPO_MAPPING:rules_rust+,rrc rules_rust++i2+rrc", - "REPO_MAPPING:rules_rust+,rules_cc rules_cc+", - "REPO_MAPPING:rules_rust+,rules_rust rules_rust+" - ], - "generatedRepoSpecs": { - "cargo_bazel_bootstrap": { - "repoRuleId": "@@rules_rust+//cargo/private:cargo_bootstrap.bzl%cargo_bootstrap_repository", - "attributes": { - "srcs": [ - "@@rules_rust+//crate_universe:src/api.rs", - "@@rules_rust+//crate_universe:src/api/lockfile.rs", - "@@rules_rust+//crate_universe:src/cli.rs", - "@@rules_rust+//crate_universe:src/cli/generate.rs", - "@@rules_rust+//crate_universe:src/cli/query.rs", - "@@rules_rust+//crate_universe:src/cli/render.rs", - "@@rules_rust+//crate_universe:src/cli/splice.rs", - "@@rules_rust+//crate_universe:src/cli/vendor.rs", - "@@rules_rust+//crate_universe:src/config.rs", - "@@rules_rust+//crate_universe:src/config/label_injection.rs", - "@@rules_rust+//crate_universe:src/context.rs", - "@@rules_rust+//crate_universe:src/context/crate_context.rs", - "@@rules_rust+//crate_universe:src/context/platforms.rs", - "@@rules_rust+//crate_universe:src/lib.rs", - "@@rules_rust+//crate_universe:src/lockfile.rs", - "@@rules_rust+//crate_universe:src/main.rs", - "@@rules_rust+//crate_universe:src/metadata.rs", - "@@rules_rust+//crate_universe:src/metadata/cargo_bin.rs", - "@@rules_rust+//crate_universe:src/metadata/cargo_tree_resolver.rs", - "@@rules_rust+//crate_universe:src/metadata/cargo_tree_rustc_wrapper.bat", - "@@rules_rust+//crate_universe:src/metadata/cargo_tree_rustc_wrapper.sh", - "@@rules_rust+//crate_universe:src/metadata/dependency.rs", - "@@rules_rust+//crate_universe:src/metadata/metadata_annotation.rs", - "@@rules_rust+//crate_universe:src/rendering.rs", - "@@rules_rust+//crate_universe:src/rendering/template_engine.rs", - "@@rules_rust+//crate_universe:src/rendering/templates/defs_bzl_shim.j2", - "@@rules_rust+//crate_universe:src/rendering/templates/module_bzl.j2", - "@@rules_rust+//crate_universe:src/rendering/templates/partials/header.j2", - "@@rules_rust+//crate_universe:src/rendering/templates/partials/module/aliases_map.j2", - "@@rules_rust+//crate_universe:src/rendering/templates/partials/module/deps_map.j2", - "@@rules_rust+//crate_universe:src/rendering/templates/partials/module/repo_git.j2", - "@@rules_rust+//crate_universe:src/rendering/templates/partials/module/repo_http.j2", - "@@rules_rust+//crate_universe:src/rendering/verbatim/alias_rules.bzl", - "@@rules_rust+//crate_universe:src/select.rs", - "@@rules_rust+//crate_universe:src/splicing.rs", - "@@rules_rust+//crate_universe:src/splicing/cargo_config.rs", - "@@rules_rust+//crate_universe:src/splicing/crate_index_lookup.rs", - "@@rules_rust+//crate_universe:src/splicing/splicer.rs", - "@@rules_rust+//crate_universe:src/test.rs", - "@@rules_rust+//crate_universe:src/utils.rs", - "@@rules_rust+//crate_universe:src/utils/starlark.rs", - "@@rules_rust+//crate_universe:src/utils/starlark/glob.rs", - "@@rules_rust+//crate_universe:src/utils/starlark/label.rs", - "@@rules_rust+//crate_universe:src/utils/starlark/select.rs", - "@@rules_rust+//crate_universe:src/utils/starlark/select_dict.rs", - "@@rules_rust+//crate_universe:src/utils/starlark/select_list.rs", - "@@rules_rust+//crate_universe:src/utils/starlark/select_scalar.rs", - "@@rules_rust+//crate_universe:src/utils/starlark/select_set.rs", - "@@rules_rust+//crate_universe:src/utils/starlark/serialize.rs", - "@@rules_rust+//crate_universe:src/utils/starlark/target_compatible_with.rs", - "@@rules_rust+//crate_universe:src/utils/symlink.rs", - "@@rules_rust+//crate_universe:src/utils/target_triple.rs" - ], - "binary": "cargo-bazel", - "cargo_lockfile": "@@rules_rust+//crate_universe:Cargo.lock", - "cargo_toml": "@@rules_rust+//crate_universe:Cargo.toml", - "version": "1.97.1", - "rust_toolchain_cargo_template": "@rust_host_tools//:bin/{tool}", - "rust_toolchain_rustc_template": "@rust_host_tools//:bin/{tool}", - "compressed_windows_toolchain_names": false, - "timeout": 900 - } - } - }, - "moduleExtensionMetadata": { - "explicitRootModuleDirectDeps": [ - "cargo_bazel_bootstrap" - ], - "explicitRootModuleDirectDevDeps": [], - "useAllRepos": "NO", - "reproducible": false - } - } } }, "facts": { @@ -399,8 +434,8 @@ "https://files.pythonhosted.org/packages/b9/fa/123043af240e49752f1c4bd24da5053b6bd00cad78c2be53c0d1e8b975bc/backports.tarfile-1.2.0-py3-none-any.whl": "sha256:77e284d754527b01fb1e6fa8a1afe577858ebe4e9dad8919e34c862cb399bc34" }, "certifi": { - "https://files.pythonhosted.org/packages/a6/bd/1436c50cc46699de0230fbaacc1d1de7bfec413176099949e9d5112e88f2/certifi-2026.10.19.tar.gz": "sha256:035c1e85572b141bf1c3a3eff0e33a8eb74917522f2559075ff8d2fea780a1ae", - "https://files.pythonhosted.org/packages/c4/51/29046e48f693c54e5b7412216d447a1d8362c9653148d98c02cdf65fa733/certifi-2026.10.19-py3-none-any.whl": "sha256:42d35340cc5b9ba78e818ddbff0c5b1c2b779de448a340fd1b989f5fa8670e1b" + "https://files.pythonhosted.org/packages/0b/a7/71ac2cff56fec219ed242bb11b8efb69fcc4bec75db06fb7bfe35de520e6/certifi-2026.7.22-py3-none-any.whl": "sha256:62f22742b58a1a33014a2b6b706588a8d7e2a88ae7bd1a6ebe8c992928483775", + "https://files.pythonhosted.org/packages/a3/c2/24167ea9858356b47a87a50d39908bfdb72ceeefe0041586e704e5376b3a/certifi-2026.7.22.tar.gz": "sha256:741e2c3b351ddf169a738da9f2c048608ff7f2c5cc02f1ebc6b118bb090d5d55" }, "cffi": { "https://files.pythonhosted.org/packages/01/9a/11f687cb39d6a3504060d5242f04f48c735afb4d3d533958a20594890cb2/cffi-2.1.1-cp313-cp313-win_arm64.whl": "sha256:63bbfd5ded17c4840ac07cd8f1c21ba9d9708141f840b324f422f41b207e3973", @@ -784,8 +819,8 @@ "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz": "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba" }, "more-itertools": { - "https://files.pythonhosted.org/packages/52/b8/c63995bebbb9ac9c059350e0fb8c82c2420c85aeca7d34e40aecf3df9281/more_itertools-11.2.1.tar.gz": "sha256:cbf08fd0af284dc69718b9b76a8e6203df624d70209ea511e4219d350c856f63", - "https://files.pythonhosted.org/packages/5d/57/3c4b1c6f2e0cc29ba023802458ea3d2a996f87085559e4a5f2192d549532/more_itertools-11.2.1-py3-none-any.whl": "sha256:35a7377edd1dd6608dcb2cdf534ded55ea32d49448875ad042cd3f879fb1ded0" + "https://files.pythonhosted.org/packages/de/1d/f4da6f02cdffe04d6362210b807146a26044c88d839208aec273bb0d9184/more_itertools-11.1.0.tar.gz": "sha256:48e8f4d9e7e5878571ecf6f2b4e57634f93cd474cc8cfbd2376f2d11b396e30d", + "https://files.pythonhosted.org/packages/e8/3d/1087453384dbde46a8c7f9356eead2c58be8a7bf156bca40243377c85715/more_itertools-11.1.0-py3-none-any.whl": "sha256:4b65538ae22f6fed0ce4874efd317463a7489796a0939fa66824dd542125a192" }, "nh3": { "https://files.pythonhosted.org/packages/00/0c/1a8985fd43fea5530c0ac890b6f0b423770ee72f111b70b7a77f2dec243a/nh3-0.3.7-cp38-abi3-musllinux_1_2_aarch64.whl": "sha256:d56e76bd3cadb09b6b0cef364850811663734b348a25f5f587a2819c495367bd", @@ -823,8 +858,8 @@ "pycparser": { "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl": "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz": "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", - "https://files.pythonhosted.org/packages/b6/ae/315bbeee126a769cbde43cf4908acb4b40408d8bf8bbf334dd7368d8dadf/pycparser-3.12-py3-none-any.whl": "sha256:a076f3f489b39b43ed13563a1dafe42447445bec2e294154ec5c57fcdda4c85e", - "https://files.pythonhosted.org/packages/cb/27/eeb9f307bf07060f3d9f9aa0fb8c15b01bdfded0a941e4b2719c7f94cacd/pycparser-3.12.tar.gz": "sha256:f03387877eab82ce01bbdfd0ba7252ee55a90b1c708d5129e2ce915475cf1db6" + "https://files.pythonhosted.org/packages/90/11/0e6f11117525ff0eec40ebac3d313376f102df93ca44ad9e893ee85e4f89/pycparser-3.11-py3-none-any.whl": "sha256:51d5a8ba2be0bbe440b99d2112604c95bbbc3c2748a64260186c541e1729cd80", + "https://files.pythonhosted.org/packages/da/a8/c5fdbeee588bb8ada9458774f43adf1bdd30bd59157055142183e769a024/pycparser-3.11.tar.gz": "sha256:d875f09c3507d00e1aba0eecc6dcadc1352f30fff09dc6bff2f1c2935e97c2bc" }, "pygments": { "https://files.pythonhosted.org/packages/49/2e/ced460408999b33da6b31b0021b0f37d329e202d4169aeb164493778f25b/pygments-2.21.0.tar.gz": "sha256:610ca751c9bc2492b38eb9a38a7fbc93edbbb2d7182edaf34e66ae493dee5c8c", @@ -871,8 +906,8 @@ "https://files.pythonhosted.org/packages/e3/05/b17359e1cefb4f909b5e40b1b90a496d987258916dbbf88e842c729f510e/urllib3-2.8.0.tar.gz": "sha256:63bf2ead4c879426ebf22ef2a781eeb4aa3b4ae798a0435506f8687fd5bb9b63" }, "zipp": { - "https://files.pythonhosted.org/packages/18/05/ba1458dee0ac485b2a01071c1b88f5199fe878a485df482e10e1ef2205cc/zipp-4.1.2.tar.gz": "sha256:cf474b90b07ea932637e17c6b9479de2b2882900b0d311bdaf4f5c000a15a97f", - "https://files.pythonhosted.org/packages/b8/44/7bd2007260da11cccca29ba2d4c345f1287aa663db38ee7cefeabf6386a5/zipp-4.1.2-py3-none-any.whl": "sha256:b30cb6cd253122169c47de245ca24a86bac2a5d3802ba15bf7ba6414eb9e144d" + "https://files.pythonhosted.org/packages/b5/98/df615823cd9419131ce19fba00de53a663794369e198aade064a244b385d/zipp-4.1.1-py3-none-any.whl": "sha256:8979f52d874162f485ff2981e3891f3a3317b7a3dd43ff1e1775b9304f307a9c", + "https://files.pythonhosted.org/packages/dc/23/655a1802fe8041302c959774ca7c80b53bc24737ff3ef45cb50ef11bd96c/zipp-4.1.1.tar.gz": "sha256:7ebb7a44c021b29fd8dbd7cce6812d0d7b5b454521f93cc71af6ccd155aaa70b" } } }, diff --git a/tests/integration/compile_pip_requirements/.bazelrc b/tests/integration/compile_pip_requirements/.bazelrc index b85f03bcb6..05ca7bfdd9 100644 --- a/tests/integration/compile_pip_requirements/.bazelrc +++ b/tests/integration/compile_pip_requirements/.bazelrc @@ -3,3 +3,6 @@ test --test_output=errors # Windows requires these for multi-python support: build --enable_runfiles common:bazel7.x --incompatible_python_disallow_native_rules + +# TODO: Delete before merging; see the root .bazelrc. +common --registry=https://bazel-contrib.github.io/rules_runfiles_group --registry=https://bcr.bazel.build diff --git a/tests/integration/compile_pip_requirements_test_from_external_repo/.bazelrc b/tests/integration/compile_pip_requirements_test_from_external_repo/.bazelrc index ab10c8caf7..fb11f5b00a 100644 --- a/tests/integration/compile_pip_requirements_test_from_external_repo/.bazelrc +++ b/tests/integration/compile_pip_requirements_test_from_external_repo/.bazelrc @@ -1,2 +1,5 @@ test --test_output=errors common:bazel7.x --incompatible_python_disallow_native_rules + +# TODO: Delete before merging; see the root .bazelrc. +common --registry=https://bazel-contrib.github.io/rules_runfiles_group --registry=https://bcr.bazel.build diff --git a/tests/integration/custom_commands/.bazelrc b/tests/integration/custom_commands/.bazelrc new file mode 100644 index 0000000000..88cad8b7df --- /dev/null +++ b/tests/integration/custom_commands/.bazelrc @@ -0,0 +1,2 @@ +# TODO: Delete before merging; see the root .bazelrc. +common --registry=https://bazel-contrib.github.io/rules_runfiles_group --registry=https://bcr.bazel.build diff --git a/tests/integration/local_toolchains/.bazelrc b/tests/integration/local_toolchains/.bazelrc index 0fbb7678d1..ee70dd411d 100644 --- a/tests/integration/local_toolchains/.bazelrc +++ b/tests/integration/local_toolchains/.bazelrc @@ -8,3 +8,6 @@ build --enable_runfiles common:bazel7.x --incompatible_python_disallow_native_rules build --//:py=local common --announce_rc + +# TODO: Delete before merging; see the root .bazelrc. +common --registry=https://bazel-contrib.github.io/rules_runfiles_group --registry=https://bcr.bazel.build diff --git a/tests/integration/pip_parse/.bazelrc b/tests/integration/pip_parse/.bazelrc index a74909297d..1ca4ceb309 100644 --- a/tests/integration/pip_parse/.bazelrc +++ b/tests/integration/pip_parse/.bazelrc @@ -6,3 +6,6 @@ build --enable_runfiles try-import %workspace%/user.bazelrc common:bazel7.x --incompatible_python_disallow_native_rules + +# TODO: Delete before merging; see the root .bazelrc. +common --registry=https://bazel-contrib.github.io/rules_runfiles_group --registry=https://bcr.bazel.build diff --git a/tests/integration/pip_parse_isolated/.bazelrc b/tests/integration/pip_parse_isolated/.bazelrc index 227ce5a4cd..0ceaf23001 100644 --- a/tests/integration/pip_parse_isolated/.bazelrc +++ b/tests/integration/pip_parse_isolated/.bazelrc @@ -6,3 +6,6 @@ common --experimental_isolated_extension_usages # https://docs.bazel.build/versions/main/best-practices.html#using-the-bazelrc-file try-import %workspace%/user.bazelrc + +# TODO: Delete before merging; see the root .bazelrc. +common --registry=https://bazel-contrib.github.io/rules_runfiles_group --registry=https://bcr.bazel.build diff --git a/tests/integration/py_cc_toolchain_registered/.bazelrc b/tests/integration/py_cc_toolchain_registered/.bazelrc index fb31561892..7c7caf3b62 100644 --- a/tests/integration/py_cc_toolchain_registered/.bazelrc +++ b/tests/integration/py_cc_toolchain_registered/.bazelrc @@ -1,3 +1,6 @@ # This aids debugging on failure build --toolchain_resolution_debug=python common:bazel7.x --incompatible_python_disallow_native_rules + +# TODO: Delete before merging; see the root .bazelrc. +common --registry=https://bazel-contrib.github.io/rules_runfiles_group --registry=https://bcr.bazel.build diff --git a/tests/integration/runtime_manifests/.bazelrc b/tests/integration/runtime_manifests/.bazelrc index d4d45a5ea7..8c93874cc3 100644 --- a/tests/integration/runtime_manifests/.bazelrc +++ b/tests/integration/runtime_manifests/.bazelrc @@ -2,3 +2,6 @@ common:fast-tests --build_tests_only=true common:fast-tests --build_tag_filters=-large,-enormous,-integration-test common:fast-tests --test_tag_filters=-large,-enormous,-integration-test + +# TODO: Delete before merging; see the root .bazelrc. +common --registry=https://bazel-contrib.github.io/rules_runfiles_group --registry=https://bcr.bazel.build diff --git a/tests/integration/toolchain_target_settings/.bazelrc b/tests/integration/toolchain_target_settings/.bazelrc index e1df951d06..4cb03b81e3 100644 --- a/tests/integration/toolchain_target_settings/.bazelrc +++ b/tests/integration/toolchain_target_settings/.bazelrc @@ -1,3 +1,6 @@ common --lockfile_mode=off test --test_output=errors build --enable_runfiles + +# TODO: Delete before merging; see the root .bazelrc. +common --registry=https://bazel-contrib.github.io/rules_runfiles_group --registry=https://bcr.bazel.build diff --git a/tests/integration/unified_pypi/.bazelrc b/tests/integration/unified_pypi/.bazelrc index b3a24e8605..36e8549e1f 100644 --- a/tests/integration/unified_pypi/.bazelrc +++ b/tests/integration/unified_pypi/.bazelrc @@ -1 +1,4 @@ common --experimental_enable_bzlmod + +# TODO: Delete before merging; see the root .bazelrc. +common --registry=https://bazel-contrib.github.io/rules_runfiles_group --registry=https://bcr.bazel.build diff --git a/tests/integration/uv_lock/.bazelrc b/tests/integration/uv_lock/.bazelrc index 511f8a2413..ac80834a56 100644 --- a/tests/integration/uv_lock/.bazelrc +++ b/tests/integration/uv_lock/.bazelrc @@ -3,3 +3,6 @@ common --experimental_isolated_extension_usages common --action_env=UV_EXTRA_INDEX_URL try-import %workspace%/user.bazelrc + +# TODO: Delete before merging; see the root .bazelrc. +common --registry=https://bazel-contrib.github.io/rules_runfiles_group --registry=https://bcr.bazel.build diff --git a/tests/integration/validate_test_main/.bazelrc b/tests/integration/validate_test_main/.bazelrc new file mode 100644 index 0000000000..88cad8b7df --- /dev/null +++ b/tests/integration/validate_test_main/.bazelrc @@ -0,0 +1,2 @@ +# TODO: Delete before merging; see the root .bazelrc. +common --registry=https://bazel-contrib.github.io/rules_runfiles_group --registry=https://bcr.bazel.build diff --git a/tests/integration/whl_library/.bazelrc b/tests/integration/whl_library/.bazelrc index eee47a8f19..7591e29f40 100644 --- a/tests/integration/whl_library/.bazelrc +++ b/tests/integration/whl_library/.bazelrc @@ -1,2 +1,5 @@ common --incompatible_default_to_explicit_init_py test --test_output=errors + +# TODO: Delete before merging; see the root .bazelrc. +common --registry=https://bazel-contrib.github.io/rules_runfiles_group --registry=https://bcr.bazel.build From 6024e174c05887faf3d460d102cb65e5610b8252 Mon Sep 17 00:00:00 2001 From: Frank Portman Date: Mon, 19 Oct 2026 10:26:55 -0400 Subject: [PATCH 04/13] chore: bump rules_runfiles_group to 99.0.0-dev.20261019.1.1cee2dd Picks up the analysis test comparing symlinks by path and target. --- MODULE.bazel | 2 +- python/private/py_repositories.bzl | 6 +++--- tests/integration/bzlmod_lockfile/MODULE.bazel.lock | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/MODULE.bazel b/MODULE.bazel index ddc27bd378..c9b64475e2 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -9,7 +9,7 @@ bazel_dep(name = "bazel_skylib", version = "1.8.2") bazel_dep(name = "package_metadata", version = "0.0.7") bazel_dep(name = "platforms", version = "0.0.11") bazel_dep(name = "rules_cc", version = "0.2.17") -bazel_dep(name = "rules_runfiles_group", version = "99.0.0-dev.20261016.1.41d2544") +bazel_dep(name = "rules_runfiles_group", version = "99.0.0-dev.20261019.1.1cee2dd") bazel_dep(name = "toml.bzl", version = "0.4.1") # Those are loaded only when using py_proto_library diff --git a/python/private/py_repositories.bzl b/python/private/py_repositories.bzl index 6b8148e406..7e3db283c4 100644 --- a/python/private/py_repositories.bzl +++ b/python/private/py_repositories.bzl @@ -74,9 +74,9 @@ def py_repositories(transition_settings = []): ) http_archive( name = "rules_runfiles_group", - sha256 = "2da2befdf87d85eafe8b77a9aa0dca0a700cf7ba929866f6c1b505e3389fbaf8", - strip_prefix = "rules_runfiles_group-41d2544bab5c1eee0117c9e8a992631be8e0d9d1", - url = "https://github.com/bazel-contrib/rules_runfiles_group/archive/41d2544bab5c1eee0117c9e8a992631be8e0d9d1.tar.gz", + sha256 = "ca3333c8cdfedf24c5732a9295e1c9633fe90363a6446929fa5d39d3f0167119", + strip_prefix = "rules_runfiles_group-1cee2dde3b5e50ce754c5121bc1759b05976d24a", + url = "https://github.com/bazel-contrib/rules_runfiles_group/archive/1cee2dde3b5e50ce754c5121bc1759b05976d24a.tar.gz", ) http_archive( name = "toml.bzl", diff --git a/tests/integration/bzlmod_lockfile/MODULE.bazel.lock b/tests/integration/bzlmod_lockfile/MODULE.bazel.lock index 8d37f1ef55..ed1f41dd6d 100644 --- a/tests/integration/bzlmod_lockfile/MODULE.bazel.lock +++ b/tests/integration/bzlmod_lockfile/MODULE.bazel.lock @@ -125,8 +125,8 @@ "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_proto/5.3.0-21.7/MODULE.bazel": "not found", "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_proto/6.0.2/MODULE.bazel": "not found", "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_proto/7.1.0/MODULE.bazel": "not found", - "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_runfiles_group/99.0.0-dev.20261016.1.41d2544/MODULE.bazel": "e35f5260f613c7cd7b29a398cd34191db239ec138ba357dbfe81c3f2ce19de70", - "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_runfiles_group/99.0.0-dev.20261016.1.41d2544/source.json": "801750c105fd41f3ca2818e93a6cbaa55b8236ea505394adfe3596e1bfc44967", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_runfiles_group/99.0.0-dev.20261019.1.1cee2dd/MODULE.bazel": "1d3e9fa86209fc70564e1c931533ec480a60ce27b6682bd3c97da63ce889806d", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_runfiles_group/99.0.0-dev.20261019.1.1cee2dd/source.json": "9ca73bfeb772529f64c51aa55e39499a1864490da7f4ad74885c95ca49f19e8d", "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_shell/0.2.0/MODULE.bazel": "not found", "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_shell/0.3.0/MODULE.bazel": "not found", "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_shell/0.6.1/MODULE.bazel": "not found", From b9818f953eed80cba9ae74df83013b4e9446c63b Mon Sep 17 00:00:00 2001 From: Frank Portman Date: Mon, 19 Oct 2026 10:47:38 -0400 Subject: [PATCH 05/13] fix(rules): load only the describer side of rules_runfiles_group rules_runfiles_group's lib.bzl loads its packager code, which needs a recent bazel_features that WORKSPACE users don't have. Its new describe.bzl doesn't, so loading the Python rules works in WORKSPACE again without extra setup. The analysis tests still go through the packager, so the dev WORKSPACE gets bazel_features 1.51.0, and the tests are skipped on Bazel versions whose aspects can't walk toolchains. --- MODULE.bazel | 2 +- internal_dev_deps.bzl | 6 +- python/private/BUILD.bazel | 2 +- python/private/py_repositories.bzl | 6 +- python/private/runfiles_groups.bzl | 58 +++---- .../bzlmod_lockfile/MODULE.bazel.lock | 141 ++++++++++++++++-- tests/runfiles_groups/BUILD.bazel | 9 ++ 7 files changed, 178 insertions(+), 46 deletions(-) diff --git a/MODULE.bazel b/MODULE.bazel index c9b64475e2..b2d52ac564 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -9,7 +9,7 @@ bazel_dep(name = "bazel_skylib", version = "1.8.2") bazel_dep(name = "package_metadata", version = "0.0.7") bazel_dep(name = "platforms", version = "0.0.11") bazel_dep(name = "rules_cc", version = "0.2.17") -bazel_dep(name = "rules_runfiles_group", version = "99.0.0-dev.20261019.1.1cee2dd") +bazel_dep(name = "rules_runfiles_group", version = "99.0.0-dev.20261019.1.d105dfa") bazel_dep(name = "toml.bzl", version = "0.4.1") # Those are loaded only when using py_proto_library diff --git a/internal_dev_deps.bzl b/internal_dev_deps.bzl index 68b8f89e76..291da0810b 100644 --- a/internal_dev_deps.bzl +++ b/internal_dev_deps.bzl @@ -252,9 +252,9 @@ def rules_python_internal_deps(): http_archive( name = "bazel_features", - sha256 = "d7787da289a7fb497352211ad200ec9f698822a9e0757a4976fd9f713ff372b3", - strip_prefix = "bazel_features-1.9.1", - url = "https://github.com/bazel-contrib/bazel_features/releases/download/v1.9.1/bazel_features-v1.9.1.tar.gz", + sha256 = "5450bfb2c8b4bc961c75368838f86156f563cc9adef1be7d504fc5619d54daab", + strip_prefix = "bazel_features-1.51.0", + url = "https://github.com/bazel-contrib/bazel_features/releases/download/v1.51.0/bazel_features-v1.51.0.tar.gz", ) http_archive( diff --git a/python/private/BUILD.bazel b/python/private/BUILD.bazel index c80f2b5d6c..0d25eb3e38 100644 --- a/python/private/BUILD.bazel +++ b/python/private/BUILD.bazel @@ -868,7 +868,7 @@ bzl_library( ":py_internal", ":py_library", ":reexports", - "@rules_runfiles_group//runfiles_group:lib", + "@rules_runfiles_group//runfiles_group:describe", ], ) diff --git a/python/private/py_repositories.bzl b/python/private/py_repositories.bzl index 7e3db283c4..d17207c950 100644 --- a/python/private/py_repositories.bzl +++ b/python/private/py_repositories.bzl @@ -74,9 +74,9 @@ def py_repositories(transition_settings = []): ) http_archive( name = "rules_runfiles_group", - sha256 = "ca3333c8cdfedf24c5732a9295e1c9633fe90363a6446929fa5d39d3f0167119", - strip_prefix = "rules_runfiles_group-1cee2dde3b5e50ce754c5121bc1759b05976d24a", - url = "https://github.com/bazel-contrib/rules_runfiles_group/archive/1cee2dde3b5e50ce754c5121bc1759b05976d24a.tar.gz", + sha256 = "17437bb769ecef3d6b14333a1f82d46f0703e9eb645208428a3af0875e79f6d7", + strip_prefix = "rules_runfiles_group-d105dfab75fb550bcfc0d2cc033b27fad0dfc2e4", + url = "https://github.com/bazel-contrib/rules_runfiles_group/archive/d105dfab75fb550bcfc0d2cc033b27fad0dfc2e4.tar.gz", ) http_archive( name = "toml.bzl", diff --git a/python/private/runfiles_groups.bzl b/python/private/runfiles_groups.bzl index bcca0f183c..feabbdaf3e 100644 --- a/python/private/runfiles_groups.bzl +++ b/python/private/runfiles_groups.bzl @@ -27,7 +27,7 @@ A library's group holds what it contributes to a consuming binary: the files the binary takes from its PyInfo, plus its plain data files. """ -load("@rules_runfiles_group//runfiles_group:lib.bzl", "runfiles_groups") +load("@rules_runfiles_group//runfiles_group:describe.bzl", "describe") load(":attributes.bzl", "PycCollectionAttr") load(":common.bzl", "is_py_source", "py_info_runfiles_files") load(":flags.bzl", "PyiDepsToRunfilesFlag") @@ -51,7 +51,7 @@ _PYPI_GROUP_PREFIX = "rules_python#pypi/" # The toolchain type //python:toolchain_type is an alias of; the aspect matches # toolchains by the actual label. -_RUNTIME_MERGE_FROM = runfiles_groups.merge_from( +_RUNTIME_MERGE_FROM = describe.merge_from( toolchain = Label("@bazel_tools//tools/python:toolchain_type"), ) @@ -84,15 +84,23 @@ def _py_info_files(target, pyc_collection_enabled): return py_info_runfiles_files(target[BuiltinPyInfo], pyc_collection_enabled) return [] +def _union(ctx, contents): + # Each content is a depset of File or a runfiles object. + files = [c for c in contents if type(c) == "depset"] + runfiles = [c for c in contents if type(c) != "depset"] + if not runfiles: + return depset(transitive = files) + return ctx.runfiles(transitive_files = depset(transitive = files)).merge_all(runfiles) + def _kind(label): return "first_party" if label.repo_name == "" else "third_party" def _pypi_entry(package, content, weight = None): - return runfiles_groups.entry( + return describe.entry( name = _PYPI_GROUP_PREFIX + package, content = content, kind = "third_party", - rank = runfiles_groups.RANK_SHARED_DEPS, + rank = describe.RANK_SHARED_DEPS, weight = weight, merge_affinity = _MERGE_AFFINITY, ) @@ -140,7 +148,7 @@ def _dep_fallback(ctx, dep, py_outputs): content.append(runfiles) if not content: return None - return runfiles_groups.entry(name = dep.label, content = runfiles_groups.union(ctx, content)) + return describe.entry(name = dep.label, content = _union(ctx, content)) def _library_dep_fallback(ctx, dep): # py_library adds the .py outputs of a dep without PyInfo to its own PyInfo. @@ -154,8 +162,8 @@ def _data_fallback(ctx, dep): default_info = dep[DefaultInfo] if _is_file(dep) or (not default_info.files and _is_empty(default_info.data_runfiles)): return None - content = runfiles_groups.union(ctx, [default_info.files, default_info.data_runfiles]) - return runfiles_groups.entry(name = dep.label, content = content) + content = _union(ctx, [default_info.files, default_info.data_runfiles]) + return describe.entry(name = dep.label, content = content) def _srcs_fallback(ctx, dep): # Plain files are in the merging target's own group. @@ -182,24 +190,24 @@ def _describe_py_library(target, ctx): entry = _pypi_entry(package, content, weight) else: kind = _kind(ctx.label) - entry = runfiles_groups.entry( + entry = describe.entry( name = ctx.label, content = content, kind = kind, - rank = runfiles_groups.RANK_SHARED_DEPS if kind == "third_party" else runfiles_groups.RANK_EXECUTABLE, + rank = describe.RANK_SHARED_DEPS if kind == "third_party" else describe.RANK_EXECUTABLE, weight = weight, merge_affinity = _MERGE_AFFINITY, ) - return runfiles_groups.node( + return describe.node( add = [entry] if content else [], merge_from = [ - runfiles_groups.merge_from("data", fallback = _data_fallback), - runfiles_groups.merge_from("deps", fallback = _library_dep_fallback), - runfiles_groups.merge_from("srcs", fallback = _srcs_fallback), + describe.merge_from("data", fallback = _data_fallback), + describe.merge_from("deps", fallback = _library_dep_fallback), + describe.merge_from("srcs", fallback = _srcs_fallback), ], ) -py_library_runfiles_group_describer = runfiles_groups.make_describer_rule( +py_library_runfiles_group_describer = describe.make_describer_rule( describe = _describe_py_library, ) @@ -265,17 +273,17 @@ def _describe_py_executable(target, ctx): add, venv_runfiles = _venv_entries(ctx, venv_runfiles) merge_from = [ _RUNTIME_MERGE_FROM, - runfiles_groups.merge_from("_debugger_flag", fallback = _executable_dep_fallback), - runfiles_groups.merge_from("data", fallback = _data_fallback), - runfiles_groups.merge_from("deps", fallback = _executable_dep_fallback), - runfiles_groups.merge_from("srcs", fallback = _srcs_fallback), + describe.merge_from("_debugger_flag", fallback = _executable_dep_fallback), + describe.merge_from("data", fallback = _data_fallback), + describe.merge_from("deps", fallback = _executable_dep_fallback), + describe.merge_from("srcs", fallback = _srcs_fallback), ] content.extend([rf for rf in (venv_runfiles, info.venv_interpreter_runfiles) if rf]) - return runfiles_groups.node( - add = [runfiles_groups.entry( + return describe.node( + add = [describe.entry( name = ctx.label, - content = runfiles_groups.union(ctx, content), + content = _union(ctx, content), kind = _kind(ctx.label), merge_affinity = _MERGE_AFFINITY, )] + add, @@ -283,22 +291,22 @@ def _describe_py_executable(target, ctx): executable_group = ctx.label, ) -py_executable_runfiles_group_describer = runfiles_groups.make_describer_rule( +py_executable_runfiles_group_describer = describe.make_describer_rule( describe = _describe_py_executable, ) def _describe_py_runtime_pair(target, ctx): runtime = target[platform_common.ToolchainInfo].py3_runtime files = get_runtime_files(ctx, runtime) if runtime else depset() - return runfiles_groups.node(add = [runfiles_groups.entry( + return describe.node(add = [describe.entry( name = _RUNTIME_GROUP, content = files, kind = "foundation", - rank = runfiles_groups.RANK_FOUNDATION, + rank = describe.RANK_FOUNDATION, do_not_merge = True, merge_affinity = _MERGE_AFFINITY, )] if files else []) -py_runtime_pair_runfiles_group_describer = runfiles_groups.make_describer_rule( +py_runtime_pair_runfiles_group_describer = describe.make_describer_rule( describe = _describe_py_runtime_pair, ) diff --git a/tests/integration/bzlmod_lockfile/MODULE.bazel.lock b/tests/integration/bzlmod_lockfile/MODULE.bazel.lock index ed1f41dd6d..100f3e611c 100644 --- a/tests/integration/bzlmod_lockfile/MODULE.bazel.lock +++ b/tests/integration/bzlmod_lockfile/MODULE.bazel.lock @@ -17,6 +17,7 @@ "https://bazel-contrib.github.io/rules_runfiles_group/modules/apple_support/1.15.1/MODULE.bazel": "not found", "https://bazel-contrib.github.io/rules_runfiles_group/modules/apple_support/1.21.0/MODULE.bazel": "not found", "https://bazel-contrib.github.io/rules_runfiles_group/modules/apple_support/1.21.1/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/apple_support/1.24.1/MODULE.bazel": "not found", "https://bazel-contrib.github.io/rules_runfiles_group/modules/apple_support/1.24.2/MODULE.bazel": "not found", "https://bazel-contrib.github.io/rules_runfiles_group/modules/bazel_features/1.10.0/MODULE.bazel": "not found", "https://bazel-contrib.github.io/rules_runfiles_group/modules/bazel_features/1.11.0/MODULE.bazel": "not found", @@ -33,6 +34,7 @@ "https://bazel-contrib.github.io/rules_runfiles_group/modules/bazel_features/1.33.0/MODULE.bazel": "not found", "https://bazel-contrib.github.io/rules_runfiles_group/modules/bazel_features/1.4.1/MODULE.bazel": "not found", "https://bazel-contrib.github.io/rules_runfiles_group/modules/bazel_features/1.42.1/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/bazel_features/1.50.0/MODULE.bazel": "not found", "https://bazel-contrib.github.io/rules_runfiles_group/modules/bazel_features/1.51.0/MODULE.bazel": "not found", "https://bazel-contrib.github.io/rules_runfiles_group/modules/bazel_skylib/1.0.3/MODULE.bazel": "not found", "https://bazel-contrib.github.io/rules_runfiles_group/modules/bazel_skylib/1.1.1/MODULE.bazel": "not found", @@ -58,6 +60,7 @@ "https://bazel-contrib.github.io/rules_runfiles_group/modules/jsoncpp/1.9.6/MODULE.bazel": "not found", "https://bazel-contrib.github.io/rules_runfiles_group/modules/libpfm/4.11.0/MODULE.bazel": "not found", "https://bazel-contrib.github.io/rules_runfiles_group/modules/nlohmann_json/3.6.1/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/package_metadata/0.0.3/MODULE.bazel": "not found", "https://bazel-contrib.github.io/rules_runfiles_group/modules/package_metadata/0.0.7/MODULE.bazel": "not found", "https://bazel-contrib.github.io/rules_runfiles_group/modules/platforms/0.0.10/MODULE.bazel": "not found", "https://bazel-contrib.github.io/rules_runfiles_group/modules/platforms/0.0.11/MODULE.bazel": "not found", @@ -68,6 +71,7 @@ "https://bazel-contrib.github.io/rules_runfiles_group/modules/platforms/0.0.8/MODULE.bazel": "not found", "https://bazel-contrib.github.io/rules_runfiles_group/modules/platforms/0.0.9/MODULE.bazel": "not found", "https://bazel-contrib.github.io/rules_runfiles_group/modules/platforms/1.0.0/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/platforms/1.1.0/MODULE.bazel": "not found", "https://bazel-contrib.github.io/rules_runfiles_group/modules/protobuf/21.7/MODULE.bazel": "not found", "https://bazel-contrib.github.io/rules_runfiles_group/modules/protobuf/27.0/MODULE.bazel": "not found", "https://bazel-contrib.github.io/rules_runfiles_group/modules/protobuf/29.0-rc2/MODULE.bazel": "not found", @@ -98,6 +102,7 @@ "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_cc/0.2.0/MODULE.bazel": "not found", "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_cc/0.2.13/MODULE.bazel": "not found", "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_cc/0.2.17/MODULE.bazel": "not found", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_cc/0.2.4/MODULE.bazel": "not found", "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_cc/0.2.8/MODULE.bazel": "not found", "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_foreign_cc/0.9.0/MODULE.bazel": "not found", "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_fuzzing/0.5.2/MODULE.bazel": "not found", @@ -125,8 +130,9 @@ "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_proto/5.3.0-21.7/MODULE.bazel": "not found", "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_proto/6.0.2/MODULE.bazel": "not found", "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_proto/7.1.0/MODULE.bazel": "not found", - "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_runfiles_group/99.0.0-dev.20261019.1.1cee2dd/MODULE.bazel": "1d3e9fa86209fc70564e1c931533ec480a60ce27b6682bd3c97da63ce889806d", - "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_runfiles_group/99.0.0-dev.20261019.1.1cee2dd/source.json": "9ca73bfeb772529f64c51aa55e39499a1864490da7f4ad74885c95ca49f19e8d", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_runfiles_group/99.0.0-dev.20261019.1.d105dfa/MODULE.bazel": "4b7f46fc4ef79043bdc3792459339d0d5351cb92e645162197a0d309da3fc814", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_runfiles_group/99.0.0-dev.20261019.1.d105dfa/source.json": "d9d04acc3ab7dba753c6219bbc01a9b2e9dddf698e034f0de3841374daf24672", + "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_rust/0.73.0/MODULE.bazel": "not found", "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_shell/0.2.0/MODULE.bazel": "not found", "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_shell/0.3.0/MODULE.bazel": "not found", "https://bazel-contrib.github.io/rules_runfiles_group/modules/rules_shell/0.6.1/MODULE.bazel": "not found", @@ -161,6 +167,7 @@ "https://bcr.bazel.build/modules/apple_support/1.15.1/MODULE.bazel": "a0556fefca0b1bb2de8567b8827518f94db6a6e7e7d632b4c48dc5f865bc7c85", "https://bcr.bazel.build/modules/apple_support/1.21.0/MODULE.bazel": "ac1824ed5edf17dee2fdd4927ada30c9f8c3b520be1b5fd02a5da15bc10bff3e", "https://bcr.bazel.build/modules/apple_support/1.21.1/MODULE.bazel": "5809fa3efab15d1f3c3c635af6974044bac8a4919c62238cce06acee8a8c11f1", + "https://bcr.bazel.build/modules/apple_support/1.24.1/MODULE.bazel": "f46e8ddad60aef170ee92b2f3d00ef66c147ceafea68b6877cb45bd91737f5f8", "https://bcr.bazel.build/modules/apple_support/1.24.2/MODULE.bazel": "0e62471818affb9f0b26f128831d5c40b074d32e6dda5a0d3852847215a41ca4", "https://bcr.bazel.build/modules/apple_support/1.24.2/source.json": "2c22c9827093250406c5568da6c54e6fdf0ef06238def3d99c71b12feb057a8d", "https://bcr.bazel.build/modules/bazel_features/1.10.0/MODULE.bazel": "f75e8807570484a99be90abcd52b5e1f390362c258bcb73106f4544957a48101", @@ -178,6 +185,7 @@ "https://bcr.bazel.build/modules/bazel_features/1.33.0/MODULE.bazel": "8b8dc9d2a4c88609409c3191165bccec0e4cb044cd7a72ccbe826583303459f6", "https://bcr.bazel.build/modules/bazel_features/1.4.1/MODULE.bazel": "e45b6bb2350aff3e442ae1111c555e27eac1d915e77775f6fdc4b351b758b5d7", "https://bcr.bazel.build/modules/bazel_features/1.42.1/MODULE.bazel": "275a59b5406ff18c01739860aa70ad7ccb3cfb474579411decca11c93b951080", + "https://bcr.bazel.build/modules/bazel_features/1.50.0/MODULE.bazel": "2083ef9c7a469f520890483ccf8e0189d6e71e2117e7752e15e6554433d5ae3e", "https://bcr.bazel.build/modules/bazel_features/1.51.0/MODULE.bazel": "8e1310d09db6ee2e4f19f9994e360aca941ddc32083edc8d9bdc19c83c94cee4", "https://bcr.bazel.build/modules/bazel_features/1.51.0/source.json": "d5af1f1748d2b4ace5f04087030c0a1cfd93f5be6bdf0b5c1beb24ed0fd24955", "https://bcr.bazel.build/modules/bazel_skylib/1.0.3/MODULE.bazel": "bcb0fd896384802d1ad283b4e4eb4d718eebd8cb820b0a2c3a347fb971afd9d8", @@ -209,6 +217,7 @@ "https://bcr.bazel.build/modules/libpfm/4.11.0/MODULE.bazel": "45061ff025b301940f1e30d2c16bea596c25b176c8b6b3087e92615adbd52902", "https://bcr.bazel.build/modules/nlohmann_json/3.6.1/MODULE.bazel": "6f7b417dcc794d9add9e556673ad25cb3ba835224290f4f848f8e2db1e1fca74", "https://bcr.bazel.build/modules/nlohmann_json/3.6.1/source.json": "f448c6e8963fdfa7eb831457df83ad63d3d6355018f6574fb017e8169deb43a9", + "https://bcr.bazel.build/modules/package_metadata/0.0.3/MODULE.bazel": "77890552ecea9e284b5424c9de827a58099348763a4359e975c359a83d4faa83", "https://bcr.bazel.build/modules/package_metadata/0.0.7/MODULE.bazel": "7adb03933fc8401f495800cf4eafcff0edc6da0ff55c7db223ef69d19f689486", "https://bcr.bazel.build/modules/package_metadata/0.0.7/source.json": "50639625e937b56115012674c797cca7a05a96b4878c87d803c13dc2b31de8a0", "https://bcr.bazel.build/modules/platforms/0.0.10/MODULE.bazel": "8cb8efaf200bdeb2150d93e162c40f388529a25852b332cec879373771e48ed5", @@ -220,7 +229,8 @@ "https://bcr.bazel.build/modules/platforms/0.0.8/MODULE.bazel": "9f142c03e348f6d263719f5074b21ef3adf0b139ee4c5133e2aa35664da9eb2d", "https://bcr.bazel.build/modules/platforms/0.0.9/MODULE.bazel": "4a87a60c927b56ddd67db50c89acaa62f4ce2a1d2149ccb63ffd871d5ce29ebc", "https://bcr.bazel.build/modules/platforms/1.0.0/MODULE.bazel": "f05feb42b48f1b3c225e4ccf351f367be0371411a803198ec34a389fb22aa580", - "https://bcr.bazel.build/modules/platforms/1.0.0/source.json": "f4ff1fd412e0246fd38c82328eb209130ead81d62dcd5a9e40910f867f733d96", + "https://bcr.bazel.build/modules/platforms/1.1.0/MODULE.bazel": "1c0c09f5bdcf4b3f924720d2478a3711cb39f4977019ca5988685e5b7e18b3d2", + "https://bcr.bazel.build/modules/platforms/1.1.0/source.json": "fcf351c47596c939140ab0d333dfdd08ed1ea6ce33c2fe70c12493a301cf1344", "https://bcr.bazel.build/modules/protobuf/21.7/MODULE.bazel": "a5a29bb89544f9b97edce05642fac225a808b5b7be74038ea3640fae2f8e66a7", "https://bcr.bazel.build/modules/protobuf/27.0/MODULE.bazel": "7873b60be88844a0a1d8f80b9d5d20cfbd8495a689b8763e76c6372998d3f64c", "https://bcr.bazel.build/modules/protobuf/29.0-rc2/MODULE.bazel": "6241d35983510143049943fc0d57937937122baf1b287862f9dc8590fc4c37df", @@ -257,6 +267,7 @@ "https://bcr.bazel.build/modules/rules_cc/0.2.13/MODULE.bazel": "eecdd666eda6be16a8d9dc15e44b5c75133405e820f620a234acc4b1fdc5aa37", "https://bcr.bazel.build/modules/rules_cc/0.2.17/MODULE.bazel": "1849602c86cb60da8613d2de887f9566a6d354a6df6d7009f9d04a14402f9a84", "https://bcr.bazel.build/modules/rules_cc/0.2.17/source.json": "3832f45d145354049137c0090df04629d9c2b5493dc5c2bf46f1834040133a07", + "https://bcr.bazel.build/modules/rules_cc/0.2.4/MODULE.bazel": "1ff1223dfd24f3ecf8f028446d4a27608aa43c3f41e346d22838a4223980b8cc", "https://bcr.bazel.build/modules/rules_cc/0.2.8/MODULE.bazel": "f1df20f0bf22c28192a794f29b501ee2018fa37a3862a1a2132ae2940a23a642", "https://bcr.bazel.build/modules/rules_foreign_cc/0.9.0/MODULE.bazel": "c9e8c682bf75b0e7c704166d79b599f93b72cfca5ad7477df596947891feeef6", "https://bcr.bazel.build/modules/rules_fuzzing/0.5.2/MODULE.bazel": "40c97d1144356f52905566c55811f13b299453a14ac7769dfba2ac38192337a8", @@ -290,6 +301,8 @@ "https://bcr.bazel.build/modules/rules_proto/6.0.2/MODULE.bazel": "ce916b775a62b90b61888052a416ccdda405212b6aaeb39522f7dc53431a5e73", "https://bcr.bazel.build/modules/rules_proto/7.1.0/MODULE.bazel": "002d62d9108f75bb807cd56245d45648f38275cb3a99dcd45dfb864c5d74cb96", "https://bcr.bazel.build/modules/rules_proto/7.1.0/source.json": "39f89066c12c24097854e8f57ab8558929f9c8d474d34b2c00ac04630ad8940e", + "https://bcr.bazel.build/modules/rules_rust/0.73.0/MODULE.bazel": "25e3b077128612754c4add1b4c90d20a6be06566b623dee6e32038d0e8f93062", + "https://bcr.bazel.build/modules/rules_rust/0.73.0/source.json": "8eeb3d9ba7c57916b63887a651e8f84c2f68b7243af9e712d728c2a0b7882255", "https://bcr.bazel.build/modules/rules_shell/0.2.0/MODULE.bazel": "fda8a652ab3c7d8fee214de05e7a9916d8b28082234e8d2c0094505c5268ed3c", "https://bcr.bazel.build/modules/rules_shell/0.3.0/MODULE.bazel": "de4402cd12f4cc8fda2354fce179fdb068c0b9ca1ec2d2b17b3e21b24c1a937b", "https://bcr.bazel.build/modules/rules_shell/0.6.1/MODULE.bazel": "72e76b0eea4e81611ef5452aa82b3da34caca0c8b7b5c0c9584338aa93bae26b", @@ -396,8 +409,8 @@ }, "@@rules_python+//python/uv:uv.bzl%uv": { "general": { - "bzlTransitiveDigest": "mkSJjS3IKYKgnN+NNEXte2ylbT3HbgQHO3Wb0v1mHPU=", - "usagesDigest": "4oeBVCEzXt49RrVnpRaNDbydAxQ9Yw+5jX6Xm82siGM=", + "bzlTransitiveDigest": "a11TI1DxtsKzJJnFDIAhAoVNhub4iqECM+h0qTqOT68=", + "usagesDigest": "UgEeJTkNLce2eABymcbLBebdHpF0D8qrT6A6PSESotA=", "recordedInputs": [ "REPO_MAPPING:rules_python+,bazel_tools bazel_tools", "REPO_MAPPING:rules_python+,platforms platforms" @@ -423,6 +436,108 @@ } } } + }, + "@@rules_rust+//crate_universe/private:internal_extensions.bzl%cu_nr": { + "general": { + "bzlTransitiveDigest": "IO6xg76Pp9YTQhgLeeHYRQocQjstmsDHBiTPgtRujCQ=", + "usagesDigest": "+1w4EnEfZLFpf8Ak18jhmCWsOTE75Noy2i16whyXYWs=", + "recordedInputs": [ + "REPO_MAPPING:apple_support+,bazel_skylib bazel_skylib+", + "REPO_MAPPING:bazel_features+,bazel_features_globals bazel_features++version_extension+bazel_features_globals", + "REPO_MAPPING:bazel_features+,bazel_features_version bazel_features++version_extension+bazel_features_version", + "REPO_MAPPING:rules_cc+,bazel_skylib bazel_skylib+", + "REPO_MAPPING:rules_cc+,bazel_tools bazel_tools", + "REPO_MAPPING:rules_cc+,cc_compatibility_proxy rules_cc++compatibility_proxy+cc_compatibility_proxy", + "REPO_MAPPING:rules_cc+,platforms platforms", + "REPO_MAPPING:rules_cc+,rules_cc rules_cc+", + "REPO_MAPPING:rules_cc++compatibility_proxy+cc_compatibility_proxy,rules_cc rules_cc+", + "REPO_MAPPING:rules_rust+,apple_support apple_support+", + "REPO_MAPPING:rules_rust+,bazel_features bazel_features+", + "REPO_MAPPING:rules_rust+,bazel_skylib bazel_skylib+", + "REPO_MAPPING:rules_rust+,bazel_tools bazel_tools", + "REPO_MAPPING:rules_rust+,cui rules_rust++cu+cui", + "REPO_MAPPING:rules_rust+,rrc rules_rust++i2+rrc", + "REPO_MAPPING:rules_rust+,rules_cc rules_cc+", + "REPO_MAPPING:rules_rust+,rules_rust rules_rust+" + ], + "generatedRepoSpecs": { + "cargo_bazel_bootstrap": { + "repoRuleId": "@@rules_rust+//cargo/private:cargo_bootstrap.bzl%cargo_bootstrap_repository", + "attributes": { + "srcs": [ + "@@rules_rust+//crate_universe:src/api.rs", + "@@rules_rust+//crate_universe:src/api/lockfile.rs", + "@@rules_rust+//crate_universe:src/cli.rs", + "@@rules_rust+//crate_universe:src/cli/generate.rs", + "@@rules_rust+//crate_universe:src/cli/query.rs", + "@@rules_rust+//crate_universe:src/cli/render.rs", + "@@rules_rust+//crate_universe:src/cli/splice.rs", + "@@rules_rust+//crate_universe:src/cli/vendor.rs", + "@@rules_rust+//crate_universe:src/config.rs", + "@@rules_rust+//crate_universe:src/config/label_injection.rs", + "@@rules_rust+//crate_universe:src/context.rs", + "@@rules_rust+//crate_universe:src/context/crate_context.rs", + "@@rules_rust+//crate_universe:src/context/platforms.rs", + "@@rules_rust+//crate_universe:src/lib.rs", + "@@rules_rust+//crate_universe:src/lockfile.rs", + "@@rules_rust+//crate_universe:src/main.rs", + "@@rules_rust+//crate_universe:src/metadata.rs", + "@@rules_rust+//crate_universe:src/metadata/cargo_bin.rs", + "@@rules_rust+//crate_universe:src/metadata/cargo_tree_resolver.rs", + "@@rules_rust+//crate_universe:src/metadata/cargo_tree_rustc_wrapper.bat", + "@@rules_rust+//crate_universe:src/metadata/cargo_tree_rustc_wrapper.sh", + "@@rules_rust+//crate_universe:src/metadata/dependency.rs", + "@@rules_rust+//crate_universe:src/metadata/metadata_annotation.rs", + "@@rules_rust+//crate_universe:src/rendering.rs", + "@@rules_rust+//crate_universe:src/rendering/template_engine.rs", + "@@rules_rust+//crate_universe:src/rendering/templates/defs_bzl_shim.j2", + "@@rules_rust+//crate_universe:src/rendering/templates/module_bzl.j2", + "@@rules_rust+//crate_universe:src/rendering/templates/partials/header.j2", + "@@rules_rust+//crate_universe:src/rendering/templates/partials/module/aliases_map.j2", + "@@rules_rust+//crate_universe:src/rendering/templates/partials/module/deps_map.j2", + "@@rules_rust+//crate_universe:src/rendering/templates/partials/module/repo_git.j2", + "@@rules_rust+//crate_universe:src/rendering/templates/partials/module/repo_http.j2", + "@@rules_rust+//crate_universe:src/rendering/verbatim/alias_rules.bzl", + "@@rules_rust+//crate_universe:src/select.rs", + "@@rules_rust+//crate_universe:src/splicing.rs", + "@@rules_rust+//crate_universe:src/splicing/cargo_config.rs", + "@@rules_rust+//crate_universe:src/splicing/crate_index_lookup.rs", + "@@rules_rust+//crate_universe:src/splicing/splicer.rs", + "@@rules_rust+//crate_universe:src/test.rs", + "@@rules_rust+//crate_universe:src/utils.rs", + "@@rules_rust+//crate_universe:src/utils/starlark.rs", + "@@rules_rust+//crate_universe:src/utils/starlark/glob.rs", + "@@rules_rust+//crate_universe:src/utils/starlark/label.rs", + "@@rules_rust+//crate_universe:src/utils/starlark/select.rs", + "@@rules_rust+//crate_universe:src/utils/starlark/select_dict.rs", + "@@rules_rust+//crate_universe:src/utils/starlark/select_list.rs", + "@@rules_rust+//crate_universe:src/utils/starlark/select_scalar.rs", + "@@rules_rust+//crate_universe:src/utils/starlark/select_set.rs", + "@@rules_rust+//crate_universe:src/utils/starlark/serialize.rs", + "@@rules_rust+//crate_universe:src/utils/starlark/target_compatible_with.rs", + "@@rules_rust+//crate_universe:src/utils/symlink.rs", + "@@rules_rust+//crate_universe:src/utils/target_triple.rs" + ], + "binary": "cargo-bazel", + "cargo_lockfile": "@@rules_rust+//crate_universe:Cargo.lock", + "cargo_toml": "@@rules_rust+//crate_universe:Cargo.toml", + "version": "1.97.1", + "rust_toolchain_cargo_template": "@rust_host_tools//:bin/{tool}", + "rust_toolchain_rustc_template": "@rust_host_tools//:bin/{tool}", + "compressed_windows_toolchain_names": false, + "timeout": 900 + } + } + }, + "moduleExtensionMetadata": { + "explicitRootModuleDirectDeps": [ + "cargo_bazel_bootstrap" + ], + "explicitRootModuleDirectDevDeps": [], + "useAllRepos": "NO", + "reproducible": false + } + } } }, "facts": { @@ -434,8 +549,8 @@ "https://files.pythonhosted.org/packages/b9/fa/123043af240e49752f1c4bd24da5053b6bd00cad78c2be53c0d1e8b975bc/backports.tarfile-1.2.0-py3-none-any.whl": "sha256:77e284d754527b01fb1e6fa8a1afe577858ebe4e9dad8919e34c862cb399bc34" }, "certifi": { - "https://files.pythonhosted.org/packages/0b/a7/71ac2cff56fec219ed242bb11b8efb69fcc4bec75db06fb7bfe35de520e6/certifi-2026.7.22-py3-none-any.whl": "sha256:62f22742b58a1a33014a2b6b706588a8d7e2a88ae7bd1a6ebe8c992928483775", - "https://files.pythonhosted.org/packages/a3/c2/24167ea9858356b47a87a50d39908bfdb72ceeefe0041586e704e5376b3a/certifi-2026.7.22.tar.gz": "sha256:741e2c3b351ddf169a738da9f2c048608ff7f2c5cc02f1ebc6b118bb090d5d55" + "https://files.pythonhosted.org/packages/a6/bd/1436c50cc46699de0230fbaacc1d1de7bfec413176099949e9d5112e88f2/certifi-2026.10.19.tar.gz": "sha256:035c1e85572b141bf1c3a3eff0e33a8eb74917522f2559075ff8d2fea780a1ae", + "https://files.pythonhosted.org/packages/c4/51/29046e48f693c54e5b7412216d447a1d8362c9653148d98c02cdf65fa733/certifi-2026.10.19-py3-none-any.whl": "sha256:42d35340cc5b9ba78e818ddbff0c5b1c2b779de448a340fd1b989f5fa8670e1b" }, "cffi": { "https://files.pythonhosted.org/packages/01/9a/11f687cb39d6a3504060d5242f04f48c735afb4d3d533958a20594890cb2/cffi-2.1.1-cp313-cp313-win_arm64.whl": "sha256:63bbfd5ded17c4840ac07cd8f1c21ba9d9708141f840b324f422f41b207e3973", @@ -819,8 +934,8 @@ "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz": "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba" }, "more-itertools": { - "https://files.pythonhosted.org/packages/de/1d/f4da6f02cdffe04d6362210b807146a26044c88d839208aec273bb0d9184/more_itertools-11.1.0.tar.gz": "sha256:48e8f4d9e7e5878571ecf6f2b4e57634f93cd474cc8cfbd2376f2d11b396e30d", - "https://files.pythonhosted.org/packages/e8/3d/1087453384dbde46a8c7f9356eead2c58be8a7bf156bca40243377c85715/more_itertools-11.1.0-py3-none-any.whl": "sha256:4b65538ae22f6fed0ce4874efd317463a7489796a0939fa66824dd542125a192" + "https://files.pythonhosted.org/packages/52/b8/c63995bebbb9ac9c059350e0fb8c82c2420c85aeca7d34e40aecf3df9281/more_itertools-11.2.1.tar.gz": "sha256:cbf08fd0af284dc69718b9b76a8e6203df624d70209ea511e4219d350c856f63", + "https://files.pythonhosted.org/packages/5d/57/3c4b1c6f2e0cc29ba023802458ea3d2a996f87085559e4a5f2192d549532/more_itertools-11.2.1-py3-none-any.whl": "sha256:35a7377edd1dd6608dcb2cdf534ded55ea32d49448875ad042cd3f879fb1ded0" }, "nh3": { "https://files.pythonhosted.org/packages/00/0c/1a8985fd43fea5530c0ac890b6f0b423770ee72f111b70b7a77f2dec243a/nh3-0.3.7-cp38-abi3-musllinux_1_2_aarch64.whl": "sha256:d56e76bd3cadb09b6b0cef364850811663734b348a25f5f587a2819c495367bd", @@ -858,8 +973,8 @@ "pycparser": { "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl": "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz": "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", - "https://files.pythonhosted.org/packages/90/11/0e6f11117525ff0eec40ebac3d313376f102df93ca44ad9e893ee85e4f89/pycparser-3.11-py3-none-any.whl": "sha256:51d5a8ba2be0bbe440b99d2112604c95bbbc3c2748a64260186c541e1729cd80", - "https://files.pythonhosted.org/packages/da/a8/c5fdbeee588bb8ada9458774f43adf1bdd30bd59157055142183e769a024/pycparser-3.11.tar.gz": "sha256:d875f09c3507d00e1aba0eecc6dcadc1352f30fff09dc6bff2f1c2935e97c2bc" + "https://files.pythonhosted.org/packages/b6/ae/315bbeee126a769cbde43cf4908acb4b40408d8bf8bbf334dd7368d8dadf/pycparser-3.12-py3-none-any.whl": "sha256:a076f3f489b39b43ed13563a1dafe42447445bec2e294154ec5c57fcdda4c85e", + "https://files.pythonhosted.org/packages/cb/27/eeb9f307bf07060f3d9f9aa0fb8c15b01bdfded0a941e4b2719c7f94cacd/pycparser-3.12.tar.gz": "sha256:f03387877eab82ce01bbdfd0ba7252ee55a90b1c708d5129e2ce915475cf1db6" }, "pygments": { "https://files.pythonhosted.org/packages/49/2e/ced460408999b33da6b31b0021b0f37d329e202d4169aeb164493778f25b/pygments-2.21.0.tar.gz": "sha256:610ca751c9bc2492b38eb9a38a7fbc93edbbb2d7182edaf34e66ae493dee5c8c", @@ -906,8 +1021,8 @@ "https://files.pythonhosted.org/packages/e3/05/b17359e1cefb4f909b5e40b1b90a496d987258916dbbf88e842c729f510e/urllib3-2.8.0.tar.gz": "sha256:63bf2ead4c879426ebf22ef2a781eeb4aa3b4ae798a0435506f8687fd5bb9b63" }, "zipp": { - "https://files.pythonhosted.org/packages/b5/98/df615823cd9419131ce19fba00de53a663794369e198aade064a244b385d/zipp-4.1.1-py3-none-any.whl": "sha256:8979f52d874162f485ff2981e3891f3a3317b7a3dd43ff1e1775b9304f307a9c", - "https://files.pythonhosted.org/packages/dc/23/655a1802fe8041302c959774ca7c80b53bc24737ff3ef45cb50ef11bd96c/zipp-4.1.1.tar.gz": "sha256:7ebb7a44c021b29fd8dbd7cce6812d0d7b5b454521f93cc71af6ccd155aaa70b" + "https://files.pythonhosted.org/packages/18/05/ba1458dee0ac485b2a01071c1b88f5199fe878a485df482e10e1ef2205cc/zipp-4.1.2.tar.gz": "sha256:cf474b90b07ea932637e17c6b9479de2b2882900b0d311bdaf4f5c000a15a97f", + "https://files.pythonhosted.org/packages/b8/44/7bd2007260da11cccca29ba2d4c345f1287aa663db38ee7cefeabf6386a5/zipp-4.1.2-py3-none-any.whl": "sha256:b30cb6cd253122169c47de245ca24a86bac2a5d3802ba15bf7ba6414eb9e144d" } } }, diff --git a/tests/runfiles_groups/BUILD.bazel b/tests/runfiles_groups/BUILD.bazel index f1d7a04d35..8f6bb264b9 100644 --- a/tests/runfiles_groups/BUILD.bazel +++ b/tests/runfiles_groups/BUILD.bazel @@ -1,3 +1,4 @@ +load("@bazel_features//:features.bzl", "bazel_features") load("@rules_runfiles_group//runfiles_group:runfiles_group_analysis_test.bzl", "runfiles_group_analysis_test") load("//python:py_binary.bzl", "py_binary") load("//python:py_library.bzl", "py_library") @@ -5,6 +6,12 @@ load("//python:py_test.bzl", "py_test") load("//tests/support:py_reconfig.bzl", "py_reconfig_binary") load(":test_rules.bzl", "outputs_and_runfiles", "outputs_only") +# Packagers walk the Python toolchain, which needs Bazel 8.8 or later. +_WALKS_TOOLCHAINS = [] if ( + bazel_features.rules.aspect_propagation_context or + bazel_features.rules.supports_toolchains_aspects_star +) else ["@platforms//:incompatible"] + outputs_only(name = "outputs_only") outputs_and_runfiles(name = "outputs_and_runfiles") @@ -167,6 +174,7 @@ runfiles_group_analysis_test( ":test", ], overlapping_group_behavior = "error", + target_compatible_with = _WALKS_TOOLCHAINS, ) runfiles_group_analysis_test( @@ -185,4 +193,5 @@ runfiles_group_analysis_test( "@@//tests/runfiles_groups:srcs_with_runfiles", "@@//tests/runfiles_groups:bin", ], + target_compatible_with = _WALKS_TOOLCHAINS, ) From 5a7dc55be5496d6fcd5da06a7ceb6165eafa4f12 Mon Sep 17 00:00:00 2001 From: Frank Portman Date: Wed, 21 Oct 2026 10:33:53 -0400 Subject: [PATCH 06/13] style: sort the runfiles group attributes, name the Bazel versions for toolchain walking --- python/private/py_executable.bzl | 14 +++++++------- python/private/py_runtime_pair_rule.bzl | 6 +++--- tests/runfiles_groups/BUILD.bazel | 3 ++- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/python/private/py_executable.bzl b/python/private/py_executable.bzl index b2f8a4fa6d..b7360b742a 100644 --- a/python/private/py_executable.bzl +++ b/python/private/py_executable.bzl @@ -93,16 +93,16 @@ EXECUTABLE_ATTRS = dicts.add( WINDOWS_CONSTRAINTS_ATTRS, # starlark flags attributes { - "_build_python_zip_flag": attr.label(default = "//python/config_settings:build_python_zip"), - "_default_to_explicit_init_py_flag": attr.label(default = "//python/config_settings:incompatible_default_to_explicit_init_py"), - "_python_import_all_repositories_flag": attr.label(default = "//python/config_settings:experimental_python_import_all_repositories"), - "_python_path_flag": attr.label(default = "//python/config_settings:python_path"), # Read by rules_runfiles_group's packaging aspects; see runfiles_groups.bzl. "_aspect_rule_hints": attr.label_list(default = [ "//python/private:py_executable_runfiles_group_describer", # What //python:toolchain_type is an alias of. "@bazel_tools//tools/python:toolchain_type", ]), + "_build_python_zip_flag": attr.label(default = "//python/config_settings:build_python_zip"), + "_default_to_explicit_init_py_flag": attr.label(default = "//python/config_settings:incompatible_default_to_explicit_init_py"), + "_python_import_all_repositories_flag": attr.label(default = "//python/config_settings:experimental_python_import_all_repositories"), + "_python_path_flag": attr.label(default = "//python/config_settings:python_path"), "_runfiles_group_attrs": attr.string_list(default = ["_debugger_flag", "data", "deps", "srcs"]), }, { @@ -1478,11 +1478,11 @@ def get_runtime_files(ctx, runtime): """Returns the runtime files an executable puts into its runfiles. Args: - ctx: The rule or aspect ctx. - runtime: PyRuntimeInfo; the runtime of the executable. + ctx: {type}`ctx` the rule or aspect ctx. + runtime: {type}`PyRuntimeInfo` the runtime of the executable. Returns: - depset[File] + {type}`depset[File]` """ direct = [] # List of files transitive = [] # List of depsets diff --git a/python/private/py_runtime_pair_rule.bzl b/python/private/py_runtime_pair_rule.bzl index a648bbdca9..ea153a0e0d 100644 --- a/python/private/py_runtime_pair_rule.bzl +++ b/python/private/py_runtime_pair_rule.bzl @@ -133,14 +133,14 @@ The runtime to use for Python 3 targets. Must have `python_version` set to `PY3`. """, ), - "_visible_for_testing": attr.label( - default = labels.VISIBLE_FOR_TESTING, - ), # Read by rules_runfiles_group's packaging aspects; see runfiles_groups.bzl. "_aspect_rule_hints": attr.label_list( default = ["//python/private:py_runtime_pair_runfiles_group_describer"], ), "_runfiles_group_attrs": attr.string_list(default = []), + "_visible_for_testing": attr.label( + default = labels.VISIBLE_FOR_TESTING, + ), }, fragments = ["py"], doc = """ diff --git a/tests/runfiles_groups/BUILD.bazel b/tests/runfiles_groups/BUILD.bazel index 8f6bb264b9..c4358e132c 100644 --- a/tests/runfiles_groups/BUILD.bazel +++ b/tests/runfiles_groups/BUILD.bazel @@ -6,7 +6,8 @@ load("//python:py_test.bzl", "py_test") load("//tests/support:py_reconfig.bzl", "py_reconfig_binary") load(":test_rules.bzl", "outputs_and_runfiles", "outputs_only") -# Packagers walk the Python toolchain, which needs Bazel 8.8 or later. +# Packagers walk the Python toolchain through toolchains_aspects, which takes +# "*" since Bazel 8.8.0 and a propagation filter since Bazel 9.0.0. _WALKS_TOOLCHAINS = [] if ( bazel_features.rules.aspect_propagation_context or bazel_features.rules.supports_toolchains_aspects_star From 7c3bb0a82d8c0f8c6ff3cf00b63336773c9359f9 Mon Sep 17 00:00:00 2001 From: Frank Portman Date: Sun, 25 Oct 2026 17:27:44 -0400 Subject: [PATCH 07/13] docs: document the runfiles group describers Give every function in runfiles_groups.bzl a docstring with typed args, list the groups a binary gets in the module docstring, and drop the copyright header. A wheel's group is named after its distribution: the name from its .dist-info directory, normalized by get_package_and_version (lowercase, runs of -, _ and . turned into _), e.g. rules_python#pypi/google_cloud_storage. --- python/private/runfiles_groups.bzl | 257 ++++++++++++++++++++++++----- 1 file changed, 212 insertions(+), 45 deletions(-) diff --git a/python/private/runfiles_groups.bzl b/python/private/runfiles_groups.bzl index feabbdaf3e..85ceb3d0d4 100644 --- a/python/private/runfiles_groups.bzl +++ b/python/private/runfiles_groups.bzl @@ -1,30 +1,23 @@ -# Copyright 2026 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. """Runfiles group describers of the Python rules, for rules_runfiles_group. -A packaging rule (a container image rule, say) walks a binary with an aspect and -asks each target's describer which runfiles it adds and which attributes it -merges runfiles in from. The Python rules list the describers below in their -`_aspect_rule_hints` attribute. The runtime comes from the Python toolchain, -which the aspect only walks on Bazel 8.8 or later. +Packaging rules built on rules_runfiles_group (e.g. rules_img's +`image_from_binary`) split a binary's runfiles into groups, typically one image +layer each. They walk the binary with an aspect and call the describer each +target lists in its `_aspect_rule_hints`, which says what runfiles the target +adds itself and which attributes the aspect merges groups in from. -Only `//python/private:BUILD.bazel` loads this file; the rules refer to the -describers by label. With Bazel 8's autoloads, rules_python loading -rules_runfiles_group from the files that define the rules is a load cycle. +A `py_binary` or `py_test` gets these groups: -A library's group holds what it contributes to a consuming binary: the files the -binary takes from its PyInfo, plus its plain data files. +* `rules_python#runtime`: the interpreter and standard library, described by the + Python toolchain's `py_runtime_pair`. The aspect only walks toolchains on + Bazel 8.8 or later. +* `rules_python#pypi/`: one per PyPI distribution. +* One per `py_library`, and one per rule target in `data`, named by its label. +* The binary's own: its sources, bootstrap files and venv. + +Only `//python/private:BUILD.bazel` loads this file, and the rules refer to the +describers by label: with Bazel 8's autoloads, loading rules_runfiles_group from +the files that define the rules is a load cycle. """ load("@rules_runfiles_group//runfiles_group:describe.bzl", "describe") @@ -42,11 +35,10 @@ load(":reexports.bzl", "BuiltinPyInfo") # merge groups to fit a layer limit. _MERGE_AFFINITY = "rules_python" -# The interpreter and standard library, shared by every binary in a configuration. _RUNTIME_GROUP = "rules_python#runtime" -# Libraries from PyPI are named by package: whl_library's repository names are an -# implementation detail. +# Named by distribution rather than by label: whl_library's repository names are +# an implementation detail. _PYPI_GROUP_PREFIX = "rules_python#pypi/" # The toolchain type //python:toolchain_type is an alias of; the aspect matches @@ -56,6 +48,17 @@ _RUNTIME_MERGE_FROM = describe.merge_from( ) def _pyc_collection_enabled(ctx, pyc_collection = PycCollectionAttr.INHERIT): + """Whether a binary includes implicit pyc files instead of their sources. + + Args: + ctx: {type}`ctx` the aspect ctx. + pyc_collection: {type}`str` a binary's `pyc_collection` attribute; the + default gives the configuration's choice. + + Returns: + {type}`bool` + """ + # PycCollectionAttr only reads these two attributes. return PycCollectionAttr.is_pyc_collection_enabled(struct(attr = struct( pyc_collection = pyc_collection, @@ -63,21 +66,56 @@ def _pyc_collection_enabled(ctx, pyc_collection = PycCollectionAttr.INHERIT): ))) def _is_file(target): - # A source file owns itself, a generated file is owned by the rule that - # generates it, and a rule target owns its own outputs. + """Whether a target is a single file rather than a rule. + + Args: + target: {type}`Target` an attribute's dependency. + + Returns: + {type}`bool` + """ files = target[DefaultInfo].files if not py_internal.is_singleton_depset(files): return False file = files.to_list()[0] + + # A source file owns itself and a generated file is owned by the rule that + # generates it, while a rule's own outputs are owned by the rule. return (file.owner == target.label) == file.is_source def _is_empty(runfiles): + """Whether a runfiles object holds nothing. + + Args: + runfiles: {type}`runfiles` + + Returns: + {type}`bool` + """ return not (runfiles.files or runfiles.symlinks or runfiles.root_symlinks) def _has_py_info(target): + """Whether a target provides PyInfo or the builtin PyInfo. + + Args: + target: {type}`Target` + + Returns: + {type}`bool` + """ return PyInfo in target or (BuiltinPyInfo != None and BuiltinPyInfo in target) def _py_info_files(target, pyc_collection_enabled): + """The files a binary takes from a target's PyInfo. + + Args: + target: {type}`Target` + pyc_collection_enabled: {type}`bool` whether the binary includes + implicit pyc files instead of their sources. + + Returns: + {type}`list[depset[File]]` empty for a target without PyInfo. + """ if PyInfo in target: return py_info_runfiles_files(target[PyInfo], pyc_collection_enabled) if BuiltinPyInfo != None and BuiltinPyInfo in target: @@ -85,7 +123,15 @@ def _py_info_files(target, pyc_collection_enabled): return [] def _union(ctx, contents): - # Each content is a depset of File or a runfiles object. + """Combines group contents into one. + + Args: + ctx: {type}`ctx` the aspect ctx. + contents: {type}`list[depset[File] | runfiles]` + + Returns: + {type}`depset[File] | runfiles` a depset if every content is one. + """ files = [c for c in contents if type(c) == "depset"] runfiles = [c for c in contents if type(c) != "depset"] if not runfiles: @@ -93,9 +139,28 @@ def _union(ctx, contents): return ctx.runfiles(transitive_files = depset(transitive = files)).merge_all(runfiles) def _kind(label): + """The `kind` of the group of a target's own files. + + Args: + label: {type}`Label` the target's label. + + Returns: + {type}`str` `first_party` for the main repository, else `third_party`. + """ return "first_party" if label.repo_name == "" else "third_party" def _pypi_entry(package, content, weight = None): + """An entry in the group of a PyPI distribution. + + Args: + package: {type}`str` the distribution name, as normalized by + `get_package_and_version`. + content: {type}`depset[File] | runfiles` + weight: {type}`int | None` see `describe.entry`. + + Returns: + {type}`RunfilesGroupInfo` + """ return describe.entry( name = _PYPI_GROUP_PREFIX + package, content = content, @@ -106,10 +171,24 @@ def _pypi_entry(package, content, weight = None): ) def _own_files(target, ctx, pyc_collection_enabled): - """The files a target itself puts into a consuming binary's runfiles.""" + """The files a target itself puts into a binary's runfiles. + + That is its sources (or their implicit pyc files), its other outputs, and + the plain files in its `data`. Its dependencies' files are left to their own + groups. + + Args: + target: {type}`Target` a `py_library`, `py_binary` or `py_test`. + ctx: {type}`ctx` the aspect ctx. + pyc_collection_enabled: {type}`bool` whether the binary includes + implicit pyc files instead of their sources. + + Returns: + {type}`depset[File]` + """ info = target[PyInfo] - # A Python target in srcs (a wheel's :pkg wraps its :srcs) describes its + # A Python target in srcs (whl_library's :pkg wraps its :srcs) describes its # own files. foreign = { f: None @@ -132,14 +211,24 @@ def _own_files(target, ctx, pyc_collection_enabled): else: direct.extend([f for f in sources if is_py_source(f)]) - # Plain files in `data` belong to the target's own group (and so to each - # target listing them); rule targets get groups of their own. + # A plain file listed in several targets' data lands in each of their groups. for dep in ctx.rule.attr.data: if _is_file(dep): direct.append(dep[DefaultInfo].files.to_list()[0]) return depset(direct, transitive = transitive) def _dep_fallback(ctx, dep, py_outputs): + """The group of a dependency that doesn't describe its own groups. + + Args: + ctx: {type}`ctx` the aspect ctx of the merging target. + dep: {type}`Target` the dependency. + py_outputs: {type}`bool` whether to include the dependency's .py outputs + when it has no PyInfo. + + Returns: + {type}`RunfilesGroupInfo | None` None if it contributes nothing. + """ content = _py_info_files(dep, _pyc_collection_enabled(ctx)) if py_outputs and not content: content.append(depset([f for f in dep[DefaultInfo].files.to_list() if is_py_source(f)])) @@ -151,14 +240,43 @@ def _dep_fallback(ctx, dep, py_outputs): return describe.entry(name = dep.label, content = _union(ctx, content)) def _library_dep_fallback(ctx, dep): - # py_library adds the .py outputs of a dep without PyInfo to its own PyInfo. + """`_dep_fallback` for a `py_library`'s `deps`. + + A `py_library` adds the .py outputs of a dependency without PyInfo to its own + PyInfo, so the dependency's group holds them. + + Args: + ctx: {type}`ctx` the aspect ctx of the library. + dep: {type}`Target` the dependency. + + Returns: + {type}`RunfilesGroupInfo | None` + """ return _dep_fallback(ctx, dep, py_outputs = True) def _executable_dep_fallback(ctx, dep): + """`_dep_fallback` for a binary's `deps`. + + Args: + ctx: {type}`ctx` the aspect ctx of the binary. + dep: {type}`Target` the dependency. + + Returns: + {type}`RunfilesGroupInfo | None` + """ return _dep_fallback(ctx, dep, py_outputs = False) def _data_fallback(ctx, dep): - # Plain files are in the merging target's own group. + """The group of a rule target in `data` that doesn't describe its own groups. + + Args: + ctx: {type}`ctx` the aspect ctx of the merging target. + dep: {type}`Target` the dependency. + + Returns: + {type}`RunfilesGroupInfo | None` None for a plain file, which is in the + merging target's own group, or a target that contributes nothing. + """ default_info = dep[DefaultInfo] if _is_file(dep) or (not default_info.files and _is_empty(default_info.data_runfiles)): return None @@ -166,17 +284,40 @@ def _data_fallback(ctx, dep): return describe.entry(name = dep.label, content = content) def _srcs_fallback(ctx, dep): - # Plain files are in the merging target's own group. + """The group of a rule target in `srcs` that doesn't describe its own groups. + + Args: + ctx: {type}`ctx` the aspect ctx of the merging target. + dep: {type}`Target` the dependency. + + Returns: + {type}`RunfilesGroupInfo | None` None for a plain file, which is in the + merging target's own group. + """ if _is_file(dep): return None return _dep_fallback(ctx, dep, py_outputs = False) def _describe_py_library(target, ctx): + """Describes a `py_library`: one group for its own files. + + The group is named after the distribution for a library with a + `.dist-info/METADATA` file in `data` (i.e. one from a wheel), and by the + library's label otherwise. Its weight is the number of files in `srcs` and + `data`. + + Args: + target: {type}`Target` the library. + ctx: {type}`ctx` the aspect ctx. + + Returns: + {type}`struct` from `describe.node`. + """ pyc_collection_enabled = _pyc_collection_enabled(ctx) content = _own_files(target, ctx, pyc_collection_enabled) - # With pyi_deps_to_runfiles, a binary takes the files of pyi_deps from - # this target's PyInfo, but not their runfiles. + # With pyi_deps_to_runfiles, a binary takes the files of pyi_deps from this + # target's PyInfo, but not their runfiles. if PyiDepsToRunfilesFlag.is_enabled(struct(attr = ctx.rule.attr, var = ctx.var)): content = depset(transitive = [content] + [ files @@ -212,18 +353,19 @@ py_library_runfiles_group_describer = describe.make_describer_rule( ) def _venv_entries(ctx, venv_runfiles): - """Splits the venv's links to PyPI package files into those packages' groups. + """Moves the venv's links to PyPI distribution files into their groups. - A packager that copies symlinked files can then keep each link with the - file it points to, instead of copying shared libraries into the binary's - group. + A packager that stores symlinks as copies of their targets can then keep each + link next to the file it points to, instead of copying the file (typically a + shared library) into the binary's group. Args: - ctx: The aspect ctx of the binary. - venv_runfiles: runfiles; PyExecutableInfo.venv_runfiles. + ctx: {type}`ctx` the aspect ctx of the binary. + venv_runfiles: {type}`runfiles` the binary's `PyExecutableInfo.venv_runfiles`. Returns: - tuple of the PyPI entries and the venv runfiles left for the binary. + {type}`tuple[list[RunfilesGroupInfo], runfiles]` the entries for the + distributions' groups, and the venv runfiles left for the binary's. """ packages = { entry.link_to_file: entry.package @@ -247,6 +389,19 @@ def _venv_entries(ctx, venv_runfiles): return entries, ctx.runfiles(transitive_files = venv_runfiles.files, root_symlinks = own_links) def _describe_py_executable(target, ctx): + """Describes a `py_binary` or `py_test`. + + Its group holds its own files, bootstrap files and venv, and is the group + the packager puts the executable in. The runtime and the dependencies' + groups are merged in. + + Args: + target: {type}`Target` the binary. + ctx: {type}`ctx` the aspect ctx. + + Returns: + {type}`struct` from `describe.node`. + """ info = target[PyExecutableInfo] pyc_collection_enabled = _pyc_collection_enabled(ctx, ctx.rule.attr.pyc_collection) content = [ @@ -296,6 +451,18 @@ py_executable_runfiles_group_describer = describe.make_describer_rule( ) def _describe_py_runtime_pair(target, ctx): + """Describes a Python toolchain: one group for its runtime's files. + + Every binary using the toolchain merges in the same group, so a packager + builds it once. + + Args: + target: {type}`Target` the `py_runtime_pair`. + ctx: {type}`ctx` the aspect ctx. + + Returns: + {type}`struct` from `describe.node`. + """ runtime = target[platform_common.ToolchainInfo].py3_runtime files = get_runtime_files(ctx, runtime) if runtime else depset() return describe.node(add = [describe.entry( From 66c84be22c2839e6175aaa9c8f9709dffde1309d Mon Sep 17 00:00:00 2001 From: Frank Portman Date: Sun, 25 Oct 2026 17:30:55 -0400 Subject: [PATCH 08/13] fix(rules): put namespace package venv links in their distribution's group Where distributions ship the same package directory (pkgutil-style namespace packages, common under google/), the venv links each file in it to the file itself, instead of linking the directory. Those links weren't found in any distribution's group, so they stayed in the binary's group and a packager copied their files into it. Look links up by every file a venv entry covers, not just the one it links to. --- python/private/runfiles_groups.bzl | 9 ++- tests/runfiles_groups/BUILD.bazel | 15 ++-- .../ns_alpha-1.0.dist-info/METADATA | 3 + .../ns_alpha/site-packages/nspkg/__init__.py | 0 .../ns_alpha/site-packages/nspkg/alpha.py | 0 .../ns_beta-1.0.dist-info/METADATA | 3 + .../ns_beta/site-packages/nspkg/__init__.py | 0 .../ns_beta/site-packages/nspkg/beta.py | 0 .../runfiles_groups/runfiles_groups_tests.bzl | 77 +++++++++++++++++++ tests/runfiles_groups/test_rules.bzl | 54 ++++++++++++- 10 files changed, 148 insertions(+), 13 deletions(-) create mode 100644 tests/runfiles_groups/ns_alpha/site-packages/ns_alpha-1.0.dist-info/METADATA create mode 100644 tests/runfiles_groups/ns_alpha/site-packages/nspkg/__init__.py create mode 100644 tests/runfiles_groups/ns_alpha/site-packages/nspkg/alpha.py create mode 100644 tests/runfiles_groups/ns_beta/site-packages/ns_beta-1.0.dist-info/METADATA create mode 100644 tests/runfiles_groups/ns_beta/site-packages/nspkg/__init__.py create mode 100644 tests/runfiles_groups/ns_beta/site-packages/nspkg/beta.py create mode 100644 tests/runfiles_groups/runfiles_groups_tests.bzl diff --git a/python/private/runfiles_groups.bzl b/python/private/runfiles_groups.bzl index 85ceb3d0d4..d48afcc4be 100644 --- a/python/private/runfiles_groups.bzl +++ b/python/private/runfiles_groups.bzl @@ -367,14 +367,19 @@ def _venv_entries(ctx, venv_runfiles): {type}`tuple[list[RunfilesGroupInfo], runfiles]` the entries for the distributions' groups, and the venv runfiles left for the binary's. """ + + # Keyed by every file an entry covers, not just the one it links to: where + # distributions share a directory (namespace packages), the venv links each + # file in it instead. packages = { - entry.link_to_file: entry.package + file: entry.package for entry in depset(transitive = [ dep[PyInfo].venv_symlinks for dep in ctx.rule.attr.deps if PyInfo in dep ]).to_list() - if entry.link_to_file and entry.package + if entry.package + for file in entry.files.to_list() } by_package = {} own_links = {} diff --git a/tests/runfiles_groups/BUILD.bazel b/tests/runfiles_groups/BUILD.bazel index c4358e132c..1ad854b303 100644 --- a/tests/runfiles_groups/BUILD.bazel +++ b/tests/runfiles_groups/BUILD.bazel @@ -1,18 +1,11 @@ -load("@bazel_features//:features.bzl", "bazel_features") load("@rules_runfiles_group//runfiles_group:runfiles_group_analysis_test.bzl", "runfiles_group_analysis_test") load("//python:py_binary.bzl", "py_binary") load("//python:py_library.bzl", "py_library") load("//python:py_test.bzl", "py_test") load("//tests/support:py_reconfig.bzl", "py_reconfig_binary") +load(":runfiles_groups_tests.bzl", "WALKS_TOOLCHAINS", "runfiles_groups_test_suite") load(":test_rules.bzl", "outputs_and_runfiles", "outputs_only") -# Packagers walk the Python toolchain through toolchains_aspects, which takes -# "*" since Bazel 8.8.0 and a propagation filter since Bazel 9.0.0. -_WALKS_TOOLCHAINS = [] if ( - bazel_features.rules.aspect_propagation_context or - bazel_features.rules.supports_toolchains_aspects_star -) else ["@platforms//:incompatible"] - outputs_only(name = "outputs_only") outputs_and_runfiles(name = "outputs_and_runfiles") @@ -175,7 +168,7 @@ runfiles_group_analysis_test( ":test", ], overlapping_group_behavior = "error", - target_compatible_with = _WALKS_TOOLCHAINS, + target_compatible_with = WALKS_TOOLCHAINS, ) runfiles_group_analysis_test( @@ -194,5 +187,7 @@ runfiles_group_analysis_test( "@@//tests/runfiles_groups:srcs_with_runfiles", "@@//tests/runfiles_groups:bin", ], - target_compatible_with = _WALKS_TOOLCHAINS, + target_compatible_with = WALKS_TOOLCHAINS, ) + +runfiles_groups_test_suite(name = "runfiles_groups_tests") diff --git a/tests/runfiles_groups/ns_alpha/site-packages/ns_alpha-1.0.dist-info/METADATA b/tests/runfiles_groups/ns_alpha/site-packages/ns_alpha-1.0.dist-info/METADATA new file mode 100644 index 0000000000..6c8624ec12 --- /dev/null +++ b/tests/runfiles_groups/ns_alpha/site-packages/ns_alpha-1.0.dist-info/METADATA @@ -0,0 +1,3 @@ +Metadata-Version: 2.1 +Name: ns-alpha +Version: 1.0 diff --git a/tests/runfiles_groups/ns_alpha/site-packages/nspkg/__init__.py b/tests/runfiles_groups/ns_alpha/site-packages/nspkg/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/runfiles_groups/ns_alpha/site-packages/nspkg/alpha.py b/tests/runfiles_groups/ns_alpha/site-packages/nspkg/alpha.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/runfiles_groups/ns_beta/site-packages/ns_beta-1.0.dist-info/METADATA b/tests/runfiles_groups/ns_beta/site-packages/ns_beta-1.0.dist-info/METADATA new file mode 100644 index 0000000000..b101664cb1 --- /dev/null +++ b/tests/runfiles_groups/ns_beta/site-packages/ns_beta-1.0.dist-info/METADATA @@ -0,0 +1,3 @@ +Metadata-Version: 2.1 +Name: ns-beta +Version: 1.0 diff --git a/tests/runfiles_groups/ns_beta/site-packages/nspkg/__init__.py b/tests/runfiles_groups/ns_beta/site-packages/nspkg/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/runfiles_groups/ns_beta/site-packages/nspkg/beta.py b/tests/runfiles_groups/ns_beta/site-packages/nspkg/beta.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/runfiles_groups/runfiles_groups_tests.bzl b/tests/runfiles_groups/runfiles_groups_tests.bzl new file mode 100644 index 0000000000..0e4554a8e3 --- /dev/null +++ b/tests/runfiles_groups/runfiles_groups_tests.bzl @@ -0,0 +1,77 @@ +"""Tests for what ends up in which runfiles group.""" + +load("@bazel_features//:features.bzl", "bazel_features") +load("@rules_testing//lib:analysis_test.bzl", "analysis_test") +load("@rules_testing//lib:test_suite.bzl", "test_suite") +load("@rules_testing//lib:truth.bzl", "matching") +load("@rules_testing//lib:util.bzl", rt_util = "util") +load("//python:py_binary.bzl", "py_binary") +load("//python:py_library.bzl", "py_library") +load("//python/private:common_labels.bzl", "labels") # buildifier: disable=bzl-visibility +load("//tests/support:support.bzl", "NOT_WINDOWS") +load(":test_rules.bzl", "ResolvedGroupsInfo", "resolved_groups") + +# Packagers walk the Python toolchain through toolchains_aspects, which takes +# "*" since Bazel 8.8.0 and a propagation filter since Bazel 9.0.0. +WALKS_TOOLCHAINS = [] if ( + bazel_features.rules.aspect_propagation_context or + bazel_features.rules.supports_toolchains_aspects_star +) else ["@platforms//:incompatible"] + +_tests = [] + +def _test_namespace_package_venv_links(name): + # Both distributions ship nspkg/__init__.py, like pkgutil-style namespace + # packages, so the venv can't link nspkg as one directory. + for distribution, module in [("ns_alpha", "alpha"), ("ns_beta", "beta")]: + rt_util.helper_target( + py_library, + name = name + "_" + distribution, + srcs = [ + "{}/site-packages/nspkg/__init__.py".format(distribution), + "{}/site-packages/nspkg/{}.py".format(distribution, module), + ], + data = ["{0}/site-packages/{0}-1.0.dist-info/METADATA".format(distribution)], + experimental_venvs_site_packages = "//python/config_settings:venvs_site_packages", + imports = ["{}/site-packages".format(distribution)], + ) + rt_util.helper_target( + py_binary, + name = name + "_bin", + srcs = ["main.py"], + main = "main.py", + deps = [name + "_ns_alpha", name + "_ns_beta"], + ) + rt_util.helper_target( + resolved_groups, + name = name + "_subject", + binary = name + "_bin", + ) + analysis_test( + name = name, + impl = _test_namespace_package_venv_links_impl, + target = name + "_subject", + config_settings = { + labels.BOOTSTRAP_IMPL: "script", + labels.VENVS_SITE_PACKAGES: "yes", + }, + attr_values = {"target_compatible_with": WALKS_TOOLCHAINS + NOT_WINDOWS}, + ) + +def _test_namespace_package_venv_links_impl(env, target): + groups = target[ResolvedGroupsInfo].groups + + for distribution, module in [("ns_alpha", "alpha"), ("ns_beta", "beta")]: + env.expect.that_collection( + groups["rules_python#pypi/" + distribution].root_symlinks, + ).contains_predicate(matching.str_endswith("site-packages/nspkg/{}.py".format(module))) + + binary = groups[str(target.label).removesuffix("_subject") + "_bin"] + env.expect.that_collection(binary.root_symlinks).not_contains_predicate( + matching.str_matches("*site-packages/nspkg/*"), + ) + +_tests.append(_test_namespace_package_venv_links) + +def runfiles_groups_test_suite(name): + test_suite(name = name, tests = _tests) diff --git a/tests/runfiles_groups/test_rules.bzl b/tests/runfiles_groups/test_rules.bzl index f5e55046f4..228c0a8744 100644 --- a/tests/runfiles_groups/test_rules.bzl +++ b/tests/runfiles_groups/test_rules.bzl @@ -1,4 +1,7 @@ -"""Rules with shapes of `data` that the runfiles group describers must cover.""" +"""Rules for testing the runfiles group describers.""" + +load("@rules_runfiles_group//runfiles_group:identity_packager.bzl", "RunfilesGroupIdentityInfo", "runfiles_groups_identity_aspect") +load("@rules_runfiles_group//runfiles_group:lib.bzl", "runfiles_groups") def _outputs_only_impl(ctx): files = [ctx.actions.declare_file(ctx.label.name + suffix) for suffix in (".a", ".b")] @@ -18,3 +21,52 @@ def _outputs_and_runfiles_impl(ctx): # Default outputs and runfiles that differ. outputs_and_runfiles = rule(implementation = _outputs_and_runfiles_impl) + +ResolvedGroupsInfo = provider( + doc = "A binary's runfiles groups, as a packager would resolve them.", + fields = { + "groups": """ +:type: dict[str, struct] + +By group name: `merge_affinity`, `do_not_merge`, and `root_symlinks`, the paths +of the group's root symlinks. +""", + }, +) + +_AspectHintsInfo = provider( + doc = "A binary's aspect_hints.", + fields = {"aspect_hints": "list[Target]"}, +) + +def _aspect_hints_aspect_impl(_target, ctx): + return [_AspectHintsInfo(aspect_hints = ctx.rule.attr.aspect_hints)] + +_aspect_hints_aspect = aspect(implementation = _aspect_hints_aspect_impl) + +def _resolved_groups_impl(ctx): + binary = ctx.attr.binary + resolved = runfiles_groups.finalize( + ctx, + binary[RunfilesGroupIdentityInfo], + runfiles_groups.IDENTITY_OPS, + aspect_hints = binary[_AspectHintsInfo].aspect_hints, + ) + groups = {} + for group in resolved.groups: + runfiles = runfiles_groups.runfiles(ctx, group.handle) + groups[runfiles_groups.name_str(group.name)] = struct( + merge_affinity = group.merge_affinity, + do_not_merge = group.do_not_merge, + root_symlinks = [link.path for link in runfiles.root_symlinks.to_list()], + ) + return [ResolvedGroupsInfo(groups = groups)] + +# Resolves a binary's groups with the identity packager, applying the +# transforms in the binary's aspect_hints like a real packager does. +resolved_groups = rule( + implementation = _resolved_groups_impl, + attrs = { + "binary": attr.label(aspects = [_aspect_hints_aspect, runfiles_groups_identity_aspect]), + }, +) From 35c4cc5b568d3cf97179cc9c96fc4f92e71b0ee2 Mon Sep 17 00:00:00 2001 From: Frank Portman Date: Sun, 25 Oct 2026 17:30:55 -0400 Subject: [PATCH 09/13] test: show configuring a binary's runfiles groups through aspect_hints A transform in a binary's aspect_hints can change the metadata of its groups, e.g. to keep a large distribution's group from being merged into others. This is the only way to configure grouping today. --- .../runfiles_groups/runfiles_groups_tests.bzl | 31 ++++++++++++++++++- tests/runfiles_groups/test_rules.bzl | 19 ++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/tests/runfiles_groups/runfiles_groups_tests.bzl b/tests/runfiles_groups/runfiles_groups_tests.bzl index 0e4554a8e3..474cc411e3 100644 --- a/tests/runfiles_groups/runfiles_groups_tests.bzl +++ b/tests/runfiles_groups/runfiles_groups_tests.bzl @@ -9,7 +9,7 @@ load("//python:py_binary.bzl", "py_binary") load("//python:py_library.bzl", "py_library") load("//python/private:common_labels.bzl", "labels") # buildifier: disable=bzl-visibility load("//tests/support:support.bzl", "NOT_WINDOWS") -load(":test_rules.bzl", "ResolvedGroupsInfo", "resolved_groups") +load(":test_rules.bzl", "ResolvedGroupsInfo", "bucket_ml", "resolved_groups") # Packagers walk the Python toolchain through toolchains_aspects, which takes # "*" since Bazel 8.8.0 and a propagation filter since Bazel 9.0.0. @@ -73,5 +73,34 @@ def _test_namespace_package_venv_links_impl(env, target): _tests.append(_test_namespace_package_venv_links) +def _test_configure_with_aspect_hints(name): + rt_util.helper_target(bucket_ml, name = name + "_bucket_ml") + rt_util.helper_target( + py_binary, + name = name + "_bin", + srcs = ["main.py"], + main = "main.py", + deps = [":foo_pkg"], + aspect_hints = [name + "_bucket_ml"], + ) + rt_util.helper_target( + resolved_groups, + name = name + "_subject", + binary = name + "_bin", + ) + analysis_test( + name = name, + impl = _test_configure_with_aspect_hints_impl, + target = name + "_subject", + attr_values = {"target_compatible_with": WALKS_TOOLCHAINS}, + ) + +def _test_configure_with_aspect_hints_impl(env, target): + foo_pkg = target[ResolvedGroupsInfo].groups["rules_python#pypi/foo_pkg"] + env.expect.that_str(foo_pkg.merge_affinity).equals("ml") + env.expect.that_bool(foo_pkg.do_not_merge).equals(True) + +_tests.append(_test_configure_with_aspect_hints) + def runfiles_groups_test_suite(name): test_suite(name = name, tests = _tests) diff --git a/tests/runfiles_groups/test_rules.bzl b/tests/runfiles_groups/test_rules.bzl index 228c0a8744..033ddbe2b1 100644 --- a/tests/runfiles_groups/test_rules.bzl +++ b/tests/runfiles_groups/test_rules.bzl @@ -2,6 +2,7 @@ load("@rules_runfiles_group//runfiles_group:identity_packager.bzl", "RunfilesGroupIdentityInfo", "runfiles_groups_identity_aspect") load("@rules_runfiles_group//runfiles_group:lib.bzl", "runfiles_groups") +load("@rules_runfiles_group//runfiles_group:providers.bzl", "RunfilesGroupTransformInfo") def _outputs_only_impl(ctx): files = [ctx.actions.declare_file(ctx.label.name + suffix) for suffix in (".a", ".b")] @@ -70,3 +71,21 @@ resolved_groups = rule( "binary": attr.label(aspects = [_aspect_hints_aspect, runfiles_groups_identity_aspect]), }, ) + +_ML_DISTRIBUTIONS = ["rules_python#pypi/foo_pkg"] + +def _bucket_ml(resolved): + return runfiles_groups.resolved( + [ + runfiles_groups.derive(group, merge_affinity = "ml", do_not_merge = True) if runfiles_groups.name_str(group.name) in _ML_DISTRIBUTIONS else group + for group in resolved.groups + ], + executable_group = resolved.executable_group, + ) + +def _bucket_ml_impl(_ctx): + return [RunfilesGroupTransformInfo(transform = _bucket_ml)] + +# An example of configuring how a binary's groups are packaged: in a binary's +# aspect_hints, it keeps foo_pkg's group from being merged into others. +bucket_ml = rule(implementation = _bucket_ml_impl) From b58566340e3b4870f5552147fb8199b7bf379e77 Mon Sep 17 00:00:00 2001 From: Frank Portman Date: Sun, 25 Oct 2026 17:37:44 -0400 Subject: [PATCH 10/13] build: fetch rules_runfiles_group from its dev registry in WORKSPACE Same tarball bzlmod resolves from the dev registry. The GitHub archive of d105dfa can go away now that the commit isn't on a branch. --- python/private/py_repositories.bzl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/private/py_repositories.bzl b/python/private/py_repositories.bzl index d17207c950..8511f83286 100644 --- a/python/private/py_repositories.bzl +++ b/python/private/py_repositories.bzl @@ -74,9 +74,9 @@ def py_repositories(transition_settings = []): ) http_archive( name = "rules_runfiles_group", - sha256 = "17437bb769ecef3d6b14333a1f82d46f0703e9eb645208428a3af0875e79f6d7", + sha256 = "0c5a2998f970e8ec27daa9bc10c3cd0d4a7167371acd70974a75ab8aafb41c74", strip_prefix = "rules_runfiles_group-d105dfab75fb550bcfc0d2cc033b27fad0dfc2e4", - url = "https://github.com/bazel-contrib/rules_runfiles_group/archive/d105dfab75fb550bcfc0d2cc033b27fad0dfc2e4.tar.gz", + url = "https://bazel-contrib.github.io/rules_runfiles_group/assets/rules_runfiles_group/99.0.0-dev.20261019.1.d105dfa/source.tar.gz", ) http_archive( name = "toml.bzl", From 7a4c7a353077c16f04074be87612d12b703e9036 Mon Sep 17 00:00:00 2001 From: Frank Portman Date: Sun, 25 Oct 2026 17:50:18 -0400 Subject: [PATCH 11/13] fix(rules): keep a wheel's console scripts in its distribution's group whl_library lists each console script in the wheel's data twice: as the script's own target and inside the :data filegroup. The script target got a group of its own, and the filegroup's file was taken as a plain file of the wheel, so the script landed in two groups. Merge everything in a wheel's data into its distribution's group. --- python/private/runfiles_groups.bzl | 13 +++++++++---- tests/runfiles_groups/BUILD.bazel | 15 +++++++++++++++ 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/python/private/runfiles_groups.bzl b/python/private/runfiles_groups.bzl index d48afcc4be..d07d8deb80 100644 --- a/python/private/runfiles_groups.bzl +++ b/python/private/runfiles_groups.bzl @@ -302,9 +302,9 @@ def _describe_py_library(target, ctx): """Describes a `py_library`: one group for its own files. The group is named after the distribution for a library with a - `.dist-info/METADATA` file in `data` (i.e. one from a wheel), and by the - library's label otherwise. Its weight is the number of files in `srcs` and - `data`. + `.dist-info/METADATA` file in `data` (i.e. one from a wheel), and takes + everything in its `data` too. Otherwise it's named by the library's label. + Its weight is the number of files in `srcs` and `data`. Args: target: {type}`Target` the library. @@ -326,9 +326,14 @@ def _describe_py_library(target, ctx): ]) weight = len(ctx.rule.files.srcs) + len(ctx.rule.files.data) + data = describe.merge_from("data", fallback = _data_fallback) package, _ = get_package_and_version(ctx.rule.files.data) if package: entry = _pypi_entry(package, content, weight) + + # whl_library lists a console script in `data` both on its own and in a + # filegroup, which would put it in two groups. + data = describe.merge_from("data", fallback = _data_fallback, into = entry.name) else: kind = _kind(ctx.label) entry = describe.entry( @@ -342,7 +347,7 @@ def _describe_py_library(target, ctx): return describe.node( add = [entry] if content else [], merge_from = [ - describe.merge_from("data", fallback = _data_fallback), + data, describe.merge_from("deps", fallback = _library_dep_fallback), describe.merge_from("srcs", fallback = _srcs_fallback), ], diff --git a/tests/runfiles_groups/BUILD.bazel b/tests/runfiles_groups/BUILD.bazel index 1ad854b303..46e2ecf344 100644 --- a/tests/runfiles_groups/BUILD.bazel +++ b/tests/runfiles_groups/BUILD.bazel @@ -53,6 +53,19 @@ py_library( deps = [":lib_a"], ) +# A console script, listed in the wheel's data both on its own and in a +# filegroup, like whl_library does. +genrule( + name = "foo_console_script", + outs = ["scripts/foo"], + cmd = "touch $@", +) + +filegroup( + name = "foo_bin", + srcs = [":foo_console_script"], +) + # Looks like a library from a wheel: grouped by package name. py_library( name = "foo_pkg", @@ -60,6 +73,8 @@ py_library( data = [ "site-packages/_foo_native.so", "site-packages/foo_pkg-1.0.dist-info/METADATA", + ":foo_bin", + ":foo_console_script", ], experimental_venvs_site_packages = "//python/config_settings:venvs_site_packages", imports = ["site-packages"], From 654e903d99270757a3953dde502b540017046c24 Mon Sep 17 00:00:00 2001 From: Frank Portman Date: Mon, 26 Oct 2026 09:02:20 -0400 Subject: [PATCH 12/13] DO NOT MERGE: use the rules_runfiles_group dev registry in prebuilt_toolchain Revert once rules_runfiles_group is released. --- tests/integration/prebuilt_toolchain/.bazelrc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/integration/prebuilt_toolchain/.bazelrc b/tests/integration/prebuilt_toolchain/.bazelrc index e1df951d06..4cb03b81e3 100644 --- a/tests/integration/prebuilt_toolchain/.bazelrc +++ b/tests/integration/prebuilt_toolchain/.bazelrc @@ -1,3 +1,6 @@ common --lockfile_mode=off test --test_output=errors build --enable_runfiles + +# TODO: Delete before merging; see the root .bazelrc. +common --registry=https://bazel-contrib.github.io/rules_runfiles_group --registry=https://bcr.bazel.build From 1b40985f4ff3951fc9b779f40f91171c260eb3cb Mon Sep 17 00:00:00 2001 From: Frank Portman Date: Mon, 26 Oct 2026 09:06:00 -0400 Subject: [PATCH 13/13] test: show bucketing distributions together through aspect_hints The example set do_not_merge alongside the affinity, which made the affinity a no-op. Give two distributions a shared affinity instead and check that the others keep the default one. --- .../runfiles_groups/runfiles_groups_tests.bzl | 23 ++++++++++++------- tests/runfiles_groups/test_rules.bzl | 8 ++++--- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/tests/runfiles_groups/runfiles_groups_tests.bzl b/tests/runfiles_groups/runfiles_groups_tests.bzl index 474cc411e3..bade20f360 100644 --- a/tests/runfiles_groups/runfiles_groups_tests.bzl +++ b/tests/runfiles_groups/runfiles_groups_tests.bzl @@ -20,9 +20,12 @@ WALKS_TOOLCHAINS = [] if ( _tests = [] -def _test_namespace_package_venv_links(name): - # Both distributions ship nspkg/__init__.py, like pkgutil-style namespace - # packages, so the venv can't link nspkg as one directory. +def _namespace_distributions(name): + """Defines two distributions that both ship nspkg/__init__.py. + + Like pkgutil-style namespace packages, so the venv can't link nspkg as + one directory. + """ for distribution, module in [("ns_alpha", "alpha"), ("ns_beta", "beta")]: rt_util.helper_target( py_library, @@ -35,12 +38,15 @@ def _test_namespace_package_venv_links(name): experimental_venvs_site_packages = "//python/config_settings:venvs_site_packages", imports = ["{}/site-packages".format(distribution)], ) + return [name + "_ns_alpha", name + "_ns_beta"] + +def _test_namespace_package_venv_links(name): rt_util.helper_target( py_binary, name = name + "_bin", srcs = ["main.py"], main = "main.py", - deps = [name + "_ns_alpha", name + "_ns_beta"], + deps = _namespace_distributions(name), ) rt_util.helper_target( resolved_groups, @@ -80,7 +86,7 @@ def _test_configure_with_aspect_hints(name): name = name + "_bin", srcs = ["main.py"], main = "main.py", - deps = [":foo_pkg"], + deps = _namespace_distributions(name) + [":foo_pkg"], aspect_hints = [name + "_bucket_ml"], ) rt_util.helper_target( @@ -96,9 +102,10 @@ def _test_configure_with_aspect_hints(name): ) def _test_configure_with_aspect_hints_impl(env, target): - foo_pkg = target[ResolvedGroupsInfo].groups["rules_python#pypi/foo_pkg"] - env.expect.that_str(foo_pkg.merge_affinity).equals("ml") - env.expect.that_bool(foo_pkg.do_not_merge).equals(True) + groups = target[ResolvedGroupsInfo].groups + for distribution in ["ns_alpha", "ns_beta"]: + env.expect.that_str(groups["rules_python#pypi/" + distribution].merge_affinity).equals("ml") + env.expect.that_str(groups["rules_python#pypi/foo_pkg"].merge_affinity).equals("rules_python") _tests.append(_test_configure_with_aspect_hints) diff --git a/tests/runfiles_groups/test_rules.bzl b/tests/runfiles_groups/test_rules.bzl index 033ddbe2b1..4f8078315b 100644 --- a/tests/runfiles_groups/test_rules.bzl +++ b/tests/runfiles_groups/test_rules.bzl @@ -72,12 +72,13 @@ resolved_groups = rule( }, ) -_ML_DISTRIBUTIONS = ["rules_python#pypi/foo_pkg"] +# Stand-ins for heavy distributions like torch and nvidia-*. +_ML_DISTRIBUTIONS = ["rules_python#pypi/ns_alpha", "rules_python#pypi/ns_beta"] def _bucket_ml(resolved): return runfiles_groups.resolved( [ - runfiles_groups.derive(group, merge_affinity = "ml", do_not_merge = True) if runfiles_groups.name_str(group.name) in _ML_DISTRIBUTIONS else group + runfiles_groups.derive(group, merge_affinity = "ml") if runfiles_groups.name_str(group.name) in _ML_DISTRIBUTIONS else group for group in resolved.groups ], executable_group = resolved.executable_group, @@ -87,5 +88,6 @@ def _bucket_ml_impl(_ctx): return [RunfilesGroupTransformInfo(transform = _bucket_ml)] # An example of configuring how a binary's groups are packaged: in a binary's -# aspect_hints, it keeps foo_pkg's group from being merged into others. +# aspect_hints, it gives the ML distributions their own merge affinity, so a +# packager that has to merge groups merges them with each other, not the rest. bucket_ml = rule(implementation = _bucket_ml_impl)