Use the `launcher_maker` toolchain if available (#294) This avoids an unnecessary dependency on a C++ toolchain matching the target platform when not building for Windows. Also fix the configuration of the launcher, which should be built for the target platform. Closes #294 COPYBARA_INTEGRATE_REVIEW=https://github.com/bazelbuild/rules_java/pull/294 from fmeum:cross-compile-to-unix 13f097a3c541f603e091c139a4543b632382c9aa PiperOrigin-RevId: 762307907 Change-Id: I1e172a81bd198b02032a705c459ead48b0012aea
diff --git a/.bazelci/presubmit.yml b/.bazelci/presubmit.yml index 6dc8cf3..18088a2 100644 --- a/.bazelci/presubmit.yml +++ b/.bazelci/presubmit.yml
@@ -42,16 +42,33 @@ flags_workspace_integration: &flags_workspace_integration - "--noenable_bzlmod" - "--enable_workspace" + # TODO(hvd): remove after Bazel 8.3.0 + - "--repositories_without_autoloads=bazel_features_version,bazel_features_globals" buildifier: latest matrix: all_platforms: ["ubuntu2004", "macos", "macos_arm64", "windows"] - integration_platforms: ["ubuntu2004", "macos", "macos_arm64", "windows"] bazel: ["7.6.1", "8.2.1", "last_green"] # Bazel 6 tested separately, needs different flags - modern_bazel: ["8.2.1", "last_green"] # Fully supported Bazel versions + modern_bazel: ["last_green"] # Fully supported Bazel versions tasks: +# Bazel 9+ + build_and_test: + name: "Bazel {modern_bazel}" + bazel: ${{ modern_bazel }} + platform: ${{ all_platforms }} + build_targets: *build_targets + test_targets: *test_targets +# Bazel 8.x + build_and_test_bazel8: + name: "Bazel 8.2.1" + bazel: "8.2.1" + platform: ${{ all_platforms }} + build_targets: *build_targets + test_targets: *test_targets + test_flags: + - "--test_tag_filters=-min_bazel_9" # Bazel 7.x build_and_test_bazel7: name: "Bazel 7.6.1" @@ -60,14 +77,7 @@ build_targets: *build_targets test_targets: *test_targets test_flags: - - "--test_tag_filters=-min_bazel_8" -# Bazel 8+ - build_and_test: - name: "Bazel {modern_bazel}" - bazel: ${{ modern_bazel }} - platform: ${{ all_platforms }} - build_targets: *build_targets - test_targets: *test_targets + - "--test_tag_filters=-min_bazel_8,-min_bazel_9" # Bazel 6.x build_and_test_bazel6: name: "Bazel 6.5.0" @@ -76,11 +86,11 @@ build_targets: *build_targets_bazel6 test_targets: *test_targets_bazel6 test_flags: - - "--test_tag_filters=-min_bazel_7,-min_bazel_8" + - "--test_tag_filters=-min_bazel_7,-min_bazel_8,-min_bazel_9" ubuntu2004_integration_bazel6: name: "Integration w/ Bazel 6.5.0" bazel: 6.5.0 - platform: ${{ integration_platforms }} + platform: ${{ all_platforms }} working_directory: "test/repo" shell_commands: - sh setup.sh @@ -93,7 +103,7 @@ integration_build_and_test: name: "Integration w/ Bazel {bazel}" bazel: ${{ bazel }} - platform: ${{ integration_platforms }} + platform: ${{ all_platforms }} working_directory: "test/repo" shell_commands: - sh setup.sh @@ -104,7 +114,7 @@ integration_build_and_test_workspace: name: "Integration (WORKSPACE) w/ Bazel {bazel}" bazel: ${{ bazel }} - platform: ${{ integration_platforms }} + platform: ${{ all_platforms }} working_directory: "test/repo" shell_commands: - sh setup.sh
diff --git a/MODULE.bazel b/MODULE.bazel index 4b70044..bcfd03a 100644 --- a/MODULE.bazel +++ b/MODULE.bazel
@@ -7,14 +7,7 @@ bazel_dep(name = "platforms", version = "0.0.11") bazel_dep(name = "rules_cc", version = "0.0.15") -bazel_dep(name = "bazel_features", version = "1.28.0") -archive_override( - module_name = "bazel_features", - integrity = "sha256-SOPLvKDy+RN7GHKN8eFjQ+58Wx4Isj+vcXoUluBqxLo=", - strip_prefix = "bazel_features-59915eb2ca215c7b2266c83c49bb7522a5b6737f", - urls = ["https://github.com/bazel-contrib/bazel_features/archive/59915eb2ca215c7b2266c83c49bb7522a5b6737f.zip"], -) - +bazel_dep(name = "bazel_features", version = "1.30.0") bazel_dep(name = "bazel_skylib", version = "1.6.1") bazel_dep(name = "protobuf", version = "27.0", repo_name = "com_google_protobuf") bazel_dep(name = "zlib", version = "1.3.1.bcr.5")
diff --git a/WORKSPACE b/WORKSPACE index 6b8beb6..acd7cae 100644 --- a/WORKSPACE +++ b/WORKSPACE
@@ -41,6 +41,10 @@ rules_java_dependencies() +load("@bazel_features//:deps.bzl", "bazel_features_deps") + +bazel_features_deps() + load("@com_google_protobuf//bazel/private:proto_bazel_features.bzl", "proto_bazel_features") # buildifier: disable=bzl-visibility proto_bazel_features(name = "proto_bazel_features") @@ -63,14 +67,3 @@ load("//test:repositories.bzl", "test_repositories") test_repositories() - -http_archive( - name = "bazel_features", - sha256 = "48e3cbbca0f2f9137b18728df1e16343ee7c5b1e08b23faf717a1496e06ac4ba", - strip_prefix = "bazel_features-59915eb2ca215c7b2266c83c49bb7522a5b6737f", - url = "https://github.com/bazel-contrib/bazel_features/archive/59915eb2ca215c7b2266c83c49bb7522a5b6737f.zip", -) - -load("@bazel_features//:deps.bzl", "bazel_features_deps") - -bazel_features_deps()
diff --git a/distro/relnotes.bzl b/distro/relnotes.bzl index 5641bf7..ce04d50 100644 --- a/distro/relnotes.bzl +++ b/distro/relnotes.bzl
@@ -18,6 +18,16 @@ ~~~ **WORKSPACE setup** + +With Bazel 8.0.0 and before 8.3.0, add the following to your `.bazelrc` file: + +~~~ +# https://github.com/bazelbuild/bazel/pull/26119 +common --repositories_without_autoloads=bazel_features_version,bazel_features_globals +~~~ + +In all cases, add the following to your `WORKSPACE` file: + ~~~ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") http_archive( @@ -31,6 +41,9 @@ load("@rules_java//java:rules_java_deps.bzl", "rules_java_dependencies") rules_java_dependencies() +load("@bazel_features//:deps.bzl", "bazel_features_deps") +bazel_features_deps() + # note that the following line is what is minimally required from protobuf for the java rules # consider using the protobuf_deps() public API from @com_google_protobuf//:protobuf_deps.bzl load("@com_google_protobuf//bazel/private:proto_bazel_features.bzl", "proto_bazel_features") # buildifier: disable=bzl-visibility
diff --git a/java/bazel/rules/bazel_java_binary.bzl b/java/bazel/rules/bazel_java_binary.bzl index def12d1..cbbe0de 100644 --- a/java/bazel/rules/bazel_java_binary.bzl +++ b/java/bazel/rules/bazel_java_binary.bzl
@@ -13,6 +13,7 @@ # limitations under the License. """Bazel java_binary rule""" +load("@bazel_features//:features.bzl", "bazel_features") load("@bazel_skylib//lib:paths.bzl", "paths") load("@rules_cc//cc:find_cc_toolchain.bzl", "use_cc_toolchain") load("//java/common:java_semantics.bzl", "semantics") @@ -202,6 +203,14 @@ return ctx.actions.declare_file(executable_name) +_LAUNCHER_MAKER_TOOLCHAIN_TYPE = "@bazel_tools//tools/launcher:launcher_maker_toolchain_type" +_LAUNCHER_MAKER_TOOLCHAIN = config_common.toolchain_type(_LAUNCHER_MAKER_TOOLCHAIN_TYPE, mandatory = True) + +def _find_launcher_maker(ctx): + if bazel_features.rules._has_launcher_maker_toolchain: + return ctx.toolchains[_LAUNCHER_MAKER_TOOLCHAIN_TYPE].binary + return ctx.executable._windows_launcher_maker + def _create_stub(ctx, java_attrs, launcher, executable, jvm_flags, main_class, coverage_main_class): java_runtime_toolchain = semantics.find_java_runtime_toolchain(ctx) java_executable = helper.get_java_executable(ctx, java_runtime_toolchain, launcher) @@ -282,11 +291,13 @@ # TODO(b/295221112): Change to use the "launcher" attribute (only windows use a fixed _launcher attribute) launcher_artifact = ctx.executable._launcher ctx.actions.run( - executable = ctx.executable._windows_launcher_maker, + executable = _find_launcher_maker(ctx), inputs = [launcher_artifact], outputs = [executable], arguments = [launcher_artifact.path, launch_info, executable.path], use_default_shell_env = True, + toolchain = _LAUNCHER_MAKER_TOOLCHAIN_TYPE if bazel_features.rules._has_launcher_maker_toolchain else None, + mnemonic = "JavaLauncherMaker", ) return executable @@ -308,6 +319,8 @@ provides = [JavaInfo], toolchains = [semantics.JAVA_TOOLCHAIN] + use_cc_toolchain() + ( [semantics.JAVA_RUNTIME_TOOLCHAIN] if executable or test else [] + ) + ( + [_LAUNCHER_MAKER_TOOLCHAIN] if bazel_features.rules._has_launcher_maker_toolchain else [] ), # TODO(hvd): replace with filegroups? outputs = { @@ -340,16 +353,18 @@ ), "_test_support": attr.label(default = _compute_test_support), "_launcher": attr.label( - cfg = "exec", + cfg = "target", executable = True, default = "@bazel_tools//tools/launcher:launcher", ), + }, + { "_windows_launcher_maker": attr.label( default = "@bazel_tools//tools/launcher:launcher_maker", cfg = "exec", executable = True, ), - }, + } if not bazel_features.rules._has_launcher_maker_toolchain else {}, ) def make_java_binary(executable):
diff --git a/java/rules_java_deps.bzl b/java/rules_java_deps.bzl index 0c96d8c..b32edd2 100644 --- a/java/rules_java_deps.bzl +++ b/java/rules_java_deps.bzl
@@ -210,6 +210,15 @@ ], ) +def bazel_features_repo(): + maybe( + http_archive, + name = "bazel_features", + sha256 = "a660027f5a87f13224ab54b8dc6e191693c554f2692fcca46e8e29ee7dabc43b", + strip_prefix = "bazel_features-1.30.0", + url = "https://github.com/bazel-contrib/bazel_features/releases/download/v1.30.0/bazel_features-v1.30.0.tar.gz", + ) + def rules_java_dependencies(): """An utility method to load non-toolchain dependencies of rules_java. @@ -223,3 +232,4 @@ zlib_repo() absl_repo() rules_license_repo() + bazel_features_repo()
diff --git a/test/java/bazel/BUILD.bazel b/test/java/bazel/BUILD.bazel new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/java/bazel/BUILD.bazel
diff --git a/test/java/bazel/rules/BUILD.bazel b/test/java/bazel/rules/BUILD.bazel new file mode 100644 index 0000000..3bf8f43 --- /dev/null +++ b/test/java/bazel/rules/BUILD.bazel
@@ -0,0 +1,3 @@ +load(":java_binary_tests.bzl", "java_binary_tests") + +java_binary_tests(name = "java_binary_tests")
diff --git a/test/java/bazel/rules/java_binary_tests.bzl b/test/java/bazel/rules/java_binary_tests.bzl new file mode 100644 index 0000000..32036d8 --- /dev/null +++ b/test/java/bazel/rules/java_binary_tests.bzl
@@ -0,0 +1,55 @@ +"""Tests for the Bazel java_binary rule""" + +load("@rules_testing//lib:analysis_test.bzl", "analysis_test", "test_suite") +load("@rules_testing//lib:util.bzl", "util") +load("//java:java_binary.bzl", "java_binary") + +def _test_java_binary_cross_compilation_to_unix(name): + # A Unix platform that: + # - has a JDK + # - does not require a launcher + # - is not supported by the default C++ toolchain + util.helper_target( + native.platform, + name = name + "/platform", + constraint_values = [ + "@platforms//os:linux", + "@platforms//cpu:s390x", + ], + ) + + util.helper_target( + java_binary, + name = name + "/bin", + srcs = ["java/C.java"], + main_class = "C", + ) + + analysis_test( + name = name, + impl = _test_java_binary_cross_compilation_to_unix_impl, + target = name + "/bin", + config_settings = { + "//command_line_option:platforms": [Label(name + "/platform")], + }, + # Requires the launcher_maker toolchain. + attr_values = {"tags": ["min_bazel_9"]}, + ) + +def _test_java_binary_cross_compilation_to_unix_impl(env, target): + # The main assertion is that analysis succeeds, but verify the absence of a + # binary launcher for good measure. We do this by checking that the output + # executable is the stub script, and not a bespoke launcher + executable = target[DefaultInfo].files_to_run.executable.short_path + assert_action = env.expect.that_target(target).action_generating(executable) + assert_action.mnemonic().equals("TemplateExpand") + assert_action.substitutions().keys().contains("%jvm_flags%") + assert_action.inputs().contains_exactly(["java/bazel/rules/java_stub_template.txt"]) + +def java_binary_tests(name): + test_suite( + name = name, + tests = [ + _test_java_binary_cross_compilation_to_unix, + ], + )
diff --git a/test/repo/WORKSPACE b/test/repo/WORKSPACE index eb68085..c272975 100644 --- a/test/repo/WORKSPACE +++ b/test/repo/WORKSPACE
@@ -9,6 +9,10 @@ rules_java_dependencies() +load("@bazel_features//:deps.bzl", "bazel_features_deps") + +bazel_features_deps() + load("@com_google_protobuf//bazel/private:proto_bazel_features.bzl", "proto_bazel_features") # buildifier: disable=bzl-visibility proto_bazel_features(name = "proto_bazel_features")