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.bzl3
-rw-r--r--rules/common/sdk_version.bzl65
-rw-r--r--rules/common/sdk_version_test.bzl150
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,
+ )