aboutsummaryrefslogtreecommitdiff
path: root/rules/common
diff options
context:
space:
mode:
Diffstat (limited to 'rules/common')
-rw-r--r--rules/common/BUILD.bazel3
-rw-r--r--rules/common/api.bzl114
-rw-r--r--rules/common/api_test.bzl77
3 files changed, 157 insertions, 37 deletions
diff --git a/rules/common/BUILD.bazel b/rules/common/BUILD.bazel
index e69de29b..e8ee30d3 100644
--- a/rules/common/BUILD.bazel
+++ b/rules/common/BUILD.bazel
@@ -0,0 +1,3 @@
+load(":api_test.bzl", "api_levels_test_suite")
+
+api_levels_test_suite(name = "api_test")
diff --git a/rules/common/api.bzl b/rules/common/api.bzl
index 54d3b402..24dd0a8a 100644
--- a/rules/common/api.bzl
+++ b/rules/common/api.bzl
@@ -12,75 +12,115 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+# An API level, can be a finalized (numbered) API, a preview (codenamed) API, or
+# the future API level (10000). Can be parsed from a string with
+# parse_api_level_with_version.
+
+load("@bazel_skylib//lib:dicts.bzl", "dicts")
load("@soong_injection//api_levels:api_levels.bzl", "api_levels_released_versions")
load("@soong_injection//product_config:product_variables.bzl", "product_vars")
-PREVIEW_API_LEVEL_BASE = 9000
-FUTURE_API_LEVEL_INT = 10000 # API Level associated with an arbitrary future release
+_PREVIEW_API_LEVEL_BASE = 9000 # Base constant for preview API levels.
+_FUTURE_API_LEVEL_INT = 10000 # API Level associated with an arbitrary future release
+
+# TODO(b/271280342): access these variables in a transition friendly way.
+_PLATFORM_SDK_FINAL = product_vars.get("Platform_sdk_final")
+_PLATFORM_SDK_VERSION = product_vars.get("Platform_sdk_version")
+_PLATFORM_SDK_CODENAME = product_vars.get("Platform_sdk_codename")
+_PLATFORM_VERSION_ACTIVE_CODENAMES = product_vars.get("Platform_version_active_codenames", [])
+
+# Dict of unfinalized codenames to a placeholder preview API int.
+_preview_codenames_to_ints = {
+ codename: _PREVIEW_API_LEVEL_BASE + i
+ for i, codename in enumerate(_PLATFORM_VERSION_ACTIVE_CODENAMES)
+}
+
+_final_codename = {
+ "current": _final_or_future(_PLATFORM_SDK_VERSION),
+} if _PLATFORM_SDK_FINAL and _PLATFORM_SDK_VERSION else {}
+
+_api_levels_with_previews = dicts.add(api_levels_released_versions, _preview_codenames_to_ints)
+_api_levels_with_final_codenames = dicts.add(api_levels_released_versions, _final_codename)
-def _api_levels_with_previews():
- ret = dict(api_levels_released_versions)
- active_codenames = product_vars.get("Platform_version_active_codenames", [])
- for i, codename in enumerate(active_codenames):
- ret[codename] = PREVIEW_API_LEVEL_BASE + i
- return ret
+# Returns true if a string or int version is in preview (not finalized).
+def _is_preview(version):
+ if type(version) == "string" and version.isdigit():
+ # normalize int types internally
+ version = int(version)
-def _api_levels_with_final_codenames():
- ret = dict(api_levels_released_versions)
- if product_vars.get("Platform_sdk_final"):
- platform_sdk_version = product_vars.get("Platform_sdk_version")
- if platform_sdk_version != None:
- ret["current"] = platform_sdk_version
- return ret
+ # Future / current is considered as a preview.
+ if version == "current" or version == _FUTURE_API_LEVEL_INT:
+ return True
-api_levels_with_previews = _api_levels_with_previews()
+ # api can be either the codename or the int level (9000+)
+ return version in _preview_codenames_to_ints or version in _preview_codenames_to_ints.values()
+
+# Return 10000 for unfinalized versions, otherwise return unchanged.
+def _final_or_future(version):
+ if _is_preview(version):
+ return _FUTURE_API_LEVEL_INT
+ else:
+ return version
# parse_api_level_from_version is a Starlark implementation of ApiLevelFromUser
# at https://cs.android.com/android/platform/superproject/+/master:build/soong/android/api_levels.go;l=221-250;drc=5095a6c4b484f34d5c4f55a855d6174e00fb7f5e
-def parse_api_level_from_version(version):
+def _parse_api_level_from_version(version):
"""converts the given string `version` to an api level
Args:
version: must be non-empty. Inputs that are not "current", known
- previews, or convertible to an integer will return an error.
+ previews, finalized codenames, or convertible to an integer will return
+ an error.
- Returns: The api level. This can be an int or unreleased version full name (string).
- Finalized codenames will be interpreted as their final API levels, not
- the preview of the associated releases. Future codenames return the
- version codename.
+ Returns: The api level as an int.
"""
- api_levels = api_levels_with_previews
if version == "":
fail("API level string must be non-empty")
if version == "current":
- return FUTURE_API_LEVEL_INT
+ return _FUTURE_API_LEVEL_INT
- if version in api_levels:
- return api_levels[version]
+ if _is_preview(version):
+ return _preview_codenames_to_ints.get(version) or int(version)
- elif version.isdigit():
+ # Not preview nor current.
+ #
+ # If the level is the codename of an API level has been finalized, this
+ # function returns the API level number associated with that API level. If the
+ # input is *not* a finalized codename, the input is returned unmodified.
+ canonical_level = api_levels_released_versions.get(version)
+ if not canonical_level:
+ if not version.isdigit():
+ fail("version %s could not be parsed as integer and is not a recognized codename" % version)
return int(version)
- else:
- fail("version could not be parsed as integer and is not a recognized codename")
+ return canonical_level
# 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():
"""default_app_target_sdk returns the API level that platform apps are targeting.
This converts a codename to the exact ApiLevel it represents.
"""
- if product_vars.get("Platform_sdk_final"):
- return product_vars.get("Platform_sdk_version")
+ if _PLATFORM_SDK_FINAL:
+ return _PLATFORM_SDK_VERSION
- codename = product_vars.get("Platform_sdk_codename")
- if codename == "" or codename == None:
+ codename = _PLATFORM_SDK_CODENAME
+ if not codename:
# soong returns NoneApiLevel here value: "(no version)", number: -1, isPreview: true
- # APEX's targetSdkVersion sets this to FUTURE_API_LEVEL
- return FUTURE_API_LEVEL_INT
+ #
+ # fail fast instead of returning an arbitrary value.
+ fail("Platform_sdk_codename must be set.")
if codename == "REL":
fail("Platform_sdk_codename should not be REL when Platform_sdk_final is false")
- return parse_api_level_from_version(codename)
+ return _parse_api_level_from_version(codename)
+
+# TODO(jingwen): refactor all callers to use an exported struct instead of individual functions
+is_preview = _is_preview
+api_final_or_future = _final_or_future
+default_app_target_sdk = _default_app_target_sdk
+parse_api_level_from_version = _parse_api_level_from_version
+api_levels_with_previews = _api_levels_with_previews
+api_levels_with_final_codenames = _api_levels_with_final_codenames
diff --git a/rules/common/api_test.bzl b/rules/common/api_test.bzl
new file mode 100644
index 00000000..8de0477b
--- /dev/null
+++ b/rules/common/api_test.bzl
@@ -0,0 +1,77 @@
+load("@bazel_skylib//lib:unittest.bzl", "asserts", "unittest")
+load("//build/bazel/rules/common:api.bzl", "api_final_or_future", "is_preview", "parse_api_level_from_version")
+
+def _api_levels_test_impl(ctx):
+ env = unittest.begin(ctx)
+
+ # schema: version string to parse: (expected api int, is preview api)
+ _LEVELS_UNDER_TEST = {
+ # numbers
+ "9": (9, False), # earliest released number
+ "21": (21, False),
+ "30": (30, False),
+ "33": (33, False),
+ # unchecked non final api level (not finalized, not preview, not current)
+ "1234": (1234, False),
+ "8999": (8999, False),
+ "9999": (9999, False),
+ "10001": (10001, False),
+ # letters
+ "G": (9, False), # earliest released letter
+ "J-MR1": (17, False),
+ "R": (30, False),
+ "S": (31, False),
+ "S-V2": (32, False),
+ # codenames
+ "Tiramisu": (33, False),
+ "UpsideDownCake": (9000, True), # preview
+ "current": (10000, True), # future (considered as preview)
+ # preview numbers
+ "9000": (9000, True), # preview
+ "10000": (10000, True), # future (considered as preview)
+ }
+
+ for level, expected in _LEVELS_UNDER_TEST.items():
+ asserts.equals(env, expected[0], parse_api_level_from_version(level), "unexpected api level parsed for %s" % level)
+ asserts.equals(env, expected[1], is_preview(level), "unexpected is_preview value for %s" % level)
+
+ return unittest.end(env)
+
+api_levels_test = unittest.make(_api_levels_test_impl)
+
+def _final_or_future_test_impl(ctx):
+ env = unittest.begin(ctx)
+
+ # schema: version string to parse: expected api int
+ _LEVELS_UNDER_TEST = {
+ # finalized
+ "30": 30,
+ "33": 33,
+ "S": 31,
+ "S-V2": 32,
+ "Tiramisu": 33,
+ # not finalized
+ "UpsideDownCake": 10000,
+ "current": 10000,
+ "9000": 10000,
+ "10000": 10000,
+ }
+
+ for level, expected in _LEVELS_UNDER_TEST.items():
+ asserts.equals(
+ env,
+ expected,
+ api_final_or_future(parse_api_level_from_version(level)),
+ "unexpected final or future api for %s" % level,
+ )
+
+ return unittest.end(env)
+
+final_or_future_test = unittest.make(_final_or_future_test_impl)
+
+def api_levels_test_suite(name):
+ unittest.suite(
+ name,
+ api_levels_test,
+ final_or_future_test,
+ )