Repository navigation
Description
With more than one execution platform registered, rules_android gives some actions tools built for another platform:
Javac,TurbineandJavaSourceJarget the JDK, Turbine and singlejar of the first registered execution platform, wherever the action runs.CompileAndroidResourcesandLinkAndroidResourcesget the host SDK's aapt2 on every execution platform.
We hit this with remote execution: Linux arm64 executors are our first execution platform, and a Mac host is the second. The repro below runs bazel aquery on main (600cc93), unpatched, so nothing is built.
Repro
Two Linux execution platforms, arm64 first, and two copies of one android_library:
load("@rules_android//rules:rules.bzl", "android_library")
platform(
name = "linux_arm64",
constraint_values = [
"@platforms//os:linux",
"@platforms//cpu:arm64",
],
)
platform(
name = "linux_x86_64",
constraint_values = [
"@platforms//os:linux",
"@platforms//cpu:x86_64",
],
)
android_library(
name = "lib",
srcs = ["java/com/example/lib/Lib.java"],
custom_package = "com.example.lib",
manifest = "AndroidManifest.xml",
resource_files = ["res/values/strings.xml"],
)
android_library(
name = "lib_x86_64",
srcs = ["java/com/example/lib/Lib.java"],
custom_package = "com.example.lib",
exec_compatible_with = ["@platforms//cpu:x86_64"],
manifest = "AndroidManifest.xml",
resource_files = ["res/values/strings.xml"],
)bazel aquery '//:lib + //:lib_x86_64' --output=jsonprotoThis was Bazel 9.3.0 on an Apple silicon Mac, with the Android SDK in ANDROID_HOME (build-tools 37.0.0).
Rest of the workspace
.bazelrc, the Java flags of examples/basicapp plus the platforms:
common --java_language_version=17
common --java_runtime_version=17
common --tool_java_language_version=17
common --tool_java_runtime_version=17
common --repositories_without_autoloads=bazel_features_version,bazel_features_globals,cc_compatibility_proxy
common --cxxopt=-std=c++17 --host_cxxopt=-std=c++17
common --extra_execution_platforms=//:linux_arm64,//:linux_x86_64
MODULE.bazel, as in examples/basicapp, with rules_cc for the C++ stand-in below:
module(name = "ra_exec_repro")
bazel_dep(name = "rules_java", version = "9.0.3")
bazel_dep(name = "bazel_skylib", version = "1.8.1")
bazel_dep(name = "rules_jvm_external", version = "6.7")
bazel_dep(name = "platforms", version = "1.0.0")
bazel_dep(name = "rules_android", version = "0.8.0")
local_path_override(
module_name = "rules_android",
path = "<checkout of main>",
)
bazel_dep(name = "protobuf", version = "33.4", repo_name = "com_google_protobuf")
remote_android_extensions = use_extension("@rules_android//bzlmod_extensions:android_extensions.bzl", "remote_android_tools_extensions")
use_repo(remote_android_extensions, "android_tools")
android_sdk_repository_extension = use_extension("@rules_android//rules/android_sdk_repository:rule.bzl", "android_sdk_repository_extension")
use_repo(android_sdk_repository_extension, "androidsdk")
register_toolchains("@androidsdk//:sdk-toolchain", "@androidsdk//:all")
bazel_dep(name = "rules_cc", version = "0.2.17")
register_toolchains("//fake_cc:linux")The Mac has no C++ toolchain for Linux, and analysing rules_android's tools for the Linux platforms needs one. fake_cc/BUILD.bazel registers rules_cc's empty toolchain config for Linux. empty_cc_toolchain_config.bzl is a copy of rules_cc's cc/private/toolchain/empty_cc_toolchain_config.bzl. It plays no part in the result.
load("@rules_cc//cc/toolchains:cc_toolchain.bzl", "cc_toolchain")
load(":empty_cc_toolchain_config.bzl", "cc_toolchain_config")
filegroup(name = "empty")
cc_toolchain_config(name = "config")
cc_toolchain(
name = "cc",
all_files = ":empty",
ar_files = ":empty",
as_files = ":empty",
compiler_files = ":empty",
dwp_files = ":empty",
linker_files = ":empty",
objcopy_files = ":empty",
strip_files = ":empty",
toolchain_config = ":config",
)
toolchain(
name = "linux",
exec_compatible_with = ["@platforms//os:linux"],
target_compatible_with = ["@platforms//os:linux"],
toolchain = ":cc",
toolchain_type = "@bazel_tools//tools/cpp:toolchain_type",
)Lib.java is an empty class, the manifest only has a package, and strings.xml has one string.
Result
The execution platform of each action, and the tool it runs:
//:lib has no constraints, so all its actions run on //:linux_arm64. Their Java tools are arm64 builds. But CompileAndroidResources and LinkAndroidResources get --aapt2 = @androidsdk//:aapt2_binary. That wraps the SDK's build-tools/37.0.0/aapt2, a Mach-O binary.
//:lib_x86_64 runs all its actions on //:linux_x86_64:
| Action | Tool | Built for |
|---|---|---|
Javac |
remotejdk21_linux_aarch64/bin/java |
Linux arm64 |
Turbine |
remote_java_tools_linux_aarch64/java_tools/turbine_direct_graal |
Linux arm64 |
JavaSourceJar |
remote_java_tools_linux_aarch64/java_tools/src/tools/singlejar/singlejar_local |
Linux arm64 |
CompileAndroidResources, LinkAndroidResources, MergeManifests and the 4 other resource actions |
remotejdk17_linux/bin/java |
Linux x86_64 |
CompileAndroidResources, LinkAndroidResources |
--aapt2: the SDK's aapt2 |
macOS |
Why
Java tools. The rules declare the Java toolchain type (android_library/rule.bzl#L172-L176), but take Java from attributes (common.bzl#L37-L45). _java_toolchain is a fixed java_toolchain target, //tools/jdk:toolchain_android_only (android_library/attrs.bzl#L231-L233, tools/jdk/BUILD#L85-L102). That target resolves no toolchains of its own. So Bazel configures its JDK, Turbine and singlejar for the first execution platform. The resource actions take their JDK from _host_javabase instead (attrs.bzl#L87-L92, java.bzl#L476-L478), and get the right one.
aapt2. The resource actions take aapt2 from the Android tools toolchain (busybox.bzl#L450-L461, android_library/impl.bzl#L186). android_default_toolchain has no exec constraints (toolchains/android/BUILD#L27-L31). Its aapt2 defaults to @androidsdk//:aapt2 (toolchain.bzl#L17-L21), from the SDK on the host. So every execution platform gets the host's aapt2. android_sdk_tools has no exec constraints either. Its exec_compatible_with is commented out, with a pointer to #72, which is closed (toolchains/android_sdk/BUILD#L19-L35).
What would fix it
- Take the Java tools from the toolchain resolved for each action's exec group, through
ctx.toolchainsorctx.exec_groups, instead of_java_toolchain.java_common.compiletakes abootclasspathargument (java_common.bzl#L64), so the Android bootclasspath needs nojava_toolchainof its own. - Run actions that use both Java and SDK tools in one exec group that declares both toolchains. RFC: Fix auto exec groups toolchains #576 starts this for the resource actions. aapt2 would have to come from that group too.
- Give
android_sdk_toolsandandroid_default_toolchainexec constraints that match the SDK binaries they hold.
Related: #78 is another effect of _java_toolchain.
Activity