Support embedding stamping info from bazel in `java_single_jar` A new attribute `stamp` controls the Bazel build info included in the output: - `stamp = 1`: Always embed Bazel build information, even in `--nostamp` builds. - `stamp = 0`: Embed Bazel build information with constant values even in `--stamp` builds. - `stamp = -1`: Embedding of Bazel build information is controlled by the `--[no]stamp` flag. The above only takes effect when `exclude_build_data = False` (default is `True`). It is an error to specify `stamp = 1` without `exclude_build_data = False`. Fixes https://github.com/bazelbuild/rules_java/issues/352 PiperOrigin-RevId: 881412734 Change-Id: I876b3a3b328eb363ad112dfc0fdfe599de03b164
diff --git a/MODULE.bazel b/MODULE.bazel index 204b8e8..e98ca3d 100644 --- a/MODULE.bazel +++ b/MODULE.bazel
@@ -6,14 +6,7 @@ ) bazel_dep(name = "platforms", version = "0.0.11") -bazel_dep(name = "rules_cc", version = "0.2.13") -archive_override( - module_name = "rules_cc", - integrity = "sha256-y3RA9zEyB7HqBXVTgrlFmvfJARcEDzILe2ugNGC4ZrE=", - strip_prefix = "rules_cc-b5a65591334f74371f4d75003768957a740cd868", - urls = ["https://github.com/bazelbuild/rules_cc/archive/b5a65591334f74371f4d75003768957a740cd868.tar.gz"], -) - +bazel_dep(name = "rules_cc", version = "0.2.17") bazel_dep(name = "bazel_features", version = "1.30.0") bazel_dep(name = "bazel_skylib", version = "1.6.1") bazel_dep(name = "protobuf", version = "32.1", repo_name = "com_google_protobuf")
diff --git a/java/common/BUILD b/java/common/BUILD index 9958a88..9881236 100644 --- a/java/common/BUILD +++ b/java/common/BUILD
@@ -36,9 +36,7 @@ name = "semantics_bzl", srcs = ["java_semantics.bzl"], visibility = ["//visibility:public"], - deps = [ - "@rules_cc//cc/common", - ], + deps = ["@rules_cc//cc/common:cc_helper_bzl"], ) bzl_library(
diff --git a/java/common/rules/BUILD b/java/common/rules/BUILD index f6fde9d..4c24d07 100644 --- a/java/common/rules/BUILD +++ b/java/common/rules/BUILD
@@ -35,7 +35,11 @@ name = "java_single_jar_bzl", srcs = ["java_single_jar.bzl"], visibility = ["//java:__subpackages__"], - deps = ["//java/common"], + deps = [ + "//java/common", + "//java/common:semantics_bzl", + "//java/common/rules/impl:java_helper_bzl", + ], ) bzl_library(
diff --git a/java/common/rules/impl/BUILD b/java/common/rules/impl/BUILD index 6ab4137..70adecc 100644 --- a/java/common/rules/impl/BUILD +++ b/java/common/rules/impl/BUILD
@@ -29,7 +29,7 @@ bzl_library( name = "java_helper_bzl", srcs = ["java_helper.bzl"], - visibility = ["//visibility:private"], + visibility = ["//java:__subpackages__"], deps = [ "//java/common:semantics_bzl", "//java/common/rules:java_helper_bzl",
diff --git a/java/common/rules/impl/java_binary_deploy_jar.bzl b/java/common/rules/impl/java_binary_deploy_jar.bzl index 7b7d69d..d6add14 100644 --- a/java/common/rules/impl/java_binary_deploy_jar.bzl +++ b/java/common/rules/impl/java_binary_deploy_jar.bzl
@@ -19,15 +19,6 @@ # copybara: default visibility -def _get_build_info(ctx, stamp): - if helper.is_stamping_enabled(ctx, stamp): - # Makes the target depend on BUILD_INFO_KEY, which helps to discover stamped targets - # See b/326620485 for more details. - ctx.version_file # buildifier: disable=no-effect - return ctx.attr._build_info_translator[OutputGroupInfo].non_redacted_build_info_files.to_list() - else: - return ctx.attr._build_info_translator[OutputGroupInfo].redacted_build_info_files.to_list() - def create_deploy_archives( ctx, java_attrs, @@ -70,7 +61,7 @@ order = "preorder", ) multi_release = ctx.fragments.java.multi_release_deploy_jars - build_info_files = _get_build_info(ctx, ctx.attr.stamp) + build_info_files = helper.get_build_info(ctx, ctx.attr.stamp) build_target = str(ctx.label) manifest_lines = ctx.attr.deploy_manifest_lines + extra_manifest_lines create_deploy_archive(
diff --git a/java/common/rules/impl/java_helper.bzl b/java/common/rules/impl/java_helper.bzl index e45fac9..638878b 100644 --- a/java/common/rules/impl/java_helper.bzl +++ b/java/common/rules/impl/java_helper.bzl
@@ -241,6 +241,15 @@ # stamp == -1 / auto return int(ctx.configuration.stamp_binaries()) +def _get_build_info(ctx, stamp): + if helper.is_stamping_enabled(ctx, stamp): + # Makes the target depend on BUILD_INFO_KEY, which helps to discover stamped targets + # See b/326620485 for more details. + ctx.version_file # buildifier: disable=no-effect + return ctx.attr._build_info_translator[OutputGroupInfo].non_redacted_build_info_files.to_list() + else: + return ctx.attr._build_info_translator[OutputGroupInfo].redacted_build_info_files.to_list() + helper = struct( collect_all_targets_as_deps = _collect_all_targets_as_deps, filter_launcher_for_target = _filter_launcher_for_target, @@ -265,6 +274,7 @@ detokenize_javacopts = _loading_phase_helper.detokenize_javacopts, tokenize_javacopts = _loading_phase_helper.tokenize_javacopts, is_stamping_enabled = _is_stamping_enabled, + get_build_info = _get_build_info, get_relative = _loading_phase_helper.get_relative, has_target_constraints = _loading_phase_helper.has_target_constraints, )
diff --git a/java/common/rules/java_single_jar.bzl b/java/common/rules/java_single_jar.bzl index 95b7ca2..c955275 100644 --- a/java/common/rules/java_single_jar.bzl +++ b/java/common/rules/java_single_jar.bzl
@@ -15,6 +15,8 @@ load("//java/common:java_common.bzl", "java_common") load("//java/common:java_info.bzl", "JavaInfo") +load("//java/common:java_semantics.bzl", "semantics") +load("//java/common/rules/impl:java_helper.bzl", "helper") # copybara: default visibility @@ -69,8 +71,15 @@ else: fail("\"compress\" attribute (%s) must be: yes, no, preserve." % ctx.attr.compress) + if ctx.attr.exclude_build_data and ctx.attr.stamp == 1: + fail("Enabling stamping has not effect with exclude_build_data enabled") + + build_info_files = [] if ctx.attr.exclude_build_data: args.add("--exclude_build_data") + else: + build_info_files = helper.get_build_info(ctx, ctx.attr.stamp) + args.add_all(build_info_files, before_each = "--build_info_file") if ctx.attr.multi_release: args.add("--multi_release") @@ -78,7 +87,7 @@ args.add("--exclude_pattern", ctx.attr.exclude_pattern) ctx.actions.run( - inputs = inputs, + inputs = depset(build_info_files, transitive = [inputs]), outputs = [ctx.outputs.output], arguments = [args], progress_message = "Merging into %s" % ctx.outputs.output.short_path, @@ -138,6 +147,20 @@ executable = True, ), "output": attr.output(), + "stamp": attr.int( + doc = """ + Whether to embed extra Bazel build information into the build_data.properties file: + * `stamp = 1`: Always embed Bazel build information, even in `--nostamp` builds. + * `stamp = 0`: Embed Bazel build information with constant values, even in `--stamp` builds. + * `stamp = -1`: Embedding of Bazel build information is controlled by the `--[no]stamp` flag. + + Note: whether the output contains the build_data.properties file is controlled + by the `exclude_build_data` attribute. + """, + default = 0, + values = [-1, 0, 1], + ), + "_build_info_translator": attr.label(default = semantics.BUILD_INFO_TRANSLATOR_LABEL), }, implementation = _bazel_java_single_jar_impl, doc = """
diff --git a/test/java/common/rules/BUILD b/test/java/common/rules/BUILD index 32870da..8e74a2a 100644 --- a/test/java/common/rules/BUILD +++ b/test/java/common/rules/BUILD
@@ -4,6 +4,7 @@ load(":java_import_tests.bzl", "java_import_tests") load(":java_library_tests.bzl", "java_library_tests") load(":java_plugin_tests.bzl", "java_plugin_tests") +load(":java_single_jar_tests.bzl", "java_single_jar_tests") load(":java_test_tests.bzl", "java_test_tests") load(":merge_attrs_tests.bzl", "merge_attrs_test_suite") @@ -21,6 +22,8 @@ java_import_tests(name = "java_import_tests") +java_single_jar_tests(name = "java_single_jar_tests") + java_test_tests(name = "java_test_tests") add_exports_tests(name = "add_exports_tests")
diff --git a/test/java/common/rules/java_single_jar_tests.bzl b/test/java/common/rules/java_single_jar_tests.bzl new file mode 100644 index 0000000..8ea43da --- /dev/null +++ b/test/java/common/rules/java_single_jar_tests.bzl
@@ -0,0 +1,164 @@ +"""Tests for the java_single_jar rule""" + +load("@rules_testing//lib:analysis_test.bzl", "analysis_test", "test_suite") +load("@rules_testing//lib:truth.bzl", "matching") +load("@rules_testing//lib:util.bzl", "util") +load("//java:java_single_jar.bzl", "java_single_jar") +load("//java/common:java_semantics.bzl", "semantics") + +def _label_to_bin_path(label): + segments = ["{bindir}"] + if label.repo_name: + segments.extend(["external", label.repo_name]) + segments.append(label.package) + return "/".join(segments) + +_BUILD_INFO_PATH = _label_to_bin_path(Label(semantics.BUILD_INFO_TRANSLATOR_LABEL)) + +def _test_java_single_jar_basic(name): + util.helper_target( + java_single_jar, + name = name + "/jar", + deps = ["1.jar", "2.jar"], + ) + + analysis_test( + name = name, + impl = _test_java_single_jar_basic_impl, + target = name + "/jar", + ) + +def _test_java_single_jar_basic_impl(env, target): + assert_that_action = env.expect.that_target(target).action_named("JavaSingleJar") + assert_that_action.argv().contains_at_least([ + "--sources", + "{package}/1.jar", + "{package}/2.jar", + "--output", + "{bindir}/{package}/{name}.jar", + "--normalize", + "--dont_change_compression", + "--exclude_build_data", + "--multi_release", + ]).in_order() + +def _test_java_single_jar_force_enable_stamping(name): + util.helper_target( + java_single_jar, + name = name + "/jar", + stamp = 1, + exclude_build_data = False, + ) + + analysis_test( + name = name, + impl = _test_java_single_jar_force_enable_stamping_impl, + target = name + "/jar", + ) + +def _test_java_single_jar_force_enable_stamping_impl(env, target): + assert_that_action = env.expect.that_target(target).action_named("JavaSingleJar") + assert_that_action.contains_flag_values([ + ("--build_info_file", _BUILD_INFO_PATH + "/non_volatile_file.properties"), + ("--build_info_file", _BUILD_INFO_PATH + "/volatile_file.properties"), + ]) + +def _test_java_single_jar_force_disable_stamping(name): + util.helper_target( + java_single_jar, + name = name + "/jar", + stamp = 0, + exclude_build_data = False, + ) + + analysis_test( + name = name, + impl = _test_java_single_jar_force_disable_stamping_impl, + target = name + "/jar", + ) + +def _test_java_single_jar_force_disable_stamping_impl(env, target): + assert_that_action = env.expect.that_target(target).action_named("JavaSingleJar") + assert_that_action.contains_flag_values([ + ("--build_info_file", _BUILD_INFO_PATH + "/redacted_file.properties"), + ]) + +def _test_java_single_jar_stamping_enabled_build_data_excluded_fails(name): + util.helper_target( + java_single_jar, + name = name + "/jar", + stamp = 1, + exclude_build_data = True, + ) + + analysis_test( + name = name, + impl = _test_java_single_jar_stamping_enabled_build_data_excluded_fails_impl, + target = name + "/jar", + expect_failure = True, + ) + +def _test_java_single_jar_stamping_enabled_build_data_excluded_fails_impl(env, target): + env.expect.that_target(target).failures().contains_predicate( + matching.str_matches("Enabling stamping has not effect with exclude_build_data enabled"), + ) + +def _test_java_single_jar_stamp_attr_auto_stamp_flag_enabled(name): + util.helper_target( + java_single_jar, + name = name + "/jar", + stamp = -1, + exclude_build_data = False, + ) + + analysis_test( + name = name, + impl = _test_java_single_jar_stamp_attr_auto_stamp_flag_enabled_impl, + target = name + "/jar", + config_settings = { + "//command_line_option:stamp": True, + }, + ) + +def _test_java_single_jar_stamp_attr_auto_stamp_flag_enabled_impl(env, target): + assert_that_action = env.expect.that_target(target).action_named("JavaSingleJar") + assert_that_action.contains_flag_values([ + ("--build_info_file", _BUILD_INFO_PATH + "/non_volatile_file.properties"), + ("--build_info_file", _BUILD_INFO_PATH + "/volatile_file.properties"), + ]) + +def _test_java_single_jar_stamp_attr_auto_stamp_flag_disabled(name): + util.helper_target( + java_single_jar, + name = name + "/jar", + stamp = -1, + exclude_build_data = False, + ) + + analysis_test( + name = name, + impl = _test_java_single_jar_stamp_attr_auto_stamp_flag_disabled_impl, + target = name + "/jar", + config_settings = { + "//command_line_option:stamp": False, + }, + ) + +def _test_java_single_jar_stamp_attr_auto_stamp_flag_disabled_impl(env, target): + assert_that_action = env.expect.that_target(target).action_named("JavaSingleJar") + assert_that_action.contains_flag_values([ + ("--build_info_file", _BUILD_INFO_PATH + "/redacted_file.properties"), + ]) + +def java_single_jar_tests(name): + test_suite( + name = name, + tests = [ + _test_java_single_jar_basic, + _test_java_single_jar_force_enable_stamping, + _test_java_single_jar_force_disable_stamping, + _test_java_single_jar_stamping_enabled_build_data_excluded_fails, + _test_java_single_jar_stamp_attr_auto_stamp_flag_enabled, + _test_java_single_jar_stamp_attr_auto_stamp_flag_disabled, + ], + )