diff options
-rw-r--r-- | android/Android.bp | 1 | ||||
-rw-r--r-- | android/test_config.go | 1 | ||||
-rw-r--r-- | android/vendor_api_levels.go | 49 | ||||
-rw-r--r-- | cc/api_level.go | 24 | ||||
-rw-r--r-- | cc/builder.go | 5 | ||||
-rw-r--r-- | cc/cc_test.go | 5 | ||||
-rw-r--r-- | cc/library.go | 26 | ||||
-rwxr-xr-x | cc/ndkstubgen/test_ndkstubgen.py | 29 | ||||
-rw-r--r-- | cc/symbolfile/__init__.py | 84 | ||||
-rw-r--r-- | cc/symbolfile/test_symbolfile.py | 56 |
10 files changed, 120 insertions, 160 deletions
diff --git a/android/Android.bp b/android/Android.bp index a9a3564ab..dfea8f999 100644 --- a/android/Android.bp +++ b/android/Android.bp @@ -111,6 +111,7 @@ bootstrap_go_package { "testing.go", "util.go", "variable.go", + "vendor_api_levels.go", "vintf_fragment.go", "vintf_data.go", "visibility.go", diff --git a/android/test_config.go b/android/test_config.go index f2510387f..3609e6b78 100644 --- a/android/test_config.go +++ b/android/test_config.go @@ -45,6 +45,7 @@ func TestConfig(buildDir string, env map[string]string, bp string, fs map[string Platform_version_active_codenames: []string{"S", "Tiramisu"}, DeviceSystemSdkVersions: []string{"29", "30", "S"}, Platform_systemsdk_versions: []string{"29", "30", "S", "Tiramisu"}, + VendorApiLevel: stringPtr("202404"), AAPTConfig: []string{"normal", "large", "xlarge", "hdpi", "xhdpi", "xxhdpi"}, AAPTPreferredConfig: stringPtr("xhdpi"), AAPTCharacteristics: stringPtr("nosdcard"), diff --git a/android/vendor_api_levels.go b/android/vendor_api_levels.go new file mode 100644 index 000000000..4d364fde6 --- /dev/null +++ b/android/vendor_api_levels.go @@ -0,0 +1,49 @@ +// Copyright 2024 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. + +package android + +import ( + "fmt" + "strconv" +) + +func getSdkVersionOfVendorApiLevel(apiLevel int) (int, bool) { + ok := true + sdkVersion := -1 + switch apiLevel { + case 202404: + sdkVersion = 35 + case 202504: + sdkVersion = 36 + default: + ok = false + } + return sdkVersion, ok +} + +func GetSdkVersionForVendorApiLevel(vendorApiLevel string) (ApiLevel, error) { + vendorApiLevelInt, err := strconv.Atoi(vendorApiLevel) + if err != nil { + return NoneApiLevel, fmt.Errorf("The vendor API level %q must be able to be parsed as an integer", vendorApiLevel) + } + if vendorApiLevelInt < 35 { + return uncheckedFinalApiLevel(vendorApiLevelInt), nil + } + + if sdkInt, ok := getSdkVersionOfVendorApiLevel(vendorApiLevelInt); ok { + return uncheckedFinalApiLevel(sdkInt), nil + } + return NoneApiLevel, fmt.Errorf("Unknown vendor API level %q. Requires updating the map in vendor_api_level.go?", vendorApiLevel) +} diff --git a/cc/api_level.go b/cc/api_level.go index 69a0d3ae4..3dac571a9 100644 --- a/cc/api_level.go +++ b/cc/api_level.go @@ -41,12 +41,25 @@ func MinApiForArch(ctx android.EarlyModuleContext, } } +// Native API levels cannot be less than the MinApiLevelForArch. This function +// sets the lower bound of the API level with the MinApiLevelForArch. +func nativeClampedApiLevel(ctx android.BaseModuleContext, + apiLevel android.ApiLevel) android.ApiLevel { + + min := MinApiForArch(ctx, ctx.Arch().ArchType) + + if apiLevel.LessThan(min) { + return min + } + + return apiLevel +} + func nativeApiLevelFromUser(ctx android.BaseModuleContext, raw string) (android.ApiLevel, error) { - min := MinApiForArch(ctx, ctx.Arch().ArchType) if raw == "minimum" { - return min, nil + return MinApiForArch(ctx, ctx.Arch().ArchType), nil } value, err := android.ApiLevelFromUser(ctx, raw) @@ -54,15 +67,12 @@ func nativeApiLevelFromUser(ctx android.BaseModuleContext, return android.NoneApiLevel, err } - if value.LessThan(min) { - return min, nil - } - - return value, nil + return nativeClampedApiLevel(ctx, value), nil } func nativeApiLevelOrPanic(ctx android.BaseModuleContext, raw string) android.ApiLevel { + value, err := nativeApiLevelFromUser(ctx, raw) if err != nil { panic(err.Error()) diff --git a/cc/builder.go b/cc/builder.go index 2948ca316..b98bef9be 100644 --- a/cc/builder.go +++ b/cc/builder.go @@ -945,7 +945,7 @@ func transformObjToDynamicBinary(ctx android.ModuleContext, func transformDumpToLinkedDump(ctx android.ModuleContext, sAbiDumps android.Paths, soFile android.Path, baseName string, exportedIncludeDirs []string, symbolFile android.OptionalPath, excludedSymbolVersions, excludedSymbolTags, includedSymbolTags []string, - api string, isLlndk bool) android.Path { + api string) android.Path { outputFile := android.PathForModuleOut(ctx, baseName+".lsdump") @@ -966,9 +966,6 @@ func transformDumpToLinkedDump(ctx android.ModuleContext, sAbiDumps android.Path for _, tag := range includedSymbolTags { symbolFilterStr += " --include-symbol-tag " + tag } - if isLlndk { - symbolFilterStr += " --symbol-tag-policy MatchTagOnly" - } apiLevelsJson := android.GetApiLevelsJson(ctx) implicits = append(implicits, apiLevelsJson) symbolFilterStr += " --api-map " + apiLevelsJson.String() diff --git a/cc/cc_test.go b/cc/cc_test.go index 144b90b94..98af7b655 100644 --- a/cc/cc_test.go +++ b/cc/cc_test.go @@ -40,9 +40,6 @@ func TestMain(m *testing.M) { var prepareForCcTest = android.GroupFixturePreparers( PrepareForIntegrationTestWithCc, - android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { - variables.VendorApiLevel = StringPtr("202404") - }), ) var apexVariationName = "apex28" @@ -1008,7 +1005,7 @@ func TestLlndkLibrary(t *testing.T) { android.AssertArrayString(t, "variants for llndk stubs", expected, actual) params := result.ModuleForTests("libllndk", "android_vendor_arm_armv7-a-neon_shared").Description("generate stub") - android.AssertSame(t, "use Vendor API level for default stubs", "999999", params.Args["apiLevel"]) + android.AssertSame(t, "use Vendor API level for default stubs", "35", params.Args["apiLevel"]) checkExportedIncludeDirs := func(module, variant string, expectedSystemDirs []string, expectedDirs ...string) { t.Helper() diff --git a/cc/library.go b/cc/library.go index 23ee9b141..ea8794644 100644 --- a/cc/library.go +++ b/cc/library.go @@ -566,10 +566,16 @@ func (library *libraryDecorator) getHeaderAbiCheckerProperties(m *Module) header func (library *libraryDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) Objects { if ctx.IsLlndk() { - futureVendorApiLevel := android.ApiLevelOrPanic(ctx, "999999") + // Get the matching SDK version for the vendor API level. + version, err := android.GetSdkVersionForVendorApiLevel(ctx.Config().VendorApiLevel()) + if err != nil { + panic(err) + } + + // This is the vendor variant of an LLNDK library, build the LLNDK stubs. nativeAbiResult := parseNativeAbiDefinition(ctx, String(library.Properties.Llndk.Symbol_file), - futureVendorApiLevel, "--llndk") + nativeClampedApiLevel(ctx, version), "--llndk") objs := compileStubLibrary(ctx, flags, nativeAbiResult.stubSrc) if !Bool(library.Properties.Llndk.Unversioned) { library.versionScriptPath = android.OptionalPathForPath( @@ -1285,15 +1291,14 @@ func (library *libraryDecorator) llndkIncludeDirsForAbiCheck(ctx ModuleContext, func (library *libraryDecorator) linkLlndkSAbiDumpFiles(ctx ModuleContext, deps PathDeps, sAbiDumpFiles android.Paths, soFile android.Path, libFileName string, excludeSymbolVersions, excludeSymbolTags []string, - vendorApiLevel string) android.Path { - // NDK symbols in version 34 are LLNDK symbols. Those in version 35 are not. + sdkVersionForVendorApiLevel string) android.Path { return transformDumpToLinkedDump(ctx, sAbiDumpFiles, soFile, libFileName+".llndk", library.llndkIncludeDirsForAbiCheck(ctx, deps), android.OptionalPathForModuleSrc(ctx, library.Properties.Llndk.Symbol_file), append([]string{"*_PLATFORM", "*_PRIVATE"}, excludeSymbolVersions...), append([]string{"platform-only"}, excludeSymbolTags...), - []string{"llndk=" + vendorApiLevel}, "34", true /* isLlndk */) + []string{"llndk"}, sdkVersionForVendorApiLevel) } func (library *libraryDecorator) linkApexSAbiDumpFiles(ctx ModuleContext, @@ -1306,7 +1311,7 @@ func (library *libraryDecorator) linkApexSAbiDumpFiles(ctx ModuleContext, android.OptionalPathForModuleSrc(ctx, library.Properties.Stubs.Symbol_file), append([]string{"*_PLATFORM", "*_PRIVATE"}, excludeSymbolVersions...), append([]string{"platform-only"}, excludeSymbolTags...), - []string{"apex", "systemapi"}, sdkVersion, false /* isLlndk */) + []string{"apex", "systemapi"}, sdkVersion) } func getRefAbiDumpFile(ctx android.ModuleInstallPathContext, @@ -1444,7 +1449,7 @@ func (library *libraryDecorator) linkSAbiDumpFiles(ctx ModuleContext, deps PathD android.OptionalPathForModuleSrc(ctx, library.symbolFileForAbiCheck(ctx)), headerAbiChecker.Exclude_symbol_versions, headerAbiChecker.Exclude_symbol_tags, - []string{} /* includeSymbolTags */, currSdkVersion, false /* isLlndk */) + []string{} /* includeSymbolTags */, currSdkVersion) var llndkDump, apexVariantDump android.Path tags := classifySourceAbiDump(ctx.Module().(*Module)) @@ -1452,12 +1457,17 @@ func (library *libraryDecorator) linkSAbiDumpFiles(ctx ModuleContext, deps PathD for _, tag := range tags { if tag == llndkLsdumpTag && currVendorVersion != "" { if llndkDump == nil { + sdkVersion, err := android.GetSdkVersionForVendorApiLevel(currVendorVersion) + if err != nil { + ctx.ModuleErrorf("Cannot create %s llndk dump: %s", fileName, err) + return + } // TODO(b/323447559): Evaluate if replacing sAbiDumpFiles with implDump is faster llndkDump = library.linkLlndkSAbiDumpFiles(ctx, deps, objs.sAbiDumpFiles, soFile, fileName, headerAbiChecker.Exclude_symbol_versions, headerAbiChecker.Exclude_symbol_tags, - currVendorVersion) + nativeClampedApiLevel(ctx, sdkVersion).String()) } addLsdumpPath(ctx.Config(), string(tag)+":"+llndkDump.String()) } else if tag == apexLsdumpTag { diff --git a/cc/ndkstubgen/test_ndkstubgen.py b/cc/ndkstubgen/test_ndkstubgen.py index 22f31d9f1..6c24b4f19 100755 --- a/cc/ndkstubgen/test_ndkstubgen.py +++ b/cc/ndkstubgen/test_ndkstubgen.py @@ -473,16 +473,17 @@ class IntegrationTest(unittest.TestCase): VERSION_35 { # introduced=35 global: wiggle; - waggle; - waggle; # llndk=202404 - bubble; # llndk=202404 - duddle; - duddle; # llndk=202504 + waggle; # llndk } VERSION_34; + VERSION_36 { # introduced=36 + global: + abc; + xyz; # llndk + } VERSION_35; """)) f = copy(self.filter) f.llndk = True - f.api = 202404 + f.api = 35 parser = symbolfile.SymbolFileParser(input_file, {}, f) versions = parser.parse() @@ -497,8 +498,8 @@ class IntegrationTest(unittest.TestCase): expected_src = textwrap.dedent("""\ void foo() {} void bar() {} + void wiggle() {} void waggle() {} - void bubble() {} """) self.assertEqual(expected_src, src_file.getvalue()) @@ -510,8 +511,8 @@ class IntegrationTest(unittest.TestCase): }; VERSION_35 { global: + wiggle; waggle; - bubble; } VERSION_34; """) self.assertEqual(expected_version, version_file.getvalue()) @@ -521,15 +522,15 @@ class IntegrationTest(unittest.TestCase): LIBANDROID { global: foo; # introduced=34 - bar; # introduced=35 - bar; # llndk=202404 - baz; # introduced=35 + bar; # introduced=35 llndk + baz; # introduced=V + qux; # introduced=36 }; """)) f = copy(self.filter) f.llndk = True - f.api = 202404 - parser = symbolfile.SymbolFileParser(input_file, {}, f) + f.api = 35 + parser = symbolfile.SymbolFileParser(input_file, {'V': 35}, f) versions = parser.parse() src_file = io.StringIO() @@ -543,6 +544,7 @@ class IntegrationTest(unittest.TestCase): expected_src = textwrap.dedent("""\ void foo() {} void bar() {} + void baz() {} """) self.assertEqual(expected_src, src_file.getvalue()) @@ -551,6 +553,7 @@ class IntegrationTest(unittest.TestCase): global: foo; bar; + baz; }; """) self.assertEqual(expected_version, version_file.getvalue()) diff --git a/cc/symbolfile/__init__.py b/cc/symbolfile/__init__.py index 4553616ac..f2bd18690 100644 --- a/cc/symbolfile/__init__.py +++ b/cc/symbolfile/__init__.py @@ -103,24 +103,13 @@ class Tags: @property def has_llndk_tags(self) -> bool: """Returns True if any LL-NDK tags are set.""" - for tag in self.tags: - if tag == 'llndk' or tag.startswith('llndk='): - return True - return False + return 'llndk' in self.tags @property def has_platform_only_tags(self) -> bool: """Returns True if any platform-only tags are set.""" return 'platform-only' in self.tags - def copy_introduced_from(self, tags: Tags) -> None: - """Copies introduced= or introduced-*= tags.""" - for tag in tags: - if tag.startswith('introduced=') or tag.startswith('introduced-'): - name, _ = split_tag(tag) - if not any(self_tag.startswith(name + '=') for self_tag in self.tags): - self.tags += (tag,) - @dataclass class Symbol: @@ -158,8 +147,6 @@ def is_api_level_tag(tag: Tag) -> bool: """Returns true if this tag has an API level that may need decoding.""" if tag.startswith('llndk-deprecated='): return True - if tag.startswith('llndk='): - return True if tag.startswith('introduced='): return True if tag.startswith('introduced-'): @@ -245,21 +232,19 @@ class Filter: self.systemapi = systemapi self.ndk = ndk + def _symbol_in_arch_api(self, tags: Tags) -> bool: + if not symbol_in_arch(tags, self.arch): + return True + if not symbol_in_api(tags, self.arch, self.api): + return True + return False + def _should_omit_tags(self, tags: Tags) -> bool: """Returns True if the tagged object should be omitted. This defines the rules shared between version tagging and symbol tagging. """ - # LLNDK mode/tags follow the similar filtering except that API level checking - # is based llndk= instead of introduced=. - if self.llndk: - if tags.has_mode_tags and not tags.has_llndk_tags: - return True - if not symbol_in_arch(tags, self.arch): - return True - if not symbol_in_llndk_api(tags, self.arch, self.api): - return True - return False + # The apex and llndk tags will only exclude APIs from other modes. If in # APEX or LLNDK mode and neither tag is provided, we fall back to the # default behavior because all NDK symbols are implicitly available to # APEX and LLNDK. @@ -268,12 +253,10 @@ class Filter: return False if self.systemapi and tags.has_systemapi_tags: return False + if self.llndk and tags.has_llndk_tags: + return self._symbol_in_arch_api(tags) return True - if not symbol_in_arch(tags, self.arch): - return True - if not symbol_in_api(tags, self.arch, self.api): - return True - return False + return self._symbol_in_arch_api(tags) def should_omit_version(self, version: Version) -> bool: """Returns True if the version section should be omitted. @@ -286,10 +269,6 @@ class Filter: return True if version.tags.has_platform_only_tags: return True - # Include all versions when targeting LLNDK because LLNDK symbols are self-versioned. - # Empty version block will be handled separately. - if self.llndk: - return False return self._should_omit_tags(version.tags) def should_omit_symbol(self, symbol: Symbol) -> bool: @@ -302,6 +281,7 @@ class Filter: return self._should_omit_tags(symbol.tags) + def symbol_in_arch(tags: Tags, arch: Arch) -> bool: """Returns true if the symbol is present for the given architecture.""" has_arch_tags = False @@ -316,14 +296,6 @@ def symbol_in_arch(tags: Tags, arch: Arch) -> bool: # for the tagged architectures. return not has_arch_tags -def symbol_in_llndk_api(tags: Iterable[Tag], arch: Arch, api: int) -> bool: - """Returns true if the symbol is present for the given LLNDK API level.""" - # Check llndk= first. - for tag in tags: - if tag.startswith('llndk='): - return api >= int(get_tag_value(tag)) - # If not, we keep old behavior: NDK symbols in <= 34 are LLNDK symbols. - return symbol_in_api(tags, arch, 34) def symbol_in_api(tags: Iterable[Tag], arch: Arch, api: int) -> bool: """Returns true if the symbol is present for the given API level.""" @@ -400,7 +372,6 @@ class SymbolFileParser: f'Unexpected contents at top level: {self.current_line}') self.check_no_duplicate_symbols(versions) - self.check_llndk_introduced(versions) return versions def check_no_duplicate_symbols(self, versions: Iterable[Version]) -> None: @@ -429,31 +400,6 @@ class SymbolFileParser: raise MultiplyDefinedSymbolError( sorted(list(multiply_defined_symbols))) - def check_llndk_introduced(self, versions: Iterable[Version]) -> None: - """Raises errors when llndk= is missing for new llndk symbols.""" - if not self.filter.llndk: - return - - def assert_llndk_with_version(tags: Tags, name: str) -> None: - has_llndk_introduced = False - for tag in tags: - if tag.startswith('llndk='): - has_llndk_introduced = True - break - if not has_llndk_introduced: - raise ParseError(f'{name}: missing version. `llndk=yyyymm`') - - arch = self.filter.arch - for version in versions: - # llndk symbols >= introduced=35 should be tagged - # explicitly with llndk=yyyymm. - for symbol in version.symbols: - if not symbol.tags.has_llndk_tags: - continue - if symbol_in_api(symbol.tags, arch, 34): - continue - assert_llndk_with_version(symbol.tags, symbol.name) - def parse_version(self) -> Version: """Parses a single version section and returns a Version object.""" assert self.current_line is not None @@ -487,9 +433,7 @@ class SymbolFileParser: else: raise ParseError('Unknown visiblity label: ' + visibility) elif global_scope and not cpp_symbols: - symbol = self.parse_symbol() - symbol.tags.copy_introduced_from(tags) - symbols.append(symbol) + symbols.append(self.parse_symbol()) else: # We're in a hidden scope or in 'extern "C++"' block. Ignore # everything. diff --git a/cc/symbolfile/test_symbolfile.py b/cc/symbolfile/test_symbolfile.py index 8b412b98a..14bb737ee 100644 --- a/cc/symbolfile/test_symbolfile.py +++ b/cc/symbolfile/test_symbolfile.py @@ -344,45 +344,6 @@ class OmitSymbolTest(unittest.TestCase): self.assertInclude(f_llndk, s_none) self.assertInclude(f_llndk, s_llndk) - def test_omit_llndk_versioned(self) -> None: - f_ndk = self.filter - f_ndk.api = 35 - - f_llndk = copy(f_ndk) - f_llndk.llndk = True - f_llndk.api = 202404 - - s = Symbol('foo', Tags()) - s_llndk = Symbol('foo', Tags.from_strs(['llndk'])) - s_llndk_202404 = Symbol('foo', Tags.from_strs(['llndk=202404'])) - s_34 = Symbol('foo', Tags.from_strs(['introduced=34'])) - s_34_llndk = Symbol('foo', Tags.from_strs(['introduced=34', 'llndk'])) - s_35 = Symbol('foo', Tags.from_strs(['introduced=35'])) - s_35_llndk_202404 = Symbol('foo', Tags.from_strs(['introduced=35', 'llndk=202404'])) - s_35_llndk_202504 = Symbol('foo', Tags.from_strs(['introduced=35', 'llndk=202504'])) - - # When targeting NDK, omit LLNDK tags - self.assertInclude(f_ndk, s) - self.assertOmit(f_ndk, s_llndk) - self.assertOmit(f_ndk, s_llndk_202404) - self.assertInclude(f_ndk, s_34) - self.assertOmit(f_ndk, s_34_llndk) - self.assertInclude(f_ndk, s_35) - self.assertOmit(f_ndk, s_35_llndk_202404) - self.assertOmit(f_ndk, s_35_llndk_202504) - - # When targeting LLNDK, old symbols without any mode tags are included as LLNDK - self.assertInclude(f_llndk, s) - # When targeting LLNDK, old symbols with #llndk are included as LLNDK - self.assertInclude(f_llndk, s_llndk) - self.assertInclude(f_llndk, s_llndk_202404) - self.assertInclude(f_llndk, s_34) - self.assertInclude(f_llndk, s_34_llndk) - # When targeting LLNDK, new symbols(>=35) should be tagged with llndk-introduced=. - self.assertOmit(f_llndk, s_35) - self.assertInclude(f_llndk, s_35_llndk_202404) - self.assertOmit(f_llndk, s_35_llndk_202504) - def test_omit_apex(self) -> None: f_none = self.filter f_apex = copy(f_none) @@ -494,8 +455,8 @@ class SymbolFileParseTest(unittest.TestCase): # should_omit_tags() can differently based on introduced API level when treating # LLNDK-available symbols. expected_symbols = [ - Symbol('baz', Tags.from_strs(['introduced=35'])), - Symbol('qux', Tags.from_strs(['apex', 'llndk', 'introduced=35'])), + Symbol('baz', Tags()), + Symbol('qux', Tags.from_strs(['apex', 'llndk'])), ] self.assertEqual(expected_symbols, version.symbols) @@ -643,19 +604,6 @@ class SymbolFileParseTest(unittest.TestCase): ] self.assertEqual(expected_symbols, version.symbols) - def test_parse_llndk_version_is_missing(self) -> None: - input_file = io.StringIO(textwrap.dedent("""\ - VERSION_1 { # introduced=35 - foo; - bar; # llndk - }; - """)) - f = copy(self.filter) - f.llndk = True - parser = symbolfile.SymbolFileParser(input_file, {}, f) - with self.assertRaises(symbolfile.ParseError): - parser.parse() - def main() -> None: suite = unittest.TestLoader().loadTestsFromName(__name__) |