76b06f3973
Implement APEX stub of API surface so any stub can be replaced with API surface when APEX stub interface is required. Unlike other stub interface, APEX stub can be decided if it should be used after APEX postdeps mutator analyzes which modules should be included in which APEX. To cover this, APEX stub is being added to the dependency if the dependency should not be covered with LLNDK or NDK stub, and APEX stub exists. From depsToPaths, if dependency to both original module and API library exists, then it choose one of the dependency and ignore the other. To cover this logic, a new property is added to the api_surface : apex_libs. This is introduced as it is difficult to gather all api library with apex stub before DepsMutator. Bug: 264963986 Test: cf_x86_64_phone_vendor build succeeded Change-Id: I9f0b1f70968e32eba94d3e0d7bb1f4bb29ff2438
509 lines
15 KiB
Go
509 lines
15 KiB
Go
// 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 (
|
|
"regexp"
|
|
"strings"
|
|
|
|
"android/soong/android"
|
|
"android/soong/multitree"
|
|
)
|
|
|
|
var (
|
|
ndkVariantRegex = regexp.MustCompile("ndk\\.([a-zA-Z0-9]+)")
|
|
stubVariantRegex = regexp.MustCompile("apex\\.([a-zA-Z0-9]+)")
|
|
)
|
|
|
|
func init() {
|
|
RegisterLibraryStubBuildComponents(android.InitRegistrationContext)
|
|
}
|
|
|
|
func RegisterLibraryStubBuildComponents(ctx android.RegistrationContext) {
|
|
ctx.RegisterModuleType("cc_api_library", CcApiLibraryFactory)
|
|
ctx.RegisterModuleType("cc_api_headers", CcApiHeadersFactory)
|
|
ctx.RegisterModuleType("cc_api_variant", CcApiVariantFactory)
|
|
}
|
|
|
|
func updateImportedLibraryDependency(ctx android.BottomUpMutatorContext) {
|
|
m, ok := ctx.Module().(*Module)
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
apiLibrary, ok := m.linker.(*apiLibraryDecorator)
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
if m.UseVndk() && apiLibrary.hasLLNDKStubs() {
|
|
// Add LLNDK variant dependency
|
|
if inList("llndk", apiLibrary.properties.Variants) {
|
|
variantName := BuildApiVariantName(m.BaseModuleName(), "llndk", "")
|
|
ctx.AddDependency(m, nil, variantName)
|
|
}
|
|
} else if m.IsSdkVariant() {
|
|
// Add NDK variant dependencies
|
|
targetVariant := "ndk." + m.StubsVersion()
|
|
if inList(targetVariant, apiLibrary.properties.Variants) {
|
|
variantName := BuildApiVariantName(m.BaseModuleName(), targetVariant, "")
|
|
ctx.AddDependency(m, nil, variantName)
|
|
}
|
|
} else if m.IsStubs() {
|
|
targetVariant := "apex." + m.StubsVersion()
|
|
if inList(targetVariant, apiLibrary.properties.Variants) {
|
|
variantName := BuildApiVariantName(m.BaseModuleName(), targetVariant, "")
|
|
ctx.AddDependency(m, nil, variantName)
|
|
}
|
|
}
|
|
}
|
|
|
|
// 'cc_api_library' is a module type which is from the exported API surface
|
|
// with C shared library type. The module will replace original module, and
|
|
// offer a link to the module that generates shared library object from the
|
|
// map file.
|
|
type apiLibraryProperties struct {
|
|
Src *string `android:"arch_variant"`
|
|
Variants []string
|
|
}
|
|
|
|
type apiLibraryDecorator struct {
|
|
*libraryDecorator
|
|
properties apiLibraryProperties
|
|
}
|
|
|
|
func CcApiLibraryFactory() android.Module {
|
|
module, decorator := NewLibrary(android.DeviceSupported)
|
|
apiLibraryDecorator := &apiLibraryDecorator{
|
|
libraryDecorator: decorator,
|
|
}
|
|
apiLibraryDecorator.BuildOnlyShared()
|
|
|
|
module.stl = nil
|
|
module.sanitize = nil
|
|
decorator.disableStripping()
|
|
|
|
module.compiler = nil
|
|
module.linker = apiLibraryDecorator
|
|
module.installer = nil
|
|
module.library = apiLibraryDecorator
|
|
module.AddProperties(&module.Properties, &apiLibraryDecorator.properties)
|
|
|
|
// Prevent default system libs (libc, libm, and libdl) from being linked
|
|
if apiLibraryDecorator.baseLinker.Properties.System_shared_libs == nil {
|
|
apiLibraryDecorator.baseLinker.Properties.System_shared_libs = []string{}
|
|
}
|
|
|
|
apiLibraryDecorator.baseLinker.Properties.No_libcrt = BoolPtr(true)
|
|
apiLibraryDecorator.baseLinker.Properties.Nocrt = BoolPtr(true)
|
|
|
|
module.Init()
|
|
|
|
return module
|
|
}
|
|
|
|
func (d *apiLibraryDecorator) Name(basename string) string {
|
|
return basename + multitree.GetApiImportSuffix()
|
|
}
|
|
|
|
// Export include dirs without checking for existence.
|
|
// The directories are not guaranteed to exist during Soong analysis.
|
|
func (d *apiLibraryDecorator) exportIncludes(ctx ModuleContext) {
|
|
exporterProps := d.flagExporter.Properties
|
|
for _, dir := range exporterProps.Export_include_dirs {
|
|
d.dirs = append(d.dirs, android.MaybeExistentPathForSource(ctx, ctx.ModuleDir(), dir))
|
|
}
|
|
// system headers
|
|
for _, dir := range exporterProps.Export_system_include_dirs {
|
|
d.systemDirs = append(d.systemDirs, android.MaybeExistentPathForSource(ctx, ctx.ModuleDir(), dir))
|
|
}
|
|
}
|
|
|
|
func (d *apiLibraryDecorator) linkerInit(ctx BaseModuleContext) {
|
|
d.baseLinker.linkerInit(ctx)
|
|
|
|
if d.hasNDKStubs() {
|
|
// Set SDK version of module as current
|
|
ctx.Module().(*Module).Properties.Sdk_version = StringPtr("current")
|
|
|
|
// Add NDK stub as NDK known libs
|
|
name := ctx.ModuleName()
|
|
|
|
ndkKnownLibsLock.Lock()
|
|
ndkKnownLibs := getNDKKnownLibs(ctx.Config())
|
|
if !inList(name, *ndkKnownLibs) {
|
|
*ndkKnownLibs = append(*ndkKnownLibs, name)
|
|
}
|
|
ndkKnownLibsLock.Unlock()
|
|
}
|
|
}
|
|
|
|
func (d *apiLibraryDecorator) link(ctx ModuleContext, flags Flags, deps PathDeps, objects Objects) android.Path {
|
|
m, _ := ctx.Module().(*Module)
|
|
|
|
var in android.Path
|
|
|
|
// src might not exist during the beginning of soong analysis in Multi-tree
|
|
if src := String(d.properties.Src); src != "" {
|
|
in = android.MaybeExistentPathForSource(ctx, ctx.ModuleDir(), src)
|
|
}
|
|
|
|
libName := m.BaseModuleName() + multitree.GetApiImportSuffix()
|
|
|
|
load_cc_variant := func(apiVariantModule string) {
|
|
var mod android.Module
|
|
|
|
ctx.VisitDirectDeps(func(depMod android.Module) {
|
|
if depMod.Name() == apiVariantModule {
|
|
mod = depMod
|
|
libName = apiVariantModule
|
|
}
|
|
})
|
|
|
|
if mod != nil {
|
|
variantMod, ok := mod.(*CcApiVariant)
|
|
if ok {
|
|
in = variantMod.Src()
|
|
|
|
// Copy LLDNK properties to cc_api_library module
|
|
d.libraryDecorator.flagExporter.Properties.Export_include_dirs = append(
|
|
d.libraryDecorator.flagExporter.Properties.Export_include_dirs,
|
|
variantMod.exportProperties.Export_include_dirs...)
|
|
|
|
// Export headers as system include dirs if specified. Mostly for libc
|
|
if Bool(variantMod.exportProperties.Export_headers_as_system) {
|
|
d.libraryDecorator.flagExporter.Properties.Export_system_include_dirs = append(
|
|
d.libraryDecorator.flagExporter.Properties.Export_system_include_dirs,
|
|
d.libraryDecorator.flagExporter.Properties.Export_include_dirs...)
|
|
d.libraryDecorator.flagExporter.Properties.Export_include_dirs = nil
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if m.UseVndk() && d.hasLLNDKStubs() {
|
|
// LLNDK variant
|
|
load_cc_variant(BuildApiVariantName(m.BaseModuleName(), "llndk", ""))
|
|
} else if m.IsSdkVariant() {
|
|
// NDK Variant
|
|
load_cc_variant(BuildApiVariantName(m.BaseModuleName(), "ndk", m.StubsVersion()))
|
|
} else if m.IsStubs() {
|
|
// APEX Variant
|
|
load_cc_variant(BuildApiVariantName(m.BaseModuleName(), "apex", m.StubsVersion()))
|
|
}
|
|
|
|
// Flags reexported from dependencies. (e.g. vndk_prebuilt_shared)
|
|
d.exportIncludes(ctx)
|
|
d.libraryDecorator.reexportDirs(deps.ReexportedDirs...)
|
|
d.libraryDecorator.reexportSystemDirs(deps.ReexportedSystemDirs...)
|
|
d.libraryDecorator.reexportFlags(deps.ReexportedFlags...)
|
|
d.libraryDecorator.reexportDeps(deps.ReexportedDeps...)
|
|
d.libraryDecorator.addExportedGeneratedHeaders(deps.ReexportedGeneratedHeaders...)
|
|
|
|
if in == nil {
|
|
ctx.PropertyErrorf("src", "Unable to locate source property")
|
|
return nil
|
|
}
|
|
|
|
// Make the _compilation_ of rdeps have an order-only dep on cc_api_library.src (an .so file)
|
|
// The .so file itself has an order-only dependency on the headers contributed by this library.
|
|
// Creating this dependency ensures that the headers are assembled before compilation of rdeps begins.
|
|
d.libraryDecorator.reexportDeps(in)
|
|
d.libraryDecorator.flagExporter.setProvider(ctx)
|
|
|
|
d.unstrippedOutputFile = in
|
|
libName += flags.Toolchain.ShlibSuffix()
|
|
|
|
tocFile := android.PathForModuleOut(ctx, libName+".toc")
|
|
d.tocFile = android.OptionalPathForPath(tocFile)
|
|
TransformSharedObjectToToc(ctx, in, tocFile)
|
|
|
|
outputFile := android.PathForModuleOut(ctx, libName)
|
|
|
|
// TODO(b/270485584) This copies with a new name, just to avoid conflict with prebuilts.
|
|
// We can just use original input if there is any way to avoid name conflict without copy.
|
|
ctx.Build(pctx, android.BuildParams{
|
|
Rule: android.Cp,
|
|
Description: "API surface imported library",
|
|
Input: in,
|
|
Output: outputFile,
|
|
Args: map[string]string{
|
|
"cpFlags": "-L",
|
|
},
|
|
})
|
|
|
|
ctx.SetProvider(SharedLibraryInfoProvider, SharedLibraryInfo{
|
|
SharedLibrary: outputFile,
|
|
Target: ctx.Target(),
|
|
|
|
TableOfContents: d.tocFile,
|
|
})
|
|
|
|
d.shareStubs(ctx)
|
|
|
|
return outputFile
|
|
}
|
|
|
|
// Share additional information about stub libraries with provider
|
|
func (d *apiLibraryDecorator) shareStubs(ctx ModuleContext) {
|
|
stubs := ctx.GetDirectDepsWithTag(stubImplDepTag)
|
|
if len(stubs) > 0 {
|
|
var stubsInfo []SharedStubLibrary
|
|
for _, stub := range stubs {
|
|
stubInfo := ctx.OtherModuleProvider(stub, SharedLibraryInfoProvider).(SharedLibraryInfo)
|
|
flagInfo := ctx.OtherModuleProvider(stub, FlagExporterInfoProvider).(FlagExporterInfo)
|
|
stubsInfo = append(stubsInfo, SharedStubLibrary{
|
|
Version: moduleLibraryInterface(stub).stubsVersion(),
|
|
SharedLibraryInfo: stubInfo,
|
|
FlagExporterInfo: flagInfo,
|
|
})
|
|
}
|
|
ctx.SetProvider(SharedLibraryStubsProvider, SharedLibraryStubsInfo{
|
|
SharedStubLibraries: stubsInfo,
|
|
|
|
IsLLNDK: ctx.IsLlndk(),
|
|
})
|
|
}
|
|
}
|
|
|
|
func (d *apiLibraryDecorator) availableFor(what string) bool {
|
|
// Stub from API surface should be available for any APEX.
|
|
return true
|
|
}
|
|
|
|
func (d *apiLibraryDecorator) hasApexStubs() bool {
|
|
for _, variant := range d.properties.Variants {
|
|
if strings.HasPrefix(variant, "apex") {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (d *apiLibraryDecorator) hasStubsVariants() bool {
|
|
return d.hasApexStubs()
|
|
}
|
|
|
|
func (d *apiLibraryDecorator) stubsVersions(ctx android.BaseMutatorContext) []string {
|
|
m, ok := ctx.Module().(*Module)
|
|
|
|
if !ok {
|
|
return nil
|
|
}
|
|
|
|
// TODO(b/244244438) Create more version information for NDK and APEX variations
|
|
// NDK variants
|
|
if m.IsSdkVariant() {
|
|
// TODO(b/249193999) Do not check if module has NDK stubs once all NDK cc_api_library contains ndk variant of cc_api_variant.
|
|
if d.hasNDKStubs() {
|
|
return d.getNdkVersions()
|
|
}
|
|
}
|
|
|
|
if d.hasLLNDKStubs() && m.UseVndk() {
|
|
// LLNDK libraries only need a single stubs variant.
|
|
return []string{android.FutureApiLevel.String()}
|
|
}
|
|
|
|
stubsVersions := d.getStubVersions()
|
|
|
|
if len(stubsVersions) != 0 {
|
|
return stubsVersions
|
|
}
|
|
|
|
if m.MinSdkVersion() == "" {
|
|
return nil
|
|
}
|
|
|
|
firstVersion, err := nativeApiLevelFromUser(ctx,
|
|
m.MinSdkVersion())
|
|
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
|
|
return ndkLibraryVersions(ctx, firstVersion)
|
|
}
|
|
|
|
func (d *apiLibraryDecorator) hasLLNDKStubs() bool {
|
|
return inList("llndk", d.properties.Variants)
|
|
}
|
|
|
|
func (d *apiLibraryDecorator) hasNDKStubs() bool {
|
|
for _, variant := range d.properties.Variants {
|
|
if ndkVariantRegex.MatchString(variant) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (d *apiLibraryDecorator) getNdkVersions() []string {
|
|
ndkVersions := []string{}
|
|
|
|
for _, variant := range d.properties.Variants {
|
|
if match := ndkVariantRegex.FindStringSubmatch(variant); len(match) == 2 {
|
|
ndkVersions = append(ndkVersions, match[1])
|
|
}
|
|
}
|
|
|
|
return ndkVersions
|
|
}
|
|
|
|
func (d *apiLibraryDecorator) getStubVersions() []string {
|
|
stubVersions := []string{}
|
|
|
|
for _, variant := range d.properties.Variants {
|
|
if match := stubVariantRegex.FindStringSubmatch(variant); len(match) == 2 {
|
|
stubVersions = append(stubVersions, match[1])
|
|
}
|
|
}
|
|
|
|
return stubVersions
|
|
}
|
|
|
|
// 'cc_api_headers' is similar with 'cc_api_library', but which replaces
|
|
// header libraries. The module will replace any dependencies to existing
|
|
// original header libraries.
|
|
type apiHeadersDecorator struct {
|
|
*libraryDecorator
|
|
}
|
|
|
|
func CcApiHeadersFactory() android.Module {
|
|
module, decorator := NewLibrary(android.DeviceSupported)
|
|
apiHeadersDecorator := &apiHeadersDecorator{
|
|
libraryDecorator: decorator,
|
|
}
|
|
apiHeadersDecorator.HeaderOnly()
|
|
|
|
module.stl = nil
|
|
module.sanitize = nil
|
|
decorator.disableStripping()
|
|
|
|
module.compiler = nil
|
|
module.linker = apiHeadersDecorator
|
|
module.installer = nil
|
|
|
|
// Prevent default system libs (libc, libm, and libdl) from being linked
|
|
if apiHeadersDecorator.baseLinker.Properties.System_shared_libs == nil {
|
|
apiHeadersDecorator.baseLinker.Properties.System_shared_libs = []string{}
|
|
}
|
|
|
|
apiHeadersDecorator.baseLinker.Properties.No_libcrt = BoolPtr(true)
|
|
apiHeadersDecorator.baseLinker.Properties.Nocrt = BoolPtr(true)
|
|
|
|
module.Init()
|
|
|
|
return module
|
|
}
|
|
|
|
func (d *apiHeadersDecorator) Name(basename string) string {
|
|
return basename + multitree.GetApiImportSuffix()
|
|
}
|
|
|
|
func (d *apiHeadersDecorator) availableFor(what string) bool {
|
|
// Stub from API surface should be available for any APEX.
|
|
return true
|
|
}
|
|
|
|
type ccApiexportProperties struct {
|
|
Src *string `android:"arch_variant"`
|
|
Variant *string
|
|
Version *string
|
|
}
|
|
|
|
type variantExporterProperties struct {
|
|
// Header directory to export
|
|
Export_include_dirs []string `android:"arch_variant"`
|
|
|
|
// Export all headers as system include
|
|
Export_headers_as_system *bool
|
|
}
|
|
|
|
type CcApiVariant struct {
|
|
android.ModuleBase
|
|
|
|
properties ccApiexportProperties
|
|
exportProperties variantExporterProperties
|
|
|
|
src android.Path
|
|
}
|
|
|
|
var _ android.Module = (*CcApiVariant)(nil)
|
|
var _ android.ImageInterface = (*CcApiVariant)(nil)
|
|
|
|
func CcApiVariantFactory() android.Module {
|
|
module := &CcApiVariant{}
|
|
|
|
module.AddProperties(&module.properties)
|
|
module.AddProperties(&module.exportProperties)
|
|
|
|
android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibBoth)
|
|
return module
|
|
}
|
|
|
|
func (v *CcApiVariant) GenerateAndroidBuildActions(ctx android.ModuleContext) {
|
|
// No need to build
|
|
|
|
if String(v.properties.Src) == "" {
|
|
ctx.PropertyErrorf("src", "src is a required property")
|
|
}
|
|
|
|
// Skip the existence check of the stub prebuilt file.
|
|
// The file is not guaranteed to exist during Soong analysis.
|
|
// Build orchestrator will be responsible for creating a connected ninja graph.
|
|
v.src = android.MaybeExistentPathForSource(ctx, ctx.ModuleDir(), String(v.properties.Src))
|
|
}
|
|
|
|
func (v *CcApiVariant) Name() string {
|
|
version := String(v.properties.Version)
|
|
return BuildApiVariantName(v.BaseModuleName(), *v.properties.Variant, version)
|
|
}
|
|
|
|
func (v *CcApiVariant) Src() android.Path {
|
|
return v.src
|
|
}
|
|
|
|
func BuildApiVariantName(baseName string, variant string, version string) string {
|
|
names := []string{baseName, variant}
|
|
if version != "" {
|
|
names = append(names, version)
|
|
}
|
|
|
|
return strings.Join(names[:], ".") + multitree.GetApiImportSuffix()
|
|
}
|
|
|
|
// Implement ImageInterface to generate image variants
|
|
func (v *CcApiVariant) ImageMutatorBegin(ctx android.BaseModuleContext) {}
|
|
func (v *CcApiVariant) CoreVariantNeeded(ctx android.BaseModuleContext) bool {
|
|
return inList(String(v.properties.Variant), []string{"ndk", "apex"})
|
|
}
|
|
func (v *CcApiVariant) RamdiskVariantNeeded(ctx android.BaseModuleContext) bool { return false }
|
|
func (v *CcApiVariant) VendorRamdiskVariantNeeded(ctx android.BaseModuleContext) bool { return false }
|
|
func (v *CcApiVariant) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool { return false }
|
|
func (v *CcApiVariant) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool { return false }
|
|
func (v *CcApiVariant) ExtraImageVariations(ctx android.BaseModuleContext) []string {
|
|
var variations []string
|
|
platformVndkVersion := ctx.DeviceConfig().PlatformVndkVersion()
|
|
|
|
if String(v.properties.Variant) == "llndk" {
|
|
variations = append(variations, VendorVariationPrefix+platformVndkVersion)
|
|
variations = append(variations, ProductVariationPrefix+platformVndkVersion)
|
|
}
|
|
|
|
return variations
|
|
}
|
|
func (v *CcApiVariant) SetImageVariation(ctx android.BaseModuleContext, variation string, module android.Module) {
|
|
}
|