From 55ab067d69bf80401353b8009d8e1185cb1f1f1a Mon Sep 17 00:00:00 2001 From: romerxzh Date: Sun, 29 Mar 2026 14:38:21 +0800 Subject: [PATCH] feat(android_sdk_repository): support string-based platform API levels (e.g., "36.1") Previously, API levels were handled as integers, which prevented support for minor version formats like "36.1". This change migrates the entire android_sdk_repository rule to treat API levels as strings throughout. Changes: - Rename `int_flag`/`_int_flag_impl` to `string_flag`/`_string_flag_impl` in helper.bzl; update config type and formatting from int to string - Update `_read_api_levels` in rule.bzl to use `is_android_revision` and store levels as strings; update default API level selection to parse and compare major.minor.micro components - Update all name/path/log formatting from `%d` to `%s` for api_level - Pass `default_api_level` as string in template.bzl - Add `test_platforms_with_minor_versions` test to verify "36.1" style API levels are correctly recognized and logged --- rules/android_sdk_repository/helper.bzl | 75 ++++++++++--------- rules/android_sdk_repository/rule.bzl | 18 +++-- rules/android_sdk_repository/template.bzl | 2 +- .../android_sdk_repository_test.sh | 13 ++++ 4 files changed, 65 insertions(+), 43 deletions(-) diff --git a/rules/android_sdk_repository/helper.bzl b/rules/android_sdk_repository/helper.bzl index 86f57debf..5f34aa461 100644 --- a/rules/android_sdk_repository/helper.bzl +++ b/rules/android_sdk_repository/helper.bzl @@ -26,27 +26,27 @@ _bool_flag = rule( build_setting = config.bool(flag = True), ) -def _int_flag_impl(ctx): +def _string_flag_impl(ctx): allowed_values = ctx.attr.values value = ctx.build_setting_value if len(allowed_values) != 0 and value not in ctx.attr.values: fail( - "Error setting %s: invalid value '%d'. Allowed values are %s" % ( + "Error setting %s: invalid value '%s'. Allowed values are %s" % ( ctx.label, value, ",".join([str(val) for val in allowed_values]), ), ) -int_flag = rule( - implementation = _int_flag_impl, - build_setting = config.int(flag = True), +string_flag = rule( + implementation = _string_flag_impl, + build_setting = config.string(flag = True), attrs = { - "values": attr.int_list( + "values": attr.string_list( doc = "The list of allowed values for this setting. An error is raised if any other value is given.", ), }, - doc = "An int-typed build setting that can be set on the command line", + doc = "An string-typed build setting that can be set on the command line", ) def _create_config_setting_rule(): @@ -94,15 +94,16 @@ def create_android_sdk_rules( build_tools_version: string, the version of Android's build tools to use. build_tools_directory: string, the directory name of the build tools in sdk's build-tools directory. - api_levels: list of ints, the API levels from which to get android.jar + api_levels: list of string, the API levels from which to get android.jar et al. and create android_sdk rules. - default_api_level: int, the API level to alias the default sdk to if + default_api_level: string, the API level to alias the default sdk to if --android_sdk is not specified on the command line. """ _create_config_setting_rule() - int_flag( + # Google use like 36.1 starts from Android 36, so we need adapt string type for api level to avoid confusion. + string_flag( name = "api_level", build_setting_default = default_api_level, values = api_levels, @@ -154,7 +155,7 @@ def create_android_sdk_rules( "build-tools/%s/lib/d8.jar" % build_tools_directory, ":build_tools_libs", ] + [ - "platforms/android-%d/%s" % (api_level, filename) + "platforms/android-%s/%s" % (api_level, filename) for api_level in api_levels for filename in ["android.jar", "core-for-system-modules.jar", "framework.aidl"] ] + select({ @@ -164,44 +165,45 @@ def create_android_sdk_rules( ) for api_level in api_levels: - if api_level >= 23: + major_api_level = int(api_level.split(".")[0]) + if major_api_level >= 23: # Android 23 removed most of org.apache.http from android.jar and moved it # to a separate jar. java_import( - name = "org_apache_http_legacy-%d" % api_level, - jars = ["platforms/android-%d/optional/org.apache.http.legacy.jar" % api_level], + name = "org_apache_http_legacy-%s" % api_level, + jars = ["platforms/android-%s/optional/org.apache.http.legacy.jar" % api_level], ) - if api_level >= 28: + if major_api_level >= 28: # Android 28 removed most of android.test from android.jar and moved it # to separate jars. java_import( - name = "legacy_test-%d" % api_level, + name = "legacy_test-%s" % api_level, jars = [ - "platforms/android-%d/optional/android.test.base.jar" % api_level, - "platforms/android-%d/optional/android.test.mock.jar" % api_level, - "platforms/android-%d/optional/android.test.runner.jar" % api_level, + "platforms/android-%s/optional/android.test.base.jar" % api_level, + "platforms/android-%s/optional/android.test.mock.jar" % api_level, + "platforms/android-%s/optional/android.test.runner.jar" % api_level, ], neverlink = 1, ) - if api_level >= 29: + if major_api_level >= 29: # Android 29 is min api that compatible with Car App Library java_import( - name = "android_car-%d" % api_level, - jars = ["platforms/android-%d/optional/android.car.jar" % api_level], + name = "android_car-%s" % api_level, + jars = ["platforms/android-%s/optional/android.car.jar" % api_level], neverlink = 1, ) native.config_setting( - name = "api_%d_enabled" % api_level, + name = "api_%s_enabled" % api_level, flag_values = { ":api_level": str(api_level), }, ) android_sdk( - name = "sdk-%d" % api_level, + name = "sdk-%s" % api_level, aapt = select({ ":windows": "build-tools/%s/aapt.exe" % build_tools_directory, "//conditions:default": ":aapt_binary", @@ -218,7 +220,7 @@ def create_android_sdk_rules( ":windows": "build-tools/%s/aidl.exe" % build_tools_directory, "//conditions:default": ":aidl_binary", }), - android_jar = "platforms/android-%d/android.jar" % api_level, + android_jar = "platforms/android-%s/android.jar" % api_level, apksigner = ":apksigner", build_tools_version = build_tools_version, dx = select({ @@ -226,7 +228,7 @@ def create_android_sdk_rules( "dx_standalone_dexer": ":dx_binary", "//conditions:default": ":d8_compat_dx", }), - framework_aidl = "platforms/android-%d/framework.aidl" % api_level, + framework_aidl = "platforms/android-%s/framework.aidl" % api_level, legacy_main_dex_list_generator = ":generate_main_dex_list", main_dex_classes = "build-tools/%s/mainDexClasses.rules" % build_tools_directory, main_dex_list_creator = ":main_dex_list_creator", @@ -243,34 +245,35 @@ def create_android_sdk_rules( ) native.toolchain( - name = "sdk-%d-toolchain" % api_level, + name = "sdk-%s-toolchain" % api_level, exec_compatible_with = HOST_CONSTRAINTS, - toolchain = ":sdk-%d" % api_level, + toolchain = ":sdk-%s" % api_level, toolchain_type = ":sdk_toolchain_type", target_settings = [ - ":api_%d_enabled" % api_level, + ":api_%s_enabled" % api_level, ], ) create_dummy_sdk_toolchain() - if default_api_level >= 29: + major_default_api_level = int(default_api_level.split(".")[0]) + if major_default_api_level >= 29: native.alias( name = "android_car", - actual = "android_car-%d" % default_api_level, + actual = "android_car-%s" % default_api_level, ) native.alias( name = "org_apache_http_legacy", - actual = ":org_apache_http_legacy-%d" % default_api_level, + actual = ":org_apache_http_legacy-%s" % default_api_level, ) sdk_alias_dict = { - "//conditions:default": "sdk-%d" % default_api_level, + "//conditions:default": "sdk-%s" % default_api_level, } for api_level in api_levels: - sdk_alias_dict[":api_%d_enabled" % api_level] = "sdk-%d" % api_level + sdk_alias_dict[":api_%s_enabled" % api_level] = "sdk-%s" % api_level native.alias( name = "sdk", @@ -282,12 +285,12 @@ def create_android_sdk_rules( native.alias( name = "sdk-toolchain", - actual = ":sdk-%d-toolchain" % default_api_level, + actual = ":sdk-%s-toolchain" % default_api_level, ) java_import( name = "core-for-system-modules-jar", - jars = ["platforms/android-%d/core-for-system-modules.jar" % default_api_level], + jars = ["platforms/android-%s/core-for-system-modules.jar" % default_api_level], ) java_binary( diff --git a/rules/android_sdk_repository/rule.bzl b/rules/android_sdk_repository/rule.bzl index f3d7431d5..34de2b53e 100644 --- a/rules/android_sdk_repository/rule.bzl +++ b/rules/android_sdk_repository/rule.bzl @@ -67,8 +67,8 @@ def _read_api_levels(repo_ctx, android_sdk_path): name = entry.basename if name.startswith("android-"): level = name[len("android-"):] - if level.isdigit(): - api_levels.append(int(level)) + if is_android_revision(level): + api_levels.append(str(level)) return api_levels def _newest_build_tools(repo_ctx, android_sdk_path): @@ -130,9 +130,15 @@ def _android_sdk_repository_impl(repo_ctx): fail("No Android SDK apis found in the Android SDK at %s. Please install APIs from the Android SDK Manager." % android_sdk_path) # Determine default SDK level. - default_api_level = max(api_levels) + parsed_default_api_levels = [parse_android_revision(api_level) for api_level in api_levels] + + default_api_level = max(parsed_default_api_levels, key=lambda level: ( + level.major, + level.minor, + level.micro, + )).version if repo_ctx.attr.api_level: - default_api_level = int(repo_ctx.attr.api_level) + default_api_level = str(repo_ctx.attr.api_level) if default_api_level not in api_levels: fail("Android SDK api level %s was requested but it is not installed in the Android SDK at %s. The api levels found were %s. Please choose an available api level or install api level %s from the Android SDK Manager." % ( default_api_level, @@ -169,8 +175,8 @@ def _android_sdk_repository_impl(repo_ctx): "__repository_name__": repo_ctx.name, "__build_tools_version__": build_tools.version, "__build_tools_directory__": build_tools.dir, - "__api_levels__": ",".join([str(level) for level in api_levels]), - "__default_api_level__": str(default_api_level), + "__api_levels__": ",".join(['"{}"'.format(level) for level in api_levels]), + "__default_api_level__": default_api_level, "__system_image_dirs__": "\n".join(["'%s'," % d for d in system_images]), # TODO(katre): implement these. #"__exported_files__": "", diff --git a/rules/android_sdk_repository/template.bzl b/rules/android_sdk_repository/template.bzl index be86c0af2..0181a4974 100644 --- a/rules/android_sdk_repository/template.bzl +++ b/rules/android_sdk_repository/template.bzl @@ -77,7 +77,7 @@ create_android_sdk_rules( build_tools_version = "__build_tools_version__", build_tools_directory = "__build_tools_directory__", api_levels = [__api_levels__], - default_api_level = __default_api_level__, + default_api_level = "__default_api_level__", ) exports_files(["platform-tools/adb"]) diff --git a/test/rules/android_sdk_repository/android_sdk_repository_test.sh b/test/rules/android_sdk_repository/android_sdk_repository_test.sh index 73db399dc..4a69a7046 100755 --- a/test/rules/android_sdk_repository/android_sdk_repository_test.sh +++ b/test/rules/android_sdk_repository/android_sdk_repository_test.sh @@ -188,4 +188,17 @@ function test_unsupported_build_tools_level() { expect_log "Bazel requires Android build tools version 35.0.0 or newer" } +function test_platforms_with_minor_versions() { + # platforms start use minor versions starts from API level 36 + local sdk_path="$(create_android_sdk)" + add_platforms "${sdk_path}" 36 36.1 + add_build_tools "${sdk_path}" 36.1.0 + + # Add to repository. + setup_android_sdk_bzlmod "${sdk_path}" "" + + check_android_sdk_provider + expect_log "api_level: 36.1" +} + run_suite "Android integration tests for SDK"