diff options
Diffstat (limited to 'rules/common')
-rw-r--r-- | rules/common/BUILD.bazel | 3 | ||||
-rw-r--r-- | rules/common/api.bzl | 114 | ||||
-rw-r--r-- | rules/common/api_test.bzl | 77 |
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, + ) |