[bazel] Add hermetic Android NDK C++ toolchain.
See //toolchain/README.md to understand how I arrived at this design.
This toolchain can be tested via the following Bazel invocations:
$ bazel build //... --config=android-arm-hermetic
$ bazel build //... --config=android-arm-hermetic-rbe
$ bazel build //... --config=android-arm64-hermetic
$ bazel build //... --config=android-arm64-hermetic-rbe
In a follow-up CL I will delete the non-hermetic toolchain that uses the `android_ndk_repository`[1] built-in Bazel rule, and I will update the skcms-Android-ARM-Bazel-Build-RBE and skcms-Android-ARM64-Bazel-Build-RBE tasks to use the hermetic toolchain.
[1] https://bazel.build/reference/be/android#android_ndk_repository
Bug: skia:12400
Change-Id: I917c65a64e5e6320842d25940b2ca5477073f577
Reviewed-on: https://skia-review.googlesource.com/c/skcms/+/528654
Reviewed-by: Kevin Lubick <kjlubick@google.com>
Commit-Queue: Leandro Lovisolo <lovisolo@google.com>
diff --git a/.bazelrc b/.bazelrc
index 6313017..e342760 100644
--- a/.bazelrc
+++ b/.bazelrc
@@ -96,3 +96,30 @@
build:android-arm64-rbe --extra_toolchains=@rbe_linux_toolchains//config:cc-toolchain
build:android-arm64-rbe --extra_execution_platforms=@rbe_linux_toolchains//config:platform
build:android-arm64-rbe --platforms=@rbe_linux_toolchains//config:platform
+
+# The experimental Android configurations below use a hermetic toolchain that does not require a
+# local copy of the Android NDK.
+
+# Experimental hermetic Android configuration for 32-bit ARM (armeabi-v7a ABI).
+build:android-arm-hermetic --crosstool_top=//toolchain:ndk_toolchain
+build:android-arm-hermetic --cpu=armeabi-v7a
+build:android-arm-hermetic --host_crosstool_top=@bazel_tools//tools/cpp:toolchain
+
+# Experimental hermetic Android RBE configuration for 32-bit ARM (armeabi-v7a ABI).
+build:android-arm-hermetic-rbe --config=remote
+build:android-arm-hermetic-rbe --config=android-arm-hermetic
+build:android-arm-hermetic-rbe --extra_toolchains=@rbe_linux_toolchains//config:cc-toolchain
+build:android-arm-hermetic-rbe --extra_execution_platforms=@rbe_linux_toolchains//config:platform
+build:android-arm-hermetic-rbe --platforms=@rbe_linux_toolchains//config:platform
+
+# Experimental hermetic Android configuration for 64-bit ARM (arm64-v8a ABI).
+build:android-arm64-hermetic --crosstool_top=//toolchain:ndk_toolchain
+build:android-arm64-hermetic --cpu=arm64-v8a
+build:android-arm64-hermetic --host_crosstool_top=@bazel_tools//tools/cpp:toolchain
+
+# Experimental hermetic Android RBE configuration for 64-bit ARM (arm64-v8a ABI).
+build:android-arm64-hermetic-rbe --config=remote
+build:android-arm64-hermetic-rbe --config=android-arm64-hermetic
+build:android-arm64-hermetic-rbe --extra_toolchains=@rbe_linux_toolchains//config:cc-toolchain
+build:android-arm64-hermetic-rbe --extra_execution_platforms=@rbe_linux_toolchains//config:platform
+build:android-arm64-hermetic-rbe --platforms=@rbe_linux_toolchains//config:platform
diff --git a/WORKSPACE b/WORKSPACE
index 21e7bfb..1386439 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -24,6 +24,12 @@
api_level = 31,
)
+load("//toolchain:download_toolchains.bzl", "download_toolchains")
+
+download_toolchains(
+ android_ndk_repository_name = "android_ndk",
+)
+
##################################
# Docker rules and dependencies. #
##################################
diff --git a/toolchain/BUILD.bazel b/toolchain/BUILD.bazel
new file mode 100644
index 0000000..091c575
--- /dev/null
+++ b/toolchain/BUILD.bazel
@@ -0,0 +1,77 @@
+load(":ndk_cc_toolchain_config.bzl", "ndk_cc_toolchain_config")
+
+package(default_visibility = ["//visibility:public"])
+
+# https://bazel.build/reference/be/c-cpp#cc_toolchain_suite
+cc_toolchain_suite(
+ name = "ndk_toolchain",
+ toolchains = {
+ "arm64-v8a": ":ndk_arm64-v8a_toolchain",
+ "armeabi-v7a": ":ndk_armeabi-v7a_toolchain",
+ },
+ tags = [
+ "manual", # Exclude it from wildcard queries, e.g. "bazel build //...".
+ ],
+)
+
+############################
+# arm64-v8a C++ toolchain. #
+############################
+
+# https://bazel.build/reference/be/c-cpp#cc_toolchain
+cc_toolchain(
+ name = "ndk_arm64-v8a_toolchain",
+ all_files = ":ndk_arm64-v8a_toolchain_all_files",
+ ar_files = ":ndk_arm64-v8a_toolchain_all_files",
+ compiler_files = ":ndk_arm64-v8a_toolchain_all_files",
+ dwp_files = ":ndk_arm64-v8a_toolchain_all_files",
+ dynamic_runtime_lib = "@android_ndk//:arm64-v8a_dynamic_runtime_libraries",
+ linker_files = ":ndk_arm64-v8a_toolchain_all_files",
+ objcopy_files = ":ndk_arm64-v8a_toolchain_all_files",
+ strip_files = ":ndk_arm64-v8a_toolchain_all_files",
+ static_runtime_lib = "@android_ndk//:arm64-v8a_static_runtime_libraries",
+ supports_param_files = False,
+ toolchain_identifier = "ndk-arm64-v8a-toolchain",
+ toolchain_config = ":ndk_arm64-v8a_toolchain_config",
+)
+
+filegroup(
+ name = "ndk_arm64-v8a_toolchain_all_files",
+ srcs = glob(["trampolines/*.sh"]) + ["@android_ndk//:arm64-v8a_all_files"],
+)
+
+ndk_cc_toolchain_config(
+ name = "ndk_arm64-v8a_toolchain_config",
+ cpu = "arm64-v8a",
+)
+
+##############################
+# armeabi-v7a C++ toolchain. #
+##############################
+
+# https://bazel.build/reference/be/c-cpp#cc_toolchain
+cc_toolchain(
+ name = "ndk_armeabi-v7a_toolchain",
+ all_files = ":ndk_armeabi-v7a_toolchain_all_files",
+ ar_files = ":ndk_armeabi-v7a_toolchain_all_files",
+ compiler_files = ":ndk_armeabi-v7a_toolchain_all_files",
+ dwp_files = ":ndk_armeabi-v7a_toolchain_all_files",
+ dynamic_runtime_lib = "@android_ndk//:armeabi-v7a_dynamic_runtime_libraries",
+ linker_files = ":ndk_armeabi-v7a_toolchain_all_files",
+ objcopy_files = ":ndk_armeabi-v7a_toolchain_all_files",
+ strip_files = ":ndk_armeabi-v7a_toolchain_all_files",
+ static_runtime_lib = "@android_ndk//:armeabi-v7a_static_runtime_libraries",
+ supports_param_files = False,
+ toolchain_identifier = "ndk-armeabi-v7a-toolchain",
+ toolchain_config = ":ndk_armeabi-v7a_toolchain_config",
+)
+
+filegroup(
+ name = "ndk_armeabi-v7a_toolchain_all_files",
+ srcs = glob(["trampolines/*.sh"]) + ["@android_ndk//:armeabi-v7a_all_files"],
+)
+
+ndk_cc_toolchain_config(
+ name = "ndk_armeabi-v7a_toolchain_config",
+ cpu = "armeabi-v7a",
+)
diff --git a/toolchain/README.md b/toolchain/README.md
new file mode 100644
index 0000000..dc278ef
--- /dev/null
+++ b/toolchain/README.md
@@ -0,0 +1,37 @@
+# Hermetic Android NDK C++ toolchain
+
+This directory defines a hermetic C++ toolchain suite to compile with the Android NDK.
+
+## Motivation
+
+Bazel has a built-in
+[`android_ndk_repository`](https://bazel.build/reference/be/android#android_ndk_repository) rule,
+which generates C++ toolchains based on a local NDK installation provided via the
+`ANDROID_NDK_HOME` environment variable. However, this rule breaks
+[hermeticity](https://bazel.build/concepts/hermeticity), and requires the user to provide an NDK
+installation.
+
+This directory provides a `download_android_ndk` repository rule, which downloads the Android NDK
+under `external/android_ndk`, and a C++ toolchain suite that targets 32- and 64-bit ARM.
+
+## Design
+
+The C++ toolchain suite is based on the C++ toolchain generated by the `android_ndk_repository`
+rule.
+
+Steps taken:
+
+- Build SkCMS with the `android_ndk_repository` rule at
+[this revision](https://skia.googlesource.com/skcms/+/30c8e303800c256febb03a09fdcda7f75d119b1b/WORKSPACE#22).
+- Inspect the contents of `bazel-skcms/external/androidndk/BUILD.bazel` and
+`bazel-skcms/external/androidndk/cc_toolchain_config.bzl`, which are generated by said rule.
+- Extract the useful parts into the C++ toolchain defined in this directory.
+
+### Trampoline scripts
+
+The
+[`cc_common.create_cc_toolchain_config_info`](https://bazel.build/rules/lib/cc_common#create_cc_toolchain_config_info)
+function expects tool paths to point to files under the directory in which it is invoked. This
+means we cannot directly reference tools under `external/android_ndk`. The solution is to use
+"trampoline" scripts that pass through any command-line arguments to the NDK binaries under
+`external/android_sdk`.
diff --git a/toolchain/download_toolchains.bzl b/toolchain/download_toolchains.bzl
new file mode 100644
index 0000000..444483a
--- /dev/null
+++ b/toolchain/download_toolchains.bzl
@@ -0,0 +1,42 @@
+"""
+This module defines the download_android_ndk repository rule.
+"""
+load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
+
+_download_android_ndk_rule_name = "android_ndk"
+
+def _download_android_ndk(name):
+ """Downloads the Android NDK under external/android_ndk.
+
+ Args:
+ name: Name of the external repository. This MUST equal "android_ndk".
+ """
+
+ # The toolchain assumes that the NDK is found at /external/android_ndk, so we must enforce this
+ # name.
+ if name != _download_android_ndk_rule_name:
+ fail("The name of this rule MUST be \"%s\"" % _download_android_ndk_rule_name)
+
+ # Archive taken from https://github.com/android/ndk/wiki/Unsupported-Downloads#r21e.
+ http_archive(
+ name = _download_android_ndk_rule_name,
+ urls = [
+ "https://dl.google.com/android/repository/android-ndk-r21e-linux-x86_64.zip",
+ "https://storage.googleapis.com/skia-world-readable/bazel/ad7ce5467e18d40050dc51b8e7affc3e635c85bd8c59be62de32352328ed467e.zip",
+ ],
+ sha256 = "ad7ce5467e18d40050dc51b8e7affc3e635c85bd8c59be62de32352328ed467e",
+ strip_prefix = "android-ndk-r21e",
+ build_file = Label("//toolchain:ndk.BUILD"),
+ )
+
+def download_toolchains(android_ndk_repository_name):
+ """Downloads the toolchains needed to build this repository.
+
+ Args:
+ android_ndk_repository_name: Name of the external repository with the android NDK. This MUST
+ equal "android_ndk".
+ """
+ _download_android_ndk(android_ndk_repository_name)
+
+# Path to the Android NDK from the point of view of the cc_toolchain rule.
+NDK_PATH = "external/%s" % _download_android_ndk_rule_name
diff --git a/toolchain/ndk.BUILD b/toolchain/ndk.BUILD
new file mode 100644
index 0000000..347a53a
--- /dev/null
+++ b/toolchain/ndk.BUILD
@@ -0,0 +1,65 @@
+# This file is based on the `external/androidndk/BUILD.bazel` file produced by the built-in
+# `android_ndk_repository` Bazel rule[1], which was used to build the SkCMS repository up until
+# this revision[2].
+#
+# The paths in this file point to locations inside the expanded Android NDK ZIP file (found at
+# external/android_ndk), and must be updated every time we upgrade to a new Android NDK version.
+#
+# [1] https://github.com/bazelbuild/bazel/blob/4710ef82ce34572878e07c52e83a0144d707f140/src/main/java/com/google/devtools/build/lib/bazel/rules/android/AndroidNdkRepositoryFunction.java
+# [2] https://skia.googlesource.com/skcms/+/30c8e303800c256febb03a09fdcda7f75d119b1b/WORKSPACE#22
+
+filegroup(
+ name = "arm64-v8a_all_files",
+ srcs = glob(["toolchains/llvm/**"]) + glob([
+ "platforms/android-29/arch-arm64/**/*",
+ "sources/cxx-stl/llvm-libc++/include/**/*",
+ "sources/cxx-stl/llvm-libc++abi/include/**/*",
+ "sources/android/support/include/**/*",
+ "sysroot/**/*",
+ "toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/**/*",
+ ]) + [
+ ":arm64-v8a_dynamic_runtime_libraries",
+ ":arm64-v8a_static_runtime_libraries",
+ ],
+ visibility = ["//visibility:public"],
+)
+
+filegroup(
+ name = "arm64-v8a_dynamic_runtime_libraries",
+ srcs = glob(["sources/cxx-stl/llvm-libc++/libs/arm64-v8a/*.so"]),
+ visibility = ["//visibility:public"],
+)
+
+filegroup(
+ name = "arm64-v8a_static_runtime_libraries",
+ srcs = glob(["sources/cxx-stl/llvm-libc++/libs/arm64-v8a/*.a"]),
+ visibility = ["//visibility:public"],
+)
+
+filegroup(
+ name = "armeabi-v7a_all_files",
+ srcs = glob(["toolchains/llvm/**"]) + glob([
+ "platforms/android-29/arch-arm/**/*",
+ "sources/cxx-stl/llvm-libc++/include/**/*",
+ "sources/cxx-stl/llvm-libc++abi/include/**/*",
+ "sources/android/support/include/**/*",
+ "sysroot/**/*",
+ "toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/**/*",
+ ]) + [
+ ":armeabi-v7a_dynamic_runtime_libraries",
+ ":armeabi-v7a_static_runtime_libraries",
+ ],
+ visibility = ["//visibility:public"],
+)
+
+filegroup(
+ name = "armeabi-v7a_dynamic_runtime_libraries",
+ srcs = glob(["sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/*.so"]),
+ visibility = ["//visibility:public"],
+)
+
+filegroup(
+ name = "armeabi-v7a_static_runtime_libraries",
+ srcs = glob(["sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/*.a"]),
+ visibility = ["//visibility:public"],
+)
diff --git a/toolchain/ndk_cc_toolchain_config.bzl b/toolchain/ndk_cc_toolchain_config.bzl
new file mode 100644
index 0000000..ccf38ea
--- /dev/null
+++ b/toolchain/ndk_cc_toolchain_config.bzl
@@ -0,0 +1,404 @@
+"""This module defines the ndk_cc_toolchain_config rule.
+
+This file is based on the `external/androidndk/cc_toolchain_config.bzl` file produced by the
+built-in `android_ndk_repository` Bazel rule[1], which was used to build the SkCMS repository up
+until this revision[2].
+
+The paths in this file point to locations inside the expanded Android NDK ZIP file (found at
+external/android_ndk), and must be updated every time we upgrade to a new Android NDK version.
+
+[1] https://github.com/bazelbuild/bazel/blob/4710ef82ce34572878e07c52e83a0144d707f140/src/main/java/com/google/devtools/build/lib/bazel/rules/android/AndroidNdkRepositoryFunction.java#L422
+[2] https://skia.googlesource.com/skcms/+/30c8e303800c256febb03a09fdcda7f75d119b1b/WORKSPACE#22
+"""
+
+load("@bazel_tools//tools/build_defs/cc:action_names.bzl", "ACTION_NAMES")
+load(
+ "@bazel_tools//tools/cpp:cc_toolchain_config_lib.bzl",
+ "feature",
+ "flag_group",
+ "flag_set",
+ "tool_path",
+ "with_feature_set",
+)
+load("download_toolchains.bzl", "NDK_PATH")
+
+# Supported CPUs.
+_ARMEABI_V7A = "armeabi-v7a"
+_ARM64_V8A = "arm64-v8a"
+
+_all_compile_actions = [
+ ACTION_NAMES.c_compile,
+ ACTION_NAMES.cpp_compile,
+ ACTION_NAMES.linkstamp_compile,
+ ACTION_NAMES.assemble,
+ ACTION_NAMES.preprocess_assemble,
+ ACTION_NAMES.cpp_header_parsing,
+ ACTION_NAMES.cpp_module_compile,
+ ACTION_NAMES.cpp_module_codegen,
+ ACTION_NAMES.clif_match,
+ ACTION_NAMES.lto_backend,
+]
+
+_all_link_actions = [
+ ACTION_NAMES.cpp_link_executable,
+ ACTION_NAMES.cpp_link_dynamic_library,
+ ACTION_NAMES.cpp_link_nodeps_dynamic_library,
+]
+
+def _get_default_compile_flags(cpu):
+ if cpu == _ARMEABI_V7A:
+ return [
+ "-D__ANDROID_API__=29",
+ "-isystem",
+ NDK_PATH + "/sysroot/usr/include/arm-linux-androideabi",
+ "-target",
+ "armv7-none-linux-androideabi",
+ "-march=armv7-a",
+ "-mfloat-abi=softfp",
+ "-mfpu=vfpv3-d16",
+ "-gcc-toolchain",
+ NDK_PATH + "/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64",
+ "-fpic",
+ "-no-canonical-prefixes",
+ "-Wno-invalid-command-line-argument",
+ "-Wno-unused-command-line-argument",
+ "-funwind-tables",
+ "-fstack-protector-strong",
+ "-fno-addrsig",
+ "-Werror=return-type",
+ "-Werror=int-to-pointer-cast",
+ "-Werror=pointer-to-int-cast",
+ "-Werror=implicit-function-declaration",
+ ]
+ if cpu == _ARM64_V8A:
+ return [
+ "-gcc-toolchain",
+ NDK_PATH + "/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64",
+ "-target",
+ "aarch64-none-linux-android",
+ "-fpic",
+ "-isystem",
+ NDK_PATH + "/sysroot/usr/include/aarch64-linux-android",
+ "-D__ANDROID_API__=29",
+ "-no-canonical-prefixes",
+ "-Wno-invalid-command-line-argument",
+ "-Wno-unused-command-line-argument",
+ "-funwind-tables",
+ "-fstack-protector-strong",
+ "-fno-addrsig",
+ "-Werror=return-type",
+ "-Werror=int-to-pointer-cast",
+ "-Werror=pointer-to-int-cast",
+ "-Werror=implicit-function-declaration",
+ ]
+ fail("Unknown CPU: " + cpu)
+
+def _get_default_link_flags(cpu):
+ if cpu == _ARMEABI_V7A:
+ return [
+ "-target",
+ "armv7-none-linux-androideabi",
+ "-gcc-toolchain",
+ NDK_PATH + "/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64",
+ "-L",
+ NDK_PATH + "/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a",
+ "-no-canonical-prefixes",
+ "-Wl,-z,relro",
+ "-Wl,--gc-sections",
+ ]
+ if cpu == _ARM64_V8A:
+ return [
+ "-gcc-toolchain",
+ NDK_PATH + "/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64",
+ "-target",
+ "aarch64-none-linux-android",
+ "-L",
+ NDK_PATH + "/sources/cxx-stl/llvm-libc++/libs/arm64-v8a",
+ "-no-canonical-prefixes",
+ "-Wl,-z,relro",
+ "-Wl,--gc-sections",
+ ]
+ fail("Unknown CPU: " + cpu)
+
+def _get_default_dbg_flags(cpu):
+ if cpu == _ARMEABI_V7A:
+ return ["-g", "-fno-strict-aliasing", "-O0", "-UNDEBUG"]
+ if cpu == _ARM64_V8A:
+ return ["-O0", "-g", "-UNDEBUG"]
+ fail("Unknown CPU: " + cpu)
+
+def _get_default_opt_flags(cpu):
+ if cpu == _ARMEABI_V7A:
+ return ["-mthumb", "-Os", "-g", "-DNDEBUG"]
+ if cpu == _ARM64_V8A:
+ return ["-O2", "-g", "-DNDEBUG"]
+ fail("Unknown CPU: " + cpu)
+
+def _get_toolchain_identifier(cpu):
+ if cpu == _ARMEABI_V7A:
+ return "ndk-armeabi-v7a-toolchain"
+ if cpu == _ARM64_V8A:
+ return "ndk-arm64-v8a-toolchain"
+ fail("Unknown CPU: " + cpu)
+
+def _get_target_system_name(cpu):
+ if cpu == _ARMEABI_V7A:
+ return "arm-linux-androideabi"
+ if cpu == _ARM64_V8A:
+ return "aarch64-linux-android"
+ fail("Unknown CPU: " + cpu)
+
+def _get_builtin_sysroot(cpu):
+ if cpu == _ARMEABI_V7A:
+ return NDK_PATH + "/platforms/android-29/arch-arm"
+ if cpu == _ARM64_V8A:
+ return NDK_PATH + "/platforms/android-29/arch-arm64"
+ fail("Unknown CPU: " + cpu)
+
+def _get_tool_paths(cpu):
+ # The cc_common.create_cc_toolchain_config_info function expects tool paths to point to files
+ # under the directory in which it is invoked. This means we cannot directly reference tools
+ # under external/android_ndk. The solution is to use "trampoline" scripts that pass through
+ # any command-line arguments to the NDK binaries under external/android_sdk.
+
+ if cpu == _ARMEABI_V7A:
+ return [
+ tool_path(
+ name = "ar",
+ path = "trampolines/arm-linux-androideabi-ar.sh",
+ ),
+ tool_path(
+ name = "cpp",
+ path = "trampolines/clang.sh",
+ ),
+ tool_path(
+ name = "dwp",
+ path = "trampolines/arm-linux-androideabi-dwp.sh",
+ ),
+ tool_path(
+ name = "gcc",
+ path = "trampolines/clang.sh",
+ ),
+ tool_path(
+ name = "gcov",
+ path = "/bin/false",
+ ),
+ tool_path(
+ name = "ld",
+ path = "trampolines/arm-linux-androideabi-ld.sh",
+ ),
+ tool_path(
+ name = "nm",
+ path = "trampolines/arm-linux-androideabi-nm.sh",
+ ),
+ tool_path(
+ name = "objcopy",
+ path = "trampolines/arm-linux-androideabi-objcopy.sh",
+ ),
+ tool_path(
+ name = "objdump",
+ path = "trampolines/arm-linux-androideabi-objdump.sh",
+ ),
+ tool_path(
+ name = "strip",
+ path = "trampolines/arm-linux-androideabi-strip.sh",
+ ),
+ ]
+ if cpu == _ARM64_V8A:
+ return [
+ tool_path(
+ name = "ar",
+ path = "trampolines/aarch64-linux-android-ar.sh",
+ ),
+ tool_path(
+ name = "cpp",
+ path = "trampolines/clang.sh",
+ ),
+ tool_path(
+ name = "dwp",
+ path = "trampolines/aarch64-linux-android-dwp.sh",
+ ),
+ tool_path(
+ name = "gcc",
+ path = "trampolines/clang.sh",
+ ),
+ tool_path(
+ name = "gcov",
+ path = "/bin/false",
+ ),
+ tool_path(
+ name = "ld",
+ path = "trampolines/aarch64-linux-android-ld.sh",
+ ),
+ tool_path(
+ name = "nm",
+ path = "trampolines/aarch64-linux-android-nm.sh",
+ ),
+ tool_path(
+ name = "objcopy",
+ path = "trampolines/aarch64-linux-android-objcopy.sh",
+ ),
+ tool_path(
+ name = "objdump",
+ path = "trampolines/aarch64-linux-android-objdump.sh",
+ ),
+ tool_path(
+ name = "strip",
+ path = "trampolines/aarch64-linux-android-strip.sh",
+ ),
+ ]
+ fail("Unknown CPU: " + cpu)
+
+def _ndk_cc_toolchain_config_impl(ctx):
+ default_compile_flags = _get_default_compile_flags(ctx.attr.cpu)
+ unfiltered_compile_flags = [
+ "-isystem",
+ NDK_PATH + "/sources/cxx-stl/llvm-libc++/include",
+ "-isystem",
+ NDK_PATH + "/sources/cxx-stl/llvm-libc++abi/include",
+ "-isystem",
+ NDK_PATH + "/sources/android/support/include",
+ "-isystem",
+ NDK_PATH + "/sysroot/usr/include",
+ ]
+ default_link_flags = _get_default_link_flags(ctx.attr.cpu)
+ default_fastbuild_flags = [""]
+ default_dbg_flags = _get_default_dbg_flags(ctx.attr.cpu)
+ default_opt_flags = _get_default_opt_flags(ctx.attr.cpu)
+
+ opt_feature = feature(name = "opt")
+ fastbuild_feature = feature(name = "fastbuild")
+ dbg_feature = feature(name = "dbg")
+ supports_dynamic_linker_feature = feature(name = "supports_dynamic_linker", enabled = True)
+ supports_pic_feature = feature(name = "supports_pic", enabled = True)
+ static_link_cpp_runtimes_feature = feature(name = "static_link_cpp_runtimes", enabled = True)
+
+ default_compile_flags_feature = feature(
+ name = "default_compile_flags",
+ enabled = True,
+ flag_sets = [
+ flag_set(
+ actions = _all_compile_actions,
+ flag_groups = [flag_group(flags = default_compile_flags)],
+ ),
+ flag_set(
+ actions = _all_compile_actions,
+ flag_groups = [flag_group(flags = default_fastbuild_flags)],
+ with_features = [with_feature_set(features = ["fastbuild"])],
+ ),
+ flag_set(
+ actions = _all_compile_actions,
+ flag_groups = [flag_group(flags = default_dbg_flags)],
+ with_features = [with_feature_set(features = ["dbg"])],
+ ),
+ flag_set(
+ actions = _all_compile_actions,
+ flag_groups = [flag_group(flags = default_opt_flags)],
+ with_features = [with_feature_set(features = ["opt"])],
+ ),
+ ],
+ )
+
+ default_link_flags_feature = feature(
+ name = "default_link_flags",
+ enabled = True,
+ flag_sets = [
+ flag_set(
+ actions = _all_link_actions,
+ flag_groups = [flag_group(flags = default_link_flags)],
+ ),
+ ],
+ )
+
+ user_compile_flags_feature = feature(
+ name = "user_compile_flags",
+ enabled = True,
+ flag_sets = [
+ flag_set(
+ actions = _all_compile_actions,
+ flag_groups = [
+ flag_group(
+ flags = ["%{user_compile_flags}"],
+ iterate_over = "user_compile_flags",
+ expand_if_available = "user_compile_flags",
+ ),
+ ],
+ ),
+ ],
+ )
+
+ sysroot_feature = feature(
+ name = "sysroot",
+ enabled = True,
+ flag_sets = [
+ flag_set(
+ actions = _all_compile_actions + _all_link_actions,
+ flag_groups = [
+ flag_group(
+ flags = ["--sysroot=%{sysroot}"],
+ expand_if_available = "sysroot",
+ ),
+ ],
+ ),
+ ],
+ )
+
+ unfiltered_compile_flags_feature = feature(
+ name = "unfiltered_compile_flags",
+ enabled = True,
+ flag_sets = [
+ flag_set(
+ actions = _all_compile_actions,
+ flag_groups = [flag_group(flags = unfiltered_compile_flags)],
+ ),
+ ],
+ )
+
+ features = [
+ default_compile_flags_feature,
+ default_link_flags_feature,
+ supports_dynamic_linker_feature,
+ supports_pic_feature,
+ static_link_cpp_runtimes_feature,
+ fastbuild_feature,
+ dbg_feature,
+ opt_feature,
+ user_compile_flags_feature,
+ sysroot_feature,
+ unfiltered_compile_flags_feature,
+ ]
+
+ cxx_builtin_include_directories = [
+ NDK_PATH + "/toolchains/llvm/prebuilt/linux-x86_64/lib64/clang/9.0.9/include",
+ "%sysroot%/usr/include",
+ NDK_PATH + "/sysroot/usr/include",
+ ]
+
+ # https://bazel.build/rules/lib/cc_common#create_cc_toolchain_config_info
+ return cc_common.create_cc_toolchain_config_info(
+ ctx = ctx,
+ toolchain_identifier = _get_toolchain_identifier(ctx.attr.cpu),
+ host_system_name = "local",
+ target_system_name = _get_target_system_name(ctx.attr.cpu),
+ target_cpu = ctx.attr.cpu,
+ target_libc = "local",
+ compiler = "clang9.0.9",
+ abi_version = ctx.attr.cpu,
+ abi_libc_version = "local",
+ features = features,
+ tool_paths = _get_tool_paths(ctx.attr.cpu),
+ cxx_builtin_include_directories = cxx_builtin_include_directories,
+ builtin_sysroot = _get_builtin_sysroot(ctx.attr.cpu),
+ )
+
+ndk_cc_toolchain_config = rule(
+ implementation = _ndk_cc_toolchain_config_impl,
+ attrs = {
+ "cpu": attr.string(
+ mandatory = True,
+ values = [_ARMEABI_V7A, _ARM64_V8A],
+ doc = "Target CPU.",
+ )
+ },
+ provides = [CcToolchainConfigInfo],
+)
diff --git a/toolchain/trampolines/aarch64-linux-android-ar.sh b/toolchain/trampolines/aarch64-linux-android-ar.sh
new file mode 100755
index 0000000..08b0744
--- /dev/null
+++ b/toolchain/trampolines/aarch64-linux-android-ar.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+external/android_ndk/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/bin/aarch64-linux-android-ar $@
diff --git a/toolchain/trampolines/aarch64-linux-android-dwp.sh b/toolchain/trampolines/aarch64-linux-android-dwp.sh
new file mode 100755
index 0000000..de8fb1d
--- /dev/null
+++ b/toolchain/trampolines/aarch64-linux-android-dwp.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+external/android_ndk/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/bin/aarch64-linux-android-dwp $@
diff --git a/toolchain/trampolines/aarch64-linux-android-ld.sh b/toolchain/trampolines/aarch64-linux-android-ld.sh
new file mode 100755
index 0000000..6d0158a
--- /dev/null
+++ b/toolchain/trampolines/aarch64-linux-android-ld.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+external/android_ndk/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/bin/aarch64-linux-android-ld $@
diff --git a/toolchain/trampolines/aarch64-linux-android-nm.sh b/toolchain/trampolines/aarch64-linux-android-nm.sh
new file mode 100755
index 0000000..4bf5e74
--- /dev/null
+++ b/toolchain/trampolines/aarch64-linux-android-nm.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+external/android_ndk/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/bin/aarch64-linux-android-nm $@
diff --git a/toolchain/trampolines/aarch64-linux-android-objcopy.sh b/toolchain/trampolines/aarch64-linux-android-objcopy.sh
new file mode 100755
index 0000000..84d17a3
--- /dev/null
+++ b/toolchain/trampolines/aarch64-linux-android-objcopy.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+external/android_ndk/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/bin/aarch64-linux-android-objcopy $@
diff --git a/toolchain/trampolines/aarch64-linux-android-objdump.sh b/toolchain/trampolines/aarch64-linux-android-objdump.sh
new file mode 100755
index 0000000..c38e60d
--- /dev/null
+++ b/toolchain/trampolines/aarch64-linux-android-objdump.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+external/android_ndk/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/bin/aarch64-linux-android-objdump $@
diff --git a/toolchain/trampolines/aarch64-linux-android-strip.sh b/toolchain/trampolines/aarch64-linux-android-strip.sh
new file mode 100755
index 0000000..e396c9d
--- /dev/null
+++ b/toolchain/trampolines/aarch64-linux-android-strip.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+external/android_ndk/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/bin/aarch64-linux-android-strip $@
diff --git a/toolchain/trampolines/arm-linux-androideabi-ar.sh b/toolchain/trampolines/arm-linux-androideabi-ar.sh
new file mode 100755
index 0000000..ee99c68
--- /dev/null
+++ b/toolchain/trampolines/arm-linux-androideabi-ar.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+external/android_ndk/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-ar $@
diff --git a/toolchain/trampolines/arm-linux-androideabi-dwp.sh b/toolchain/trampolines/arm-linux-androideabi-dwp.sh
new file mode 100755
index 0000000..b046d28
--- /dev/null
+++ b/toolchain/trampolines/arm-linux-androideabi-dwp.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+external/android_ndk/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-dwp $@
diff --git a/toolchain/trampolines/arm-linux-androideabi-ld.sh b/toolchain/trampolines/arm-linux-androideabi-ld.sh
new file mode 100755
index 0000000..c9c7a87
--- /dev/null
+++ b/toolchain/trampolines/arm-linux-androideabi-ld.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+external/android_ndk/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-ld $@
diff --git a/toolchain/trampolines/arm-linux-androideabi-nm.sh b/toolchain/trampolines/arm-linux-androideabi-nm.sh
new file mode 100755
index 0000000..fe1fa2a
--- /dev/null
+++ b/toolchain/trampolines/arm-linux-androideabi-nm.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+external/android_ndk/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-nm $@
diff --git a/toolchain/trampolines/arm-linux-androideabi-objcopy.sh b/toolchain/trampolines/arm-linux-androideabi-objcopy.sh
new file mode 100755
index 0000000..9fc92a3
--- /dev/null
+++ b/toolchain/trampolines/arm-linux-androideabi-objcopy.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+external/android_ndk/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-objcopy $@
diff --git a/toolchain/trampolines/arm-linux-androideabi-objdump.sh b/toolchain/trampolines/arm-linux-androideabi-objdump.sh
new file mode 100755
index 0000000..b161b35
--- /dev/null
+++ b/toolchain/trampolines/arm-linux-androideabi-objdump.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+external/android_ndk/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-objdump $@
diff --git a/toolchain/trampolines/arm-linux-androideabi-strip.sh b/toolchain/trampolines/arm-linux-androideabi-strip.sh
new file mode 100755
index 0000000..b8f7499
--- /dev/null
+++ b/toolchain/trampolines/arm-linux-androideabi-strip.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+external/android_ndk/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-strip $@
diff --git a/toolchain/trampolines/clang.sh b/toolchain/trampolines/clang.sh
new file mode 100755
index 0000000..2d743f0
--- /dev/null
+++ b/toolchain/trampolines/clang.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+external/android_ndk/toolchains/llvm/prebuilt/linux-x86_64/bin/clang $@
diff --git a/toolchain/trampolines/gen_trampolines/gen_trampolines.go b/toolchain/trampolines/gen_trampolines/gen_trampolines.go
new file mode 100644
index 0000000..68e66a3
--- /dev/null
+++ b/toolchain/trampolines/gen_trampolines/gen_trampolines.go
@@ -0,0 +1,75 @@
+// Helper program to generate trampoline scripts for NDK tools.
+//
+// This program is meant to be run by hand when making changes to the hermetic Android NDK
+// toolchain, e.g. when upgrading to a new Android NDK version.
+//
+// Trampoline scripts are necessary because the `cc_common.create_cc_toolchain_config_info`[1]
+// built-in Bazel function expects tool paths to point to files under the directory in which it is
+// invoked, thus we cannot directly reference tools under `external/android_ndk`. The solution is
+// to use trampoline scripts that pass through any command-line arguments to the NDK binaries under
+// `external/android_sdk`.
+//
+// [1] https://bazel.build/rules/lib/cc_common#create_cc_toolchain_config_info
+package main
+
+import (
+ "errors"
+ "flag"
+ "fmt"
+ "os"
+ "path/filepath"
+)
+
+const bazelNdkPath = "external/android_ndk"
+
+// Paths relative to the Android NDK root directory. These paths can be determined by inspecting
+// the Android NDK ZIP file downloaded by the `download_toolchains` macro defined in
+// //toolchains/download_toolchains.bzl.
+var tools = []string{
+ "toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-ar",
+ "toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-dwp",
+ "toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-ld",
+ "toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-nm",
+ "toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-objcopy",
+ "toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-objdump",
+ "toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-strip",
+ "toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/bin/aarch64-linux-android-ar",
+ "toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/bin/aarch64-linux-android-dwp",
+ "toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/bin/aarch64-linux-android-ld",
+ "toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/bin/aarch64-linux-android-nm",
+ "toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/bin/aarch64-linux-android-objcopy",
+ "toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/bin/aarch64-linux-android-objdump",
+ "toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/bin/aarch64-linux-android-strip",
+ "toolchains/llvm/prebuilt/linux-x86_64/bin/clang",
+}
+
+const trampolineScriptTemplate = `#!/bin/sh
+%s $@
+`
+
+func main() {
+ ndkDirFlag := flag.String("ndk-dir", "", "Path to a local copy of the NDK. Used only to verify that the tool paths assumed by this program are valid. Required.")
+ outDirFlag := flag.String("out-dir", "", "Directory where to save the trampoline scripts. Required.")
+ flag.Parse()
+
+ if *ndkDirFlag == "" || *outDirFlag == "" {
+ flag.Usage()
+ os.Exit(1)
+ }
+
+ for _, tool := range tools {
+ // Verify that the tool exists in the NDK.
+ ndkPath := filepath.Join(*ndkDirFlag, tool)
+ if _, err := os.Stat(ndkPath); errors.Is(err, os.ErrNotExist) {
+ fmt.Fprintf(os.Stderr, "File %s not found.", ndkPath)
+ os.Exit(1)
+ }
+
+ // Generate trampoline script.
+ trampolineScript := fmt.Sprintf(trampolineScriptTemplate, filepath.Join(bazelNdkPath, tool))
+ trampolineScriptPath := filepath.Join(*outDirFlag, filepath.Base(tool)+".sh")
+ if err := os.WriteFile(trampolineScriptPath, []byte(trampolineScript), 0750); err != nil {
+ fmt.Fprintf(os.Stderr, "Error writing file %s: %s", trampolineScriptPath, err)
+ }
+ }
+}