| // 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) { |
| } |