Skip to content

Commit eb1d207

Browse files
committed
Support RunfilesGroupInfo
Teach the Java rules to describe their runfiles as named, ordered groups so that downstream packaging rules can build more efficient artifacts. `java_binary`, `java_test`, `java_library`, and `java_import` now return `RunfilesGroupInfo` (and `RunfilesGroupMetadataInfo`) from the `rules_runfiles_group` ruleset alongside `DefaultInfo`. Instead of seeing a single flat runfiles tree, a packaging rule (e.g. a container-image or archive rule) can split a binary's runfiles into layers and order them so that the content that changes least often lands in the most cacheable layers: * the JDK / java_runtime at the foundation tier (never merged), * `java_import` targets (typically third-party jars) at the shared-deps tier, * first-party `java_library` code, the binary's own jars, and the executable at the executable tier. Libraries propagate fine-grained per-target groups up through their dependents; the binary collects them, adds its own groups, and tags every group it produces with the `rules_java` merge affinity so that JVM-shaped groups stay together when a packager has to merge groups to fit a layer limit. `java_import` gains a `runfiles_weight` attribute so dependency-management rulesets can hint at relative sizes to guide those merge decisions. Consumers that don't understand `RunfilesGroupInfo` are unaffected and keep using `DefaultInfo.default_runfiles`. Adds a dependency on `rules_runfiles_group` for both Bzlmod and WORKSPACE setups.
1 parent 9ed4512 commit eb1d207

15 files changed

Lines changed: 316 additions & 13 deletions

MODULE.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ bazel_dep(name = "platforms", version = "0.0.11")
99
bazel_dep(name = "rules_cc", version = "0.2.17")
1010
bazel_dep(name = "bazel_features", version = "1.30.0")
1111
bazel_dep(name = "bazel_skylib", version = "1.6.1")
12+
bazel_dep(name = "rules_runfiles_group", version = "0.0.1")
1213
bazel_dep(name = "protobuf", version = "32.1", repo_name = "com_google_protobuf")
1314
bazel_dep(name = "zlib", version = "1.3.1.bcr.5")
1415

WORKSPACE

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,9 @@ http_archive(
3939

4040
http_archive(
4141
name = "bazel_features",
42-
sha256 = "a660027f5a87f13224ab54b8dc6e191693c554f2692fcca46e8e29ee7dabc43b",
43-
strip_prefix = "bazel_features-1.30.0",
44-
url = "https://github.com/bazel-contrib/bazel_features/releases/download/v1.30.0/bazel_features-v1.30.0.tar.gz",
42+
sha256 = "497a10560c07b0478026f49766ebdb6fd07b4fba9767db4f1c25cb6ab2fa1cdb",
43+
strip_prefix = "bazel_features-1.46.0",
44+
url = "https://github.com/bazel-contrib/bazel_features/releases/download/v1.46.0/bazel_features-v1.46.0.tar.gz",
4545
)
4646

4747
load("@bazel_features//:deps.bzl", "bazel_features_deps")

distro/relnotes.bzl

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,12 @@ bazel_dep(name = "rules_java", version = "{VERSION}")
2020
2121
**WORKSPACE setup**
2222
23-
With Bazel 8.0.0 and before 8.3.0, add the following to your `.bazelrc` file:
23+
With Bazel 8.x, add the following to your `.bazelrc` file:
2424
2525
~~~
26+
# https://github.com/bazelbuild/bazel/issues/23043
2627
# https://github.com/bazelbuild/bazel/pull/26119
27-
common --repositories_without_autoloads=bazel_features_version,bazel_features_globals
28+
common --repositories_without_autoloads=rules_runfiles_group,bazel_features_version,bazel_features_globals
2829
~~~
2930
3031
In all cases, add the following to your `WORKSPACE` file:
@@ -41,9 +42,9 @@ http_archive(
4142
4243
http_archive(
4344
name = "bazel_features",
44-
sha256 = "a660027f5a87f13224ab54b8dc6e191693c554f2692fcca46e8e29ee7dabc43b",
45-
strip_prefix = "bazel_features-1.30.0",
46-
url = "https://github.com/bazel-contrib/bazel_features/releases/download/v1.30.0/bazel_features-v1.30.0.tar.gz",
45+
sha256 = "497a10560c07b0478026f49766ebdb6fd07b4fba9767db4f1c25cb6ab2fa1cdb",
46+
strip_prefix = "bazel_features-1.46.0",
47+
url = "https://github.com/bazel-contrib/bazel_features/releases/download/v1.46.0/bazel_features-v1.46.0.tar.gz",
4748
)
4849
4950
load("@bazel_features//:deps.bzl", "bazel_features_deps")

java/bazel/rules/BUILD.bazel

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ bzl_library(
3636
"@bazel_skylib//lib:paths",
3737
"@rules_cc//cc:find_cc_toolchain_bzl",
3838
"@rules_cc//cc/common",
39+
"@rules_runfiles_group//runfiles_group:lib",
40+
"@rules_runfiles_group//runfiles_group:providers",
3941
],
4042
)
4143

java/bazel/rules/bazel_java_binary.bzl

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
load("@bazel_features//:features.bzl", "bazel_features")
1717
load("@bazel_skylib//lib:paths.bzl", "paths")
1818
load("@rules_cc//cc:find_cc_toolchain.bzl", "use_cc_toolchain")
19+
load("@rules_runfiles_group//runfiles_group:lib.bzl", "lib")
20+
load("@rules_runfiles_group//runfiles_group:providers.bzl", "RunfilesGroupInfo", "RunfilesGroupMetadataInfo")
1921
load("//java/common:java_semantics.bzl", "semantics")
2022
load(
2123
"//java/common/rules:android_lint.bzl",
@@ -100,9 +102,11 @@ def bazel_base_binary_impl(ctx, is_test_rule_class):
100102

101103
runfiles = default_info.runfiles
102104

105+
toolchain_files = depset()
103106
if executable:
104107
runtime_toolchain = semantics.find_java_runtime_toolchain(ctx)
105-
runfiles = runfiles.merge(ctx.runfiles(transitive_files = runtime_toolchain.files))
108+
toolchain_files = runtime_toolchain.files
109+
runfiles = runfiles.merge(ctx.runfiles(transitive_files = toolchain_files))
106110

107111
test_support = helper.get_test_support(ctx)
108112
if test_support:
@@ -114,6 +118,8 @@ def bazel_base_binary_impl(ctx, is_test_rule_class):
114118
executable = default_info.executable,
115119
)
116120

121+
_add_runfiles_group_providers(providers, ctx, java_attrs, executable, toolchain_files, test_support)
122+
117123
info = providers.pop("InternalDeployJarInfo")
118124
create_deploy_archives(
119125
ctx,
@@ -128,6 +134,34 @@ def bazel_base_binary_impl(ctx, is_test_rule_class):
128134

129135
return providers.values()
130136

137+
def _add_runfiles_group_providers(providers, ctx, java_attrs, executable, toolchain_files, test_support):
138+
all_deps = list(ctx.attr.deps) + list(ctx.attr.runtime_deps)
139+
if test_support:
140+
all_deps.append(test_support)
141+
dep_collected = lib.collect_groups(ctx, all_deps)
142+
data_collected = lib.collect_groups(ctx, ctx.attr.data) if hasattr(ctx.attr, "data") else struct(groups = {}, metadata = None)
143+
144+
groups = {}
145+
groups.update(dep_collected.groups)
146+
groups.update(data_collected.groups)
147+
148+
own_group_name = str(ctx.label)
149+
groups[own_group_name] = ctx.runfiles(transitive_files = java_attrs.runtime_jars)
150+
151+
groups["rules_java#java_runtime"] = ctx.runfiles(transitive_files = toolchain_files)
152+
153+
groups["rules_java#binary"] = ctx.runfiles(files = [executable] if executable else [])
154+
155+
own_metadata = RunfilesGroupMetadataInfo(groups = {
156+
"rules_java#java_runtime": lib.group_metadata(rank = lib.RANK_FOUNDATION, do_not_merge = True, merge_affinity = "rules_java"),
157+
own_group_name: lib.group_metadata(rank = lib.RANK_EXECUTABLE, merge_affinity = "rules_java"),
158+
"rules_java#binary": lib.group_metadata(rank = lib.RANK_EXECUTABLE, executable_group = True, merge_affinity = "rules_java"),
159+
})
160+
metadata = lib.merge_metadata(dep_collected.metadata, data_collected.metadata, own_metadata)
161+
162+
providers["RunfilesGroupInfo"] = RunfilesGroupInfo(**groups)
163+
providers["RunfilesGroupMetadataInfo"] = metadata
164+
131165
def _get_coverage_runner(ctx):
132166
if ctx.configuration.coverage_enabled and ctx.attr.create_executable:
133167
toolchain = semantics.find_java_toolchain(ctx)

java/bazel/rules/bazel_java_import.bzl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ def _proxy(ctx):
3333
ctx.files.proguard_specs,
3434
ctx.attr.add_exports,
3535
ctx.attr.add_opens,
36+
runfiles_weight = ctx.attr.runfiles_weight,
3637
).values()
3738

3839
java_import = rule(

java/common/rules/impl/BUILD

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ bzl_library(
2323
"//java/common:proguard_spec_info_bzl",
2424
"@com_google_protobuf//bazel/common:proto_info_bzl",
2525
"@rules_cc//cc/common:cc_helper_bzl",
26+
"@rules_runfiles_group//runfiles_group:lib",
27+
"@rules_runfiles_group//runfiles_group:providers",
2628
],
2729
)
2830

java/common/rules/impl/bazel_java_import_impl.bzl

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ Definition of java_import rule.
1717
"""
1818

1919
load("@rules_cc//cc/common:cc_info.bzl", "CcInfo")
20+
load("@rules_runfiles_group//runfiles_group:lib.bzl", "lib")
21+
load("@rules_runfiles_group//runfiles_group:providers.bzl", "RunfilesGroupInfo", "RunfilesGroupMetadataInfo")
2022
load("//java/common:java_semantics.bzl", "semantics")
2123
load("//java/common/rules/impl:basic_java_library_impl.bzl", "construct_defaultinfo")
2224
load("//java/common/rules/impl:import_deps_check.bzl", "import_deps_check")
@@ -85,7 +87,8 @@ def bazel_java_import_rule(
8587
add_exports = [],
8688
add_opens = [],
8789
permit_exports = True,
88-
skip_incomplete_deps_check = True):
90+
skip_incomplete_deps_check = True,
91+
runfiles_weight = 0):
8992
"""Implements java_import.
9093
9194
This rule allows the use of precompiled .jar files as libraries in other Java rules.
@@ -103,6 +106,7 @@ def bazel_java_import_rule(
103106
add_opens: (list[str]) Allow this library to reflectively access the given <module>/<package>.
104107
permit_exports: (bool) Allow using exports
105108
skip_incomplete_deps_check: (bool) If this target is allowed to have incomplete deps
109+
runfiles_weight: (int) Weight hint for RunfilesGroupMetadataInfo. If > 0, set as weight.
106110
107111
Returns:
108112
(list[provider]) A list containing DefaultInfo, JavaInfo,
@@ -182,4 +186,24 @@ def bazel_java_import_rule(
182186
"_hidden_top_level_INTERNAL_": target["ProguardSpecProvider"].specs,
183187
}
184188
)
189+
190+
if not neverlink:
191+
_add_runfiles_group_providers(target, ctx, collected_jars, deps, exports, runtime_deps, runfiles_weight = runfiles_weight)
192+
185193
return target
194+
195+
def _add_runfiles_group_providers(target, ctx, own_jars, deps, exports, runtime_deps, runfiles_weight = 0):
196+
collected = lib.collect_groups(ctx, deps + exports + runtime_deps)
197+
198+
groups = dict(collected.groups)
199+
own_group_name = str(ctx.label)
200+
groups[own_group_name] = ctx.runfiles(files = own_jars)
201+
202+
own_weight = runfiles_weight if runfiles_weight > 0 else None
203+
own_metadata = RunfilesGroupMetadataInfo(groups = {
204+
own_group_name: lib.group_metadata(rank = lib.RANK_SHARED_DEPS, weight = own_weight, merge_affinity = "rules_java"),
205+
})
206+
metadata = lib.merge_metadata(collected.metadata, own_metadata)
207+
208+
target["RunfilesGroupInfo"] = RunfilesGroupInfo(**groups)
209+
target["RunfilesGroupMetadataInfo"] = metadata

java/common/rules/impl/bazel_java_library_impl.bzl

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
Definition of java_library rule.
1717
"""
1818

19+
load("@rules_runfiles_group//runfiles_group:lib.bzl", "lib")
20+
load("@rules_runfiles_group//runfiles_group:providers.bzl", "RunfilesGroupInfo", "RunfilesGroupMetadataInfo")
1921
load("//java/common/rules/impl:basic_java_library_impl.bzl", "basic_java_library", "construct_defaultinfo")
2022

2123
# copybara: default visibility
@@ -98,4 +100,21 @@ def bazel_java_library_rule(
98100
)
99101
target["OutputGroupInfo"] = OutputGroupInfo(**base_info.output_groups)
100102

103+
if not neverlink:
104+
_add_runfiles_group_providers(target, ctx, base_info.runfiles, deps, exports, runtime_deps)
105+
101106
return target
107+
108+
def _add_runfiles_group_providers(target, ctx, own_runfiles, deps, exports, runtime_deps):
109+
collected = lib.collect_groups(ctx, deps + exports + runtime_deps)
110+
111+
groups = dict(collected.groups)
112+
own_group_name = str(ctx.label)
113+
groups[own_group_name] = ctx.runfiles(files = own_runfiles)
114+
own_metadata = RunfilesGroupMetadataInfo(groups = {
115+
own_group_name: lib.group_metadata(merge_affinity = "rules_java"),
116+
})
117+
metadata = lib.merge_metadata(collected.metadata, own_metadata)
118+
119+
target["RunfilesGroupInfo"] = RunfilesGroupInfo(**groups)
120+
target["RunfilesGroupMetadataInfo"] = metadata

java/common/rules/java_import.bzl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,5 +124,14 @@ This corresponds to the javac and JVM --add-opens= flags.
124124
""",
125125
),
126126
"licenses": attr.license() if hasattr(attr, "license") else attr.string_list(),
127+
"runfiles_weight": attr.int(
128+
default = 0,
129+
doc = """
130+
Weight hint for RunfilesGroupMetadataInfo. If set to a value greater than 0,
131+
this weight is attached to the target's runfiles group to help packaging rules
132+
make informed merge decisions. Intended to be set by dependency management
133+
rulesets (e.g. rules_jvm_external) using actual jar byte sizes.
134+
""",
135+
),
127136
"_java_toolchain_type": attr.label(default = semantics.JAVA_TOOLCHAIN_TYPE),
128137
}

0 commit comments

Comments
 (0)