Integrate Android NDK with toolchains

Fixes https://github.com/bazelbuild/bazel/issues/8871

Generates toolchains for the `cc_toolchain` targets in the `@androidndk` BUILD file:

```python
toolchain(
  name = "x86-clang8.0.7-libcpp_toolchain",
  toolchain_type = "@bazel_tools//tools/cpp:toolchain_type",
  target_compatible_with = [
      "@bazel_tools//platforms:android",
      "@bazel_tools//platforms:x86_32"
  ],
  toolchain = "@androidndk//:x86-clang8.0.7-libcpp",
)
toolchain(
  name = "x86_64-clang8.0.7-libcpp_toolchain",
  toolchain_type = "@bazel_tools//tools/cpp:toolchain_type",
  target_compatible_with = [
      "@bazel_tools//platforms:android",
      "@bazel_tools//platforms:x86_64"
  ],
  toolchain = "@androidndk//:x86_64-clang8.0.7-libcpp",
)
toolchain(
  name = "arm-linux-androideabi-clang8.0.7-v7a-libcpp_toolchain",
  toolchain_type = "@bazel_tools//tools/cpp:toolchain_type",
  target_compatible_with = [
      "@bazel_tools//platforms:android",
      "@bazel_tools//platforms:arm"
  ],
  toolchain = "@androidndk//:arm-linux-androideabi-clang8.0.7-v7a-libcpp",
)
toolchain(
  name = "aarch64-linux-android-clang8.0.7-libcpp_toolchain",
  toolchain_type = "@bazel_tools//tools/cpp:toolchain_type",
  target_compatible_with = [
      "@bazel_tools//platforms:android",
      "@bazel_tools//platforms:aarch64"
  ],
  toolchain = "@androidndk//:aarch64-linux-android-clang8.0.7-libcpp",
)
```

Users can use them by using the `--extra_toolchains=@androidndk//:all` flag or by registering them in the WORKSPACE with `register_toolchains("androidndk//:all")`.

RELNOTES: The Android NDK is now integrated with toolchains. To use them, pass the `--extra_toolchains=@androidndk//:all` flag or register them in your WORKSPACE with `register_toolchains("@androidndk//:all")`.

Closes #8918.

Change-Id: Iba708d1749efacbd47fc3584efd55634c70d2b18
PiperOrigin-RevId: 258633109
diff --git a/site/docs/android-ndk.md b/site/docs/android-ndk.md
index b75ca67..8558207 100644
--- a/site/docs/android-ndk.md
+++ b/site/docs/android-ndk.md
@@ -255,6 +255,77 @@
 )
 ```
 
+## Integration with platforms and toolchains
+
+Bazel's configuration model is moving towards
+[platforms](https://docs.bazel.build/versions/master/platforms.html) and
+[toolchains](https://docs.bazel.build/versions/master/toolchains.html). If your
+build uses the `--platforms` flag to select for the architecture or operating system
+to build for, you will need to pass the `--extra_toolchains` flag to Bazel in
+order to use the NDK.
+
+For example, to integrate with the `android_arm64_cgo` toolchain provided by
+the Go rules, pass `--extra_toolchains=@androidndk//:all` in addition to the
+`--platforms` flag.
+
+```
+bazel build //my/cc:lib \
+  --platforms=@io_bazel_rules_go//go/toolchain:android_arm64_cgo \
+  --extra_toolchains=@androidndk//:all
+```
+
+You can also register them directly in the `WORKSPACE` file:
+
+```python
+android_ndk_repository(name = "androidndk")
+register_toolchains("@androidndk//:all")
+```
+
+Registering these toolchains tells Bazel to look for them in the NDK BUILD file
+(for NDK 20) when resolving architecture and operating system constraints:
+
+```python
+toolchain(
+  name = "x86-clang8.0.7-libcpp_toolchain",
+  toolchain_type = "@bazel_tools//tools/cpp:toolchain_type",
+  target_compatible_with = [
+      "@bazel_tools//platforms:android",
+      "@bazel_tools//platforms:x86_32"
+  ],
+  toolchain = "@androidndk//:x86-clang8.0.7-libcpp",
+)
+
+toolchain(
+  name = "x86_64-clang8.0.7-libcpp_toolchain",
+  toolchain_type = "@bazel_tools//tools/cpp:toolchain_type",
+  target_compatible_with = [
+      "@bazel_tools//platforms:android",
+      "@bazel_tools//platforms:x86_64"
+  ],
+  toolchain = "@androidndk//:x86_64-clang8.0.7-libcpp",
+)
+
+toolchain(
+  name = "arm-linux-androideabi-clang8.0.7-v7a-libcpp_toolchain",
+  toolchain_type = "@bazel_tools//tools/cpp:toolchain_type",
+  target_compatible_with = [
+      "@bazel_tools//platforms:android",
+      "@bazel_tools//platforms:arm"
+  ],
+  toolchain = "@androidndk//:arm-linux-androideabi-clang8.0.7-v7a-libcpp",
+)
+
+toolchain(
+  name = "aarch64-linux-android-clang8.0.7-libcpp_toolchain",
+  toolchain_type = "@bazel_tools//tools/cpp:toolchain_type",
+  target_compatible_with = [
+      "@bazel_tools//platforms:android",
+      "@bazel_tools//platforms:aarch64"
+  ],
+  toolchain = "@androidndk//:aarch64-linux-android-clang8.0.7-libcpp",
+)
+```
+
 ## How it works: introducing Android configuration transitions
 
 The `android_binary` rule can explicitly ask Bazel to build its dependencies in
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/android/AndroidNdkRepositoryFunction.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/android/AndroidNdkRepositoryFunction.java
index 375104e..89b40ae 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/rules/android/AndroidNdkRepositoryFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/android/AndroidNdkRepositoryFunction.java
@@ -201,6 +201,7 @@
     return ccToolchainTemplate
         .replace("%toolchainName%", toolchain.getToolchainIdentifier())
         .replace("%cpu%", toolchain.getTargetCpu())
+        .replace("%platform_cpu%", getPlatformCpuLabel(toolchain.getTargetCpu()))
         .replace("%compiler%", toolchain.getCompiler())
         .replace("%version%", version)
         .replace("%dynamicRuntimeLibs%", toolchain.getDynamicRuntimesFilegroup())
@@ -209,6 +210,21 @@
         .replace("%toolchainFileGlobs%", toolchainFileGlobs.toString().trim());
   }
 
+  private static String getPlatformCpuLabel(String targetCpu) {
+    // Create a mapping of CcToolchain CPU values to platform arch constraint values
+    // in @bazel_tools//platforms
+    switch (targetCpu) {
+      case "x86":
+        return "x86_32";
+      case "armeabi-v7a":
+        return "arm";
+      case "arm64-v8a":
+        return "aarch64";
+      default:
+        return "x86_64";
+    }
+  }
+
   private static String getTemplate(String templateFile) {
     try {
       return ResourceFileLoader.loadResource(AndroidNdkRepositoryFunction.class, templateFile);
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/android/android_ndk_cc_toolchain_template.txt b/src/main/java/com/google/devtools/build/lib/bazel/rules/android/android_ndk_cc_toolchain_template.txt
index d9bd152..d622f0c 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/rules/android/android_ndk_cc_toolchain_template.txt
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/android/android_ndk_cc_toolchain_template.txt
@@ -26,6 +26,16 @@
     version = "%version%",
 )
 
+toolchain(
+    name = "%toolchainName%_toolchain",
+    target_compatible_with = [
+        "@bazel_tools//platforms:android",
+        "@bazel_tools//platforms:%platform_cpu%",
+    ],
+    toolchain = "@androidndk//:%toolchainName%",
+    toolchain_type = "@bazel_tools//tools/cpp:toolchain_type",
+)
+
 filegroup(
     name = "%toolchainName%-all_files",
     srcs = glob(["ndk/toolchains/%toolchainDirectory%/**"]) + glob([
diff --git a/src/test/shell/bazel/android/android_ndk_integration_test.sh b/src/test/shell/bazel/android/android_ndk_integration_test.sh
index c8e85e7..4095bc8 100755
--- a/src/test/shell/bazel/android/android_ndk_integration_test.sh
+++ b/src/test/shell/bazel/android/android_ndk_integration_test.sh
@@ -400,6 +400,45 @@
     --host_crosstool_top=@bazel_tools//tools/cpp:toolchain
 }
 
+function test_platforms_and_toolchains() {
+  create_new_workspace
+  setup_android_ndk_support
+  cat > BUILD <<EOF
+cc_binary(
+    name = "foo",
+    srcs = ["foo.cc"],
+    linkopts = ["-ldl", "-lm"],
+)
+
+platform(
+    name = 'android_arm',
+    constraint_values = ['@bazel_tools//platforms:arm', '@bazel_tools//platforms:android'],
+    visibility = ['//visibility:public']
+)
+EOF
+  cat > foo.cc <<EOF
+#include <string>
+#include <jni.h>
+#include <android/log.h>
+#include <cstdio>
+#include <iostream>
+
+using namespace std;
+int main(){
+  string foo = "foo";
+  string bar = "bar";
+  string foobar = foo + bar;
+  return 0;
+}
+EOF
+  assert_build //:foo \
+    --cpu=armeabi-v7a \
+    --crosstool_top=@androidndk//:toolchain-libcpp \
+    --host_crosstool_top=@bazel_tools//tools/cpp:toolchain \
+    --platforms=//:android_arm \
+    --extra_toolchains=@androidndk//:all
+}
+
 function test_crosstool_libcpp_with_multiarch() {
   create_new_workspace
   setup_android_sdk_support