Skip to content

Android rules give actions tools built for another execution platform #583

Open

Description

@reuben

With more than one execution platform registered, rules_android gives some actions tools built for another platform:

  1. Javac, Turbine and JavaSourceJar get the JDK, Turbine and singlejar of the first registered execution platform, wherever the action runs.
  2. CompileAndroidResources and LinkAndroidResources get 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=jsonproto

This 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.toolchains or ctx.exec_groups, instead of _java_toolchain. java_common.compile takes a bootclasspath argument (java_common.bzl#L64), so the Android bootclasspath needs no java_toolchain of 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_tools and android_default_toolchain exec constraints that match the SDK binaries they hold.

Related: #78 is another effect of _java_toolchain.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions