diff --git a/README.md b/README.md index 5709048..0ab8d46 100644 --- a/README.md +++ b/README.md @@ -28,12 +28,31 @@ To use the Android NDK rules, add the following to your `WORKSPACE` file: sha256 = RULES_ANDROID_NDK_SHA, strip_prefix = "rules_android_ndk-%s" % RULES_ANDROID_NDK_COMMIT, ) - load("@rules_android_ndk//:rules.bzl", "android_ndk_repository") - android_ndk_repository(name = "androidndk") -Then, set the `ANDROID_NDK_HOME` environment variable or the `path` attribute of -`android_ndk_repository` to the path of the local Android NDK installation -directory. + load("@rules_android_ndk//:rules.bzl", "android_ndk_repository") + android_ndk_repository(name = "androidndk", version = "r25c") + + # either register the toolchains, or use `--extra_toolchains` when invoking Bazel + register_toolchains("@androidndk//:all") + +You can also customize the `base_url` attribute if, for example, you mirror the NDK archives +on a private server. + +Some sha256 checksums are included in this repository, but these might not be up to date, +if you want to use a version of the NDK that's not included, you can also specify the `sha256sums` +attribute which maps platforms to checksums, like so: + +``` +android_ndk_repository( + name = "androidndk", + version = "r25c" + sha256sums = { + "windows": "f70093964f6cbbe19268f9876a20f92d3a593db3ad2037baadd25fd8d71e84e2", + "darwin": "b01bae969a5d0bfa0da18469f650a1628dc388672f30e0ba231da5c74245bc92", + "linux": "769ee342ea75f80619d985c2da990c48b3d8eaf45f48783a2d48870d04b46108", + } +) +``` The `api_level` attribute can also be used to set the Android API level to build against. diff --git a/rules.bzl b/rules.bzl index aa993cb..38ed0dd 100644 --- a/rules.bzl +++ b/rules.bzl @@ -14,8 +14,21 @@ """A repository rule for integrating the Android NDK.""" +load(":sha256sums.bzl", "ndk_sha256") + +def _ndk_platform(ctx): + os_name = ctx.os.name.lower() + if os_name == "linux": + return "linux" + elif os_name.startswith("mac os"): + return "darwin" + elif os_name.startswith("windows"): + return "windows" + else: + fail("Unsupported platform for the Android NDK: {}", ctx.os.name) + def _android_ndk_repository_impl(ctx): - """Install the Android NDK files. + """Download and extract the Android NDK files. Args: ctx: An implementation context. @@ -23,37 +36,50 @@ def _android_ndk_repository_impl(ctx): Returns: A final dict of configuration attributes and values. """ - ndk_path = ctx.attr.path or ctx.os.environ.get("ANDROID_NDK_HOME", None) - if not ndk_path: - fail("Either the ANDROID_NDK_HOME environment variable or the " + - "path attribute of android_ndk_repository must be set.") - if ctx.os.name == "linux": + ndk_version = ctx.attr.version + ndk_platform = _ndk_platform(ctx) + ndk_url = "{base_url}/android-ndk-{version}-{platform}.zip".format( + base_url = ctx.attr.base_url, + version = ndk_version, + platform = ndk_platform, + ) + + filename = ndk_url.split("/")[-1] + sha256 = ndk_sha256(filename, ctx) + prefix = "android-ndk-{}".format(ndk_version) + + result = ctx.download_and_extract(url = ndk_url, sha256 = sha256, stripPrefix = prefix) + if not result.success: + fail("Failed to download NDK archive", ndk_url) + + if ndk_platform == "linux": clang_directory = "toolchains/llvm/prebuilt/linux-x86_64" - elif ctx.os.name == "mac os x": + elif ndk_platform == "darwin": # Note: darwin-x86_64 does indeed contain fat binaries with arm64 slices, too. clang_directory = "toolchains/llvm/prebuilt/darwin-x86_64" + elif ndk_platform == "windows": + clang_directory = "toolchains/llvm/prebuilt/windows-x86_64" else: - fail("Unsupported operating system: " + ctx.os.name) + fail("Unsupported NDK platform", ndk_platform) sysroot_directory = "%s/sysroot" % clang_directory - _create_symlinks(ctx, ndk_path, clang_directory, sysroot_directory) + # TODO(#32): Remove this hack + ctx.symlink("sources", "ndk/sources") - api_level = ctx.attr.api_level or 31 + api_level = ctx.attr.api_level result = ctx.execute([clang_directory + "/bin/clang", "--print-resource-dir"]) if result.return_code != 0: fail("Failed to execute clang: %s" % result.stderr) clang_resource_directory = result.stdout.strip().split(clang_directory)[1].strip("/") - # Use a label relative to the workspace from which this repository rule came - # to get the workspace name. - repository_name = Label("//:BUILD").workspace_name + repository_name = ctx.attr._build.workspace_name ctx.template( - "BUILD", - Label("//:BUILD.ndk_root.tpl"), + "BUILD.bazel", + ctx.attr._template_ndk_root, { "{clang_directory}": clang_directory, }, @@ -62,7 +88,7 @@ def _android_ndk_repository_impl(ctx): ctx.template( "target_systems.bzl", - Label("//:target_systems.bzl.tpl"), + ctx.attr._template_target_systems, { }, executable = False, @@ -70,7 +96,7 @@ def _android_ndk_repository_impl(ctx): ctx.template( "%s/BUILD" % clang_directory, - Label("//:BUILD.ndk_clang.tpl"), + ctx.attr._template_ndk_clang, { "{repository_name}": repository_name, "{api_level}": str(api_level), @@ -82,52 +108,24 @@ def _android_ndk_repository_impl(ctx): ctx.template( "%s/BUILD" % sysroot_directory, - Label("//:BUILD.ndk_sysroot.tpl"), + ctx.attr._template_ndk_sysroot, { "{api_level}": str(api_level), }, executable = False, ) -# Manually create a partial symlink tree of the NDK to avoid creating BUILD -# files in the real NDK directory. -def _create_symlinks(ctx, ndk_path, clang_directory, sysroot_directory): - # Path needs to end in "/" for replace() below to work - if not ndk_path.endswith("/"): - ndk_path = ndk_path + "/" - - for p in ctx.path(ndk_path + clang_directory).readdir(): - repo_relative_path = str(p).replace(ndk_path, "") - - # Skip sysroot directory, since it gets its own BUILD file - if repo_relative_path != sysroot_directory: - ctx.symlink(p, repo_relative_path) - - for p in ctx.path(ndk_path + sysroot_directory).readdir(): - repo_relative_path = str(p).replace(ndk_path, "") - ctx.symlink(p, repo_relative_path) - - ctx.symlink(ndk_path + "sources", "sources") - - # TODO(#32): Remove this hack - ctx.symlink(ndk_path + "sources", "ndk/sources") - -_android_ndk_repository = repository_rule( +android_ndk_repository = repository_rule( attrs = { - "path": attr.string(), - "api_level": attr.int(), + "api_level": attr.int(default = 31), + "version": attr.string(default = "r25b"), + "base_url": attr.string(default = "https://dl.google.com/android/repository"), + "sha256s": attr.string_dict(), + "_build": attr.label(default = ":BUILD", allow_single_file = True), + "_template_ndk_root": attr.label(default = ":BUILD.ndk_root.tpl", allow_single_file = True), + "_template_target_systems": attr.label(default = ":target_systems.bzl.tpl", allow_single_file = True), + "_template_ndk_clang": attr.label(default = ":BUILD.ndk_clang.tpl", allow_single_file = True), + "_template_ndk_sysroot": attr.label(default = ":BUILD.ndk_sysroot.tpl", allow_single_file = True), }, - local = True, implementation = _android_ndk_repository_impl, ) - -def android_ndk_repository(name, **kwargs): - _android_ndk_repository( - name = name, - **kwargs - ) - native.register_toolchains("@%s//:all" % name) - native.bind( - name = "android/crosstool", - actual = "@%s//:toolchain" % name, - ) diff --git a/sha256sums.bzl b/sha256sums.bzl new file mode 100644 index 0000000..0cc8027 --- /dev/null +++ b/sha256sums.bzl @@ -0,0 +1,34 @@ +""" +SHA256 checksums for downloaded NDK archives +""" + +_NDK_PACKAGE_SHA256SUMS = { + # r26 + "android-ndk-r26-windows.zip": "a748c6634b96991e15cb8902ffa4a498bba2ec6aa8028526de3c4c9dfcf00663", + "android-ndk-r26-darwin.zip": "b2ab2fd17f71e2d2994c8c0ba2e48e99377806e05bf7477093344c26ab71dec0", + "android-ndk-r26-linux.zip": "1505c2297a5b7a04ed20b5d44da5665e91bac2b7c0fbcd3ae99b6ccc3a61289a", + # r25c + "android-ndk-r25c-windows.zip": "f70093964f6cbbe19268f9876a20f92d3a593db3ad2037baadd25fd8d71e84e2", + "android-ndk-r25c-darwin.zip": "b01bae969a5d0bfa0da18469f650a1628dc388672f30e0ba231da5c74245bc92", + "android-ndk-r25c-linux.zip": "769ee342ea75f80619d985c2da990c48b3d8eaf45f48783a2d48870d04b46108", + # r25b + "android-ndk-r25b-windows.zip": "c9a72beda4663ab714c9fb3dc06bb9b9f124f2b5199957c86cd6f57eb59fd49a", + "android-ndk-r25b-darwin.zip": "7e12f1f809878d4f5d5a901809277aa31546d36c10730fade2036d7d95b3607a", + "android-ndk-r25b-linux.zip": "403ac3e3020dd0db63a848dcaba6ceb2603bf64de90949d5c4361f848e44b005", +} + +def ndk_sha256(filename, repository_ctx): + """Get the sha256 for a specific NDK release + + Args: + filename: the name of the NDK release file (as seen on https://developer.android.com/ndk/downloads) + repository_ctx: the repository_rule ctx + + Returns: + a sha256sum string to use with ctx.download_and_extract + """ + internal_sha256 = _NDK_PACKAGE_SHA256SUMS.get(filename) + external_sha256 = repository_ctx.attr.sha256s.get(filename) + if internal_sha256 == None and external_sha256 == None: + fail("This NDK version is unsupported, and you haven't supplied a custom sha256sum for", filename) + return _NDK_PACKAGE_SHA256SUMS.get(filename) diff --git a/utils/checksums.sh b/utils/checksums.sh new file mode 100755 index 0000000..48dce07 --- /dev/null +++ b/utils/checksums.sh @@ -0,0 +1,14 @@ +#!/bin/bash +# Download a specific release of the NDK for all three platforms and calculate SHA256 checksums +# to add to the sha256sums.bzl file. +set -eu + +ndk_version=$1 + +cd "$(mktemp -d)" +echo "Working in $PWD" + +curl -LO "https://dl.google.com/android/repository/android-ndk-${ndk_version}-windows.zip" +curl -LO "https://dl.google.com/android/repository/android-ndk-${ndk_version}-darwin.zip" +curl -LO "https://dl.google.com/android/repository/android-ndk-${ndk_version}-linux.zip" +sha256sum ./* \ No newline at end of file