diff --git a/rules/android_sdk_repository/rule.bzl b/rules/android_sdk_repository/rule.bzl index 2d64c5c7d..354435252 100644 --- a/rules/android_sdk_repository/rule.bzl +++ b/rules/android_sdk_repository/rule.bzl @@ -41,6 +41,52 @@ _DIRS_TO_LINK = [ _MIN_BUILD_TOOLS_VERSION = parse_android_revision("30.0.0") +def sdk_package( + linux_url = None, + linux_sha256 = None, + darwin_url = None, + darwin_sha256 = None, + windows_url = None, + windows_sha256 = None, + strip_prefix = None, + add_prefix = None): + """Creates a dictionary for an SDK package for multiple platforms. + + Args: + linux_url: The download URL for the Linux package. + linux_sha256: The SHA256 checksum for the Linux package. + darwin_url: The download URL for the Darwin (macOS) package. + darwin_sha256: The SHA256 checksum for the Darwin (macOS) package. + windows_url: The download URL for the Windows package. + windows_sha256: The SHA256 checksum for the Windows package. + strip_prefix: A directory prefix to strip from the extracted files. + add_prefix: A directory prefix to add to the output path. + + Returns: + A dictionary formatted for the android_sdk_repository rule. + """ + package = {} + if linux_url: + if not linux_sha256: + fail("linux_sha256 must be provided with linux_url") + package["linux_url"] = linux_url + package["linux_sha256"] = linux_sha256 + if darwin_url: + if not darwin_sha256: + fail("darwin_sha256 must be provided with darwin_url") + package["darwin_url"] = darwin_url + package["darwin_sha256"] = darwin_sha256 + if windows_url: + if not windows_sha256: + fail("windows_sha256 must be provided with windows_url") + package["windows_url"] = windows_url + package["windows_sha256"] = windows_sha256 + if strip_prefix: + package["strip_prefix"] = strip_prefix + if add_prefix: + package["add_prefix"] = add_prefix + return package + def _read_api_levels(repo_ctx, android_sdk_path): platforms_dir = "%s/%s" % (android_sdk_path, _PLATFORMS_DIR) api_levels = [] @@ -90,23 +136,18 @@ def _find_system_images(repo_ctx, android_sdk_path): return system_images -def _android_sdk_repository_impl(repo_ctx): - # Determine the SDK path to use, either from the attribute or the environment. - android_sdk_path = repo_ctx.attr.path - if not android_sdk_path: - android_sdk_path = repo_ctx.getenv("ANDROID_HOME") - if not android_sdk_path: - # Create an empty repository that allows non-Android code to build. - repo_ctx.template("BUILD.bazel", _EMPTY_SDK_REPO_TEMPLATE) - return None - if android_sdk_path.startswith("$WORKSPACE_ROOT"): - android_sdk_path = str(repo_ctx.workspace_root) + android_sdk_path.removeprefix("$WORKSPACE_ROOT") +def _get_platform_key(repo_ctx): + os_name = repo_ctx.os.name.lower() + if "linux" in os_name: + return "linux" + if "mac os x" in os_name: + return "darwin" + if "windows" in os_name: + return "windows" + fail("Unsupported operating system: " + repo_ctx.os.name) - # Symlink the needed contents to this repository. - for dir_to_link in _DIRS_TO_LINK: - source = "%s/%s" % (android_sdk_path, dir_to_link) - dest = dir_to_link - repo_ctx.symlink(source, dest) +def _configure_sdk_repository(repo_ctx, android_sdk_path): + """Common logic for configuring the Android SDK repository.""" # Read list of supported SDK levels api_levels = _read_api_levels(repo_ctx, android_sdk_path) @@ -164,6 +205,114 @@ def _android_sdk_repository_impl(repo_ctx): # repo is reproducible return None +def _sandboxed_android_sdk_repository_impl(repo_ctx): + platform_key = _get_platform_key(repo_ctx) + + sdk_packages = { + "build_tools": _BUILD_TOOLS_DIR, + "cmdline_tools": "cmdline-tools", + "emulator": "emulator", + "ndk": "ndk", + "platform_tools": "platform-tools", + "platforms": _PLATFORMS_DIR, + } + for attr_name, output_dir in sdk_packages.items(): + attr_value = getattr(repo_ctx.attr, attr_name) + url_key = platform_key + "_url" + sha256_key = platform_key + "_sha256" + if url_key not in attr_value: + fail("Missing URL for package '{}' on platform '{}'".format(attr_name, platform_key)) + + output_path = output_dir + add_prefix = attr_value.get("add_prefix") + if add_prefix: + output_path = output_path + "/" + add_prefix + + repo_ctx.download_and_extract( + url = attr_value[url_key], + sha256 = attr_value[sha256_key], + output = output_path, + stripPrefix = attr_value.get("strip_prefix", ""), + ) + + return _configure_sdk_repository(repo_ctx, "./") + +_sandboxed_android_sdk_repository = repository_rule( + implementation = _sandboxed_android_sdk_repository_impl, + attrs = { + "api_level": attr.int(default = 0), + "build_tools_version": attr.string(), + "build_tools": attr.string_dict(mandatory = True), + "cmdline_tools": attr.string_dict(mandatory = True), + "emulator": attr.string_dict(mandatory = True), + "ndk": attr.string_dict(mandatory = True), + "platform_tools": attr.string_dict(mandatory = True), + "platforms": attr.string_dict(mandatory = True), + }, + local = True, +) + +def sandboxed_android_sdk_repository( + name, + build_tools, + cmdline_tools, + emulator, + ndk, + platform_tools, + platforms, + api_level = 0, + build_tools_version = ""): + """Create a repository with Android SDK toolchains. + + This rule downloads the Android SDK components from the provided URLs. + + Args: + name: The repository name. + build_tools: A dictionary with "url" and "sha256" for the build tools. + cmdline_tools: A dictionary with "url" and "sha256" for the command-line tools. + emulator: A dictionary with "url" and "sha256" for the emulator. + ndk: A dictionary with "url" and "sha256" for the NDK. + platform_tools: A dictionary with "url" and "sha256" for the platform tools. + platforms: A dictionary with "url" and "sha256" for the platforms. + api_level: The SDK API level to use. + build_tools_version: The build_tools in the SDK to use. + """ + + _sandboxed_android_sdk_repository( + name = name, + build_tools = build_tools, + cmdline_tools = cmdline_tools, + emulator = emulator, + ndk = ndk, + platform_tools = platform_tools, + platforms = platforms, + api_level = api_level, + build_tools_version = build_tools_version, + ) + + native.register_toolchains("@%s//:sdk-toolchain" % name) + native.register_toolchains("@%s//:all" % name) + +def _android_sdk_repository_impl(repo_ctx): + # Determine the SDK path to use, either from the attribute or the environment. + android_sdk_path = repo_ctx.attr.path + if not android_sdk_path: + android_sdk_path = repo_ctx.os.environ.get("ANDROID_HOME") + if not android_sdk_path: + # Create an empty repository that allows non-Android code to build. + repo_ctx.template("BUILD.bazel", _EMPTY_SDK_REPO_TEMPLATE) + return None + if android_sdk_path.startswith("$WORKSPACE_ROOT"): + android_sdk_path = str(repo_ctx.workspace_root) + android_sdk_path.removeprefix("$WORKSPACE_ROOT") + + # Symlink the needed contents to this repository. + for dir_to_link in _DIRS_TO_LINK: + source = "%s/%s" % (android_sdk_path, dir_to_link) + dest = dir_to_link + repo_ctx.symlink(source, dest) + + return _configure_sdk_repository(repo_ctx, android_sdk_path) + _android_sdk_repository = repository_rule( implementation = _android_sdk_repository_impl, attrs = { diff --git a/rules/rules.bzl b/rules/rules.bzl index af06e80e2..b672fb324 100644 --- a/rules/rules.bzl +++ b/rules/rules.bzl @@ -15,6 +15,12 @@ # Don't use relative paths since this file is coppied to //android/rules.bzl. +load( + "//providers:providers.bzl", + _AndroidAppsInfo = "AndroidAppsInfo", + _ApkInfo = "ApkInfo", + _StarlarkApkInfo = "StarlarkApkInfo", +) load( "//rules:android_sdk.bzl", _android_sdk = "android_sdk", @@ -27,12 +33,6 @@ load( "//rules:instrumented_app_info_aspect.bzl", _instrumented_app_info_aspect = "instrumented_app_info_aspect", ) -load( - "//providers:providers.bzl", - _AndroidAppsInfo = "AndroidAppsInfo", - _ApkInfo = "ApkInfo", - _StarlarkApkInfo = "StarlarkApkInfo", -) load( "//rules/aar_import:rule.bzl", _aar_import = "aar_import", @@ -68,6 +68,8 @@ load( load( "//rules/android_sdk_repository:rule.bzl", _android_sdk_repository = "android_sdk_repository", + _sandboxed_android_sdk_repository = "sandboxed_android_sdk_repository", + _sdk_package = "sdk_package", ) # Current version. Tools may check this to determine compatibility. @@ -82,6 +84,8 @@ android_sandboxed_sdk = _android_sandboxed_sdk android_sandboxed_sdk_bundle = _android_sandboxed_sdk_bundle android_sdk = _android_sdk android_sdk_repository = _android_sdk_repository +sandboxed_android_sdk_repository = _sandboxed_android_sdk_repository +sdk_package = _sdk_package android_tools_defaults_jar = _android_tools_defaults_jar asar_import = _asar_import instrumented_app_info_aspect = _instrumented_app_info_aspect