Add vendor snapshot modules
This implements four modules (static/shared/header libraries, and
binaries) for vendor snapshot. These modules will override source
modules if BOARD_VNDK_VERSION != current.
Bug: 65377115
Test: 1) VNDK_SNAPSHOT_BUILD_ARTIFACTS=true m dist vndk vendor-snapshot
Test: 2) install snapshot under source tree
Test: 3) set BOARD_VNDK_VERSION and boot cuttlefish
Change-Id: I24ddb4c3aa6abeab60bbfd31bcbd8753e2592dc5
diff --git a/cc/vendor_snapshot.go b/cc/vendor_snapshot.go
index d952a4c..d92caa1 100644
--- a/cc/vendor_snapshot.go
+++ b/cc/vendor_snapshot.go
@@ -18,14 +18,358 @@
"path/filepath"
"sort"
"strings"
+ "sync"
"github.com/google/blueprint/proptools"
"android/soong/android"
)
+const (
+ vendorSnapshotHeaderSuffix = ".vendor_header."
+ vendorSnapshotSharedSuffix = ".vendor_shared."
+ vendorSnapshotStaticSuffix = ".vendor_static."
+ vendorSnapshotBinarySuffix = ".vendor_binary."
+)
+
+var (
+ vendorSnapshotsLock sync.Mutex
+ vendorSuffixModulesKey = android.NewOnceKey("vendorSuffixModules")
+ vendorSnapshotHeaderLibsKey = android.NewOnceKey("vendorSnapshotHeaderLibs")
+ vendorSnapshotStaticLibsKey = android.NewOnceKey("vendorSnapshotStaticLibs")
+ vendorSnapshotSharedLibsKey = android.NewOnceKey("vendorSnapshotSharedLibs")
+ vendorSnapshotBinariesKey = android.NewOnceKey("vendorSnapshotBinaries")
+)
+
+// vendor snapshot maps hold names of vendor snapshot modules per arch.
+func vendorSuffixModules(config android.Config) map[string]bool {
+ return config.Once(vendorSuffixModulesKey, func() interface{} {
+ return make(map[string]bool)
+ }).(map[string]bool)
+}
+
+func vendorSnapshotHeaderLibs(config android.Config) *snapshotMap {
+ return config.Once(vendorSnapshotHeaderLibsKey, func() interface{} {
+ return newSnapshotMap()
+ }).(*snapshotMap)
+}
+
+func vendorSnapshotSharedLibs(config android.Config) *snapshotMap {
+ return config.Once(vendorSnapshotSharedLibsKey, func() interface{} {
+ return newSnapshotMap()
+ }).(*snapshotMap)
+}
+
+func vendorSnapshotStaticLibs(config android.Config) *snapshotMap {
+ return config.Once(vendorSnapshotStaticLibsKey, func() interface{} {
+ return newSnapshotMap()
+ }).(*snapshotMap)
+}
+
+func vendorSnapshotBinaries(config android.Config) *snapshotMap {
+ return config.Once(vendorSnapshotBinariesKey, func() interface{} {
+ return newSnapshotMap()
+ }).(*snapshotMap)
+}
+
+type vendorSnapshotLibraryProperties struct {
+ // snapshot version.
+ Version string
+
+ // Target arch name of the snapshot (e.g. 'arm64' for variant 'aosp_arm64')
+ Target_arch string
+
+ // Prebuilt file for each arch.
+ Src *string `android:"arch_variant"`
+
+ // list of flags that will be used for any module that links against this module.
+ Export_flags []string `android:"arch_variant"`
+
+ // Check the prebuilt ELF files (e.g. DT_SONAME, DT_NEEDED, resolution of undefined symbols,
+ // etc).
+ Check_elf_files *bool
+
+ // Whether this prebuilt needs to depend on sanitize ubsan runtime or not.
+ Sanitize_ubsan_dep *bool `android:"arch_variant"`
+
+ // Whether this prebuilt needs to depend on sanitize minimal runtime or not.
+ Sanitize_minimal_dep *bool `android:"arch_variant"`
+}
+
+type vendorSnapshotLibraryDecorator struct {
+ *libraryDecorator
+ properties vendorSnapshotLibraryProperties
+ androidMkVendorSuffix bool
+}
+
+func (p *vendorSnapshotLibraryDecorator) Name(name string) string {
+ return name + p.NameSuffix()
+}
+
+func (p *vendorSnapshotLibraryDecorator) NameSuffix() string {
+ versionSuffix := p.version()
+ if p.arch() != "" {
+ versionSuffix += "." + p.arch()
+ }
+
+ var linkageSuffix string
+ if p.buildShared() {
+ linkageSuffix = vendorSnapshotSharedSuffix
+ } else if p.buildStatic() {
+ linkageSuffix = vendorSnapshotStaticSuffix
+ } else {
+ linkageSuffix = vendorSnapshotHeaderSuffix
+ }
+
+ return linkageSuffix + versionSuffix
+}
+
+func (p *vendorSnapshotLibraryDecorator) version() string {
+ return p.properties.Version
+}
+
+func (p *vendorSnapshotLibraryDecorator) arch() string {
+ return p.properties.Target_arch
+}
+
+func (p *vendorSnapshotLibraryDecorator) linkerFlags(ctx ModuleContext, flags Flags) Flags {
+ p.libraryDecorator.libName = strings.TrimSuffix(ctx.ModuleName(), p.NameSuffix())
+ return p.libraryDecorator.linkerFlags(ctx, flags)
+}
+
+func (p *vendorSnapshotLibraryDecorator) matchesWithDevice(config android.DeviceConfig) bool {
+ arches := config.Arches()
+ if len(arches) == 0 || arches[0].ArchType.String() != p.arch() {
+ return false
+ }
+ if !p.header() && p.properties.Src == nil {
+ return false
+ }
+ return true
+}
+
+func (p *vendorSnapshotLibraryDecorator) link(ctx ModuleContext,
+ flags Flags, deps PathDeps, objs Objects) android.Path {
+ m := ctx.Module().(*Module)
+ p.androidMkVendorSuffix = vendorSuffixModules(ctx.Config())[m.BaseModuleName()]
+
+ if p.header() {
+ return p.libraryDecorator.link(ctx, flags, deps, objs)
+ }
+
+ if !p.matchesWithDevice(ctx.DeviceConfig()) {
+ return nil
+ }
+
+ p.libraryDecorator.exportIncludes(ctx)
+ p.libraryDecorator.reexportFlags(p.properties.Export_flags...)
+
+ in := android.PathForModuleSrc(ctx, *p.properties.Src)
+ p.unstrippedOutputFile = in
+
+ if p.shared() {
+ libName := in.Base()
+ builderFlags := flagsToBuilderFlags(flags)
+
+ // Optimize out relinking against shared libraries whose interface hasn't changed by
+ // depending on a table of contents file instead of the library itself.
+ tocFile := android.PathForModuleOut(ctx, libName+".toc")
+ p.tocFile = android.OptionalPathForPath(tocFile)
+ TransformSharedObjectToToc(ctx, in, tocFile, builderFlags)
+ }
+
+ return in
+}
+
+func (p *vendorSnapshotLibraryDecorator) nativeCoverage() bool {
+ return false
+}
+
+func (p *vendorSnapshotLibraryDecorator) install(ctx ModuleContext, file android.Path) {
+ if p.matchesWithDevice(ctx.DeviceConfig()) && (p.shared() || p.static()) {
+ p.baseInstaller.install(ctx, file)
+ }
+}
+
+type vendorSnapshotInterface interface {
+ version() string
+}
+
+func vendorSnapshotLoadHook(ctx android.LoadHookContext, p vendorSnapshotInterface) {
+ if p.version() != ctx.DeviceConfig().VndkVersion() {
+ ctx.Module().Disable()
+ return
+ }
+}
+
+func vendorSnapshotLibrary() (*Module, *vendorSnapshotLibraryDecorator) {
+ module, library := NewLibrary(android.DeviceSupported)
+
+ module.stl = nil
+ module.sanitize = nil
+ library.StripProperties.Strip.None = BoolPtr(true)
+
+ prebuilt := &vendorSnapshotLibraryDecorator{
+ libraryDecorator: library,
+ }
+
+ prebuilt.baseLinker.Properties.No_libcrt = BoolPtr(true)
+ prebuilt.baseLinker.Properties.Nocrt = BoolPtr(true)
+
+ // Prevent default system libs (libc, libm, and libdl) from being linked
+ if prebuilt.baseLinker.Properties.System_shared_libs == nil {
+ prebuilt.baseLinker.Properties.System_shared_libs = []string{}
+ }
+
+ module.compiler = nil
+ module.linker = prebuilt
+ module.installer = prebuilt
+
+ module.AddProperties(
+ &prebuilt.properties,
+ )
+
+ return module, prebuilt
+}
+
+func VendorSnapshotSharedFactory() android.Module {
+ module, prebuilt := vendorSnapshotLibrary()
+ prebuilt.libraryDecorator.BuildOnlyShared()
+ android.AddLoadHook(module, func(ctx android.LoadHookContext) {
+ vendorSnapshotLoadHook(ctx, prebuilt)
+ })
+ return module.Init()
+}
+
+func VendorSnapshotStaticFactory() android.Module {
+ module, prebuilt := vendorSnapshotLibrary()
+ prebuilt.libraryDecorator.BuildOnlyStatic()
+ android.AddLoadHook(module, func(ctx android.LoadHookContext) {
+ vendorSnapshotLoadHook(ctx, prebuilt)
+ })
+ return module.Init()
+}
+
+func VendorSnapshotHeaderFactory() android.Module {
+ module, prebuilt := vendorSnapshotLibrary()
+ prebuilt.libraryDecorator.HeaderOnly()
+ android.AddLoadHook(module, func(ctx android.LoadHookContext) {
+ vendorSnapshotLoadHook(ctx, prebuilt)
+ })
+ return module.Init()
+}
+
+type vendorSnapshotBinaryProperties struct {
+ // snapshot version.
+ Version string
+
+ // Target arch name of the snapshot (e.g. 'arm64' for variant 'aosp_arm64_ab')
+ Target_arch string
+
+ // Prebuilt file for each arch.
+ Src *string `android:"arch_variant"`
+}
+
+type vendorSnapshotBinaryDecorator struct {
+ *binaryDecorator
+ properties vendorSnapshotBinaryProperties
+ androidMkVendorSuffix bool
+}
+
+func (p *vendorSnapshotBinaryDecorator) Name(name string) string {
+ return name + p.NameSuffix()
+}
+
+func (p *vendorSnapshotBinaryDecorator) NameSuffix() string {
+ versionSuffix := p.version()
+ if p.arch() != "" {
+ versionSuffix += "." + p.arch()
+ }
+ return vendorSnapshotBinarySuffix + versionSuffix
+}
+
+func (p *vendorSnapshotBinaryDecorator) version() string {
+ return p.properties.Version
+}
+
+func (p *vendorSnapshotBinaryDecorator) arch() string {
+ return p.properties.Target_arch
+}
+
+func (p *vendorSnapshotBinaryDecorator) matchesWithDevice(config android.DeviceConfig) bool {
+ if config.DeviceArch() != p.arch() {
+ return false
+ }
+ if p.properties.Src == nil {
+ return false
+ }
+ return true
+}
+
+func (p *vendorSnapshotBinaryDecorator) link(ctx ModuleContext,
+ flags Flags, deps PathDeps, objs Objects) android.Path {
+ if !p.matchesWithDevice(ctx.DeviceConfig()) {
+ return nil
+ }
+
+ in := android.PathForModuleSrc(ctx, *p.properties.Src)
+ builderFlags := flagsToBuilderFlags(flags)
+ p.unstrippedOutputFile = in
+ binName := in.Base()
+ if p.needsStrip(ctx) {
+ stripped := android.PathForModuleOut(ctx, "stripped", binName)
+ p.stripExecutableOrSharedLib(ctx, in, stripped, builderFlags)
+ in = stripped
+ }
+
+ m := ctx.Module().(*Module)
+ p.androidMkVendorSuffix = vendorSuffixModules(ctx.Config())[m.BaseModuleName()]
+
+ // use cpExecutable to make it executable
+ outputFile := android.PathForModuleOut(ctx, binName)
+ ctx.Build(pctx, android.BuildParams{
+ Rule: android.CpExecutable,
+ Description: "prebuilt",
+ Output: outputFile,
+ Input: in,
+ })
+
+ return outputFile
+}
+
+func VendorSnapshotBinaryFactory() android.Module {
+ module, binary := NewBinary(android.DeviceSupported)
+ binary.baseLinker.Properties.No_libcrt = BoolPtr(true)
+ binary.baseLinker.Properties.Nocrt = BoolPtr(true)
+
+ // Prevent default system libs (libc, libm, and libdl) from being linked
+ if binary.baseLinker.Properties.System_shared_libs == nil {
+ binary.baseLinker.Properties.System_shared_libs = []string{}
+ }
+
+ prebuilt := &vendorSnapshotBinaryDecorator{
+ binaryDecorator: binary,
+ }
+
+ module.compiler = nil
+ module.sanitize = nil
+ module.stl = nil
+ module.linker = prebuilt
+
+ android.AddLoadHook(module, func(ctx android.LoadHookContext) {
+ vendorSnapshotLoadHook(ctx, prebuilt)
+ })
+
+ module.AddProperties(&prebuilt.properties)
+ return module.Init()
+}
+
func init() {
android.RegisterSingletonType("vendor-snapshot", VendorSnapshotSingleton)
+ android.RegisterModuleType("vendor_snapshot_shared", VendorSnapshotSharedFactory)
+ android.RegisterModuleType("vendor_snapshot_static", VendorSnapshotStaticFactory)
+ android.RegisterModuleType("vendor_snapshot_header", VendorSnapshotHeaderFactory)
+ android.RegisterModuleType("vendor_snapshot_binary", VendorSnapshotBinaryFactory)
}
func VendorSnapshotSingleton() android.Singleton {
@@ -367,3 +711,120 @@
func (c *vendorSnapshotSingleton) MakeVars(ctx android.MakeVarsContext) {
ctx.Strict("SOONG_VENDOR_SNAPSHOT_ZIP", c.vendorSnapshotZipFile.String())
}
+
+type snapshotInterface interface {
+ matchesWithDevice(config android.DeviceConfig) bool
+}
+
+var _ snapshotInterface = (*vndkPrebuiltLibraryDecorator)(nil)
+var _ snapshotInterface = (*vendorSnapshotLibraryDecorator)(nil)
+var _ snapshotInterface = (*vendorSnapshotBinaryDecorator)(nil)
+
+// gathers all snapshot modules for vendor, and disable unnecessary snapshots
+// TODO(b/145966707): remove mutator and utilize android.Prebuilt to override source modules
+func VendorSnapshotMutator(ctx android.BottomUpMutatorContext) {
+ vndkVersion := ctx.DeviceConfig().VndkVersion()
+ // don't need snapshot if current
+ if vndkVersion == "current" || vndkVersion == "" {
+ return
+ }
+
+ module, ok := ctx.Module().(*Module)
+ if !ok || !module.Enabled() || module.VndkVersion() != vndkVersion {
+ return
+ }
+
+ snapshot, ok := module.linker.(snapshotInterface)
+ if !ok {
+ return
+ }
+
+ if !snapshot.matchesWithDevice(ctx.DeviceConfig()) {
+ // Disable unnecessary snapshot module, but do not disable
+ // vndk_prebuilt_shared because they might be packed into vndk APEX
+ if !module.IsVndk() {
+ module.Disable()
+ }
+ return
+ }
+
+ var snapshotMap *snapshotMap
+
+ if lib, ok := module.linker.(libraryInterface); ok {
+ if lib.static() {
+ snapshotMap = vendorSnapshotStaticLibs(ctx.Config())
+ } else if lib.shared() {
+ snapshotMap = vendorSnapshotSharedLibs(ctx.Config())
+ } else {
+ // header
+ snapshotMap = vendorSnapshotHeaderLibs(ctx.Config())
+ }
+ } else if _, ok := module.linker.(*vendorSnapshotBinaryDecorator); ok {
+ snapshotMap = vendorSnapshotBinaries(ctx.Config())
+ } else {
+ return
+ }
+
+ vendorSnapshotsLock.Lock()
+ defer vendorSnapshotsLock.Unlock()
+ snapshotMap.add(module.BaseModuleName(), ctx.Arch().ArchType, ctx.ModuleName())
+}
+
+// Disables source modules which have snapshots
+func VendorSnapshotSourceMutator(ctx android.BottomUpMutatorContext) {
+ vndkVersion := ctx.DeviceConfig().VndkVersion()
+ // don't need snapshot if current
+ if vndkVersion == "current" || vndkVersion == "" {
+ return
+ }
+
+ module, ok := ctx.Module().(*Module)
+ if !ok {
+ return
+ }
+
+ if module.HasVendorVariant() {
+ vendorSnapshotsLock.Lock()
+ defer vendorSnapshotsLock.Unlock()
+
+ vendorSuffixModules(ctx.Config())[ctx.ModuleName()] = true
+ }
+
+ if module.isSnapshotPrebuilt() || module.VndkVersion() != ctx.DeviceConfig().VndkVersion() {
+ // only non-snapshot modules with BOARD_VNDK_VERSION
+ return
+ }
+
+ var snapshotMap *snapshotMap
+
+ if lib, ok := module.linker.(libraryInterface); ok {
+ if lib.static() {
+ snapshotMap = vendorSnapshotStaticLibs(ctx.Config())
+ } else if lib.shared() {
+ snapshotMap = vendorSnapshotSharedLibs(ctx.Config())
+ } else {
+ // header
+ snapshotMap = vendorSnapshotHeaderLibs(ctx.Config())
+ }
+ } else if _, ok := module.linker.(*binaryDecorator); ok {
+ snapshotMap = vendorSnapshotBinaries(ctx.Config())
+ } else if _, ok := module.linker.(*prebuiltBinaryLinker); ok {
+ snapshotMap = vendorSnapshotBinaries(ctx.Config())
+ } else {
+ return
+ }
+
+ if _, ok := snapshotMap.get(ctx.ModuleName(), ctx.Arch().ArchType); !ok {
+ // Corresponding snapshot doesn't exist
+ return
+ }
+
+ // Disables source modules if corresponding snapshot exists.
+ if lib, ok := module.linker.(libraryInterface); ok && lib.buildStatic() && lib.buildShared() {
+ // But do not disable because the shared variant depends on the static variant.
+ module.SkipInstall()
+ module.Properties.HideFromMake = true
+ } else {
+ module.Disable()
+ }
+}