Merge "Use libabigail to track NDK ABIs."
This commit is contained in:
@@ -1566,7 +1566,10 @@ type archConfig struct {
|
|||||||
abi []string
|
abi []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// getNdkAbisConfig returns a list of archConfigs for the ABIs supported by the NDK.
|
// getNdkAbisConfig returns the list of archConfigs that are used for bulding
|
||||||
|
// the API stubs and static libraries that are included in the NDK. These are
|
||||||
|
// built *without Neon*, because non-Neon is still supported and building these
|
||||||
|
// with Neon will break those users.
|
||||||
func getNdkAbisConfig() []archConfig {
|
func getNdkAbisConfig() []archConfig {
|
||||||
return []archConfig{
|
return []archConfig{
|
||||||
{"arm", "armv7-a", "", []string{"armeabi-v7a"}},
|
{"arm", "armv7-a", "", []string{"armeabi-v7a"}},
|
||||||
|
@@ -65,9 +65,10 @@ bootstrap_go_package {
|
|||||||
"test.go",
|
"test.go",
|
||||||
"toolchain_library.go",
|
"toolchain_library.go",
|
||||||
|
|
||||||
"ndk_prebuilt.go",
|
"ndk_abi.go",
|
||||||
"ndk_headers.go",
|
"ndk_headers.go",
|
||||||
"ndk_library.go",
|
"ndk_library.go",
|
||||||
|
"ndk_prebuilt.go",
|
||||||
"ndk_sysroot.go",
|
"ndk_sysroot.go",
|
||||||
|
|
||||||
"llndk_library.go",
|
"llndk_library.go",
|
||||||
|
9
cc/cc.go
9
cc/cc.go
@@ -2117,6 +2117,15 @@ func (c *Module) DepsMutator(actx android.BottomUpMutatorContext) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if c.isNDKStubLibrary() {
|
||||||
|
// NDK stubs depend on their implementation because the ABI dumps are
|
||||||
|
// generated from the implementation library.
|
||||||
|
actx.AddFarVariationDependencies(append(ctx.Target().Variations(),
|
||||||
|
c.ImageVariation(),
|
||||||
|
blueprint.Variation{Mutator: "link", Variation: "shared"},
|
||||||
|
), stubImplementation, c.BaseModuleName())
|
||||||
|
}
|
||||||
|
|
||||||
// sysprop_library has to support both C++ and Java. So sysprop_library internally creates one
|
// sysprop_library has to support both C++ and Java. So sysprop_library internally creates one
|
||||||
// C++ implementation library and one Java implementation library. When a module links against
|
// C++ implementation library and one Java implementation library. When a module links against
|
||||||
// sysprop_library, the C++ implementation library has to be linked. syspropImplLibraries is a
|
// sysprop_library, the C++ implementation library has to be linked. syspropImplLibraries is a
|
||||||
|
@@ -865,16 +865,23 @@ func (library *libraryDecorator) compile(ctx ModuleContext, flags Flags, deps Pa
|
|||||||
if library.stubsVersion() != "" {
|
if library.stubsVersion() != "" {
|
||||||
vndkVer = library.stubsVersion()
|
vndkVer = library.stubsVersion()
|
||||||
}
|
}
|
||||||
objs, versionScript := compileStubLibrary(ctx, flags, String(library.Properties.Llndk.Symbol_file), vndkVer, "--llndk")
|
nativeAbiResult := parseNativeAbiDefinition(ctx,
|
||||||
|
String(library.Properties.Llndk.Symbol_file),
|
||||||
|
android.ApiLevelOrPanic(ctx, vndkVer), "--llndk")
|
||||||
|
objs := compileStubLibrary(ctx, flags, nativeAbiResult.stubSrc)
|
||||||
if !Bool(library.Properties.Llndk.Unversioned) {
|
if !Bool(library.Properties.Llndk.Unversioned) {
|
||||||
library.versionScriptPath = android.OptionalPathForPath(versionScript)
|
library.versionScriptPath = android.OptionalPathForPath(
|
||||||
|
nativeAbiResult.versionScript)
|
||||||
}
|
}
|
||||||
return objs
|
return objs
|
||||||
}
|
}
|
||||||
if ctx.IsVendorPublicLibrary() {
|
if ctx.IsVendorPublicLibrary() {
|
||||||
objs, versionScript := compileStubLibrary(ctx, flags, String(library.Properties.Vendor_public_library.Symbol_file), "current", "")
|
nativeAbiResult := parseNativeAbiDefinition(ctx,
|
||||||
|
String(library.Properties.Vendor_public_library.Symbol_file),
|
||||||
|
android.FutureApiLevel, "")
|
||||||
|
objs := compileStubLibrary(ctx, flags, nativeAbiResult.stubSrc)
|
||||||
if !Bool(library.Properties.Vendor_public_library.Unversioned) {
|
if !Bool(library.Properties.Vendor_public_library.Unversioned) {
|
||||||
library.versionScriptPath = android.OptionalPathForPath(versionScript)
|
library.versionScriptPath = android.OptionalPathForPath(nativeAbiResult.versionScript)
|
||||||
}
|
}
|
||||||
return objs
|
return objs
|
||||||
}
|
}
|
||||||
@@ -884,8 +891,12 @@ func (library *libraryDecorator) compile(ctx ModuleContext, flags Flags, deps Pa
|
|||||||
ctx.PropertyErrorf("symbol_file", "%q doesn't have .map.txt suffix", symbolFile)
|
ctx.PropertyErrorf("symbol_file", "%q doesn't have .map.txt suffix", symbolFile)
|
||||||
return Objects{}
|
return Objects{}
|
||||||
}
|
}
|
||||||
objs, versionScript := compileStubLibrary(ctx, flags, String(library.Properties.Stubs.Symbol_file), library.MutatedProperties.StubsVersion, "--apex")
|
nativeAbiResult := parseNativeAbiDefinition(ctx, symbolFile,
|
||||||
library.versionScriptPath = android.OptionalPathForPath(versionScript)
|
android.ApiLevelOrPanic(ctx, library.MutatedProperties.StubsVersion),
|
||||||
|
"--apex")
|
||||||
|
objs := compileStubLibrary(ctx, flags, nativeAbiResult.stubSrc)
|
||||||
|
library.versionScriptPath = android.OptionalPathForPath(
|
||||||
|
nativeAbiResult.versionScript)
|
||||||
return objs
|
return objs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
102
cc/ndk_abi.go
Normal file
102
cc/ndk_abi.go
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
// Copyright 2021 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 cc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"android/soong/android"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
android.RegisterSingletonType("ndk_abi_dump", NdkAbiDumpSingleton)
|
||||||
|
android.RegisterSingletonType("ndk_abi_diff", NdkAbiDiffSingleton)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getNdkAbiDumpInstallBase(ctx android.PathContext) android.OutputPath {
|
||||||
|
return android.PathForOutput(ctx).Join(ctx, "abi-dumps/ndk")
|
||||||
|
}
|
||||||
|
|
||||||
|
func getNdkAbiDumpTimestampFile(ctx android.PathContext) android.OutputPath {
|
||||||
|
return android.PathForOutput(ctx, "ndk_abi_dump.timestamp")
|
||||||
|
}
|
||||||
|
|
||||||
|
func NdkAbiDumpSingleton() android.Singleton {
|
||||||
|
return &ndkAbiDumpSingleton{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ndkAbiDumpSingleton struct{}
|
||||||
|
|
||||||
|
func (n *ndkAbiDumpSingleton) GenerateBuildActions(ctx android.SingletonContext) {
|
||||||
|
var depPaths android.Paths
|
||||||
|
ctx.VisitAllModules(func(module android.Module) {
|
||||||
|
if !module.Enabled() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if m, ok := module.(*Module); ok {
|
||||||
|
if installer, ok := m.installer.(*stubDecorator); ok {
|
||||||
|
if canDumpAbi(m) {
|
||||||
|
depPaths = append(depPaths, installer.abiDumpPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// `m dump-ndk-abi` will dump the NDK ABI.
|
||||||
|
// `development/tools/ndk/update_ndk_abi.sh` will dump the NDK ABI and
|
||||||
|
// update the golden copies in prebuilts/abi-dumps/ndk.
|
||||||
|
ctx.Build(pctx, android.BuildParams{
|
||||||
|
Rule: android.Touch,
|
||||||
|
Output: getNdkAbiDumpTimestampFile(ctx),
|
||||||
|
Implicits: depPaths,
|
||||||
|
})
|
||||||
|
|
||||||
|
ctx.Phony("dump-ndk-abi", getNdkAbiDumpTimestampFile(ctx))
|
||||||
|
}
|
||||||
|
|
||||||
|
func getNdkAbiDiffTimestampFile(ctx android.PathContext) android.WritablePath {
|
||||||
|
return android.PathForOutput(ctx, "ndk_abi_diff.timestamp")
|
||||||
|
}
|
||||||
|
|
||||||
|
func NdkAbiDiffSingleton() android.Singleton {
|
||||||
|
return &ndkAbiDiffSingleton{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ndkAbiDiffSingleton struct{}
|
||||||
|
|
||||||
|
func (n *ndkAbiDiffSingleton) GenerateBuildActions(ctx android.SingletonContext) {
|
||||||
|
var depPaths android.Paths
|
||||||
|
ctx.VisitAllModules(func(module android.Module) {
|
||||||
|
if m, ok := module.(android.Module); ok && !m.Enabled() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if m, ok := module.(*Module); ok {
|
||||||
|
if installer, ok := m.installer.(*stubDecorator); ok {
|
||||||
|
depPaths = append(depPaths, installer.abiDiffPaths...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
depPaths = append(depPaths, getNdkAbiDumpTimestampFile(ctx))
|
||||||
|
|
||||||
|
// `m diff-ndk-abi` will diff the NDK ABI.
|
||||||
|
ctx.Build(pctx, android.BuildParams{
|
||||||
|
Rule: android.Touch,
|
||||||
|
Output: getNdkAbiDiffTimestampFile(ctx),
|
||||||
|
Implicits: depPaths,
|
||||||
|
})
|
||||||
|
|
||||||
|
ctx.Phony("diff-ndk-abi", getNdkAbiDiffTimestampFile(ctx))
|
||||||
|
}
|
@@ -16,6 +16,7 @@ package cc
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
@@ -28,6 +29,9 @@ import (
|
|||||||
func init() {
|
func init() {
|
||||||
pctx.HostBinToolVariable("ndkStubGenerator", "ndkstubgen")
|
pctx.HostBinToolVariable("ndkStubGenerator", "ndkstubgen")
|
||||||
pctx.HostBinToolVariable("ndk_api_coverage_parser", "ndk_api_coverage_parser")
|
pctx.HostBinToolVariable("ndk_api_coverage_parser", "ndk_api_coverage_parser")
|
||||||
|
pctx.HostBinToolVariable("abidiff", "abidiff")
|
||||||
|
pctx.HostBinToolVariable("abitidy", "abitidy")
|
||||||
|
pctx.HostBinToolVariable("abidw", "abidw")
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -44,11 +48,31 @@ var (
|
|||||||
CommandDeps: []string{"$ndk_api_coverage_parser"},
|
CommandDeps: []string{"$ndk_api_coverage_parser"},
|
||||||
}, "apiMap")
|
}, "apiMap")
|
||||||
|
|
||||||
|
abidw = pctx.AndroidStaticRule("abidw",
|
||||||
|
blueprint.RuleParams{
|
||||||
|
Command: "$abidw --type-id-style hash --no-corpus-path " +
|
||||||
|
"--no-show-locs --no-comp-dir-path -w $symbolList $in | " +
|
||||||
|
"$abitidy --all -o $out",
|
||||||
|
CommandDeps: []string{"$abitidy", "$abidw"},
|
||||||
|
}, "symbolList")
|
||||||
|
|
||||||
|
abidiff = pctx.AndroidStaticRule("abidiff",
|
||||||
|
blueprint.RuleParams{
|
||||||
|
// Need to create *some* output for ninja. We don't want to use tee
|
||||||
|
// because we don't want to spam the build output with "nothing
|
||||||
|
// changed" messages, so redirect output message to $out, and if
|
||||||
|
// changes were detected print the output and fail.
|
||||||
|
Command: "$abidiff $args $in > $out || (cat $out && false)",
|
||||||
|
CommandDeps: []string{"$abidiff"},
|
||||||
|
}, "args")
|
||||||
|
|
||||||
ndkLibrarySuffix = ".ndk"
|
ndkLibrarySuffix = ".ndk"
|
||||||
|
|
||||||
ndkKnownLibsKey = android.NewOnceKey("ndkKnownLibsKey")
|
ndkKnownLibsKey = android.NewOnceKey("ndkKnownLibsKey")
|
||||||
// protects ndkKnownLibs writes during parallel BeginMutator.
|
// protects ndkKnownLibs writes during parallel BeginMutator.
|
||||||
ndkKnownLibsLock sync.Mutex
|
ndkKnownLibsLock sync.Mutex
|
||||||
|
|
||||||
|
stubImplementation = dependencyTag{name: "stubImplementation"}
|
||||||
)
|
)
|
||||||
|
|
||||||
// The First_version and Unversioned_until properties of this struct should not
|
// The First_version and Unversioned_until properties of this struct should not
|
||||||
@@ -89,6 +113,8 @@ type stubDecorator struct {
|
|||||||
versionScriptPath android.ModuleGenPath
|
versionScriptPath android.ModuleGenPath
|
||||||
parsedCoverageXmlPath android.ModuleOutPath
|
parsedCoverageXmlPath android.ModuleOutPath
|
||||||
installPath android.Path
|
installPath android.Path
|
||||||
|
abiDumpPath android.OutputPath
|
||||||
|
abiDiffPaths android.Paths
|
||||||
|
|
||||||
apiLevel android.ApiLevel
|
apiLevel android.ApiLevel
|
||||||
firstVersion android.ApiLevel
|
firstVersion android.ApiLevel
|
||||||
@@ -123,6 +149,16 @@ func (this *stubDecorator) stubsVersions(ctx android.BaseMutatorContext) []strin
|
|||||||
if !ctx.Module().Enabled() {
|
if !ctx.Module().Enabled() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
if ctx.Os() != android.Android {
|
||||||
|
// These modules are always android.DeviceEnabled only, but
|
||||||
|
// those include Fuchsia devices, which we don't support.
|
||||||
|
ctx.Module().Disable()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if ctx.Target().NativeBridge == android.NativeBridgeEnabled {
|
||||||
|
ctx.Module().Disable()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
firstVersion, err := nativeApiLevelFromUser(ctx,
|
firstVersion, err := nativeApiLevelFromUser(ctx,
|
||||||
String(this.properties.First_version))
|
String(this.properties.First_version))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -204,30 +240,45 @@ func (stub *stubDecorator) compilerFlags(ctx ModuleContext, flags Flags, deps Pa
|
|||||||
return addStubLibraryCompilerFlags(flags)
|
return addStubLibraryCompilerFlags(flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
func compileStubLibrary(ctx ModuleContext, flags Flags, symbolFile, apiLevel, genstubFlags string) (Objects, android.ModuleGenPath) {
|
type ndkApiOutputs struct {
|
||||||
arch := ctx.Arch().ArchType.String()
|
stubSrc android.ModuleGenPath
|
||||||
|
versionScript android.ModuleGenPath
|
||||||
|
symbolList android.ModuleGenPath
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseNativeAbiDefinition(ctx ModuleContext, symbolFile string,
|
||||||
|
apiLevel android.ApiLevel, genstubFlags string) ndkApiOutputs {
|
||||||
|
|
||||||
stubSrcPath := android.PathForModuleGen(ctx, "stub.c")
|
stubSrcPath := android.PathForModuleGen(ctx, "stub.c")
|
||||||
versionScriptPath := android.PathForModuleGen(ctx, "stub.map")
|
versionScriptPath := android.PathForModuleGen(ctx, "stub.map")
|
||||||
symbolFilePath := android.PathForModuleSrc(ctx, symbolFile)
|
symbolFilePath := android.PathForModuleSrc(ctx, symbolFile)
|
||||||
|
symbolListPath := android.PathForModuleGen(ctx, "abi_symbol_list.txt")
|
||||||
apiLevelsJson := android.GetApiLevelsJson(ctx)
|
apiLevelsJson := android.GetApiLevelsJson(ctx)
|
||||||
ctx.Build(pctx, android.BuildParams{
|
ctx.Build(pctx, android.BuildParams{
|
||||||
Rule: genStubSrc,
|
Rule: genStubSrc,
|
||||||
Description: "generate stubs " + symbolFilePath.Rel(),
|
Description: "generate stubs " + symbolFilePath.Rel(),
|
||||||
Outputs: []android.WritablePath{stubSrcPath, versionScriptPath},
|
Outputs: []android.WritablePath{stubSrcPath, versionScriptPath,
|
||||||
Input: symbolFilePath,
|
symbolListPath},
|
||||||
Implicits: []android.Path{apiLevelsJson},
|
Input: symbolFilePath,
|
||||||
|
Implicits: []android.Path{apiLevelsJson},
|
||||||
Args: map[string]string{
|
Args: map[string]string{
|
||||||
"arch": arch,
|
"arch": ctx.Arch().ArchType.String(),
|
||||||
"apiLevel": apiLevel,
|
"apiLevel": apiLevel.String(),
|
||||||
"apiMap": apiLevelsJson.String(),
|
"apiMap": apiLevelsJson.String(),
|
||||||
"flags": genstubFlags,
|
"flags": genstubFlags,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
subdir := ""
|
return ndkApiOutputs{
|
||||||
srcs := []android.Path{stubSrcPath}
|
stubSrc: stubSrcPath,
|
||||||
return compileObjs(ctx, flagsToBuilderFlags(flags), subdir, srcs, nil, nil), versionScriptPath
|
versionScript: versionScriptPath,
|
||||||
|
symbolList: symbolListPath,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func compileStubLibrary(ctx ModuleContext, flags Flags, src android.Path) Objects {
|
||||||
|
return compileObjs(ctx, flagsToBuilderFlags(flags), "",
|
||||||
|
android.Paths{src}, nil, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseSymbolFileForCoverage(ctx ModuleContext, symbolFile string) android.ModuleOutPath {
|
func parseSymbolFileForCoverage(ctx ModuleContext, symbolFile string) android.ModuleOutPath {
|
||||||
@@ -248,6 +299,140 @@ func parseSymbolFileForCoverage(ctx ModuleContext, symbolFile string) android.Mo
|
|||||||
return parsedApiCoveragePath
|
return parsedApiCoveragePath
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *stubDecorator) findImplementationLibrary(ctx ModuleContext) android.Path {
|
||||||
|
dep := ctx.GetDirectDepWithTag(strings.TrimSuffix(ctx.ModuleName(), ndkLibrarySuffix),
|
||||||
|
stubImplementation)
|
||||||
|
if dep == nil {
|
||||||
|
ctx.ModuleErrorf("Could not find implementation for stub")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
impl, ok := dep.(*Module)
|
||||||
|
if !ok {
|
||||||
|
ctx.ModuleErrorf("Implementation for stub is not correct module type")
|
||||||
|
}
|
||||||
|
output := impl.UnstrippedOutputFile()
|
||||||
|
if output == nil {
|
||||||
|
ctx.ModuleErrorf("implementation module (%s) has no output", impl)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return output
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *stubDecorator) libraryName(ctx ModuleContext) string {
|
||||||
|
return strings.TrimSuffix(ctx.ModuleName(), ndkLibrarySuffix)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *stubDecorator) findPrebuiltAbiDump(ctx ModuleContext,
|
||||||
|
apiLevel android.ApiLevel) android.OptionalPath {
|
||||||
|
|
||||||
|
subpath := filepath.Join("prebuilts/abi-dumps/ndk", apiLevel.String(),
|
||||||
|
ctx.Arch().ArchType.String(), this.libraryName(ctx), "abi.xml")
|
||||||
|
return android.ExistentPathForSource(ctx, subpath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Feature flag.
|
||||||
|
func canDumpAbi(module android.Module) bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Feature flag to disable diffing against prebuilts.
|
||||||
|
func canDiffAbi(module android.Module) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *stubDecorator) dumpAbi(ctx ModuleContext, symbolList android.Path) {
|
||||||
|
implementationLibrary := this.findImplementationLibrary(ctx)
|
||||||
|
this.abiDumpPath = getNdkAbiDumpInstallBase(ctx).Join(ctx,
|
||||||
|
this.apiLevel.String(), ctx.Arch().ArchType.String(),
|
||||||
|
this.libraryName(ctx), "abi.xml")
|
||||||
|
ctx.Build(pctx, android.BuildParams{
|
||||||
|
Rule: abidw,
|
||||||
|
Description: fmt.Sprintf("abidw %s", implementationLibrary),
|
||||||
|
Output: this.abiDumpPath,
|
||||||
|
Input: implementationLibrary,
|
||||||
|
Implicit: symbolList,
|
||||||
|
Args: map[string]string{
|
||||||
|
"symbolList": symbolList.String(),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func findNextApiLevel(ctx ModuleContext, apiLevel android.ApiLevel) *android.ApiLevel {
|
||||||
|
apiLevels := append(ctx.Config().AllSupportedApiLevels(),
|
||||||
|
android.FutureApiLevel)
|
||||||
|
for _, api := range apiLevels {
|
||||||
|
if api.GreaterThan(apiLevel) {
|
||||||
|
return &api
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *stubDecorator) diffAbi(ctx ModuleContext) {
|
||||||
|
missingPrebuiltError := fmt.Sprintf(
|
||||||
|
"Did not find prebuilt ABI dump for %q. Generate with "+
|
||||||
|
"//development/tools/ndk/update_ndk_abi.sh.", this.libraryName(ctx))
|
||||||
|
|
||||||
|
// Catch any ABI changes compared to the checked-in definition of this API
|
||||||
|
// level.
|
||||||
|
abiDiffPath := android.PathForModuleOut(ctx, "abidiff.timestamp")
|
||||||
|
prebuiltAbiDump := this.findPrebuiltAbiDump(ctx, this.apiLevel)
|
||||||
|
if !prebuiltAbiDump.Valid() {
|
||||||
|
ctx.Build(pctx, android.BuildParams{
|
||||||
|
Rule: android.ErrorRule,
|
||||||
|
Output: abiDiffPath,
|
||||||
|
Args: map[string]string{
|
||||||
|
"error": missingPrebuiltError,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
ctx.Build(pctx, android.BuildParams{
|
||||||
|
Rule: abidiff,
|
||||||
|
Description: fmt.Sprintf("abidiff %s %s", prebuiltAbiDump,
|
||||||
|
this.abiDumpPath),
|
||||||
|
Output: abiDiffPath,
|
||||||
|
Inputs: android.Paths{prebuiltAbiDump.Path(), this.abiDumpPath},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.abiDiffPaths = append(this.abiDiffPaths, abiDiffPath)
|
||||||
|
|
||||||
|
// Also ensure that the ABI of the next API level (if there is one) matches
|
||||||
|
// this API level. *New* ABI is allowed, but any changes to APIs that exist
|
||||||
|
// in this API level are disallowed.
|
||||||
|
if !this.apiLevel.IsCurrent() {
|
||||||
|
nextApiLevel := findNextApiLevel(ctx, this.apiLevel)
|
||||||
|
if nextApiLevel == nil {
|
||||||
|
panic(fmt.Errorf("could not determine which API level follows "+
|
||||||
|
"non-current API level %s", this.apiLevel))
|
||||||
|
}
|
||||||
|
nextAbiDiffPath := android.PathForModuleOut(ctx,
|
||||||
|
"abidiff_next.timestamp")
|
||||||
|
nextAbiDump := this.findPrebuiltAbiDump(ctx, *nextApiLevel)
|
||||||
|
if !nextAbiDump.Valid() {
|
||||||
|
ctx.Build(pctx, android.BuildParams{
|
||||||
|
Rule: android.ErrorRule,
|
||||||
|
Output: nextAbiDiffPath,
|
||||||
|
Args: map[string]string{
|
||||||
|
"error": missingPrebuiltError,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
ctx.Build(pctx, android.BuildParams{
|
||||||
|
Rule: abidiff,
|
||||||
|
Description: fmt.Sprintf("abidiff %s %s", this.abiDumpPath,
|
||||||
|
nextAbiDump),
|
||||||
|
Output: nextAbiDiffPath,
|
||||||
|
Inputs: android.Paths{this.abiDumpPath, nextAbiDump.Path()},
|
||||||
|
Args: map[string]string{
|
||||||
|
"args": "--no-added-syms",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.abiDiffPaths = append(this.abiDiffPaths, nextAbiDiffPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (c *stubDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) Objects {
|
func (c *stubDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) Objects {
|
||||||
if !strings.HasSuffix(String(c.properties.Symbol_file), ".map.txt") {
|
if !strings.HasSuffix(String(c.properties.Symbol_file), ".map.txt") {
|
||||||
ctx.PropertyErrorf("symbol_file", "must end with .map.txt")
|
ctx.PropertyErrorf("symbol_file", "must end with .map.txt")
|
||||||
@@ -264,9 +449,15 @@ func (c *stubDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) O
|
|||||||
}
|
}
|
||||||
|
|
||||||
symbolFile := String(c.properties.Symbol_file)
|
symbolFile := String(c.properties.Symbol_file)
|
||||||
objs, versionScript := compileStubLibrary(ctx, flags, symbolFile,
|
nativeAbiResult := parseNativeAbiDefinition(ctx, symbolFile, c.apiLevel, "")
|
||||||
c.apiLevel.String(), "")
|
objs := compileStubLibrary(ctx, flags, nativeAbiResult.stubSrc)
|
||||||
c.versionScriptPath = versionScript
|
c.versionScriptPath = nativeAbiResult.versionScript
|
||||||
|
if canDumpAbi(ctx.Module()) {
|
||||||
|
c.dumpAbi(ctx, nativeAbiResult.symbolList)
|
||||||
|
if canDiffAbi(ctx.Module()) {
|
||||||
|
c.diffAbi(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
if c.apiLevel.IsCurrent() && ctx.PrimaryArch() {
|
if c.apiLevel.IsCurrent() && ctx.PrimaryArch() {
|
||||||
c.parsedCoverageXmlPath = parseSymbolFileForCoverage(ctx, symbolFile)
|
c.parsedCoverageXmlPath = parseSymbolFileForCoverage(ctx, symbolFile)
|
||||||
}
|
}
|
||||||
|
@@ -144,10 +144,9 @@ func (n *ndkSingleton) GenerateBuildActions(ctx android.SingletonContext) {
|
|||||||
Inputs: licensePaths,
|
Inputs: licensePaths,
|
||||||
})
|
})
|
||||||
|
|
||||||
baseDepPaths := append(installPaths, combinedLicense)
|
baseDepPaths := append(installPaths, combinedLicense,
|
||||||
|
getNdkAbiDiffTimestampFile(ctx))
|
||||||
|
|
||||||
// There's a dummy "ndk" rule defined in ndk/Android.mk that depends on
|
|
||||||
// this. `m ndk` will build the sysroots.
|
|
||||||
ctx.Build(pctx, android.BuildParams{
|
ctx.Build(pctx, android.BuildParams{
|
||||||
Rule: android.Touch,
|
Rule: android.Touch,
|
||||||
Output: getNdkBaseTimestampFile(ctx),
|
Output: getNdkBaseTimestampFile(ctx),
|
||||||
@@ -156,6 +155,11 @@ func (n *ndkSingleton) GenerateBuildActions(ctx android.SingletonContext) {
|
|||||||
|
|
||||||
fullDepPaths := append(staticLibInstallPaths, getNdkBaseTimestampFile(ctx))
|
fullDepPaths := append(staticLibInstallPaths, getNdkBaseTimestampFile(ctx))
|
||||||
|
|
||||||
|
// There's a phony "ndk" rule defined in core/main.mk that depends on this.
|
||||||
|
// `m ndk` will build the sysroots for the architectures in the current
|
||||||
|
// lunch target. `build/soong/scripts/build-ndk-prebuilts.sh` will build the
|
||||||
|
// sysroots for all the NDK architectures and package them so they can be
|
||||||
|
// imported into the NDK's build.
|
||||||
ctx.Build(pctx, android.BuildParams{
|
ctx.Build(pctx, android.BuildParams{
|
||||||
Rule: android.Touch,
|
Rule: android.Touch,
|
||||||
Output: getNdkFullTimestampFile(ctx),
|
Output: getNdkFullTimestampFile(ctx),
|
||||||
|
@@ -18,7 +18,7 @@
|
|||||||
import argparse
|
import argparse
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
from pathlib import Path
|
||||||
import sys
|
import sys
|
||||||
from typing import Iterable, TextIO
|
from typing import Iterable, TextIO
|
||||||
|
|
||||||
@@ -28,10 +28,12 @@ from symbolfile import Arch, Version
|
|||||||
|
|
||||||
class Generator:
|
class Generator:
|
||||||
"""Output generator that writes stub source files and version scripts."""
|
"""Output generator that writes stub source files and version scripts."""
|
||||||
def __init__(self, src_file: TextIO, version_script: TextIO, arch: Arch,
|
def __init__(self, src_file: TextIO, version_script: TextIO,
|
||||||
api: int, llndk: bool, apex: bool) -> None:
|
symbol_list: TextIO, arch: Arch, api: int, llndk: bool,
|
||||||
|
apex: bool) -> None:
|
||||||
self.src_file = src_file
|
self.src_file = src_file
|
||||||
self.version_script = version_script
|
self.version_script = version_script
|
||||||
|
self.symbol_list = symbol_list
|
||||||
self.arch = arch
|
self.arch = arch
|
||||||
self.api = api
|
self.api = api
|
||||||
self.llndk = llndk
|
self.llndk = llndk
|
||||||
@@ -39,6 +41,7 @@ class Generator:
|
|||||||
|
|
||||||
def write(self, versions: Iterable[Version]) -> None:
|
def write(self, versions: Iterable[Version]) -> None:
|
||||||
"""Writes all symbol data to the output files."""
|
"""Writes all symbol data to the output files."""
|
||||||
|
self.symbol_list.write('[abi_symbol_list]\n')
|
||||||
for version in versions:
|
for version in versions:
|
||||||
self.write_version(version)
|
self.write_version(version)
|
||||||
|
|
||||||
@@ -76,11 +79,11 @@ class Generator:
|
|||||||
weak = '__attribute__((weak)) '
|
weak = '__attribute__((weak)) '
|
||||||
|
|
||||||
if 'var' in symbol.tags:
|
if 'var' in symbol.tags:
|
||||||
self.src_file.write('{}int {} = 0;\n'.format(
|
self.src_file.write(f'{weak}int {symbol.name} = 0;\n')
|
||||||
weak, symbol.name))
|
|
||||||
else:
|
else:
|
||||||
self.src_file.write('{}void {}() {{}}\n'.format(
|
self.src_file.write(f'{weak}void {symbol.name}() {{}}\n')
|
||||||
weak, symbol.name))
|
|
||||||
|
self.symbol_list.write(f'{symbol.name}\n')
|
||||||
|
|
||||||
if not version_empty and section_versioned:
|
if not version_empty and section_versioned:
|
||||||
base = '' if version.base is None else ' ' + version.base
|
base = '' if version.base is None else ' ' + version.base
|
||||||
@@ -91,6 +94,10 @@ def parse_args() -> argparse.Namespace:
|
|||||||
"""Parses and returns command line arguments."""
|
"""Parses and returns command line arguments."""
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
|
|
||||||
|
def resolved_path(raw: str) -> Path:
|
||||||
|
"""Returns a resolved Path for the given string."""
|
||||||
|
return Path(raw).resolve()
|
||||||
|
|
||||||
parser.add_argument('-v', '--verbose', action='count', default=0)
|
parser.add_argument('-v', '--verbose', action='count', default=0)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
@@ -103,26 +110,23 @@ def parse_args() -> argparse.Namespace:
|
|||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--apex', action='store_true', help='Use the APEX variant.')
|
'--apex', action='store_true', help='Use the APEX variant.')
|
||||||
|
|
||||||
# https://github.com/python/mypy/issues/1317
|
parser.add_argument('--api-map',
|
||||||
# mypy has issues with using os.path.realpath as an argument here.
|
type=resolved_path,
|
||||||
parser.add_argument(
|
required=True,
|
||||||
'--api-map',
|
help='Path to the API level map JSON file.')
|
||||||
type=os.path.realpath, # type: ignore
|
|
||||||
required=True,
|
|
||||||
help='Path to the API level map JSON file.')
|
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument('symbol_file',
|
||||||
'symbol_file',
|
type=resolved_path,
|
||||||
type=os.path.realpath, # type: ignore
|
help='Path to symbol file.')
|
||||||
help='Path to symbol file.')
|
parser.add_argument('stub_src',
|
||||||
parser.add_argument(
|
type=resolved_path,
|
||||||
'stub_src',
|
help='Path to output stub source file.')
|
||||||
type=os.path.realpath, # type: ignore
|
parser.add_argument('version_script',
|
||||||
help='Path to output stub source file.')
|
type=resolved_path,
|
||||||
parser.add_argument(
|
help='Path to output version script.')
|
||||||
'version_script',
|
parser.add_argument('symbol_list',
|
||||||
type=os.path.realpath, # type: ignore
|
type=resolved_path,
|
||||||
help='Path to output version script.')
|
help='Path to output abigail symbol list.')
|
||||||
|
|
||||||
return parser.parse_args()
|
return parser.parse_args()
|
||||||
|
|
||||||
@@ -131,7 +135,7 @@ def main() -> None:
|
|||||||
"""Program entry point."""
|
"""Program entry point."""
|
||||||
args = parse_args()
|
args = parse_args()
|
||||||
|
|
||||||
with open(args.api_map) as map_file:
|
with args.api_map.open() as map_file:
|
||||||
api_map = json.load(map_file)
|
api_map = json.load(map_file)
|
||||||
api = symbolfile.decode_api_level(args.api, api_map)
|
api = symbolfile.decode_api_level(args.api, api_map)
|
||||||
|
|
||||||
@@ -141,19 +145,20 @@ def main() -> None:
|
|||||||
verbosity = 2
|
verbosity = 2
|
||||||
logging.basicConfig(level=verbose_map[verbosity])
|
logging.basicConfig(level=verbose_map[verbosity])
|
||||||
|
|
||||||
with open(args.symbol_file) as symbol_file:
|
with args.symbol_file.open() as symbol_file:
|
||||||
try:
|
try:
|
||||||
versions = symbolfile.SymbolFileParser(symbol_file, api_map,
|
versions = symbolfile.SymbolFileParser(symbol_file, api_map,
|
||||||
args.arch, api, args.llndk,
|
args.arch, api, args.llndk,
|
||||||
args.apex).parse()
|
args.apex).parse()
|
||||||
except symbolfile.MultiplyDefinedSymbolError as ex:
|
except symbolfile.MultiplyDefinedSymbolError as ex:
|
||||||
sys.exit('{}: error: {}'.format(args.symbol_file, ex))
|
sys.exit(f'{args.symbol_file}: error: {ex}')
|
||||||
|
|
||||||
with open(args.stub_src, 'w') as src_file:
|
with args.stub_src.open('w') as src_file:
|
||||||
with open(args.version_script, 'w') as version_file:
|
with args.version_script.open('w') as version_script:
|
||||||
generator = Generator(src_file, version_file, args.arch, api,
|
with args.symbol_list.open('w') as symbol_list:
|
||||||
args.llndk, args.apex)
|
generator = Generator(src_file, version_script, symbol_list,
|
||||||
generator.write(versions)
|
args.arch, api, args.llndk, args.apex)
|
||||||
|
generator.write(versions)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
@@ -33,8 +33,10 @@ class GeneratorTest(unittest.TestCase):
|
|||||||
# OmitVersionTest, PrivateVersionTest, and SymbolPresenceTest.
|
# OmitVersionTest, PrivateVersionTest, and SymbolPresenceTest.
|
||||||
src_file = io.StringIO()
|
src_file = io.StringIO()
|
||||||
version_file = io.StringIO()
|
version_file = io.StringIO()
|
||||||
generator = ndkstubgen.Generator(src_file, version_file, Arch('arm'),
|
symbol_list_file = io.StringIO()
|
||||||
9, False, False)
|
generator = ndkstubgen.Generator(src_file,
|
||||||
|
version_file, symbol_list_file,
|
||||||
|
Arch('arm'), 9, False, False)
|
||||||
|
|
||||||
version = symbolfile.Version('VERSION_PRIVATE', None, [], [
|
version = symbolfile.Version('VERSION_PRIVATE', None, [], [
|
||||||
symbolfile.Symbol('foo', []),
|
symbolfile.Symbol('foo', []),
|
||||||
@@ -62,8 +64,10 @@ class GeneratorTest(unittest.TestCase):
|
|||||||
# SymbolPresenceTest.
|
# SymbolPresenceTest.
|
||||||
src_file = io.StringIO()
|
src_file = io.StringIO()
|
||||||
version_file = io.StringIO()
|
version_file = io.StringIO()
|
||||||
generator = ndkstubgen.Generator(src_file, version_file, Arch('arm'),
|
symbol_list_file = io.StringIO()
|
||||||
9, False, False)
|
generator = ndkstubgen.Generator(src_file,
|
||||||
|
version_file, symbol_list_file,
|
||||||
|
Arch('arm'), 9, False, False)
|
||||||
|
|
||||||
version = symbolfile.Version('VERSION_1', None, [], [
|
version = symbolfile.Version('VERSION_1', None, [], [
|
||||||
symbolfile.Symbol('foo', [Tag('x86')]),
|
symbolfile.Symbol('foo', [Tag('x86')]),
|
||||||
@@ -96,8 +100,10 @@ class GeneratorTest(unittest.TestCase):
|
|||||||
def test_write(self) -> None:
|
def test_write(self) -> None:
|
||||||
src_file = io.StringIO()
|
src_file = io.StringIO()
|
||||||
version_file = io.StringIO()
|
version_file = io.StringIO()
|
||||||
generator = ndkstubgen.Generator(src_file, version_file, Arch('arm'),
|
symbol_list_file = io.StringIO()
|
||||||
9, False, False)
|
generator = ndkstubgen.Generator(src_file,
|
||||||
|
version_file, symbol_list_file,
|
||||||
|
Arch('arm'), 9, False, False)
|
||||||
|
|
||||||
versions = [
|
versions = [
|
||||||
symbolfile.Version('VERSION_1', None, [], [
|
symbolfile.Version('VERSION_1', None, [], [
|
||||||
@@ -141,6 +147,17 @@ class GeneratorTest(unittest.TestCase):
|
|||||||
""")
|
""")
|
||||||
self.assertEqual(expected_version, version_file.getvalue())
|
self.assertEqual(expected_version, version_file.getvalue())
|
||||||
|
|
||||||
|
expected_allowlist = textwrap.dedent("""\
|
||||||
|
[abi_symbol_list]
|
||||||
|
foo
|
||||||
|
bar
|
||||||
|
woodly
|
||||||
|
doodly
|
||||||
|
baz
|
||||||
|
qux
|
||||||
|
""")
|
||||||
|
self.assertEqual(expected_allowlist, symbol_list_file.getvalue())
|
||||||
|
|
||||||
|
|
||||||
class IntegrationTest(unittest.TestCase):
|
class IntegrationTest(unittest.TestCase):
|
||||||
def test_integration(self) -> None:
|
def test_integration(self) -> None:
|
||||||
@@ -186,8 +203,10 @@ class IntegrationTest(unittest.TestCase):
|
|||||||
|
|
||||||
src_file = io.StringIO()
|
src_file = io.StringIO()
|
||||||
version_file = io.StringIO()
|
version_file = io.StringIO()
|
||||||
generator = ndkstubgen.Generator(src_file, version_file, Arch('arm'),
|
symbol_list_file = io.StringIO()
|
||||||
9, False, False)
|
generator = ndkstubgen.Generator(src_file,
|
||||||
|
version_file, symbol_list_file,
|
||||||
|
Arch('arm'), 9, False, False)
|
||||||
generator.write(versions)
|
generator.write(versions)
|
||||||
|
|
||||||
expected_src = textwrap.dedent("""\
|
expected_src = textwrap.dedent("""\
|
||||||
@@ -215,6 +234,16 @@ class IntegrationTest(unittest.TestCase):
|
|||||||
""")
|
""")
|
||||||
self.assertEqual(expected_version, version_file.getvalue())
|
self.assertEqual(expected_version, version_file.getvalue())
|
||||||
|
|
||||||
|
expected_allowlist = textwrap.dedent("""\
|
||||||
|
[abi_symbol_list]
|
||||||
|
foo
|
||||||
|
baz
|
||||||
|
qux
|
||||||
|
wibble
|
||||||
|
wobble
|
||||||
|
""")
|
||||||
|
self.assertEqual(expected_allowlist, symbol_list_file.getvalue())
|
||||||
|
|
||||||
def test_integration_future_api(self) -> None:
|
def test_integration_future_api(self) -> None:
|
||||||
api_map = {
|
api_map = {
|
||||||
'O': 9000,
|
'O': 9000,
|
||||||
@@ -238,8 +267,10 @@ class IntegrationTest(unittest.TestCase):
|
|||||||
|
|
||||||
src_file = io.StringIO()
|
src_file = io.StringIO()
|
||||||
version_file = io.StringIO()
|
version_file = io.StringIO()
|
||||||
generator = ndkstubgen.Generator(src_file, version_file, Arch('arm'),
|
symbol_list_file = io.StringIO()
|
||||||
9001, False, False)
|
generator = ndkstubgen.Generator(src_file,
|
||||||
|
version_file, symbol_list_file,
|
||||||
|
Arch('arm'), 9001, False, False)
|
||||||
generator.write(versions)
|
generator.write(versions)
|
||||||
|
|
||||||
expected_src = textwrap.dedent("""\
|
expected_src = textwrap.dedent("""\
|
||||||
@@ -257,6 +288,13 @@ class IntegrationTest(unittest.TestCase):
|
|||||||
""")
|
""")
|
||||||
self.assertEqual(expected_version, version_file.getvalue())
|
self.assertEqual(expected_version, version_file.getvalue())
|
||||||
|
|
||||||
|
expected_allowlist = textwrap.dedent("""\
|
||||||
|
[abi_symbol_list]
|
||||||
|
foo
|
||||||
|
bar
|
||||||
|
""")
|
||||||
|
self.assertEqual(expected_allowlist, symbol_list_file.getvalue())
|
||||||
|
|
||||||
def test_multiple_definition(self) -> None:
|
def test_multiple_definition(self) -> None:
|
||||||
input_file = io.StringIO(textwrap.dedent("""\
|
input_file = io.StringIO(textwrap.dedent("""\
|
||||||
VERSION_1 {
|
VERSION_1 {
|
||||||
@@ -336,8 +374,10 @@ class IntegrationTest(unittest.TestCase):
|
|||||||
|
|
||||||
src_file = io.StringIO()
|
src_file = io.StringIO()
|
||||||
version_file = io.StringIO()
|
version_file = io.StringIO()
|
||||||
generator = ndkstubgen.Generator(src_file, version_file, Arch('arm'),
|
symbol_list_file = io.StringIO()
|
||||||
9, False, True)
|
generator = ndkstubgen.Generator(src_file,
|
||||||
|
version_file, symbol_list_file,
|
||||||
|
Arch('arm'), 9, False, True)
|
||||||
generator.write(versions)
|
generator.write(versions)
|
||||||
|
|
||||||
expected_src = textwrap.dedent("""\
|
expected_src = textwrap.dedent("""\
|
||||||
|
Reference in New Issue
Block a user