diff options
Diffstat (limited to 'rules/common')
-rw-r--r-- | rules/common/BUILD.bazel | 3 | ||||
-rw-r--r-- | rules/common/api.bzl | 3 | ||||
-rw-r--r-- | rules/common/sdk_version.bzl | 65 | ||||
-rw-r--r-- | rules/common/sdk_version_test.bzl | 150 |
4 files changed, 221 insertions, 0 deletions
diff --git a/rules/common/BUILD.bazel b/rules/common/BUILD.bazel index e8ee30d3..e4988862 100644 --- a/rules/common/BUILD.bazel +++ b/rules/common/BUILD.bazel @@ -1,3 +1,6 @@ load(":api_test.bzl", "api_levels_test_suite") +load(":sdk_version_test.bzl", "sdk_version_test_suite") api_levels_test_suite(name = "api_test") + +sdk_version_test_suite(name = "sdk_version_test_suite") diff --git a/rules/common/api.bzl b/rules/common/api.bzl index 31fa964a..2547133e 100644 --- a/rules/common/api.bzl +++ b/rules/common/api.bzl @@ -20,6 +20,7 @@ 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") +_NONE_API_LEVEL_INT = -1 _PREVIEW_API_LEVEL_BASE = 9000 # Base constant for preview API levels. _FUTURE_API_LEVEL_INT = 10000 # API Level associated with an arbitrary future release @@ -118,6 +119,8 @@ def _default_app_target_sdk(): return _parse_api_level_from_version(codename) api = struct( + NONE_API_LEVEL = _NONE_API_LEVEL_INT, + FUTURE_API_LEVEL = _FUTURE_API_LEVEL_INT, is_preview = _is_preview, final_or_future = _final_or_future, default_app_target_sdk = _default_app_target_sdk, diff --git a/rules/common/sdk_version.bzl b/rules/common/sdk_version.bzl new file mode 100644 index 00000000..7b853762 --- /dev/null +++ b/rules/common/sdk_version.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("//build/bazel/rules/common:api.bzl", "api") + +# Only scopes that are available in prebuilts (and "none") are listed +# here for now, but the list should eventually match Soong's SdkKind +# enum. +ALL_KINDS = [ + "public", + "system", + "test", + "system_server", + "module", + "core", + "none", +] + +# Starlark implementation of SdkSpecFrom at https://cs.android.com/android/platform/build/soong/+/master:android/sdk_version.go;l=248-299;drc=69f4218c4feaeca953237cd9e76a9a8cc423d3e3. +def sdk_spec_from(sdk_version): + """Parses an sdk_version string into kind and api_level. + + Args: + sdk_version: a string to specify which SDK version to depend on. + - The empty string maps to the full set of private APIs and is currently unsupported. + - "core_platform" maps to the module scope of the core system modules. + - "none" maps to no SDK (used for bootstrapping the core). + - Otherwise, the format is "{kind}_{api_level}", where kind must be one of the strings + in ALL_KINDS, and api_level is either an integer, and android codename, or "current". + The default kind is "public", and can be omitted by simply providing "{api_level}". + + Returns: + A struct with a kind attribute set to one of the string in ALL_KINDS, and an api_level + attribute as returned by api.bzl's parse_api_level_from_version. + """ + if not sdk_version: + fail("Only prebuilt SDK versions are available, sdk_version must be specified and non-empty.") + 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 = "none", api_level = api.NONE_API_LEVEL) + if type(sdk_version) != type(""): + fail("sdk_version must be a string") + sep_index = sdk_version.rfind("_") + api_level_string = sdk_version if sep_index < 0 else sdk_version[sep_index + 1:] + api_level = api.parse_api_level_from_version(api_level_string) + kind = "public" if sep_index == -1 else sdk_version[:sep_index] + if kind not in ALL_KINDS: + fail("kind %s parsed from sdk_version %s must be one of %s" % ( + kind, + sdk_version, + ",".join(ALL_KINDS), + )) + return struct(kind = kind, api_level = api_level) diff --git a/rules/common/sdk_version_test.bzl b/rules/common/sdk_version_test.bzl new file mode 100644 index 00000000..f751fe2a --- /dev/null +++ b/rules/common/sdk_version_test.bzl @@ -0,0 +1,150 @@ +# 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", "analysistest", "asserts") +load("//build/bazel/rules/common:sdk_version.bzl", "sdk_spec_from") +load("//build/bazel/rules/common:api.bzl", "api") + +# Warning: this is a *lot* of boilerplate to test just one function. +# Scroll down to sdk_version_test_suite for the actual test cases. + +SdkSpec = provider() + +def _sdk_spec_from_tester_impl(ctx): + sdk_spec = sdk_spec_from(ctx.attr.sdk_version) + return [SdkSpec(kind = sdk_spec.kind, api_level = sdk_spec.api_level)] + +sdk_spec_from_tester = rule( + implementation = _sdk_spec_from_tester_impl, + attrs = { + "sdk_version": attr.string(), + }, +) + +def _sdk_spec_from_failure_test_impl(ctx): + env = analysistest.begin(ctx) + asserts.expect_failure(env, ctx.attr.expected_failure_message) + return analysistest.end(env) + +sdk_spec_from_failure_test = analysistest.make( + impl = _sdk_spec_from_failure_test_impl, + expect_failure = True, + attrs = {"expected_failure_message": attr.string()}, +) + +def test_sdk_spec_from_failure(name, sdk_version, expected_failure_message = ""): + sdk_spec_from_tester( + name = name + "_target", + sdk_version = sdk_version, + tags = ["manual"], + ) + sdk_spec_from_failure_test( + name = name, + target_under_test = name + "_target", + expected_failure_message = expected_failure_message, + ) + return name + +def _sdk_spec_from_output_test_impl(ctx): + env = analysistest.begin(ctx) + actual_sdk_spec = analysistest.target_under_test(env)[SdkSpec] + actual_kind = actual_sdk_spec.kind + asserts.equals( + env, + ctx.attr.expected_kind, + actual_kind, + "Expected kind %s, but got %s for sdk version %s" % ( + ctx.attr.expected_kind, + actual_kind, + ctx.attr.actual_sdk_version, + ), + ) + + actual_api_level = actual_sdk_spec.api_level + asserts.equals( + env, + ctx.attr.expected_api_level, + actual_api_level, + "Expected api_level %s, but got %s for sdk version %s" % ( + ctx.attr.expected_api_level, + actual_api_level, + ctx.attr.actual_sdk_version, + ), + ) + return analysistest.end(env) + +sdk_spec_from_output_test = analysistest.make( + impl = _sdk_spec_from_output_test_impl, + attrs = { + "actual_sdk_version": attr.string(), + "expected_kind": attr.string(), + "expected_api_level": attr.int(), + }, +) + +def test_sdk_spec_from_success(name, sdk_version, expected_kind, expected_api_level): + sdk_spec_from_tester( + name = name + "_target", + sdk_version = sdk_version, + tags = ["manual"], + ) + sdk_spec_from_output_test( + name = name, + target_under_test = name + "_target", + actual_sdk_version = sdk_version, + expected_kind = expected_kind, + expected_api_level = expected_api_level, + ) + return name + +def sdk_version_test_suite(name): + # sdk version expected to fail to parse. + failing_sdk_versions = [ + "malformed_malformed", + "malformed", + "", + "core_platform", + ] + failure_tests = [ + test_sdk_spec_from_failure( + name = sdk_version + "_failure_test", + sdk_version = sdk_version, + ) + for sdk_version in failing_sdk_versions + ] + + # Map of sdk_version to expected kind and api_level + sdk_version_to_kind_and_api_level = { + "current": ("public", api.FUTURE_API_LEVEL), + "core_current": ("core", api.FUTURE_API_LEVEL), + "Tiramisu": ("public", 33), + "33": ("public", 33), + "public_33": ("public", 33), + "none": ("none", api.NONE_API_LEVEL), + "system_Tiramisu": ("system", 33), + "system_32": ("system", 32), + } + success_tests = [ + test_sdk_spec_from_success( + name = sdk_version + "_success_test", + sdk_version = sdk_version, + expected_kind = sdk_version_to_kind_and_api_level[sdk_version][0], + expected_api_level = sdk_version_to_kind_and_api_level[sdk_version][1], + ) + for sdk_version in sdk_version_to_kind_and_api_level.keys() + ] + native.test_suite( + name = name, + tests = failure_tests + success_tests, + ) |