Match Soong's handling of {min,target}_sdk_version in android_binary
To determine values to use for manifest fixing, Soong calls
EffectiveVersionString on the raw {min,target}_sdk_version properties
if defined, or on sdk_version if not. Additionally, a few product
variables, as well as whether the app is updatable, can affect ways
the raw value is overriden.
This CL matches this logic in android_binary.
Another CL in the same topic has android_binary's bp2build converter pass the
raw {min,target}_sdk_version property values directly to
android_binary's manifest_values, instead of the post-processed ones.
Bug: 274474008
Test: CI + Unit tests
Change-Id: I3a132ee09dffe67f3d84062bc37f4310bf36ffe3
diff --git a/product_config/BUILD b/product_config/BUILD
index 4d4795f..e27a982 100644
--- a/product_config/BUILD
+++ b/product_config/BUILD
@@ -69,6 +69,7 @@
"malloc_zero_contents",
"native_coverage",
"pdk",
+ "platform_sdk_final",
"release_aidl_use_unfrozen",
"safestack",
"treble_linker_namespaces",
diff --git a/rules/android/BUILD.bazel b/rules/android/BUILD.bazel
index e9a1090..a753c2a 100644
--- a/rules/android/BUILD.bazel
+++ b/rules/android/BUILD.bazel
@@ -12,8 +12,13 @@
# See the License for the specific language governing permissions and
# limitations under the License.
load(":android_app_certificate.bzl", "no_android_app_certificate")
+load(":manifest_fixer_test.bzl", "manifest_fixer_test_suite")
no_android_app_certificate(
name = "no_android_app_certificate",
visibility = ["//visibility:public"],
)
+
+manifest_fixer_test_suite(
+ name = "manifest_fixer_tests",
+)
diff --git a/rules/android/android_binary.bzl b/rules/android/android_binary.bzl
index 1627d86..b3ec57a 100644
--- a/rules/android/android_binary.bzl
+++ b/rules/android/android_binary.bzl
@@ -41,8 +41,13 @@
)
)
+ # The following attributes are unknown the native android_binary rule and must be removed
+ # prior to instantiating it.
attrs.pop("$enable_manifest_merging", None)
attrs["proguard_specs"] = []
+ attrs.pop("sdk_version")
+ if "updatable" in attrs:
+ attrs.pop("updatable")
native.android_binary(
application_resources = android_binary_aosp_internal_name,
@@ -105,6 +110,7 @@
target_compatible_with = target_compatible_with,
tags = tags + ["manual"],
visibility = ["//visibility:private"],
+ sdk_version = sdk_version,
**kwargs
)
diff --git a/rules/android/android_binary_aosp_internal/impl.bzl b/rules/android/android_binary_aosp_internal/impl.bzl
index 3479d40..f27909e 100644
--- a/rules/android/android_binary_aosp_internal/impl.bzl
+++ b/rules/android/android_binary_aosp_internal/impl.bzl
@@ -24,8 +24,10 @@
load("@rules_android//rules:resources.bzl", _resources = "resources")
load("@rules_android//rules:utils.bzl", "get_android_toolchain", "utils")
load("@rules_android//rules/android_binary_internal:impl.bzl", "finalize", _BASE_PROCESSORS = "PROCESSORS")
+load("//build/bazel/rules/android:manifest_fixer.bzl", "manifest_fixer")
load("//build/bazel/rules/cc:cc_stub_library.bzl", "CcStubInfo")
load("//build/bazel/rules/common:api.bzl", "api")
+load("//build/bazel/rules/common:sdk_version.bzl", "sdk_version")
CollectedCcStubsInfo = provider(
"Tracks cc stub libraries to exclude from APK packaging.",
@@ -125,17 +127,64 @@
]),
)
-def _process_manifest_aosp(ctx, **_unused_ctxs):
- manifest_ctx = _resources.set_default_min_sdk(
- ctx,
- manifest = ctx.file.manifest,
- default = api.default_app_target_sdk(),
- enforce_min_sdk_floor_tool = get_android_toolchain(ctx).enforce_min_sdk_floor_tool.files_to_run,
+# Starlark implementation of AndroidApp.MinSdkVersion from build/soong/java/app.go
+def _maybe_override_min_sdk_version(ctx):
+ min_sdk_version = sdk_version.api_level_string_with_fallback(
+ ctx.attr.manifest_values.get("minSdkVersion"),
+ ctx.attr.sdk_version,
)
+ override_apex_manifest_default_version = ctx.attr._override_apex_manifest_default_version[BuildSettingInfo].value
+ if (ctx.attr.updatable and
+ override_apex_manifest_default_version and
+ (api.parse_api_level_from_version(override_apex_manifest_default_version) >
+ api.parse_api_level_from_version(min_sdk_version))):
+ return override_apex_manifest_default_version
+ return min_sdk_version
+
+def _maybe_override_manifest_values(ctx):
+ min_sdk_version = api.effective_version_string(_maybe_override_min_sdk_version(ctx))
+
+ has_unbundled_build_apps = (ctx.attr._unbundled_build_apps[BuildSettingInfo].value != None and
+ len(ctx.attr._unbundled_build_apps[BuildSettingInfo].value) > 0)
+
+ # TODO: b/300916281 - When Api fingerprinting is used, it should be appended to the target SDK version here.
+ target_sdk_version = manifest_fixer.target_sdk_version_for_manifest_fixer(
+ target_sdk_version = sdk_version.api_level_string_with_fallback(
+ ctx.attr.manifest_values.get("targetSdkVersion"),
+ ctx.attr.sdk_version,
+ ),
+ platform_sdk_final = ctx.attr._platform_sdk_final[BuildSettingInfo].value,
+ has_unbundled_build_apps = has_unbundled_build_apps,
+ )
+ return struct(
+ min_sdk_version = min_sdk_version,
+ target_sdk_version = target_sdk_version,
+ )
+
+def _process_manifest_aosp(ctx, **_unused_ctxs):
+ maybe_overriden_values = _maybe_override_manifest_values(ctx)
+ out_manifest = ctx.actions.declare_file("fixed_manifest/" + ctx.label.name + "/" + "AndroidManifest.xml")
+ manifest_fixer.fix(
+ ctx,
+ manifest_fixer = ctx.executable._manifest_fixer,
+ in_manifest = ctx.file.manifest,
+ out_manifest = out_manifest,
+ min_sdk_version = maybe_overriden_values.min_sdk_version,
+ target_sdk_version = maybe_overriden_values.target_sdk_version,
+ )
+
+ updated_manifest_values = {
+ key: ctx.attr.manifest_values[key]
+ for key in ctx.attr.manifest_values.keys()
+ if key not in ("minSdkVersion", "targetSdkVersion")
+ }
return ProviderInfo(
name = "manifest_ctx",
- value = manifest_ctx,
+ value = _resources.ManifestContextInfo(
+ processed_manifest = out_manifest,
+ processed_manifest_values = updated_manifest_values,
+ ),
)
# (b/274150785) validation processor does not allow min_sdk that are a string
diff --git a/rules/android/android_binary_aosp_internal/rule.bzl b/rules/android/android_binary_aosp_internal/rule.bzl
index 2b6e1b0..3ac3a06 100644
--- a/rules/android/android_binary_aosp_internal/rule.bzl
+++ b/rules/android/android_binary_aosp_internal/rule.bzl
@@ -46,6 +46,31 @@
default = Label("//build/bazel/product_config:device_abi"),
doc = "Implicit attr used to extract target device ABI information (for apk lib naming).",
),
+ _platform_sdk_final = attr.label(
+ default = "//build/bazel/product_config:platform_sdk_final",
+ doc = "PlatformSdkFinal product variable",
+ ),
+ _unbundled_build_apps = attr.label(
+ default = "//build/bazel/product_config:unbundled_build_apps",
+ doc = "UnbundledBuildApps product variable",
+ ),
+ _override_apex_manifest_default_version = attr.label(
+ default = "//build/bazel/rules/apex:override_apex_manifest_default_version",
+ doc = "If the app is updatable, and this attribute is specified, and higher than the value specified in manifest_values, will override minSdkVersion in manifest with this value instead of the value in manifest_values.",
+ ),
+ _manifest_fixer = attr.label(
+ cfg = "exec",
+ executable = True,
+ default = "//build/soong/scripts:manifest_fixer",
+ ),
+ sdk_version = attr.string(
+ doc = "The sdk_version this app should build against.",
+ ),
+ # TODO: b/301425155 - Handle all of the ways updatable affects this rule.
+ updatable = attr.bool(
+ default = False,
+ doc = "Whether this app is updatable.",
+ ),
),
)
@@ -57,4 +82,4 @@
Args:
**attrs: Rule attributes
"""
- android_binary_aosp_internal(**sanitize_attrs(attrs))
+ android_binary_aosp_internal(**sanitize_attrs(attrs, _ATTRS))
diff --git a/rules/android/android_binary_aosp_internal/test/test.bzl b/rules/android/android_binary_aosp_internal/test/test.bzl
index 4f9ec5f..4eeb909 100644
--- a/rules/android/android_binary_aosp_internal/test/test.bzl
+++ b/rules/android/android_binary_aosp_internal/test/test.bzl
@@ -102,6 +102,7 @@
],
srcs = ["fake.java"],
tags = ["manual"],
+ sdk_version = "current",
)
android_binary_aosp_internal_providers_test(
diff --git a/rules/android/manifest_fixer.bzl b/rules/android/manifest_fixer.bzl
index ec2e057..25fefd9 100644
--- a/rules/android/manifest_fixer.bzl
+++ b/rules/android/manifest_fixer.bzl
@@ -12,6 +12,33 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+load("@soong_injection//api_levels:platform_versions.bzl", "platform_versions")
+load("//build/bazel/rules/common:api.bzl", "api", "api_from_product")
+load(":manifest_fixer_internal.bzl", _internal = "manifest_fixer_internal")
+
+# TODO(b/300428335): access these variables in a transition friendly way.
+_PLATFORM_SDK_VERSION = platform_versions.platform_sdk_version
+_PLATFORM_SDK_CODENAME = platform_versions.platform_sdk_codename
+_PLATFORM_VERSION_ACTIVE_CODENAMES = platform_versions.platform_version_active_codenames
+
+# Starlark implementation of TargetSdkVersionForManifestFixer from build/soong/java/android_manifest.go
+def _target_sdk_version_for_manifest_fixer(
+ target_sdk_version,
+ platform_sdk_final,
+ has_unbundled_build_apps):
+ platform_sdk_variables = struct(
+ platform_sdk_final = platform_sdk_final,
+ platform_sdk_version = _PLATFORM_SDK_VERSION,
+ platform_sdk_codename = _PLATFORM_SDK_CODENAME,
+ platform_version_active_codenames = _PLATFORM_VERSION_ACTIVE_CODENAMES,
+ )
+ return _internal.target_sdk_version_for_manifest_fixer(
+ target_sdk_version = target_sdk_version,
+ has_unbundled_build_apps = has_unbundled_build_apps,
+ platform_sdk_variables = platform_sdk_variables,
+ )
+
+# TODO: b/301430823 - Only pass ctx.actions to limit the scope of what this function can access.
def _fix(
ctx,
manifest_fixer,
@@ -42,4 +69,5 @@
manifest_fixer = struct(
fix = _fix,
+ target_sdk_version_for_manifest_fixer = _target_sdk_version_for_manifest_fixer,
)
diff --git a/rules/android/manifest_fixer_internal.bzl b/rules/android/manifest_fixer_internal.bzl
new file mode 100644
index 0000000..d05b631
--- /dev/null
+++ b/rules/android/manifest_fixer_internal.bzl
@@ -0,0 +1,43 @@
+# Copyright (C) 2023 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+load("//build/bazel/rules/common:api.bzl", "api", "api_from_product")
+
+visibility("private")
+
+# Starlark implementation of shouldReturnFinalOrFutureInt from build/soong/java/android_manifest.go
+# TODO: b/300916781 - In Soong this also returns true when the module is an MTS test.
+def _should_return_future_int(
+ target_sdk_version,
+ platform_sdk_variables,
+ has_unbundled_build_apps):
+ if platform_sdk_variables.platform_sdk_final:
+ return False
+ return api_from_product(platform_sdk_variables).is_preview(target_sdk_version) and has_unbundled_build_apps
+
+# Starlark implementation of TargetSdkVersionForManifestFixer from build/soong/java/android_manifest.go
+def _target_sdk_version_for_manifest_fixer(
+ target_sdk_version,
+ platform_sdk_variables,
+ has_unbundled_build_apps):
+ if _should_return_future_int(
+ target_sdk_version = target_sdk_version,
+ platform_sdk_variables = platform_sdk_variables,
+ has_unbundled_build_apps = has_unbundled_build_apps,
+ ):
+ return str(api.FUTURE_API_LEVEL)
+ return api_from_product(platform_sdk_variables).effective_version_string(target_sdk_version)
+
+manifest_fixer_internal = struct(
+ target_sdk_version_for_manifest_fixer = _target_sdk_version_for_manifest_fixer,
+)
diff --git a/rules/android/manifest_fixer_test.bzl b/rules/android/manifest_fixer_test.bzl
new file mode 100644
index 0000000..f30af55
--- /dev/null
+++ b/rules/android/manifest_fixer_test.bzl
@@ -0,0 +1,65 @@
+# Copyright (C) 2023 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+load("@bazel_skylib//lib:unittest.bzl", "asserts", "unittest")
+load("//build/bazel/rules/android:manifest_fixer_internal.bzl", manifest_fixer_for_testing = "manifest_fixer_internal")
+load("//build/bazel/rules/common:api.bzl", "api")
+
+def _target_sdk_version_override_test_impl(ctx):
+ env = unittest.begin(ctx)
+ platform_sdk_codename = "Tiramisu"
+ platform_sdk_version = "33"
+ platform_version_active_codenames = [platform_sdk_codename]
+
+ # Schema: (Input targetSdkVersion, PlatformSdkFinal, Is unbundled app build) -> Expected targetSdkVersion
+ _VERSIONS_UNDER_TEST = {
+ ("29", False, False): "29",
+ ("30", False, True): "30",
+ ("current", False, True): str(api.FUTURE_API_LEVEL),
+ ("30", True, False): "30",
+ ("30", True, True): "30",
+ ("Tiramisu", True, True): "33",
+ ("current", True, True): "33",
+ }
+ for (target_sdk_version, platform_sdk_final, is_unbundled_app_build), expected_target_sdk_version in _VERSIONS_UNDER_TEST.items():
+ platform_sdk_variables = struct(
+ platform_sdk_codename = platform_sdk_codename,
+ platform_sdk_final = platform_sdk_final,
+ platform_sdk_version = platform_sdk_version,
+ platform_version_active_codenames = platform_version_active_codenames,
+ )
+ asserts.equals(
+ env,
+ expected_target_sdk_version,
+ manifest_fixer_for_testing.target_sdk_version_for_manifest_fixer(
+ target_sdk_version,
+ platform_sdk_variables,
+ is_unbundled_app_build,
+ ),
+ ("unexpected target SDK version for manifest fixer %s with input target" +
+ "SDK version %s, platform SDK variables %s and is_unbundled_app_build %s") % (
+ expected_target_sdk_version,
+ target_sdk_version,
+ platform_sdk_variables,
+ is_unbundled_app_build,
+ ),
+ )
+ return unittest.end(env)
+
+target_sdk_version_override_test = unittest.make(_target_sdk_version_override_test_impl)
+
+def manifest_fixer_test_suite(name):
+ unittest.suite(
+ name,
+ target_sdk_version_override_test,
+ )
diff --git a/rules/common/api.bzl b/rules/common/api.bzl
index 052bbba..553d655 100644
--- a/rules/common/api.bzl
+++ b/rules/common/api.bzl
@@ -31,12 +31,17 @@
_preview_codenames_to_ints = api_internal.preview_codenames_to_ints(_PLATFORM_VERSION_ACTIVE_CODENAMES)
# Returns true if a string or int version is in preview (not finalized).
-def _is_preview(version):
- return api_internal.is_preview(version, _preview_codenames_to_ints)
+def _is_preview(version, platform_sdk_variables):
+ return api_internal.is_preview(
+ version = version,
+ preview_codenames_to_ints = api_internal.preview_codenames_to_ints(
+ platform_sdk_variables.platform_version_active_codenames,
+ ),
+ )
# Return 10000 for unfinalized versions, otherwise return unchanged.
def _final_or_future(version):
- if _is_preview(version):
+ if api_internal.is_preview(version, _preview_codenames_to_ints):
return api_internal.FUTURE_API_LEVEL
else:
return version
@@ -66,7 +71,7 @@
if version == "current":
return api_internal.FUTURE_API_LEVEL
- if _is_preview(version):
+ if api_internal.is_preview(version = version, preview_codenames_to_ints = _preview_codenames_to_ints):
return _preview_codenames_to_ints.get(version) or int(version)
# Not preview nor current.
@@ -84,15 +89,15 @@
# Starlark implementation of DefaultAppTargetSDK from build/soong/android/config.go
# https://cs.android.com/android/platform/superproject/+/master:build/soong/android/config.go;l=875-889;drc=b0dc477ef740ec959548fe5517bd92ac4ea0325c
# check what you want returned for codename == "" case before using
-def _default_app_target_sdk():
+def _default_app_target_sdk(platform_sdk_variables):
"""default_app_target_sdk returns the API level that platform apps are targeting.
This converts a codename to the exact ApiLevel it represents.
"""
return _parse_api_level_from_version(
api_internal.default_app_target_sdk_string(
- _PLATFORM_SDK_FINAL,
- _PLATFORM_SDK_VERSION,
- _PLATFORM_SDK_CODENAME,
+ platform_sdk_final = platform_sdk_variables.platform_sdk_final,
+ platform_sdk_version = platform_sdk_variables.platform_sdk_version,
+ platform_sdk_codename = platform_sdk_variables.platform_sdk_codename,
),
)
@@ -100,25 +105,40 @@
# EffectiveVersionString converts an api level string into the concrete version string that the module
# should use. For modules targeting an unreleased SDK (meaning it does not yet have a number)
# it returns the codename (P, Q, R, etc.)
-def _effective_version_string(version):
+def _effective_version_string(version, platform_sdk_variables):
return api_internal.effective_version_string(
version,
- _preview_codenames_to_ints,
- api_internal.default_app_target_sdk_string(
- _PLATFORM_SDK_FINAL,
- _PLATFORM_SDK_VERSION,
- _PLATFORM_SDK_CODENAME,
+ api_internal.preview_codenames_to_ints(
+ platform_sdk_variables.platform_version_active_codenames,
),
- _PLATFORM_VERSION_ACTIVE_CODENAMES,
+ api_internal.default_app_target_sdk_string(
+ platform_sdk_final = platform_sdk_variables.platform_sdk_final,
+ platform_sdk_version = platform_sdk_variables.platform_sdk_version,
+ platform_sdk_codename = platform_sdk_variables.platform_sdk_codename,
+ ),
+ platform_sdk_variables.platform_version_active_codenames,
)
-api = struct(
+api_from_product = lambda platform_sdk_variables: struct(
NONE_API_LEVEL = api_internal.NONE_API_LEVEL,
FUTURE_API_LEVEL = api_internal.FUTURE_API_LEVEL,
- is_preview = _is_preview,
+ is_preview = lambda version: _is_preview(
+ version = version,
+ platform_sdk_variables = platform_sdk_variables,
+ ),
final_or_future = _final_or_future,
- default_app_target_sdk = _default_app_target_sdk,
+ default_app_target_sdk = lambda: _default_app_target_sdk(platform_sdk_variables),
parse_api_level_from_version = _parse_api_level_from_version,
api_levels = _api_levels_with_previews,
- effective_version_string = _effective_version_string,
+ effective_version_string = lambda version: _effective_version_string(
+ version = version,
+ platform_sdk_variables = platform_sdk_variables,
+ ),
)
+
+api = api_from_product(struct(
+ platform_sdk_final = _PLATFORM_SDK_FINAL,
+ platform_sdk_version = _PLATFORM_SDK_VERSION,
+ platform_sdk_codename = _PLATFORM_SDK_CODENAME,
+ platform_version_active_codenames = _PLATFORM_VERSION_ACTIVE_CODENAMES,
+))
diff --git a/rules/common/sdk_version.bzl b/rules/common/sdk_version.bzl
index 33f2f61..1c57acc 100644
--- a/rules/common/sdk_version.bzl
+++ b/rules/common/sdk_version.bzl
@@ -56,7 +56,7 @@
if sdk_version == "core_platform":
fail("Only prebuilt SDK versions are available, sdk_version core_platform is not yet supported.")
if sdk_version == "none":
- return struct(kind = _KIND_NONE, api_level = api.NONE_API_LEVEL)
+ return struct(kind = _KIND_NONE, api_level = api.NONE_API_LEVEL, _api_level_string = "(no version)")
if type(sdk_version) != type(""):
fail("sdk_version must be a string")
sep_index = sdk_version.rfind("_")
@@ -69,7 +69,10 @@
sdk_version,
",".join(_ALL_KINDS),
))
- return struct(kind = kind, api_level = api_level)
+ return struct(kind = kind, api_level = api_level, _api_level_string = api_level_string)
+
+def _api_level_string_with_fallback(api_level_string, sdk_version):
+ return api_level_string if api_level_string else _sdk_spec_from(sdk_version)._api_level_string
sdk_version = struct(
KIND_PUBLIC = _KIND_PUBLIC,
@@ -80,5 +83,6 @@
KIND_CORE = _KIND_CORE,
KIND_NONE = _KIND_NONE,
ALL_KINDS = _ALL_KINDS,
+ api_level_string_with_fallback = _api_level_string_with_fallback,
sdk_spec_from = _sdk_spec_from,
)