aboutsummaryrefslogtreecommitdiff
path: root/rules/apis
diff options
context:
space:
mode:
authorSpandan Das <spandandas@google.com>2022-11-23 01:33:13 +0000
committerSpandan Das <spandandas@google.com>2022-12-09 18:04:22 +0000
commit34d41932ceb8dca20f05c40b1635612491a4bf8e (patch)
treea712ad1e60a7136815d3ce10f6a06fa30bd5e156 /rules/apis
parent3d7ee6f703e719b1bce793817468ee25e32f9a07 (diff)
downloadbazel-34d41932ceb8dca20f05c40b1635612491a4bf8e.tar.gz
Implement Bazel rule for java_api_contribution
- This rule does not have any build action, but returns providers containing metadata about the api file and the api_surface - Since java api surfaces are heirarchical ("system" api surface is really the union of current.txt and system-current.txt), there is an internal calculation to determine all the api files that constitute the API surface. As an example, current.txt will be listed as an api file of every other API surface, system-current.txt will be listed as an api file of systemapi, testapi, ... and so on - This does not handle CorePlatformApi surface yet. I think this heirarchical computation should be done internally and not explicitly listed by the users in the BUILD files. This prevents users from having to understand the heirarchy. Even if they understand the heirarchy, there might be errors while writing the BUILD files Bug: 220938703 Test: b test //build/bazel/rules/apis:api_domain_test_suite Change-Id: Iff4dcdf591dcda7ee56967deb6c0777f97f4bce6
Diffstat (limited to 'rules/apis')
-rw-r--r--rules/apis/api_domain.bzl21
-rw-r--r--rules/apis/api_domain_test.bzl53
-rw-r--r--rules/apis/cc_api_contribution.bzl8
-rw-r--r--rules/apis/java_api_contribution.bzl94
4 files changed, 158 insertions, 18 deletions
diff --git a/rules/apis/api_domain.bzl b/rules/apis/api_domain.bzl
index 4114a374..5f10998b 100644
--- a/rules/apis/api_domain.bzl
+++ b/rules/apis/api_domain.bzl
@@ -16,17 +16,30 @@ limitations under the License.
"""Bazel rules for generating the metadata of API domain contributions to an API surface"""
-load(":cc_api_contribution.bzl", "CcApiContributionInfo", "VALID_API_SURFACES")
+load("@bazel_skylib//lib:sets.bzl", "sets")
+load(":cc_api_contribution.bzl", "CcApiContributionInfo", "VALID_CC_API_SURFACES")
+load(":java_api_contribution.bzl", "JavaApiContributionInfo", "VALID_JAVA_API_SURFACES")
+
+def _all_api_surfaces():
+ # Returns a deduped union of cc and java api surfaces
+ api_surfaces = sets.make()
+ for api_surface in VALID_CC_API_SURFACES + VALID_JAVA_API_SURFACES:
+ sets.insert(api_surfaces, api_surface)
+ return sets.to_list(api_surfaces)
def _api_domain_impl(ctx):
"""Implementation of the api_domain rule
Currently it only supports exporting the API surface contributions of the API domain
"""
out = []
- for api_surface in VALID_API_SURFACES:
- # TODO(spandandas): Add other contributions (e.g. java_api_contribution)
+ for api_surface in _all_api_surfaces():
+ # TODO(b/220938703): Add other contributions (e.g. resource_api_contribution)
+ # cc
cc_libraries = [cc[CcApiContributionInfo] for cc in ctx.attr.cc_api_contributions if api_surface in cc[CcApiContributionInfo].api_surfaces]
+ # java
+ java_libraries = [java[JavaApiContributionInfo] for java in ctx.attr.java_api_contributions if api_surface in java[JavaApiContributionInfo].api_surfaces]
+
# The contributions of an API domain are always at ver=current
# Contributions of an API domain to previous Android SDKs will be snapshot and imported into the build graph by a separate Bazel rule
api_surface_metadata = struct(
@@ -34,6 +47,7 @@ def _api_domain_impl(ctx):
version = "current",
api_domain = ctx.attr.name,
cc_libraries = cc_libraries,
+ java_libraries = java_libraries,
)
api_surface_filestem = "-".join([api_surface, "current", ctx.attr.name])
api_surface_file = ctx.actions.declare_file(api_surface_filestem + ".json")
@@ -46,5 +60,6 @@ api_domain = rule(
implementation = _api_domain_impl,
attrs = {
"cc_api_contributions": attr.label_list(providers = [CcApiContributionInfo]),
+ "java_api_contributions": attr.label_list(providers = [JavaApiContributionInfo]),
},
)
diff --git a/rules/apis/api_domain_test.bzl b/rules/apis/api_domain_test.bzl
index 3b624ec8..21b6ba63 100644
--- a/rules/apis/api_domain_test.bzl
+++ b/rules/apis/api_domain_test.bzl
@@ -16,8 +16,9 @@ limitations under the License.
load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts")
load("@bazel_skylib//lib:paths.bzl", "paths")
-load(":cc_api_contribution.bzl", "VALID_API_SURFACES", "cc_api_contribution")
load(":api_domain.bzl", "api_domain")
+load(":cc_api_contribution.bzl", "cc_api_contribution")
+load(":java_api_contribution.bzl", "java_api_contribution")
# Check that a .json file is created
def _json_output_test_impl(ctx):
@@ -25,7 +26,7 @@ def _json_output_test_impl(ctx):
actions = analysistest.target_actions(env)
asserts.equals(
env,
- expected = len(VALID_API_SURFACES),
+ expected = 7, # union of cc and java api surfaces
actual = len(actions),
)
asserts.equals(
@@ -56,18 +57,20 @@ def _json_output_test():
)
return test_name
-# Check that output contains cc_libraries information
-def _json_output_contains_cc_test_impl(ctx):
+# Check that output contains contribution information
+# e.g. cc_libraries, java_libraries
+def _json_output_contains_contributions_test_impl(ctx):
env = analysistest.begin(ctx)
actions = analysistest.target_actions(env)
asserts.equals(
env,
- expected = len(VALID_API_SURFACES),
+ expected = 7, # union of cc and java api surfaces
actual = len(actions),
)
- # First element corresponds to publicapi
output = json.decode(actions[0].content.replace("'", "")) # Trim the surrounding '
+
+ # cc
asserts.true(env, "cc_libraries" in output)
cc_contributions_in_output = output.get("cc_libraries")
asserts.equals(
@@ -89,36 +92,64 @@ def _json_output_contains_cc_test_impl(ctx):
),
actual = test_contribution.get("api"),
)
+
+ # java
+ asserts.true(env, "java_libraries" in output)
+ java_contributions_in_output = output.get("java_libraries")
+ asserts.equals(
+ env,
+ expected = 1,
+ actual = len(java_contributions_in_output),
+ )
+ test_java_contribution = java_contributions_in_output[0]
+ asserts.equals(
+ env,
+ expected = paths.join(
+ paths.dirname(ctx.build_file_path),
+ ctx.attr.expected_java_apifile,
+ ),
+ actual = test_java_contribution.get("api"),
+ )
return analysistest.end(env)
-json_output_contains_cc_test = analysistest.make(
- impl = _json_output_contains_cc_test_impl,
+json_output_contains_contributions_test = analysistest.make(
+ impl = _json_output_contains_contributions_test_impl,
attrs = {
"expected_cc_library_name": attr.string(),
"expected_symbolfile": attr.string(),
+ "expected_java_apifile": attr.string(),
},
)
-def _json_output_contains_cc_test():
+def _json_output_contains_contributions_test():
test_name = "json_output_contains_cc_test"
subject_name = test_name + "_subject"
cc_subject_name = subject_name + "_cc"
+ java_subject_name = subject_name + "_java"
symbolfile = "libfoo.map.txt"
+ java_apifile = "current.txt"
cc_api_contribution(
name = cc_subject_name,
api = symbolfile,
tags = ["manual"],
)
+ java_api_contribution(
+ name = java_subject_name,
+ api = java_apifile,
+ tags = ["manual"],
+ )
api_domain(
name = subject_name,
cc_api_contributions = [cc_subject_name],
+ java_api_contributions = [java_subject_name],
tags = ["manual"],
)
- json_output_contains_cc_test(
+ json_output_contains_contributions_test(
name = test_name,
target_under_test = subject_name,
expected_cc_library_name = cc_subject_name,
expected_symbolfile = symbolfile,
+ expected_java_apifile = java_apifile,
)
return test_name
@@ -127,6 +158,6 @@ def api_domain_test_suite(name):
name = name,
tests = [
_json_output_test(),
- _json_output_contains_cc_test(),
+ _json_output_contains_contributions_test(),
],
)
diff --git a/rules/apis/cc_api_contribution.bzl b/rules/apis/cc_api_contribution.bzl
index 554899f2..a465f962 100644
--- a/rules/apis/cc_api_contribution.bzl
+++ b/rules/apis/cc_api_contribution.bzl
@@ -179,7 +179,7 @@ CcApiContributionInfo = provider(
},
)
-VALID_API_SURFACES = [
+VALID_CC_API_SURFACES = [
"publicapi",
"systemapi",
"vendorapi",
@@ -187,8 +187,8 @@ VALID_API_SURFACES = [
def _validate_api_surfaces(api_surfaces):
for api_surface in api_surfaces:
- if api_surface not in VALID_API_SURFACES:
- fail(api_surface, " is not a valid API surface. Acceptable values: ", VALID_API_SURFACES)
+ if api_surface not in VALID_CC_API_SURFACES:
+ fail(api_surface, " is not a valid API surface. Acceptable values: ", VALID_CC_API_SURFACES)
def _cc_api_contribution_impl(ctx):
"""Implemenation for the cc_api_contribution rule
@@ -229,7 +229,7 @@ cc_api_contribution = rule(
doc = "Header contributions of the cc library. This should return a `CcApiHeaderInfo` provider",
),
"api_surfaces": attr.string_list(
- doc = "API surface(s) this library contributes to. See VALID_API_SURFACES in cc_api_contribution.bzl for valid values for API surfaces",
+ doc = "API surface(s) this library contributes to. See VALID_CC_API_SURFACES in cc_api_contribution.bzl for valid values for API surfaces",
default = ["publicapi"],
),
},
diff --git a/rules/apis/java_api_contribution.bzl b/rules/apis/java_api_contribution.bzl
new file mode 100644
index 00000000..30d41041
--- /dev/null
+++ b/rules/apis/java_api_contribution.bzl
@@ -0,0 +1,94 @@
+"""
+Copyright (C) 2022 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.
+"""
+
+"""Bazel rules for exporting API contributions of Java libraries"""
+
+"""A Bazel provider that encapsulates the contributions of a Java library to an API surface"""
+JavaApiContributionInfo = provider(
+ fields = {
+ "name": "Name of the contribution target",
+ "api": "Path of partial current.txt file describing the stable APIs of the library. Path is relative to workspace root",
+ "api_surfaces": "List of API surfaces that this partial api file contributes to",
+ },
+)
+
+_PUBLIC_API = "publicapi"
+_SYSTEM_API = "systemapi"
+_TEST_API = "testapi"
+_MODULE_LIB_API = "module-libapi"
+_SYSTEM_SERVER_API = "systemserver-api"
+_INTRA_CORE_API = "intracoreapi"
+
+# Java API surfaces are hierarchical.
+# This hierarchy map was created by looking at the stub definitions in frameworks/base/StubLibraries.bp
+# Key is the full api surface
+# Values are the partial metalava signature files that are combined to generate the full api surface stubs.
+_JAVA_FULLAPISURFACE_TO_PARTIALSIGNATUREFILE = {
+ _PUBLIC_API: [_PUBLIC_API],
+ _SYSTEM_API: [_PUBLIC_API, _SYSTEM_API],
+ _TEST_API: [_PUBLIC_API, _SYSTEM_API, _TEST_API],
+ _MODULE_LIB_API: [_PUBLIC_API, _SYSTEM_API, _MODULE_LIB_API],
+ _SYSTEM_SERVER_API: [_PUBLIC_API, _SYSTEM_API, _MODULE_LIB_API, _SYSTEM_SERVER_API],
+ # intracore is publicapi + "@IntraCoreApi".
+ # e.g. art.module.intra.core.api uses the following `droiddoc_option`
+ # [<hide>, --show-single-annotation libcore.api.IntraCoreApi"]
+ # conscrypt and icu4j use similar droidoc_options
+ _INTRA_CORE_API: [_PUBLIC_API, _INTRA_CORE_API],
+ # TODO: Handle CorePlatformApi
+ # coreapi does not have an entry here, it really is the public stubs of the 3 core modules
+ # (art, conscrypt, i18n)
+}
+
+VALID_JAVA_API_SURFACES = _JAVA_FULLAPISURFACE_TO_PARTIALSIGNATUREFILE.keys()
+
+def _java_api_contribution_impl(ctx):
+ """Implemenation for the java_api_contribution rule
+ This rule does not have any build actions, but returns a `JavaApiContributionInfo` provider object"""
+
+ full_api_surfaces = []
+
+ # The checked-in signature files are parital signatures. e.g. SystemAPI surface
+ # (android_system_stubs_current.jar) contains the classes
+ # and methods present in current.txt and system-current.txt.
+ # The jar representing the full api surface is created by combining these partial signature files.
+ for full_api_surface, partials in _JAVA_FULLAPISURFACE_TO_PARTIALSIGNATUREFILE.items():
+ if ctx.attr.api_surface in partials:
+ full_api_surfaces.append(full_api_surface)
+
+ return [
+ JavaApiContributionInfo(
+ name = ctx.label.name,
+ api = ctx.file.api.path,
+ api_surfaces = full_api_surfaces,
+ ),
+ ]
+
+java_api_contribution = rule(
+ implementation = _java_api_contribution_impl,
+ attrs = {
+ "api": attr.label(
+ mandatory = True,
+ allow_single_file = [".txt"],
+ doc = "The partial signature file describing the APIs of this module",
+ ),
+ # TODO: Better name for this
+ "api_surface": attr.string(
+ doc = "The partial api surface signature represented by this file. See _JAVA_FULLAPISURFACE_TO_PARTIALSIGNATUREFILE in java_api_contribution.bzl for relationship between partial signature files and full API surfaces",
+ default = "publicapi",
+ values = VALID_JAVA_API_SURFACES,
+ ),
+ },
+)