Merge changes from topic "llndk-use-sdk-versioning" into main am: a4a6ec4d89

Original change: https://android-review.googlesource.com/c/platform/build/soong/+/3342597

Change-Id: Ie218a95aca07ca430a52e55d8a630a509b908df5
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Treehugger Robot 2024-11-21 01:23:48 +00:00 committed by Automerger Merge Worker
commit c9b0fa4ef6
10 changed files with 120 additions and 160 deletions

View file

@ -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",

View file

@ -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"),

View file

@ -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)
}

View file

@ -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())

View file

@ -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()

View file

@ -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()

View file

@ -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 {

View file

@ -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())

View file

@ -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.

View file

@ -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__)