// Copyright 2018 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 android import ( "sort" "sync" ) // ApexModule is the interface that a module type is expected to implement if // the module has to be built differently depending on whether the module // is destined for an apex or not (installed to one of the regular partitions). // // Native shared libraries are one such module type; when it is built for an // APEX, it should depend only on stable interfaces such as NDK, stable AIDL, // or C APIs from other APEXs. // // A module implementing this interface will be mutated into multiple // variations by apex.apexMutator if it is directly or indirectly included // in one or more APEXs. Specifically, if a module is included in apex.foo and // apex.bar then three apex variants are created: platform, apex.foo and // apex.bar. The platform variant is for the regular partitions // (e.g., /system or /vendor, etc.) while the other two are for the APEXs, // respectively. type ApexModule interface { Module apexModuleBase() *ApexModuleBase // Marks that this module should be built for the APEX of the specified name. // Call this before apex.apexMutator is run. BuildForApex(apexName string) // Returns the name of APEX that this module will be built for. Empty string // is returned when 'IsForPlatform() == true'. Note that a module can be // included in multiple APEXes, in which case, the module is mutated into // multiple modules each of which for an APEX. This method returns the // name of the APEX that a variant module is for. // Call this after apex.apexMutator is run. ApexName() string // Tests whether this module will be built for the platform or not. // This is a shortcut for ApexName() == "" IsForPlatform() bool // Tests if this module could have APEX variants. APEX variants are // created only for the modules that returns true here. This is useful // for not creating APEX variants for certain types of shared libraries // such as NDK stubs. CanHaveApexVariants() bool // Tests if this module can be installed to APEX as a file. For example, // this would return true for shared libs while return false for static // libs. IsInstallableToApex() bool // Mutate this module into one or more variants each of which is built // for an APEX marked via BuildForApex(). CreateApexVariations(mctx BottomUpMutatorContext) []Module // Sets the name of the apex variant of this module. Called inside // CreateApexVariations. setApexName(apexName string) // Tests if this module is available for the specified APEX or ":platform" AvailableFor(what string) bool // DepIsInSameApex tests if the other module 'dep' is installed to the same // APEX as this module DepIsInSameApex(ctx BaseModuleContext, dep Module) bool } type ApexProperties struct { // Availability of this module in APEXes. Only the listed APEXes can include this module. // "//apex_available:anyapex" is a pseudo APEX name that matches to any APEX. // "//apex_available:platform" refers to non-APEX partitions like "system.img". // Default is ["//apex_available:platform", "//apex_available:anyapex"]. // TODO(b/128708192) change the default to ["//apex_available:platform"] Apex_available []string // Name of the apex variant that this module is mutated into ApexName string `blueprint:"mutated"` } // Provides default implementation for the ApexModule interface. APEX-aware // modules are expected to include this struct and call InitApexModule(). type ApexModuleBase struct { ApexProperties ApexProperties canHaveApexVariants bool apexVariationsLock sync.Mutex // protects apexVariations during parallel apexDepsMutator apexVariations []string } func (m *ApexModuleBase) apexModuleBase() *ApexModuleBase { return m } func (m *ApexModuleBase) BuildForApex(apexName string) { m.apexVariationsLock.Lock() defer m.apexVariationsLock.Unlock() if !InList(apexName, m.apexVariations) { m.apexVariations = append(m.apexVariations, apexName) } } func (m *ApexModuleBase) ApexName() string { return m.ApexProperties.ApexName } func (m *ApexModuleBase) IsForPlatform() bool { return m.ApexProperties.ApexName == "" } func (m *ApexModuleBase) setApexName(apexName string) { m.ApexProperties.ApexName = apexName } func (m *ApexModuleBase) CanHaveApexVariants() bool { return m.canHaveApexVariants } func (m *ApexModuleBase) IsInstallableToApex() bool { // should be overriden if needed return false } const ( AvailableToPlatform = "//apex_available:platform" availableToAnyApex = "//apex_available:anyapex" ) func CheckAvailableForApex(what string, apex_available []string) bool { if len(apex_available) == 0 { // apex_available defaults to ["//apex_available:platform", "//apex_available:anyapex"], // which means 'available to everybody'. return true } return InList(what, apex_available) || (what != AvailableToPlatform && InList(availableToAnyApex, apex_available)) } func (m *ApexModuleBase) AvailableFor(what string) bool { return CheckAvailableForApex(what, m.ApexProperties.Apex_available) } func (m *ApexModuleBase) DepIsInSameApex(ctx BaseModuleContext, dep Module) bool { // By default, if there is a dependency from A to B, we try to include both in the same APEX, // unless B is explicitly from outside of the APEX (i.e. a stubs lib). Thus, returning true. // This is overridden by some module types like apex.ApexBundle, cc.Module, java.Module, etc. return true } func (m *ApexModuleBase) checkApexAvailableProperty(mctx BaseModuleContext) { for _, n := range m.ApexProperties.Apex_available { if n == AvailableToPlatform || n == availableToAnyApex { continue } if !mctx.OtherModuleExists(n) && !mctx.Config().AllowMissingDependencies() { mctx.PropertyErrorf("apex_available", "%q is not a valid module name", n) } } } func (m *ApexModuleBase) CreateApexVariations(mctx BottomUpMutatorContext) []Module { if len(m.apexVariations) > 0 { m.checkApexAvailableProperty(mctx) sort.Strings(m.apexVariations) variations := []string{} availableForPlatform := mctx.Module().(ApexModule).AvailableFor(AvailableToPlatform) || mctx.Host() if availableForPlatform { variations = append(variations, "") // Original variation for platform } variations = append(variations, m.apexVariations...) modules := mctx.CreateVariations(variations...) for i, m := range modules { if availableForPlatform && i == 0 { continue } m.(ApexModule).setApexName(variations[i]) } return modules } return nil } var apexData OncePer var apexNamesMapMutex sync.Mutex var apexNamesKey = NewOnceKey("apexNames") // This structure maintains the global mapping in between modules and APEXes. // Examples: // // apexNamesMap()["foo"]["bar"] == true: module foo is directly depended on by APEX bar // apexNamesMap()["foo"]["bar"] == false: module foo is indirectly depended on by APEX bar // apexNamesMap()["foo"]["bar"] doesn't exist: foo is not built for APEX bar func apexNamesMap() map[string]map[string]bool { return apexData.Once(apexNamesKey, func() interface{} { return make(map[string]map[string]bool) }).(map[string]map[string]bool) } // Update the map to mark that a module named moduleName is directly or indirectly // depended on by an APEX named apexName. Directly depending means that a module // is explicitly listed in the build definition of the APEX via properties like // native_shared_libs, java_libs, etc. func UpdateApexDependency(apexName string, moduleName string, directDep bool) { apexNamesMapMutex.Lock() defer apexNamesMapMutex.Unlock() apexNames, ok := apexNamesMap()[moduleName] if !ok { apexNames = make(map[string]bool) apexNamesMap()[moduleName] = apexNames } apexNames[apexName] = apexNames[apexName] || directDep } // TODO(b/146393795): remove this when b/146393795 is fixed func ClearApexDependency() { m := apexNamesMap() for k := range m { delete(m, k) } } // Tests whether a module named moduleName is directly depended on by an APEX // named apexName. func DirectlyInApex(apexName string, moduleName string) bool { apexNamesMapMutex.Lock() defer apexNamesMapMutex.Unlock() if apexNames, ok := apexNamesMap()[moduleName]; ok { return apexNames[apexName] } return false } type hostContext interface { Host() bool } // Tests whether a module named moduleName is directly depended on by any APEX. func DirectlyInAnyApex(ctx hostContext, moduleName string) bool { if ctx.Host() { // Host has no APEX. return false } apexNamesMapMutex.Lock() defer apexNamesMapMutex.Unlock() if apexNames, ok := apexNamesMap()[moduleName]; ok { for an := range apexNames { if apexNames[an] { return true } } } return false } // Tests whether a module named module is depended on (including both // direct and indirect dependencies) by any APEX. func InAnyApex(moduleName string) bool { apexNamesMapMutex.Lock() defer apexNamesMapMutex.Unlock() apexNames, ok := apexNamesMap()[moduleName] return ok && len(apexNames) > 0 } func GetApexesForModule(moduleName string) []string { ret := []string{} apexNamesMapMutex.Lock() defer apexNamesMapMutex.Unlock() if apexNames, ok := apexNamesMap()[moduleName]; ok { for an := range apexNames { ret = append(ret, an) } } return ret } func InitApexModule(m ApexModule) { base := m.apexModuleBase() base.canHaveApexVariants = true m.AddProperties(&base.ApexProperties) }