blob: 9643ec2a193310acd9fa215a4b00d240b9a97c35 [file] [log] [blame]
// 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"
"github.com/google/blueprint/proptools"
)
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.InVendorOrProduct() && 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.GetOrDefault(ctx, nil) {
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
exportIncludeDirs := append(d.libraryDecorator.flagExporter.Properties.Export_include_dirs.GetOrDefault(ctx, nil),
variantMod.exportProperties.Export_include_dirs...)
d.libraryDecorator.flagExporter.Properties.Export_include_dirs = proptools.NewConfigurable[[]string](
nil,
[]proptools.ConfigurableCase[[]string]{
proptools.NewConfigurableCase[[]string](nil, &exportIncludeDirs),
},
)
// 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.GetOrDefault(ctx, nil)...)
d.libraryDecorator.flagExporter.Properties.Export_include_dirs = proptools.NewConfigurable[[]string](nil, nil)
}
}
}
}
if m.InVendorOrProduct() && 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",
},
})
android.SetProvider(ctx, 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, _ := android.OtherModuleProvider(ctx, stub, SharedLibraryInfoProvider)
flagInfo, _ := android.OtherModuleProvider(ctx, stub, FlagExporterInfoProvider)
stubsInfo = append(stubsInfo, SharedStubLibrary{
Version: moduleLibraryInterface(stub).stubsVersion(),
SharedLibraryInfo: stubInfo,
FlagExporterInfo: flagInfo,
})
}
android.SetProvider(ctx, 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.InVendorOrProduct() {
// 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
if String(v.properties.Variant) == "llndk" {
variations = append(variations, VendorVariation)
variations = append(variations, ProductVariation)
}
return variations
}
func (v *CcApiVariant) SetImageVariation(ctx android.BaseModuleContext, variation string) {
}