diff options
-rw-r--r-- | apex/apex_test.go | 2 | ||||
-rw-r--r-- | java/builder.go | 8 | ||||
-rw-r--r-- | java/droidstubs.go | 22 | ||||
-rw-r--r-- | java/droidstubs_test.go | 6 | ||||
-rw-r--r-- | java/java_test.go | 4 | ||||
-rw-r--r-- | scripts/Android.bp | 10 | ||||
-rw-r--r-- | scripts/aconfig-to-metalava-flags.py | 103 | ||||
-rwxr-xr-x | scripts/keep-flagged-apis.sh | 37 |
8 files changed, 130 insertions, 62 deletions
diff --git a/apex/apex_test.go b/apex/apex_test.go index a0a42a149..36a697487 100644 --- a/apex/apex_test.go +++ b/apex/apex_test.go @@ -11667,7 +11667,7 @@ func TestAconfifDeclarationsValidation(t *testing.T) { // Arguments passed to aconfig to retrieve the state of the flags defined in the // textproto files - aconfigFlagArgs := m.Output("released-flagged-apis-exportable.txt").Args["flags_path"] + aconfigFlagArgs := m.Output("released-flags-exportable.pb").Args["flags_path"] // "bar-lib" is a static_lib of "foo" and is passed to metalava as classpath. Thus the // cache file provided by the associated aconfig_declarations module "bar" should be passed diff --git a/java/builder.go b/java/builder.go index ade978450..dff0032d8 100644 --- a/java/builder.go +++ b/java/builder.go @@ -310,7 +310,7 @@ var ( gatherReleasedFlaggedApisRule = pctx.AndroidStaticRule("gatherReleasedFlaggedApisRule", blueprint.RuleParams{ - Command: `${aconfig} dump-cache --dedup --format='{fully_qualified_name}' ` + + Command: `${aconfig} dump-cache --dedup --format=protobuf ` + `--out ${out} ` + `${flags_path} ` + `${filter_args} `, @@ -320,8 +320,8 @@ var ( generateMetalavaRevertAnnotationsRule = pctx.AndroidStaticRule("generateMetalavaRevertAnnotationsRule", blueprint.RuleParams{ - Command: `${keep-flagged-apis} ${in} > ${out}`, - CommandDeps: []string{"${keep-flagged-apis}"}, + Command: `${aconfig-to-metalava-flags} ${in} > ${out}`, + CommandDeps: []string{"${aconfig-to-metalava-flags}"}, }) generateApiXMLRule = pctx.AndroidStaticRule("generateApiXMLRule", @@ -339,7 +339,7 @@ func init() { pctx.HostBinToolVariable("aconfig", "aconfig") pctx.HostBinToolVariable("ravenizer", "ravenizer") pctx.HostBinToolVariable("apimapper", "apimapper") - pctx.HostBinToolVariable("keep-flagged-apis", "keep-flagged-apis") + pctx.HostBinToolVariable("aconfig-to-metalava-flags", "aconfig-to-metalava-flags") } type javaBuilderFlags struct { diff --git a/java/droidstubs.go b/java/droidstubs.go index caad6883e..b3241cca9 100644 --- a/java/droidstubs.go +++ b/java/droidstubs.go @@ -821,19 +821,17 @@ func generateRevertAnnotationArgs(ctx android.ModuleContext, cmd *android.RuleBu } } - if len(aconfigFlagsPaths) == 0 { - // This argument should not be added for "everything" stubs - cmd.Flag("--revert-annotation android.annotation.FlaggedApi") - return - } + // If aconfigFlagsPaths is empty then it is still important to generate the + // Metalava flags config file, albeit with an empty set of flags, so that all + // flagged APIs will be reverted. - releasedFlaggedApisFile := android.PathForModuleOut(ctx, fmt.Sprintf("released-flagged-apis-%s.txt", stubsType.String())) - revertAnnotationsFile := android.PathForModuleOut(ctx, fmt.Sprintf("revert-annotations-%s.txt", stubsType.String())) + releasedFlagsFile := android.PathForModuleOut(ctx, fmt.Sprintf("released-flags-%s.pb", stubsType.String())) + metalavaFlagsConfigFile := android.PathForModuleOut(ctx, fmt.Sprintf("flags-config-%s.xml", stubsType.String())) ctx.Build(pctx, android.BuildParams{ Rule: gatherReleasedFlaggedApisRule, Inputs: aconfigFlagsPaths, - Output: releasedFlaggedApisFile, + Output: releasedFlagsFile, Description: fmt.Sprintf("%s gather aconfig flags", stubsType), Args: map[string]string{ "flags_path": android.JoinPathsWithPrefix(aconfigFlagsPaths, "--cache "), @@ -843,12 +841,12 @@ func generateRevertAnnotationArgs(ctx android.ModuleContext, cmd *android.RuleBu ctx.Build(pctx, android.BuildParams{ Rule: generateMetalavaRevertAnnotationsRule, - Input: releasedFlaggedApisFile, - Output: revertAnnotationsFile, - Description: fmt.Sprintf("%s revert annotations", stubsType), + Input: releasedFlagsFile, + Output: metalavaFlagsConfigFile, + Description: fmt.Sprintf("%s metalava flags config", stubsType), }) - cmd.FlagWithInput("@", revertAnnotationsFile) + cmd.FlagWithInput("--config-file ", metalavaFlagsConfigFile) } func (d *Droidstubs) commonMetalavaStubCmd(ctx android.ModuleContext, rule *android.RuleBuilder, diff --git a/java/droidstubs_test.go b/java/droidstubs_test.go index dfdf87703..1f9d223b5 100644 --- a/java/droidstubs_test.go +++ b/java/droidstubs_test.go @@ -410,12 +410,12 @@ func TestAconfigDeclarations(t *testing.T) { m := result.ModuleForTests(t, "foo", "android_common") android.AssertStringDoesContain(t, "foo generates revert annotations file", - strings.Join(m.AllOutputs(), ""), "revert-annotations-exportable.txt") + strings.Join(m.AllOutputs(), ""), "flags-config-exportable.xml") // revert-annotations.txt passed to exportable stubs generation metalava command manifest := m.Output("metalava_exportable.sbox.textproto") cmdline := String(android.RuleBuilderSboxProtoForTests(t, result.TestContext, manifest).Commands[0].Command) - android.AssertStringDoesContain(t, "flagged api hide command not included", cmdline, "revert-annotations-exportable.txt") + android.AssertStringDoesContain(t, "flagged api hide command not included", cmdline, "flags-config-exportable.xml") android.AssertStringDoesContain(t, "foo generates exportable stubs jar", strings.Join(m.AllOutputs(), ""), "exportable/foo-stubs.srcjar") @@ -460,7 +460,7 @@ func TestReleaseExportRuntimeApis(t *testing.T) { m := result.ModuleForTests(t, "foo", "android_common") - rule := m.Output("released-flagged-apis-exportable.txt") + rule := m.Output("released-flags-exportable.pb") exposeWritableApisFilter := "--filter='state:ENABLED+permission:READ_ONLY' --filter='permission:READ_WRITE'" android.AssertStringEquals(t, "Filter argument expected to contain READ_WRITE permissions", exposeWritableApisFilter, rule.Args["filter_args"]) } diff --git a/java/java_test.go b/java/java_test.go index a6290a628..6df81e595 100644 --- a/java/java_test.go +++ b/java/java_test.go @@ -2912,12 +2912,12 @@ func TestApiLibraryAconfigDeclarations(t *testing.T) { m := result.ModuleForTests(t, "foo", "android_common") android.AssertStringDoesContain(t, "foo generates revert annotations file", - strings.Join(m.AllOutputs(), ""), "revert-annotations-exportable.txt") + strings.Join(m.AllOutputs(), ""), "flags-config-exportable.xml") // revert-annotations.txt passed to exportable stubs generation metalava command manifest := m.Output("metalava.sbox.textproto") cmdline := String(android.RuleBuilderSboxProtoForTests(t, result.TestContext, manifest).Commands[0].Command) - android.AssertStringDoesContain(t, "flagged api hide command not included", cmdline, "revert-annotations-exportable.txt") + android.AssertStringDoesContain(t, "flagged api hide command not included", cmdline, "flags-config-exportable.xml") } func TestTestOnly(t *testing.T) { diff --git a/scripts/Android.bp b/scripts/Android.bp index 94163a5c2..c0e13d52f 100644 --- a/scripts/Android.bp +++ b/scripts/Android.bp @@ -287,9 +287,13 @@ python_test_host { ], } -sh_binary_host { - name: "keep-flagged-apis", - src: "keep-flagged-apis.sh", +python_binary_host { + name: "aconfig-to-metalava-flags", + main: "aconfig-to-metalava-flags.py", + srcs: ["aconfig-to-metalava-flags.py"], + libs: [ + "libaconfig_python_proto", + ], } python_binary_host { diff --git a/scripts/aconfig-to-metalava-flags.py b/scripts/aconfig-to-metalava-flags.py new file mode 100644 index 000000000..efa85ecb3 --- /dev/null +++ b/scripts/aconfig-to-metalava-flags.py @@ -0,0 +1,103 @@ +#!/usr/bin/env python3 + +# Copyright (C) 2025 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. + +"""Converts a set of aconfig protobuf flags to a Metalava config file.""" + +# Formatted using `pyformat -i scripts/aconfig-to-metalava-flags.py` + +import argparse +import sys +import xml.etree.ElementTree as ET + +from protos import aconfig_pb2 + +_READ_ONLY = aconfig_pb2.flag_permission.READ_ONLY +_ENABLED = aconfig_pb2.flag_state.ENABLED +_DISABLED = aconfig_pb2.flag_state.DISABLED + +# The namespace of the Metalava config file. +CONFIG_NS = 'http://www.google.com/tools/metalava/config' + + +def config_name(tag: str): + """Create a QName in the config namespace. + + :param:tag the name of the entity in the config namespace. + """ + return f'{{{CONFIG_NS}}}{tag}' + + +def main(): + """Program entry point.""" + args_parser = argparse.ArgumentParser( + description='Generate Metalava flags config from aconfig protobuf', + ) + args_parser.add_argument( + 'input', + help='The path to the aconfig protobuf file', + ) + args = args_parser.parse_args(sys.argv[1:]) + + # Read the parsed_flags from the protobuf file. + with open(args.input, 'rb') as f: + parsed_flags = aconfig_pb2.parsed_flags.FromString(f.read()) + + # Create the structure of the XML config file. + config = ET.Element(config_name('config')) + api_flags = ET.SubElement(config, config_name('api-flags')) + # Create an <api-flag> element for each parsed_flag. + for flag in parsed_flags.parsed_flag: + if flag.permission == _READ_ONLY: + # Ignore any read only disabled flags as Metalava assumes that as the + # default when an <api-flags/> element is provided so this reduces the + # size of the file. + if flag.state == _DISABLED: + continue + mutability = 'immutable' + else: + mutability = 'mutable' + if flag.state == _ENABLED: + status = 'enabled' + else: + status = 'disabled' + attributes = { + 'package': flag.package, + 'name': flag.name, + 'mutability': mutability, + 'status': status, + } + # Convert the attribute names into qualified names in, what will become, the + # default namespace for the XML file. This is needed to ensure that the + # attribute will be written in the XML file without a prefix, e.g. + # `name="flag_name"`. Without it, a namespace prefix, e.g. `ns1`, will be + # synthesized for the attribute when writing the XML file, i.e. it + # will be written as `ns1:name="flag_name"`. Strictly speaking, that is + # unnecessary as the "Namespaces in XML 1.0 (Third Edition)" specification + # says that unprefixed attribute names have no namespace. + qualified_attributes = {config_name(k): v for (k, v) in attributes.items()} + ET.SubElement(api_flags, config_name('api-flag'), qualified_attributes) + + # Create a tree and add white space so it will pretty print when written out. + tree = ET.ElementTree(config) + ET.indent(tree) + + # Write the tree using the config namespace as the default. + tree.write(sys.stdout, encoding='unicode', default_namespace=CONFIG_NS) + sys.stdout.close() + + +if __name__ == '__main__': + main() diff --git a/scripts/keep-flagged-apis.sh b/scripts/keep-flagged-apis.sh deleted file mode 100755 index 48efb7a29..000000000 --- a/scripts/keep-flagged-apis.sh +++ /dev/null @@ -1,37 +0,0 @@ -#!/bin/bash -e -# -# Copyright 2023 Google Inc. All rights reserved. -# -# 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. - -# Convert a list of flags in the input file to a list of metalava options -# that will keep the APIs for those flags will hiding all other flagged -# APIs. - -FLAGS="$1" - -FLAGGED="android.annotation.FlaggedApi" - -# Convert the list of feature flags in the input file to Metalava options -# of the form `--revert-annotation !android.annotation.FlaggedApi("<flag>")` -# to prevent the annotated APIs from being hidden, i.e. include the annotated -# APIs in the SDK snapshots. -while read -r line; do - # Escape and quote the key for sed - escaped_line=$(echo "$line" | sed "s/'/\\\'/g; s/ /\\ /g") - - echo "--revert-annotation '!$FLAGGED(\"$escaped_line\")'" -done < "$FLAGS" - -# Revert all flagged APIs, unless listed above. -echo "--revert-annotation $FLAGGED" |