Merge "Remove restriction on exported plugins that generate APIs"
diff --git a/android/Android.bp b/android/Android.bp
index 26605a7..279d442 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -6,6 +6,7 @@
"blueprint-bootstrap",
"soong",
"soong-android-soongconfig",
+ "soong-bazel",
"soong-env",
"soong-shared",
"soong-ui-metrics_proto",
@@ -15,6 +16,7 @@
"apex.go",
"api_levels.go",
"arch.go",
+ "arch_list.go",
"bazel_handler.go",
"config.go",
"csuite_config.go",
diff --git a/android/androidmk.go b/android/androidmk.go
index cfd7c91..4adbb22 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -417,7 +417,7 @@
type androidMkSingleton struct{}
func (c *androidMkSingleton) GenerateBuildActions(ctx SingletonContext) {
- if !ctx.Config().EmbeddedInMake() {
+ if !ctx.Config().KatiEnabled() {
return
}
diff --git a/android/androidmk_test.go b/android/androidmk_test.go
index 10527b9..bb03378 100644
--- a/android/androidmk_test.go
+++ b/android/androidmk_test.go
@@ -78,7 +78,7 @@
`
config := TestConfig(buildDir, nil, bp, nil)
- config.inMake = true // Enable androidmk Singleton
+ config.katiEnabled = true // Enable androidmk Singleton
ctx := NewTestContext(config)
ctx.RegisterSingletonType("androidmk", AndroidMkSingleton)
@@ -250,7 +250,7 @@
for _, testCase := range testCases {
config := TestConfig(buildDir, nil, testCase.bp, nil)
- config.inMake = true // Enable androidmk Singleton
+ config.katiEnabled = true // Enable androidmk Singleton
ctx := NewTestContext(config)
ctx.RegisterSingletonType("androidmk", AndroidMkSingleton)
diff --git a/android/apex.go b/android/apex.go
index 276f7a4..a4ff0f9 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -25,32 +25,54 @@
)
var (
+ // This is the sdk version when APEX was first introduced
SdkVersion_Android10 = uncheckedFinalApiLevel(29)
)
-// ApexInfo describes the metadata common to all modules in an apexBundle.
+// ApexInfo describes the metadata about one or more apexBundles that an apex variant of a module is
+// part of. When an apex variant is created, the variant is associated with one apexBundle. But
+// when multiple apex variants are merged for deduping (see mergeApexVariations), this holds the
+// information about the apexBundles that are merged together.
+// Accessible via `ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)`
type ApexInfo struct {
- // Name of the apex variation that this module is mutated into, or "" for
- // a platform variant. Note that a module can be included in multiple APEXes,
- // in which case, the module is mutated into one or more variants, each of
- // which is for one or more APEXes.
+ // Name of the apex variation that this module (i.e. the apex variant of the module) is
+ // mutated into, or "" for a platform (i.e. non-APEX) variant. Note that a module can be
+ // included in multiple APEXes, in which case, the module is mutated into one or more
+ // variants, each of which is for an APEX. The variants then can later be deduped if they
+ // don't need to be compiled differently. This is an optimization done in
+ // mergeApexVariations.
ApexVariationName string
- // Serialized ApiLevel. Use via MinSdkVersion() method. Cannot be stored in
- // its struct form because this is cloned into properties structs, and
- // ApiLevel has private members.
+ // Serialized ApiLevel that this module has to support at minimum. Should be accessed via
+ // MinSdkVersion() method. Cannot be stored in its struct form because this is cloned into
+ // properties structs, and ApiLevel has private members.
MinSdkVersionStr string
- // True if the module comes from an updatable APEX.
- Updatable bool
+ // True if this module comes from an updatable apexBundle.
+ Updatable bool
+
+ // The list of SDK modules that the containing apexBundle depends on.
RequiredSdks SdkRefs
- InApexes []string
+ // List of apexBundles that this apex variant of the module is associated with. Initially,
+ // the size of this list is one because one apex variant is associated with one apexBundle.
+ // When multiple apex variants are merged in mergeApexVariations, ApexInfo struct of the
+ // merged variant holds the list of apexBundles that are merged together.
+ InApexes []string
+
+ // Pointers to the ApexContents struct each of which is for apexBundle modules that this
+ // module is part of. The ApexContents gives information about which modules the apexBundle
+ // has and whether a module became part of the apexBundle via a direct dependency or not.
ApexContents []*ApexContents
}
var ApexInfoProvider = blueprint.NewMutatorProvider(ApexInfo{}, "apex")
+// mergedName gives the name of the alias variation that will be used when multiple apex variations
+// of a module can be deduped into one variation. For example, if libfoo is included in both apex.a
+// and apex.b, and if the two APEXes have the same min_sdk_version (say 29), then libfoo doesn't
+// have to be built twice, but only once. In that case, the two apex variations apex.a and apex.b
+// are configured to have the same alias variation named apex29.
func (i ApexInfo) mergedName(ctx PathContext) string {
name := "apex" + strconv.Itoa(i.MinSdkVersion(ctx).FinalOrFutureInt())
for _, sdk := range i.RequiredSdks {
@@ -59,14 +81,19 @@
return name
}
-func (this *ApexInfo) MinSdkVersion(ctx PathContext) ApiLevel {
- return ApiLevelOrPanic(ctx, this.MinSdkVersionStr)
+// MinSdkVersion gives the api level that this module has to support at minimum. This is from the
+// min_sdk_version property of the containing apexBundle.
+func (i ApexInfo) MinSdkVersion(ctx PathContext) ApiLevel {
+ return ApiLevelOrPanic(ctx, i.MinSdkVersionStr)
}
+// IsForPlatform tells whether this module is for the platform or not. If false is returned, it
+// means that this apex variant of the module is built for an APEX.
func (i ApexInfo) IsForPlatform() bool {
return i.ApexVariationName == ""
}
+// InApex tells whether this apex variant of the module is part of the given apexBundle or not.
func (i ApexInfo) InApex(apex string) bool {
for _, a := range i.InApexes {
if a == apex {
@@ -76,102 +103,111 @@
return false
}
-// ApexTestForInfo stores the contents of APEXes for which this module is a test and thus has
-// access to APEX internals.
+// ApexTestForInfo stores the contents of APEXes for which this module is a test - although this
+// module is not part of the APEX - and thus has access to APEX internals.
type ApexTestForInfo struct {
ApexContents []*ApexContents
}
var ApexTestForInfoProvider = blueprint.NewMutatorProvider(ApexTestForInfo{}, "apex_test_for")
-// Extracted from ApexModule to make it easier to define custom subsets of the
-// ApexModule interface and improve code navigation within the IDE.
+// DepIsInSameApex defines an interface that should be used to determine whether a given dependency
+// should be considered as part of the same APEX as the current module or not. Note: this was
+// extracted from ApexModule to make it easier to define custom subsets of the ApexModule interface
+// and improve code navigation within the IDE.
type DepIsInSameApex interface {
- // DepIsInSameApex tests if the other module 'dep' is installed to the same
- // APEX as this module
+ // DepIsInSameApex tests if the other module 'dep' is considered as part of the same APEX as
+ // this module. For example, a static lib dependency usually returns true here, while a
+ // shared lib dependency to a stub library returns false.
DepIsInSameApex(ctx BaseModuleContext, dep Module) bool
}
-// 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).
+// 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 (i.e., 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.
+// 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 APEXes.
//
-// 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.
+// 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 APEXes. 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. The latter two variations can be merged (see
+// mergedName) when the two APEXes have the same min_sdk_version requirement.
type ApexModule interface {
Module
DepIsInSameApex
apexModuleBase() *ApexModuleBase
- // Marks that this module should be built for the specified APEX.
- // Call this before apex.apexMutator is run.
+ // Marks that this module should be built for the specified APEX. Call this BEFORE
+ // apex.apexMutator is run.
BuildForApex(apex ApexInfo)
- // Returns true if this module is present in any APEXes
- // directly or indirectly.
- // Call this after apex.apexMutator is run.
+ // Returns true if this module is present in any APEX either directly or indirectly. Call
+ // this after apex.apexMutator is run.
InAnyApex() bool
- // Returns true if this module is directly in any APEXes.
- // Call this after apex.apexMutator is run.
+ // Returns true if this module is directly in any APEX. Call this AFTER apex.apexMutator is
+ // run.
DirectlyInAnyApex() bool
- // Returns true if any variant of this module is directly in any APEXes.
- // Call this after apex.apexMutator is run.
+ // Returns true in the primary variant of a module if _any_ variant of the module is
+ // directly in any apex. This includes host, arch, asan, etc. variants. It is unused in any
+ // variant that is not the primary variant. Ideally this wouldn't be used, as it incorrectly
+ // mixes arch variants if only one arch is in an apex, but a few places depend on it, for
+ // example when an ASAN variant is created before the apexMutator. Call this after
+ // apex.apexMutator is run.
AnyVariantDirectlyInAnyApex() 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.
+ // Tests if this module could have APEX variants. Even when a module type implements
+ // ApexModule interface, APEX variants are created only for the module instances that return
+ // 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.
+ // 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 because static libs are not
+ // installable module (but it can still be mutated for APEX)
IsInstallableToApex() bool
- // Tests if this module is available for the specified APEX or ":platform"
+ // Tests if this module is available for the specified APEX or ":platform". This is from the
+ // apex_available property of the module.
AvailableFor(what string) bool
- // Return true if this module is not available to platform (i.e. apex_available
- // property doesn't have "//apex_available:platform"), or shouldn't be available
- // to platform, which is the case when this module depends on other module that
- // isn't available to platform.
+ // Returns true if this module is not available to platform (i.e. apex_available property
+ // doesn't have "//apex_available:platform"), or shouldn't be available to platform, which
+ // is the case when this module depends on other module that isn't available to platform.
NotAvailableForPlatform() bool
- // Mark that this module is not available to platform. Set by the
+ // Marks that this module is not available to platform. Set by the
// check-platform-availability mutator in the apex package.
SetNotAvailableForPlatform()
- // List of APEXes that this module tests. The module has access to
- // the private part of the listed APEXes even when it is not included in the
- // APEXes.
+ // Returns the list of APEXes that this module is a test for. The module has access to the
+ // private part of the listed APEXes even when it is not included in the APEXes. This by
+ // default returns nil. A module type should override the default implementation. For
+ // example, cc_test module type returns the value of test_for here.
TestFor() []string
- // Returns nil if this module supports sdkVersion
- // Otherwise, returns error with reason
+ // Returns nil (success) if this module should support the given sdk version. Returns an
+ // error if not. No default implementation is provided for this method. A module type
+ // implementing this interface should provide an implementation. A module supports an sdk
+ // version when the module's min_sdk_version is equal to or less than the given sdk version.
ShouldSupportSdkVersion(ctx BaseModuleContext, sdkVersion ApiLevel) error
- // Returns true if this module needs a unique variation per apex, for example if
- // use_apex_name_macro is set.
+ // Returns true if this module needs a unique variation per apex, effectively disabling the
+ // deduping. This is turned on when, for example if use_apex_name_macro is set so that each
+ // apex variant should be built with different macro definitions.
UniqueApexVariations() bool
}
+// Properties that are common to all module types implementing ApexModule interface.
type ApexProperties struct {
- // Availability of this module in APEXes. Only the listed APEXes can contain
- // this module. If the module has stubs then other APEXes and the platform may
- // access it through them (subject to visibility).
+ // Availability of this module in APEXes. Only the listed APEXes can contain this module. If
+ // the module has stubs then other APEXes and the platform may access it through them
+ // (subject to visibility).
//
// "//apex_available:anyapex" is a pseudo APEX name that matches to any APEX.
// "//apex_available:platform" refers to non-APEX partitions like "system.img".
@@ -179,29 +215,23 @@
// Default is ["//apex_available:platform"].
Apex_available []string
- // AnyVariantDirectlyInAnyApex is true in the primary variant of a module if _any_ variant
- // of the module is directly in any apex. This includes host, arch, asan, etc. variants.
- // It is unused in any variant that is not the primary variant.
- // Ideally this wouldn't be used, as it incorrectly mixes arch variants if only one arch
- // is in an apex, but a few places depend on it, for example when an ASAN variant is
- // created before the apexMutator.
- AnyVariantDirectlyInAnyApex bool `blueprint:"mutated"`
-
- // DirectlyInAnyApex is true if any APEX variant (including the "" variant used for the
- // platform) of this module is directly in any APEX.
- DirectlyInAnyApex bool `blueprint:"mutated"`
-
- // DirectlyInAnyApex is true if any APEX variant (including the "" variant used for the
- // platform) of this module is directly or indirectly in any APEX.
+ // See ApexModule.InAnyApex()
InAnyApex bool `blueprint:"mutated"`
+ // See ApexModule.DirectlyInAnyApex()
+ DirectlyInAnyApex bool `blueprint:"mutated"`
+
+ // See ApexModule.AnyVariantDirectlyInAnyApex()
+ AnyVariantDirectlyInAnyApex bool `blueprint:"mutated"`
+
+ // See ApexModule.NotAvailableForPlatform()
NotAvailableForPlatform bool `blueprint:"mutated"`
+ // See ApexModule.UniqueApexVariants()
UniqueApexVariationsForDeps bool `blueprint:"mutated"`
}
-// Marker interface that identifies dependencies that are excluded from APEX
-// contents.
+// Marker interface that identifies dependencies that are excluded from APEX contents.
type ExcludeFromApexContentsTag interface {
blueprint.DependencyTag
@@ -209,85 +239,122 @@
ExcludeFromApexContents()
}
-// Marker interface that identifies dependencies that should inherit the DirectlyInAnyApex
-// state from the parent to the child. For example, stubs libraries are marked as
-// DirectlyInAnyApex if their implementation is in an apex.
+// Marker interface that identifies dependencies that should inherit the DirectlyInAnyApex state
+// from the parent to the child. For example, stubs libraries are marked as DirectlyInAnyApex if
+// their implementation is in an apex.
type CopyDirectlyInAnyApexTag interface {
blueprint.DependencyTag
+ // Method that differentiates this interface from others.
CopyDirectlyInAnyApex()
}
-// Provides default implementation for the ApexModule interface. APEX-aware
+// ApexModuleBase provides the 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 []ApexInfo
+ apexInfos []ApexInfo
+ apexInfosLock sync.Mutex // protects apexInfos during parallel apexDepsMutator
}
+// Initializes ApexModuleBase struct. Not calling this (even when inheriting from ApexModuleBase)
+// prevents the module from being mutated for apexBundle.
+func InitApexModule(m ApexModule) {
+ base := m.apexModuleBase()
+ base.canHaveApexVariants = true
+
+ m.AddProperties(&base.ApexProperties)
+}
+
+// Implements ApexModule
func (m *ApexModuleBase) apexModuleBase() *ApexModuleBase {
return m
}
+// Implements ApexModule
func (m *ApexModuleBase) ApexAvailable() []string {
return m.ApexProperties.Apex_available
}
-func (m *ApexModuleBase) TestFor() []string {
- // To be implemented by concrete types inheriting ApexModuleBase
- return nil
-}
-
-func (m *ApexModuleBase) UniqueApexVariations() bool {
- return false
-}
-
+// Implements ApexModule
func (m *ApexModuleBase) BuildForApex(apex ApexInfo) {
- m.apexVariationsLock.Lock()
- defer m.apexVariationsLock.Unlock()
- for _, v := range m.apexVariations {
+ m.apexInfosLock.Lock()
+ defer m.apexInfosLock.Unlock()
+ for _, v := range m.apexInfos {
if v.ApexVariationName == apex.ApexVariationName {
return
}
}
- m.apexVariations = append(m.apexVariations, apex)
+ m.apexInfos = append(m.apexInfos, apex)
}
-func (m *ApexModuleBase) DirectlyInAnyApex() bool {
- return m.ApexProperties.DirectlyInAnyApex
-}
-
-func (m *ApexModuleBase) AnyVariantDirectlyInAnyApex() bool {
- return m.ApexProperties.AnyVariantDirectlyInAnyApex
-}
-
+// Implements ApexModule
func (m *ApexModuleBase) InAnyApex() bool {
return m.ApexProperties.InAnyApex
}
+// Implements ApexModule
+func (m *ApexModuleBase) DirectlyInAnyApex() bool {
+ return m.ApexProperties.DirectlyInAnyApex
+}
+
+// Implements ApexModule
+func (m *ApexModuleBase) AnyVariantDirectlyInAnyApex() bool {
+ return m.ApexProperties.AnyVariantDirectlyInAnyApex
+}
+
+// Implements ApexModule
func (m *ApexModuleBase) CanHaveApexVariants() bool {
return m.canHaveApexVariants
}
+// Implements ApexModule
func (m *ApexModuleBase) IsInstallableToApex() bool {
- // should be overriden if needed
+ // If needed, this will bel overridden by concrete types inheriting
+ // ApexModuleBase
return false
}
+// Implements ApexModule
+func (m *ApexModuleBase) TestFor() []string {
+ // If needed, this will be overridden by concrete types inheriting
+ // ApexModuleBase
+ return nil
+}
+
+// Implements ApexModule
+func (m *ApexModuleBase) UniqueApexVariations() bool {
+ // If needed, this will bel overridden by concrete types inheriting
+ // ApexModuleBase
+ return false
+}
+
+// Implements ApexModule
+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
+}
+
const (
AvailableToPlatform = "//apex_available:platform"
AvailableToAnyApex = "//apex_available:anyapex"
AvailableToGkiApex = "com.android.gki.*"
)
+// CheckAvailableForApex provides the default algorithm for checking the apex availability. When the
+// availability is empty, it defaults to ["//apex_available:platform"] which means "available to the
+// platform but not available to any APEX". When the list is not empty, `what` is matched against
+// the list. If there is any matching element in the list, thus function returns true. The special
+// availability "//apex_available:anyapex" matches with anything except for
+// "//apex_available:platform".
func CheckAvailableForApex(what string, apex_available []string) bool {
if len(apex_available) == 0 {
- // apex_available defaults to ["//apex_available:platform"],
- // which means 'available to the platform but no apexes'.
return what == AvailableToPlatform
}
return InList(what, apex_available) ||
@@ -295,25 +362,22 @@
(strings.HasPrefix(what, "com.android.gki.") && InList(AvailableToGkiApex, apex_available))
}
+// Implements ApexModule
func (m *ApexModuleBase) AvailableFor(what string) bool {
return CheckAvailableForApex(what, m.ApexProperties.Apex_available)
}
+// Implements ApexModule
func (m *ApexModuleBase) NotAvailableForPlatform() bool {
return m.ApexProperties.NotAvailableForPlatform
}
+// Implements ApexModule
func (m *ApexModuleBase) SetNotAvailableForPlatform() {
m.ApexProperties.NotAvailableForPlatform = true
}
-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
-}
-
+// This function makes sure that the apex_available property is valid
func (m *ApexModuleBase) checkApexAvailableProperty(mctx BaseModuleContext) {
for _, n := range m.ApexProperties.Apex_available {
if n == AvailableToPlatform || n == AvailableToAnyApex || n == AvailableToGkiApex {
@@ -331,22 +395,23 @@
func (a byApexName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a byApexName) Less(i, j int) bool { return a[i].ApexVariationName < a[j].ApexVariationName }
-// mergeApexVariations deduplicates APEX variations that would build identically into a common
-// variation. It returns the reduced list of variations and a list of aliases from the original
+// mergeApexVariations deduplicates apex variations that would build identically into a common
+// variation. It returns the reduced list of variations and a list of aliases from the original
// variation names to the new variation names.
-func mergeApexVariations(ctx PathContext, apexVariations []ApexInfo) (merged []ApexInfo, aliases [][2]string) {
- sort.Sort(byApexName(apexVariations))
+func mergeApexVariations(ctx PathContext, apexInfos []ApexInfo) (merged []ApexInfo, aliases [][2]string) {
+ sort.Sort(byApexName(apexInfos))
seen := make(map[string]int)
- for _, apexInfo := range apexVariations {
+ for _, apexInfo := range apexInfos {
apexName := apexInfo.ApexVariationName
mergedName := apexInfo.mergedName(ctx)
if index, exists := seen[mergedName]; exists {
+ // Variants having the same mergedName are deduped
merged[index].InApexes = append(merged[index].InApexes, apexName)
merged[index].ApexContents = append(merged[index].ApexContents, apexInfo.ApexContents...)
merged[index].Updatable = merged[index].Updatable || apexInfo.Updatable
} else {
seen[mergedName] = len(merged)
- apexInfo.ApexVariationName = apexInfo.mergedName(ctx)
+ apexInfo.ApexVariationName = mergedName
apexInfo.InApexes = CopyOf(apexInfo.InApexes)
apexInfo.ApexContents = append([]*ApexContents(nil), apexInfo.ApexContents...)
merged = append(merged, apexInfo)
@@ -356,75 +421,78 @@
return merged, aliases
}
+// CreateApexVariations mutates a given module into multiple apex variants each of which is for an
+// apexBundle (and/or the platform) where the module is part of.
func CreateApexVariations(mctx BottomUpMutatorContext, module ApexModule) []Module {
base := module.apexModuleBase()
- if len(base.apexVariations) > 0 {
- base.checkApexAvailableProperty(mctx)
- var apexVariations []ApexInfo
- var aliases [][2]string
- if !mctx.Module().(ApexModule).UniqueApexVariations() && !base.ApexProperties.UniqueApexVariationsForDeps {
- apexVariations, aliases = mergeApexVariations(mctx, base.apexVariations)
- } else {
- apexVariations = base.apexVariations
- }
- // base.apexVariations is only needed to propagate the list of apexes from
- // apexDepsMutator to apexMutator. It is no longer accurate after
- // mergeApexVariations, and won't be copied to all but the first created
- // variant. Clear it so it doesn't accidentally get used later.
- base.apexVariations = nil
-
- sort.Sort(byApexName(apexVariations))
- variations := []string{}
- variations = append(variations, "") // Original variation for platform
- for _, apex := range apexVariations {
- variations = append(variations, apex.ApexVariationName)
- }
-
- defaultVariation := ""
- mctx.SetDefaultDependencyVariation(&defaultVariation)
-
- var inApex ApexMembership
- for _, a := range apexVariations {
- for _, apexContents := range a.ApexContents {
- inApex = inApex.merge(apexContents.contents[mctx.ModuleName()])
- }
- }
-
- base.ApexProperties.InAnyApex = true
- base.ApexProperties.DirectlyInAnyApex = inApex == directlyInApex
-
- modules := mctx.CreateVariations(variations...)
- for i, mod := range modules {
- platformVariation := i == 0
- if platformVariation && !mctx.Host() && !mod.(ApexModule).AvailableFor(AvailableToPlatform) {
- // Do not install the module for platform, but still allow it to output
- // uninstallable AndroidMk entries in certain cases when they have
- // side effects.
- mod.MakeUninstallable()
- }
- if !platformVariation {
- mctx.SetVariationProvider(mod, ApexInfoProvider, apexVariations[i-1])
- }
- }
-
- for _, alias := range aliases {
- mctx.CreateAliasVariation(alias[0], alias[1])
- }
-
- return modules
+ // Shortcut
+ if len(base.apexInfos) == 0 {
+ return nil
}
- return nil
+
+ // Do some validity checks.
+ // TODO(jiyong): is this the right place?
+ base.checkApexAvailableProperty(mctx)
+
+ var apexInfos []ApexInfo
+ var aliases [][2]string
+ if !mctx.Module().(ApexModule).UniqueApexVariations() && !base.ApexProperties.UniqueApexVariationsForDeps {
+ apexInfos, aliases = mergeApexVariations(mctx, base.apexInfos)
+ } else {
+ apexInfos = base.apexInfos
+ }
+ // base.apexInfos is only needed to propagate the list of apexes from apexDepsMutator to
+ // apexMutator. It is no longer accurate after mergeApexVariations, and won't be copied to
+ // all but the first created variant. Clear it so it doesn't accidentally get used later.
+ base.apexInfos = nil
+ sort.Sort(byApexName(apexInfos))
+
+ var inApex ApexMembership
+ for _, a := range apexInfos {
+ for _, apexContents := range a.ApexContents {
+ inApex = inApex.merge(apexContents.contents[mctx.ModuleName()])
+ }
+ }
+ base.ApexProperties.InAnyApex = true
+ base.ApexProperties.DirectlyInAnyApex = inApex == directlyInApex
+
+ defaultVariation := ""
+ mctx.SetDefaultDependencyVariation(&defaultVariation)
+
+ variations := []string{defaultVariation}
+ for _, a := range apexInfos {
+ variations = append(variations, a.ApexVariationName)
+ }
+ modules := mctx.CreateVariations(variations...)
+ for i, mod := range modules {
+ platformVariation := i == 0
+ if platformVariation && !mctx.Host() && !mod.(ApexModule).AvailableFor(AvailableToPlatform) {
+ // Do not install the module for platform, but still allow it to output
+ // uninstallable AndroidMk entries in certain cases when they have side
+ // effects. TODO(jiyong): move this routine to somewhere else
+ mod.MakeUninstallable()
+ }
+ if !platformVariation {
+ mctx.SetVariationProvider(mod, ApexInfoProvider, apexInfos[i-1])
+ }
+ }
+
+ for _, alias := range aliases {
+ mctx.CreateAliasVariation(alias[0], alias[1])
+ }
+
+ return modules
}
-// UpdateUniqueApexVariationsForDeps sets UniqueApexVariationsForDeps if any dependencies
-// that are in the same APEX have unique APEX variations so that the module can link against
-// the right variant.
+// UpdateUniqueApexVariationsForDeps sets UniqueApexVariationsForDeps if any dependencies that are
+// in the same APEX have unique APEX variations so that the module can link against the right
+// variant.
func UpdateUniqueApexVariationsForDeps(mctx BottomUpMutatorContext, am ApexModule) {
- // anyInSameApex returns true if the two ApexInfo lists contain any values in an InApexes list
- // in common. It is used instead of DepIsInSameApex because it needs to determine if the dep
- // is in the same APEX due to being directly included, not only if it is included _because_ it
- // is a dependency.
+ // anyInSameApex returns true if the two ApexInfo lists contain any values in an InApexes
+ // list in common. It is used instead of DepIsInSameApex because it needs to determine if
+ // the dep is in the same APEX due to being directly included, not only if it is included
+ // _because_ it is a dependency.
anyInSameApex := func(a, b []ApexInfo) bool {
collectApexes := func(infos []ApexInfo) []string {
var ret []string
@@ -446,9 +514,10 @@
return false
}
+ // If any of the dependencies requires unique apex variations, so does this module.
mctx.VisitDirectDeps(func(dep Module) {
if depApexModule, ok := dep.(ApexModule); ok {
- if anyInSameApex(depApexModule.apexModuleBase().apexVariations, am.apexModuleBase().apexVariations) &&
+ if anyInSameApex(depApexModule.apexModuleBase().apexInfos, am.apexModuleBase().apexInfos) &&
(depApexModule.UniqueApexVariations() ||
depApexModule.apexModuleBase().ApexProperties.UniqueApexVariationsForDeps) {
am.apexModuleBase().ApexProperties.UniqueApexVariationsForDeps = true
@@ -457,10 +526,10 @@
})
}
-// UpdateDirectlyInAnyApex uses the final module to store if any variant of this
-// module is directly in any APEX, and then copies the final value to all the modules.
-// It also copies the DirectlyInAnyApex value to any direct dependencies with a
-// CopyDirectlyInAnyApexTag dependency tag.
+// UpdateDirectlyInAnyApex uses the final module to store if any variant of this module is directly
+// in any APEX, and then copies the final value to all the modules. It also copies the
+// DirectlyInAnyApex value to any direct dependencies with a CopyDirectlyInAnyApexTag dependency
+// tag.
func UpdateDirectlyInAnyApex(mctx BottomUpMutatorContext, am ApexModule) {
base := am.apexModuleBase()
// Copy DirectlyInAnyApex and InAnyApex from any direct dependencies with a
@@ -475,14 +544,13 @@
if base.ApexProperties.DirectlyInAnyApex {
// Variants of a module are always visited sequentially in order, so it is safe to
- // write to another variant of this module.
- // For a BottomUpMutator the PrimaryModule() is visited first and FinalModule() is
- // visited last.
+ // write to another variant of this module. For a BottomUpMutator the
+ // PrimaryModule() is visited first and FinalModule() is visited last.
mctx.FinalModule().(ApexModule).apexModuleBase().ApexProperties.AnyVariantDirectlyInAnyApex = true
}
- // If this is the FinalModule (last visited module) copy AnyVariantDirectlyInAnyApex to
- // all the other variants
+ // If this is the FinalModule (last visited module) copy
+ // AnyVariantDirectlyInAnyApex to all the other variants
if am == mctx.FinalModule().(ApexModule) {
mctx.VisitAllModuleVariants(func(variant Module) {
variant.(ApexModule).apexModuleBase().ApexProperties.AnyVariantDirectlyInAnyApex =
@@ -491,6 +559,7 @@
}
}
+// ApexMembership tells how a module became part of an APEX.
type ApexMembership int
const (
@@ -499,20 +568,21 @@
directlyInApex
)
-// Each apexBundle has an apexContents, and modules in that apex have a provider containing the
-// apexContents of each apexBundle they are part of.
+// ApexContents gives an information about member modules of an apexBundle. Each apexBundle has an
+// apexContents, and modules in that apex have a provider containing the apexContents of each
+// apexBundle they are part of.
type ApexContents struct {
- ApexName string
+ // map from a module name to its membership to this apexBUndle
contents map[string]ApexMembership
}
-func NewApexContents(name string, contents map[string]ApexMembership) *ApexContents {
+func NewApexContents(contents map[string]ApexMembership) *ApexContents {
return &ApexContents{
- ApexName: name,
contents: contents,
}
}
+// Updates an existing membership by adding a new direct (or indirect) membership
func (i ApexMembership) Add(direct bool) ApexMembership {
if direct || i == directlyInApex {
return directlyInApex
@@ -520,6 +590,10 @@
return indirectlyInApex
}
+// Merges two membership into one. Merging is needed because a module can be a part of an apexBundle
+// in many different paths. For example, it could be dependend on by the apexBundle directly, but at
+// the same time, there might be an indirect dependency to the module. In that case, the more
+// specific dependency (the direct one) is chosen.
func (i ApexMembership) merge(other ApexMembership) ApexMembership {
if other == directlyInApex || i == directlyInApex {
return directlyInApex
@@ -531,16 +605,19 @@
return notInApex
}
-func (ac *ApexContents) DirectlyInApex(name string) bool {
- return ac.contents[name] == directlyInApex
+// Tests whether a module named moduleName is directly included in the apexBundle where this
+// ApexContents is tagged.
+func (ac *ApexContents) DirectlyInApex(moduleName string) bool {
+ return ac.contents[moduleName] == directlyInApex
}
-func (ac *ApexContents) InApex(name string) bool {
- return ac.contents[name] != notInApex
+// Tests whether a module named moduleName is included in the apexBundle where this ApexContent is
+// tagged.
+func (ac *ApexContents) InApex(moduleName string) bool {
+ return ac.contents[moduleName] != notInApex
}
-// Tests whether a module named moduleName is directly depended on by all APEXes
-// in an ApexInfo.
+// Tests whether a module named moduleName is directly depended on by all APEXes in an ApexInfo.
func DirectlyInAllApexes(apexInfo ApexInfo, moduleName string) bool {
for _, contents := range apexInfo.ApexContents {
if !contents.DirectlyInApex(moduleName) {
@@ -550,12 +627,15 @@
return true
}
-func InitApexModule(m ApexModule) {
- base := m.apexModuleBase()
- base.canHaveApexVariants = true
-
- m.AddProperties(&base.ApexProperties)
-}
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//Below are routines for extra safety checks.
+//
+// BuildDepsInfoLists is to flatten the dependency graph for an apexBundle into a text file
+// (actually two in slightly different formats). The files are mostly for debugging, for example to
+// see why a certain module is included in an APEX via which dependency path.
+//
+// CheckMinSdkVersion is to make sure that all modules in an apexBundle satisfy the min_sdk_version
+// requirement of the apexBundle.
// A dependency info for a single ApexModule, either direct or transitive.
type ApexModuleDepInfo struct {
@@ -705,7 +785,8 @@
WalkPayloadDeps(ctx ModuleContext, do PayloadDepsCallback)
}
-// CheckMinSdkVersion checks if every dependency of an updatable module sets min_sdk_version accordingly
+// CheckMinSdkVersion checks if every dependency of an updatable module sets min_sdk_version
+// accordingly
func CheckMinSdkVersion(m UpdatableModule, ctx ModuleContext, minSdkVersion ApiLevel) {
// do not enforce min_sdk_version for host
if ctx.Host() {
@@ -725,8 +806,9 @@
m.WalkPayloadDeps(ctx, func(ctx ModuleContext, from blueprint.Module, to ApexModule, externalDep bool) bool {
if externalDep {
- // external deps are outside the payload boundary, which is "stable" interface.
- // We don't have to check min_sdk_version for external dependencies.
+ // external deps are outside the payload boundary, which is "stable"
+ // interface. We don't have to check min_sdk_version for external
+ // dependencies.
return false
}
if am, ok := from.(DepIsInSameApex); ok && !am.DepIsInSameApex(ctx, to) {
diff --git a/android/arch.go b/android/arch.go
index afb9c7f..eb651e6 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -27,28 +27,6 @@
"github.com/google/blueprint/proptools"
)
-const COMMON_VARIANT = "common"
-
-var (
- archTypeList []ArchType
-
- Arm = newArch("arm", "lib32")
- Arm64 = newArch("arm64", "lib64")
- X86 = newArch("x86", "lib32")
- X86_64 = newArch("x86_64", "lib64")
-
- Common = ArchType{
- Name: COMMON_VARIANT,
- }
-)
-
-var archTypeMap = map[string]ArchType{
- "arm": Arm,
- "arm64": Arm64,
- "x86": X86,
- "x86_64": X86_64,
-}
-
/*
Example blueprints file containing all variant property groups, with comment listing what type
of variants get properties in that group:
@@ -111,405 +89,26 @@
}
*/
-var archVariants = map[ArchType][]string{
- Arm: {
- "armv7-a",
- "armv7-a-neon",
- "armv8-a",
- "armv8-2a",
- "cortex-a7",
- "cortex-a8",
- "cortex-a9",
- "cortex-a15",
- "cortex-a53",
- "cortex-a53-a57",
- "cortex-a55",
- "cortex-a72",
- "cortex-a73",
- "cortex-a75",
- "cortex-a76",
- "krait",
- "kryo",
- "kryo385",
- "exynos-m1",
- "exynos-m2",
- },
- Arm64: {
- "armv8_a",
- "armv8_2a",
- "armv8-2a-dotprod",
- "cortex-a53",
- "cortex-a55",
- "cortex-a72",
- "cortex-a73",
- "cortex-a75",
- "cortex-a76",
- "kryo",
- "kryo385",
- "exynos-m1",
- "exynos-m2",
- },
- X86: {
- "amberlake",
- "atom",
- "broadwell",
- "haswell",
- "icelake",
- "ivybridge",
- "kabylake",
- "sandybridge",
- "silvermont",
- "skylake",
- "stoneyridge",
- "tigerlake",
- "whiskeylake",
- "x86_64",
- },
- X86_64: {
- "amberlake",
- "broadwell",
- "haswell",
- "icelake",
- "ivybridge",
- "kabylake",
- "sandybridge",
- "silvermont",
- "skylake",
- "stoneyridge",
- "tigerlake",
- "whiskeylake",
- },
-}
-
-var archFeatures = map[ArchType][]string{
- Arm: {
- "neon",
- },
- Arm64: {
- "dotprod",
- },
- X86: {
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "aes_ni",
- "avx",
- "avx2",
- "avx512",
- "popcnt",
- "movbe",
- },
- X86_64: {
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "aes_ni",
- "avx",
- "avx2",
- "avx512",
- "popcnt",
- },
-}
-
-var archFeatureMap = map[ArchType]map[string][]string{
- Arm: {
- "armv7-a-neon": {
- "neon",
- },
- "armv8-a": {
- "neon",
- },
- "armv8-2a": {
- "neon",
- },
- },
- Arm64: {
- "armv8-2a-dotprod": {
- "dotprod",
- },
- },
- X86: {
- "amberlake": {
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "avx",
- "avx2",
- "aes_ni",
- "popcnt",
- },
- "atom": {
- "ssse3",
- "movbe",
- },
- "broadwell": {
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "avx",
- "avx2",
- "aes_ni",
- "popcnt",
- },
- "haswell": {
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "aes_ni",
- "avx",
- "popcnt",
- "movbe",
- },
- "icelake": {
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "avx",
- "avx2",
- "avx512",
- "aes_ni",
- "popcnt",
- },
- "ivybridge": {
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "aes_ni",
- "avx",
- "popcnt",
- },
- "kabylake": {
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "avx",
- "avx2",
- "aes_ni",
- "popcnt",
- },
- "sandybridge": {
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "popcnt",
- },
- "silvermont": {
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "aes_ni",
- "popcnt",
- "movbe",
- },
- "skylake": {
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "avx",
- "avx2",
- "avx512",
- "aes_ni",
- "popcnt",
- },
- "stoneyridge": {
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "aes_ni",
- "avx",
- "avx2",
- "popcnt",
- "movbe",
- },
- "tigerlake": {
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "avx",
- "avx2",
- "avx512",
- "aes_ni",
- "popcnt",
- },
- "whiskeylake": {
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "avx",
- "avx2",
- "avx512",
- "aes_ni",
- "popcnt",
- },
- "x86_64": {
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "popcnt",
- },
- },
- X86_64: {
- "amberlake": {
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "avx",
- "avx2",
- "aes_ni",
- "popcnt",
- },
- "broadwell": {
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "avx",
- "avx2",
- "aes_ni",
- "popcnt",
- },
- "haswell": {
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "aes_ni",
- "avx",
- "popcnt",
- },
- "icelake": {
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "avx",
- "avx2",
- "avx512",
- "aes_ni",
- "popcnt",
- },
- "ivybridge": {
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "aes_ni",
- "avx",
- "popcnt",
- },
- "kabylake": {
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "avx",
- "avx2",
- "aes_ni",
- "popcnt",
- },
- "sandybridge": {
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "popcnt",
- },
- "silvermont": {
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "aes_ni",
- "popcnt",
- },
- "skylake": {
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "avx",
- "avx2",
- "avx512",
- "aes_ni",
- "popcnt",
- },
- "stoneyridge": {
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "aes_ni",
- "avx",
- "avx2",
- "popcnt",
- },
- "tigerlake": {
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "avx",
- "avx2",
- "avx512",
- "aes_ni",
- "popcnt",
- },
- "whiskeylake": {
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "avx",
- "avx2",
- "avx512",
- "aes_ni",
- "popcnt",
- },
- },
-}
-
-var defaultArchFeatureMap = map[OsType]map[ArchType][]string{}
-
-func RegisterDefaultArchVariantFeatures(os OsType, arch ArchType, features ...string) {
- checkCalledFromInit()
-
- for _, feature := range features {
- if !InList(feature, archFeatures[arch]) {
- panic(fmt.Errorf("Invalid feature %q for arch %q variant \"\"", feature, arch))
- }
- }
-
- if defaultArchFeatureMap[os] == nil {
- defaultArchFeatureMap[os] = make(map[ArchType][]string)
- }
- defaultArchFeatureMap[os][arch] = features
-}
-
// An Arch indicates a single CPU architecture.
type Arch struct {
- ArchType ArchType
- ArchVariant string
- CpuVariant string
- Abi []string
+ // The type of the architecture (arm, arm64, x86, or x86_64).
+ ArchType ArchType
+
+ // The variant of the architecture, for example "armv7-a" or "armv7-a-neon" for arm.
+ ArchVariant string
+
+ // The variant of the CPU, for example "cortex-a53" for arm64.
+ CpuVariant string
+
+ // The list of Android app ABIs supported by the CPU architecture, for example "arm64-v8a".
+ Abi []string
+
+ // The list of arch-specific features supported by the CPU architecture, for example "neon".
ArchFeatures []string
}
+// String returns the Arch as a string. The value is used as the name of the variant created
+// by archMutator.
func (a Arch) String() string {
s := a.ArchType.String()
if a.ArchVariant != "" {
@@ -521,12 +120,42 @@
return s
}
+// ArchType is used to define the 4 supported architecture types (arm, arm64, x86, x86_64), as
+// well as the "common" architecture used for modules that support multiple architectures, for
+// example Java modules.
type ArchType struct {
- Name string
- Field string
+ // Name is the name of the architecture type, "arm", "arm64", "x86", or "x86_64".
+ Name string
+
+ // Field is the name of the field used in properties that refer to the architecture, e.g. "Arm64".
+ Field string
+
+ // Multilib is either "lib32" or "lib64" for 32-bit or 64-bit architectures.
Multilib string
}
+// String returns the name of the ArchType.
+func (a ArchType) String() string {
+ return a.Name
+}
+
+const COMMON_VARIANT = "common"
+
+var (
+ archTypeList []ArchType
+
+ Arm = newArch("arm", "lib32")
+ Arm64 = newArch("arm64", "lib64")
+ X86 = newArch("x86", "lib32")
+ X86_64 = newArch("x86_64", "lib64")
+
+ Common = ArchType{
+ Name: COMMON_VARIANT,
+ }
+)
+
+var archTypeMap = map[string]ArchType{}
+
func newArch(name, multilib string) ArchType {
archType := ArchType{
Name: name,
@@ -534,25 +163,25 @@
Multilib: multilib,
}
archTypeList = append(archTypeList, archType)
+ archTypeMap[name] = archType
return archType
}
+// ArchTypeList returns the 4 supported ArchTypes for arm, arm64, x86 and x86_64.
func ArchTypeList() []ArchType {
return append([]ArchType(nil), archTypeList...)
}
-func (a ArchType) String() string {
- return a.Name
-}
-
-var _ encoding.TextMarshaler = ArchType{}
-
+// MarshalText allows an ArchType to be serialized through any encoder that supports
+// encoding.TextMarshaler.
func (a ArchType) MarshalText() ([]byte, error) {
return []byte(strconv.Quote(a.String())), nil
}
-var _ encoding.TextUnmarshaler = &ArchType{}
+var _ encoding.TextMarshaler = ArchType{}
+// UnmarshalText allows an ArchType to be deserialized through any decoder that supports
+// encoding.TextUnmarshaler.
func (a *ArchType) UnmarshalText(text []byte) error {
if u, ok := archTypeMap[string(text)]; ok {
*a = u
@@ -562,67 +191,22 @@
return fmt.Errorf("unknown ArchType %q", text)
}
-var BuildOs = func() OsType {
- switch runtime.GOOS {
- case "linux":
- return Linux
- case "darwin":
- return Darwin
- default:
- panic(fmt.Sprintf("unsupported OS: %s", runtime.GOOS))
- }
-}()
+var _ encoding.TextUnmarshaler = &ArchType{}
-var BuildArch = func() ArchType {
- switch runtime.GOARCH {
- case "amd64":
- return X86_64
- default:
- panic(fmt.Sprintf("unsupported Arch: %s", runtime.GOARCH))
- }
-}()
-
-var (
- OsTypeList []OsType
- commonTargetMap = make(map[string]Target)
-
- NoOsType OsType
- Linux = NewOsType("linux_glibc", Host, false)
- Darwin = NewOsType("darwin", Host, false)
- LinuxBionic = NewOsType("linux_bionic", Host, false)
- Windows = NewOsType("windows", Host, true)
- Android = NewOsType("android", Device, false)
- Fuchsia = NewOsType("fuchsia", Device, false)
-
- // A pseudo OSType for a common os variant, which is OSType agnostic and which
- // has dependencies on all the OS variants.
- CommonOS = NewOsType("common_os", Generic, false)
-
- osArchTypeMap = map[OsType][]ArchType{
- Linux: []ArchType{X86, X86_64},
- LinuxBionic: []ArchType{Arm64, X86_64},
- Darwin: []ArchType{X86_64},
- Windows: []ArchType{X86, X86_64},
- Android: []ArchType{Arm, Arm64, X86, X86_64},
- Fuchsia: []ArchType{Arm64, X86_64},
- }
-)
-
-type OsType struct {
- Name, Field string
- Class OsClass
-
- DefaultDisabled bool
-}
-
+// OsClass is an enum that describes whether a variant of a module runs on the host, on the device,
+// or is generic.
type OsClass int
const (
+ // Generic is used for variants of modules that are not OS-specific.
Generic OsClass = iota
+ // Device is used for variants of modules that run on the device.
Device
+ // Host is used for variants of modules that run on the host.
Host
)
+// String returns the OsClass as a string.
func (class OsClass) String() string {
switch class {
case Generic:
@@ -636,22 +220,48 @@
}
}
+// OsType describes an OS variant of a module.
+type OsType struct {
+ // Name is the name of the OS. It is also used as the name of the property in Android.bp
+ // files.
+ Name string
+
+ // Field is the name of the OS converted to an exported field name, i.e. with the first
+ // character capitalized.
+ Field string
+
+ // Class is the OsClass of the OS.
+ Class OsClass
+
+ // DefaultDisabled is set when the module variants for the OS should not be created unless
+ // the module explicitly requests them. This is used to limit Windows cross compilation to
+ // only modules that need it.
+ DefaultDisabled bool
+}
+
+// String returns the name of the OsType.
func (os OsType) String() string {
return os.Name
}
+// Bionic returns true if the OS uses the Bionic libc runtime, i.e. if the OS is Android or
+// is Linux with Bionic.
func (os OsType) Bionic() bool {
return os == Android || os == LinuxBionic
}
+// Linux returns true if the OS uses the Linux kernel, i.e. if the OS is Android or is Linux
+// with or without the Bionic libc runtime.
func (os OsType) Linux() bool {
return os == Android || os == Linux || os == LinuxBionic
}
-func NewOsType(name string, class OsClass, defDisabled bool) OsType {
+// newOsType constructs an OsType and adds it to the global lists.
+func newOsType(name string, class OsClass, defDisabled bool, archTypes ...ArchType) OsType {
+ checkCalledFromInit()
os := OsType{
Name: name,
- Field: strings.Title(name),
+ Field: proptools.FieldNameForProperty(name),
Class: class,
DefaultDisabled: defDisabled,
@@ -663,10 +273,12 @@
} else {
commonTargetMap[name] = Target{Os: os, Arch: Arch{ArchType: Common}}
}
+ osArchTypeMap[os] = archTypes
return os
}
+// osByName returns the OsType that has the given name, or NoOsType if none match.
func osByName(name string) OsType {
for _, os := range OsTypeList {
if os.Name == name {
@@ -677,18 +289,74 @@
return NoOsType
}
-type NativeBridgeSupport bool
+// BuildOs returns the OsType for the OS that the build is running on.
+var BuildOs = func() OsType {
+ switch runtime.GOOS {
+ case "linux":
+ return Linux
+ case "darwin":
+ return Darwin
+ default:
+ panic(fmt.Sprintf("unsupported OS: %s", runtime.GOOS))
+ }
+}()
-const (
- NativeBridgeDisabled NativeBridgeSupport = false
- NativeBridgeEnabled NativeBridgeSupport = true
+// BuildArch returns the ArchType for the CPU that the build is running on.
+var BuildArch = func() ArchType {
+ switch runtime.GOARCH {
+ case "amd64":
+ return X86_64
+ default:
+ panic(fmt.Sprintf("unsupported Arch: %s", runtime.GOARCH))
+ }
+}()
+
+var (
+ // OsTypeList contains a list of all the supported OsTypes, including ones not supported
+ // by the current build host or the target device.
+ OsTypeList []OsType
+ // commonTargetMap maps names of OsTypes to the corresponding common Target, i.e. the
+ // Target with the same OsType and the common ArchType.
+ commonTargetMap = make(map[string]Target)
+ // osArchTypeMap maps OsTypes to the list of supported ArchTypes for that OS.
+ osArchTypeMap = map[OsType][]ArchType{}
+
+ // NoOsType is a placeholder for when no OS is needed.
+ NoOsType OsType
+ // Linux is the OS for the Linux kernel plus the glibc runtime.
+ Linux = newOsType("linux_glibc", Host, false, X86, X86_64)
+ // Darwin is the OS for MacOS/Darwin host machines.
+ Darwin = newOsType("darwin", Host, false, X86_64)
+ // LinuxBionic is the OS for the Linux kernel plus the Bionic libc runtime, but without the
+ // rest of Android.
+ LinuxBionic = newOsType("linux_bionic", Host, false, Arm64, X86_64)
+ // Windows the OS for Windows host machines.
+ Windows = newOsType("windows", Host, true, X86, X86_64)
+ // Android is the OS for target devices that run all of Android, including the Linux kernel
+ // and the Bionic libc runtime.
+ Android = newOsType("android", Device, false, Arm, Arm64, X86, X86_64)
+ // Fuchsia is the OS for target devices that run Fuchsia.
+ Fuchsia = newOsType("fuchsia", Device, false, Arm64, X86_64)
+
+ // CommonOS is a pseudo OSType for a common OS variant, which is OsType agnostic and which
+ // has dependencies on all the OS variants.
+ CommonOS = newOsType("common_os", Generic, false)
)
+// Target specifies the OS and architecture that a module is being compiled for.
type Target struct {
- Os OsType
- Arch Arch
- NativeBridge NativeBridgeSupport
+ // Os the OS that the module is being compiled for (e.g. "linux_glibc", "android").
+ Os OsType
+ // Arch is the architecture that the module is being compiled for.
+ Arch Arch
+ // NativeBridge is NativeBridgeEnabled if the architecture is supported using NativeBridge
+ // (i.e. arm on x86) for this device.
+ NativeBridge NativeBridgeSupport
+ // NativeBridgeHostArchName is the name of the real architecture that is used to implement
+ // the NativeBridge architecture. For example, for arm on x86 this would be "x86".
NativeBridgeHostArchName string
+ // NativeBridgeRelativePath is the name of the subdirectory that will contain NativeBridge
+ // libraries and binaries.
NativeBridgeRelativePath string
// HostCross is true when the target cannot run natively on the current build host.
@@ -697,14 +365,25 @@
HostCross bool
}
+// NativeBridgeSupport is an enum that specifies if a Target supports NativeBridge.
+type NativeBridgeSupport bool
+
+const (
+ NativeBridgeDisabled NativeBridgeSupport = false
+ NativeBridgeEnabled NativeBridgeSupport = true
+)
+
+// String returns the OS and arch variations used for the Target.
func (target Target) String() string {
return target.OsVariation() + "_" + target.ArchVariation()
}
+// OsVariation returns the name of the variation used by the osMutator for the Target.
func (target Target) OsVariation() string {
return target.Os.String()
}
+// ArchVariation returns the name of the variation used by the archMutator for the Target.
func (target Target) ArchVariation() string {
var variation string
if target.NativeBridge {
@@ -715,6 +394,8 @@
return variation
}
+// Variations returns a list of blueprint.Variations for the osMutator and archMutator for the
+// Target.
func (target Target) Variations() []blueprint.Variation {
return []blueprint.Variation{
{Mutator: "os", Variation: target.OsVariation()},
@@ -722,10 +403,16 @@
}
}
+// osMutator splits an arch-specific module into a variant for each OS that is enabled for the
+// module. It uses the HostOrDevice value passed to InitAndroidArchModule and the
+// device_supported and host_supported properties to determine which OsTypes are enabled for this
+// module, then searches through the Targets to determine which have enabled Targets for this
+// module.
func osMutator(bpctx blueprint.BottomUpMutatorContext) {
var module Module
var ok bool
if module, ok = bpctx.Module().(Module); !ok {
+ // The module is not a Soong module, it is a Blueprint module.
if bootstrap.IsBootstrapModule(bpctx.Module()) {
// Bootstrap Go modules are always the build OS or linux bionic.
config := bpctx.Config().(Config)
@@ -749,12 +436,14 @@
base := module.base()
+ // Nothing to do for modules that are not architecture specific (e.g. a genrule).
if !base.ArchSpecific() {
return
}
+ // Collect a list of OSTypes supported by this module based on the HostOrDevice value
+ // passed to InitAndroidArchModule and the device_supported and host_supported properties.
var moduleOSList []OsType
-
for _, os := range OsTypeList {
for _, t := range mctx.Config().Targets[os] {
if base.supportsTarget(t) {
@@ -764,20 +453,21 @@
}
}
+ // If there are no supported OSes then disable the module.
if len(moduleOSList) == 0 {
base.Disable()
return
}
+ // Convert the list of supported OsTypes to the variation names.
osNames := make([]string, len(moduleOSList))
-
for i, os := range moduleOSList {
osNames[i] = os.String()
}
createCommonOSVariant := base.commonProperties.CreateCommonOSVariant
if createCommonOSVariant {
- // A CommonOS variant was requested so add it to the list of OS's variants to
+ // A CommonOS variant was requested so add it to the list of OS variants to
// create. It needs to be added to the end because it needs to depend on the
// the other variants in the list returned by CreateVariations(...) and inter
// variant dependencies can only be created from a later variant in that list to
@@ -787,6 +477,8 @@
moduleOSList = append(moduleOSList, CommonOS)
}
+ // Create the variations, annotate each one with which OS it was created for, and
+ // squash the appropriate OS-specific properties into the top level properties.
modules := mctx.CreateVariations(osNames...)
for i, m := range modules {
m.base().commonProperties.CompileOS = moduleOSList[i]
@@ -841,7 +533,7 @@
}
// archMutator splits a module into a variant for each Target requested by the module. Target selection
-// for a module is in three levels, OsClass, mulitlib, and then Target.
+// for a module is in three levels, OsClass, multilib, and then Target.
// OsClass selection is determined by:
// - The HostOrDeviceSupported value passed in to InitAndroidArchModule by the module type factory, which selects
// whether the module type can compile for host, device or both.
@@ -858,7 +550,11 @@
// but may be arm for a 32-bit only build.
// "32": compile for only a single 32-bit Target supported by the OsClass.
// "64": compile for only a single 64-bit Target supported by the OsClass.
-// "common": compile a for a single Target that will work on all Targets suported by the OsClass (for example Java).
+// "common": compile a for a single Target that will work on all Targets supported by the OsClass (for example Java).
+// "common_first": compile a for a Target that will work on all Targets supported by the OsClass
+// (same as "common"), plus a second Target for the preferred Target supported by the OsClass
+// (same as "first"). This is used for java_binary that produces a common .jar and a wrapper
+// executable script.
//
// Once the list of Targets is determined, the module is split into a variant for each Target.
//
@@ -899,8 +595,8 @@
osTargets := mctx.Config().Targets[os]
image := base.commonProperties.ImageVariation
- // Filter NativeBridge targets unless they are explicitly supported
- // Skip creating native bridge variants for vendor modules
+ // Filter NativeBridge targets unless they are explicitly supported.
+ // Skip creating native bridge variants for non-core modules.
if os == Android &&
!(Bool(base.commonProperties.Native_bridge_supported) && image == CoreVariation) {
@@ -919,17 +615,24 @@
osTargets = []Target{osTargets[0]}
}
+ // Some modules want compile_multilib: "first" to mean 32-bit, not 64-bit.
+ // This is used for Windows support and for HOST_PREFER_32_BIT=true support for Art modules.
prefer32 := false
if base.prefer32 != nil {
prefer32 = base.prefer32(mctx, base, os)
}
+ // Determine the multilib selection for this module.
multilib, extraMultilib := decodeMultilib(base, os.Class)
+
+ // Convert the multilib selection into a list of Targets.
targets, err := decodeMultilibTargets(multilib, osTargets, prefer32)
if err != nil {
mctx.ModuleErrorf("%s", err.Error())
}
+ // If the module is using extraMultilib, decode the extraMultilib selection into
+ // a separate list of Targets.
var multiTargets []Target
if extraMultilib != "" {
multiTargets, err = decodeMultilibTargets(extraMultilib, osTargets, prefer32)
@@ -938,23 +641,27 @@
}
}
+ // Recovery is always the primary architecture, filter out any other architectures.
if image == RecoveryVariation {
primaryArch := mctx.Config().DevicePrimaryArchType()
targets = filterToArch(targets, primaryArch)
multiTargets = filterToArch(multiTargets, primaryArch)
}
+ // If there are no supported targets disable the module.
if len(targets) == 0 {
base.Disable()
return
}
+ // Convert the targets into a list of arch variation names.
targetNames := make([]string, len(targets))
-
for i, target := range targets {
targetNames[i] = target.ArchVariation()
}
+ // Create the variations, annotate each one with which Target it was created for, and
+ // squash the appropriate arch-specific properties into the top level properties.
modules := mctx.CreateVariations(targetNames...)
for i, m := range modules {
addTargetProperties(m, targets[i], multiTargets, i == 0)
@@ -968,22 +675,34 @@
}
}
+// addTargetProperties annotates a variant with the Target is is being compiled for, the list
+// of additional Targets it is supporting (if any), and whether it is the primary Target for
+// the module.
func addTargetProperties(m Module, target Target, multiTargets []Target, primaryTarget bool) {
m.base().commonProperties.CompileTarget = target
m.base().commonProperties.CompileMultiTargets = multiTargets
m.base().commonProperties.CompilePrimary = primaryTarget
}
+// decodeMultilib returns the appropriate compile_multilib property for the module, or the default
+// multilib from the factory's call to InitAndroidArchModule if none was set. For modules that
+// called InitAndroidMultiTargetsArchModule it always returns "common" for multilib, and returns
+// the actual multilib in extraMultilib.
func decodeMultilib(base *ModuleBase, class OsClass) (multilib, extraMultilib string) {
+ // First check the "android.compile_multilib" or "host.compile_multilib" properties.
switch class {
case Device:
multilib = String(base.commonProperties.Target.Android.Compile_multilib)
case Host:
multilib = String(base.commonProperties.Target.Host.Compile_multilib)
}
+
+ // If those aren't set, try the "compile_multilib" property.
if multilib == "" {
multilib = String(base.commonProperties.Compile_multilib)
}
+
+ // If that wasn't set, use the default multilib set by the factory.
if multilib == "" {
multilib = base.commonProperties.Default_multilib
}
@@ -1000,6 +719,8 @@
}
}
+// filterToArch takes a list of Targets and an ArchType, and returns a modified list that contains
+// only Targets that have the specified ArchType.
func filterToArch(targets []Target, arch ArchType) []Target {
for i := 0; i < len(targets); i++ {
if targets[i].Arch.ArchType != arch {
@@ -1010,17 +731,27 @@
return targets
}
-type archPropTypeDesc struct {
- arch, multilib, target reflect.Type
-}
-
+// archPropRoot is a struct type used as the top level of the arch-specific properties. It
+// contains the "arch", "multilib", and "target" property structs. It is used to split up the
+// property structs to limit how much is allocated when a single arch-specific property group is
+// used. The types are interface{} because they will hold instances of runtime-created types.
type archPropRoot struct {
Arch, Multilib, Target interface{}
}
+// archPropTypeDesc holds the runtime-created types for the property structs to instantiate to
+// create an archPropRoot property struct.
+type archPropTypeDesc struct {
+ arch, multilib, target reflect.Type
+}
+
// createArchPropTypeDesc takes a reflect.Type that is either a struct or a pointer to a struct, and
// returns lists of reflect.Types that contains the arch-variant properties inside structs for each
// arch, multilib and target property.
+//
+// This is a relatively expensive operation, so the results are cached in the global
+// archPropTypeMap. It is constructed entirely based on compile-time data, so there is no need
+// to isolate the results between multiple tests running in parallel.
func createArchPropTypeDesc(props reflect.Type) []archPropTypeDesc {
// Each property struct shard will be nested many times under the runtime generated arch struct,
// which can hit the limit of 64kB for the name of runtime generated structs. They are nested
@@ -1029,7 +760,12 @@
// could cause problems if a single deeply nested property no longer fits in the name.
const maxArchTypeNameSize = 500
+ // Convert the type to a new set of types that contains only the arch-specific properties
+ // (those that are tagged with `android:"arch_specific"`), and sharded into multiple types
+ // to keep the runtime-generated names under the limit.
propShards, _ := proptools.FilterPropertyStructSharded(props, maxArchTypeNameSize, filterArchStruct)
+
+ // If the type has no arch-specific properties there is nothing to do.
if len(propShards) == 0 {
return nil
}
@@ -1037,6 +773,8 @@
var ret []archPropTypeDesc
for _, props := range propShards {
+ // variantFields takes a list of variant property field names and returns a list the
+ // StructFields with the names and the type of the current shard.
variantFields := func(names []string) []reflect.StructField {
ret := make([]reflect.StructField, len(names))
@@ -1048,9 +786,11 @@
return ret
}
+ // Create a type that contains the properties in this shard repeated for each
+ // architecture, architecture variant, and architecture feature.
archFields := make([]reflect.StructField, len(archTypeList))
for i, arch := range archTypeList {
- variants := []string{}
+ var variants []string
for _, archVariant := range archVariants[arch] {
archVariant := variantReplacer.Replace(archVariant)
@@ -1061,8 +801,13 @@
variants = append(variants, proptools.FieldNameForProperty(feature))
}
+ // Create the StructFields for each architecture variant architecture feature
+ // (e.g. "arch.arm.cortex-a53" or "arch.arm.neon").
fields := variantFields(variants)
+ // Create the StructField for the architecture itself (e.g. "arch.arm"). The special
+ // "BlueprintEmbed" name is used by Blueprint to put the properties in the
+ // parent struct.
fields = append([]reflect.StructField{{
Name: "BlueprintEmbed",
Type: props,
@@ -1074,10 +819,15 @@
Type: reflect.StructOf(fields),
}
}
+
+ // Create the type of the "arch" property struct for this shard.
archType := reflect.StructOf(archFields)
+ // Create the type for the "multilib" property struct for this shard, containing the
+ // "multilib.lib32" and "multilib.lib64" property structs.
multilibType := reflect.StructOf(variantFields([]string{"Lib32", "Lib64"}))
+ // Start with a list of the special targets
targets := []string{
"Host",
"Android64",
@@ -1090,11 +840,14 @@
"Native_bridge",
}
for _, os := range OsTypeList {
+ // Add all the OSes.
targets = append(targets, os.Field)
+ // Add the OS/Arch combinations, e.g. "android_arm64".
for _, archType := range osArchTypeMap[os] {
targets = append(targets, os.Field+"_"+archType.Name)
+ // Also add the special "linux_<arch>" and "bionic_<arch>" property structs.
if os.Linux() {
target := "Linux_" + archType.Name
if !InList(target, targets) {
@@ -1110,8 +863,10 @@
}
}
+ // Create the type for the "target" property struct for this shard.
targetType := reflect.StructOf(variantFields(targets))
+ // Return a descriptor of the 3 runtime-created types.
ret = append(ret, archPropTypeDesc{
arch: reflect.PtrTo(archType),
multilib: reflect.PtrTo(multilibType),
@@ -1121,6 +876,11 @@
return ret
}
+// variantReplacer converts architecture variant or architecture feature names into names that
+// are valid for an Android.bp file.
+var variantReplacer = strings.NewReplacer("-", "_", ".", "_")
+
+// filterArchStruct returns true if the given field is an architecture specific property.
func filterArchStruct(field reflect.StructField, prefix string) (bool, reflect.StructField) {
if proptools.HasTag(field, "android", "arch_variant") {
// The arch_variant field isn't necessary past this point
@@ -1147,12 +907,17 @@
return false, field
}
+// archPropTypeMap contains a cache of the results of createArchPropTypeDesc for each type. It is
+// shared across all Contexts, but is constructed based only on compile-time information so there
+// is no risk of contaminating one Context with data from another.
var archPropTypeMap OncePer
-func InitArchModule(m Module) {
+// initArchModule adds the architecture-specific property structs to a Module.
+func initArchModule(m Module) {
base := m.base()
+ // Store the original list of top level property structs
base.generalProperties = m.GetProperties()
for _, properties := range base.generalProperties {
@@ -1169,10 +934,13 @@
propertiesValue.Interface()))
}
+ // Get or create the arch-specific property struct types for this property struct type.
archPropTypes := archPropTypeMap.Once(NewCustomOnceKey(t), func() interface{} {
return createArchPropTypeDesc(t)
}).([]archPropTypeDesc)
+ // Instantiate one of each arch-specific property struct type and add it to the
+ // properties for the Module.
var archProperties []interface{}
for _, t := range archPropTypes {
archProperties = append(archProperties, &archPropRoot{
@@ -1185,14 +953,18 @@
m.AddProperties(archProperties...)
}
+ // Update the list of properties that can be set by a defaults module or a call to
+ // AppendMatchingProperties or PrependMatchingProperties.
base.customizableProperties = m.GetProperties()
}
-var variantReplacer = strings.NewReplacer("-", "_", ".", "_")
-
+// appendProperties squashes properties from the given field of the given src property struct
+// into the dst property struct. Returns the reflect.Value of the field in the src property
+// struct to be used for further appendProperties calls on fields of that property struct.
func (m *ModuleBase) appendProperties(ctx BottomUpMutatorContext,
dst interface{}, src reflect.Value, field, srcPrefix string) reflect.Value {
+ // Step into non-nil pointers to structs in the src value.
if src.Kind() == reflect.Ptr {
if src.IsNil() {
return src
@@ -1200,18 +972,25 @@
src = src.Elem()
}
+ // Find the requested field in the src struct.
src = src.FieldByName(field)
if !src.IsValid() {
ctx.ModuleErrorf("field %q does not exist", srcPrefix)
return src
}
+ // Save the value of the field in the src struct to return.
ret := src
+ // If the value of the field is a struct (as opposed to a pointer to a struct) then step
+ // into the BlueprintEmbed field.
if src.Kind() == reflect.Struct {
src = src.FieldByName("BlueprintEmbed")
}
+ // order checks the `android:"variant_prepend"` tag to handle properties where the
+ // arch-specific value needs to come before the generic value, for example for lists of
+ // include directories.
order := func(property string,
dstField, srcField reflect.StructField,
dstValue, srcValue interface{}) (proptools.Order, error) {
@@ -1222,6 +1001,7 @@
}
}
+ // Squash the located property struct into the destination property struct.
err := proptools.ExtendMatchingProperties([]interface{}{dst}, src.Interface(), nil, order)
if err != nil {
if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok {
@@ -1234,7 +1014,8 @@
return ret
}
-// Rewrite the module's properties structs to contain os-specific values.
+// Squash the appropriate OS-specific property structs into the matching top level property structs
+// based on the CompileOS value that was annotated on the variant.
func (m *ModuleBase) setOSProperties(ctx BottomUpMutatorContext) {
os := m.commonProperties.CompileOS
@@ -1328,7 +1109,8 @@
}
}
-// Rewrite the module's properties structs to contain arch-specific values.
+// Squash the appropriate arch-specific property structs into the matching top level property
+// structs based on the CompileTarget value that was annotated on the variant.
func (m *ModuleBase) setArchProperties(ctx BottomUpMutatorContext) {
arch := m.Arch()
os := m.Os()
@@ -1480,22 +1262,7 @@
}
}
-func forEachInterface(v reflect.Value, f func(reflect.Value)) {
- switch v.Kind() {
- case reflect.Interface:
- f(v)
- case reflect.Struct:
- for i := 0; i < v.NumField(); i++ {
- forEachInterface(v.Field(i), f)
- }
- case reflect.Ptr:
- forEachInterface(v.Elem(), f)
- default:
- panic(fmt.Errorf("Unsupported kind %s", v.Kind()))
- }
-}
-
-// Convert the arch product variables into a list of targets for each os class structs
+// Convert the arch product variables into a list of targets for each OsType.
func decodeTargetProductVariables(config *config) (map[OsType][]Target, error) {
variables := config.productVariables
@@ -1567,19 +1334,26 @@
return nil, fmt.Errorf("No host primary architecture set")
}
+ // The primary host target, which must always exist.
addTarget(BuildOs, *variables.HostArch, nil, nil, nil, NativeBridgeDisabled, nil, nil)
+ // An optional secondary host target.
if variables.HostSecondaryArch != nil && *variables.HostSecondaryArch != "" {
addTarget(BuildOs, *variables.HostSecondaryArch, nil, nil, nil, NativeBridgeDisabled, nil, nil)
}
+ // An optional host target that uses the Bionic glibc runtime.
if Bool(config.Host_bionic) {
addTarget(LinuxBionic, "x86_64", nil, nil, nil, NativeBridgeDisabled, nil, nil)
}
+
+ // An optional cross-compiled host target that uses the Bionic glibc runtime on an arm64
+ // architecture.
if Bool(config.Host_bionic_arm64) {
addTarget(LinuxBionic, "arm64", nil, nil, nil, NativeBridgeDisabled, nil, nil)
}
+ // Optional cross-compiled host targets, generally Windows.
if String(variables.CrossHost) != "" {
crossHostOs := osByName(*variables.CrossHost)
if crossHostOs == NoOsType {
@@ -1590,28 +1364,34 @@
return nil, fmt.Errorf("No cross-host primary architecture set")
}
+ // The primary cross-compiled host target.
addTarget(crossHostOs, *variables.CrossHostArch, nil, nil, nil, NativeBridgeDisabled, nil, nil)
+ // An optional secondary cross-compiled host target.
if variables.CrossHostSecondaryArch != nil && *variables.CrossHostSecondaryArch != "" {
addTarget(crossHostOs, *variables.CrossHostSecondaryArch, nil, nil, nil, NativeBridgeDisabled, nil, nil)
}
}
+ // Optional device targets
if variables.DeviceArch != nil && *variables.DeviceArch != "" {
var target = Android
if Bool(variables.Fuchsia) {
target = Fuchsia
}
+ // The primary device target.
addTarget(target, *variables.DeviceArch, variables.DeviceArchVariant,
variables.DeviceCpuVariant, variables.DeviceAbi, NativeBridgeDisabled, nil, nil)
+ // An optional secondary device target.
if variables.DeviceSecondaryArch != nil && *variables.DeviceSecondaryArch != "" {
addTarget(Android, *variables.DeviceSecondaryArch,
variables.DeviceSecondaryArchVariant, variables.DeviceSecondaryCpuVariant,
variables.DeviceSecondaryAbi, NativeBridgeDisabled, nil, nil)
}
+ // An optional NativeBridge device target.
if variables.NativeBridgeArch != nil && *variables.NativeBridgeArch != "" {
addTarget(Android, *variables.NativeBridgeArch,
variables.NativeBridgeArchVariant, variables.NativeBridgeCpuVariant,
@@ -1619,6 +1399,7 @@
variables.NativeBridgeRelativePath)
}
+ // An optional secondary NativeBridge device target.
if variables.DeviceSecondaryArch != nil && *variables.DeviceSecondaryArch != "" &&
variables.NativeBridgeSecondaryArch != nil && *variables.NativeBridgeSecondaryArch != "" {
addTarget(Android, *variables.NativeBridgeSecondaryArch,
@@ -1653,6 +1434,7 @@
return false
}
+// archConfig describes a built-in configuration.
type archConfig struct {
arch string
archVariant string
@@ -1660,6 +1442,7 @@
abi []string
}
+// getMegaDeviceConfig returns a list of archConfigs for every architecture simultaneously.
func getMegaDeviceConfig() []archConfig {
return []archConfig{
{"arm", "armv7-a", "generic", []string{"armeabi-v7a"}},
@@ -1706,6 +1489,7 @@
}
}
+// getNdkAbisConfig returns a list of archConfigs for the ABIs supported by the NDK.
func getNdkAbisConfig() []archConfig {
return []archConfig{
{"arm", "armv7-a", "", []string{"armeabi-v7a"}},
@@ -1715,6 +1499,7 @@
}
}
+// getAmlAbisConfig returns a list of archConfigs for the ABIs supported by mainline modules.
func getAmlAbisConfig() []archConfig {
return []archConfig{
{"arm", "armv7-a-neon", "", []string{"armeabi-v7a"}},
@@ -1724,6 +1509,7 @@
}
}
+// decodeArchSettings converts a list of archConfigs into a list of Targets for the given OsType.
func decodeArchSettings(os OsType, archConfigs []archConfig) ([]Target, error) {
var ret []Target
@@ -1743,15 +1529,9 @@
return ret, nil
}
-// Convert a set of strings from product variables into a single Arch struct
+// decodeArch converts a set of strings from product variables into an Arch struct.
func decodeArch(os OsType, arch string, archVariant, cpuVariant *string, abi []string) (Arch, error) {
- stringPtr := func(p *string) string {
- if p != nil {
- return *p
- }
- return ""
- }
-
+ // Verify the arch is valid
archType, ok := archTypeMap[arch]
if !ok {
return Arch{}, fmt.Errorf("unknown arch %q", arch)
@@ -1759,19 +1539,22 @@
a := Arch{
ArchType: archType,
- ArchVariant: stringPtr(archVariant),
- CpuVariant: stringPtr(cpuVariant),
+ ArchVariant: String(archVariant),
+ CpuVariant: String(cpuVariant),
Abi: abi,
}
+ // Convert generic arch variants into the empty string.
if a.ArchVariant == a.ArchType.Name || a.ArchVariant == "generic" {
a.ArchVariant = ""
}
+ // Convert generic CPU variants into the empty string.
if a.CpuVariant == a.ArchType.Name || a.CpuVariant == "generic" {
a.CpuVariant = ""
}
+ // Filter empty ABIs out of the list.
for i := 0; i < len(a.Abi); i++ {
if a.Abi[i] == "" {
a.Abi = append(a.Abi[:i], a.Abi[i+1:]...)
@@ -1780,10 +1563,12 @@
}
if a.ArchVariant == "" {
+ // Set ArchFeatures from the default arch features.
if featureMap, ok := defaultArchFeatureMap[os]; ok {
a.ArchFeatures = featureMap[archType]
}
} else {
+ // Set ArchFeatures from the arch type.
if featureMap, ok := archFeatureMap[archType]; ok {
a.ArchFeatures = featureMap[a.ArchVariant]
}
@@ -1792,6 +1577,8 @@
return a, nil
}
+// filterMultilibTargets takes a list of Targets and a multilib value and returns a new list of
+// Targets containing only those that have the given multilib value.
func filterMultilibTargets(targets []Target, multilib string) []Target {
var ret []Target
for _, t := range targets {
@@ -1802,8 +1589,8 @@
return ret
}
-// Return the set of Os specific common architecture targets for each Os in a list of
-// targets.
+// getCommonTargets returns the set of Os specific common architecture targets for each Os in a list
+// of targets.
func getCommonTargets(targets []Target) []Target {
var ret []Target
set := make(map[string]bool)
@@ -1818,6 +1605,9 @@
return ret
}
+// firstTarget takes a list of Targets and a list of multilib values and returns a list of Targets
+// that contains zero or one Target for each OsType, selecting the one that matches the earliest
+// filter.
func firstTarget(targets []Target, filters ...string) []Target {
// find the first target from each OS
var ret []Target
@@ -1837,9 +1627,10 @@
return ret
}
-// Use the module multilib setting to select one or more targets from a target list
+// decodeMultilibTargets uses the module's multilib setting to select one or more targets from a
+// list of Targets.
func decodeMultilibTargets(multilib string, targets []Target, prefer32 bool) ([]Target, error) {
- buildTargets := []Target{}
+ var buildTargets []Target
switch multilib {
case "common":
diff --git a/android/arch_list.go b/android/arch_list.go
new file mode 100644
index 0000000..0c33b9d
--- /dev/null
+++ b/android/arch_list.go
@@ -0,0 +1,410 @@
+// Copyright 2020 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 "fmt"
+
+var archVariants = map[ArchType][]string{
+ Arm: {
+ "armv7-a",
+ "armv7-a-neon",
+ "armv8-a",
+ "armv8-2a",
+ "cortex-a7",
+ "cortex-a8",
+ "cortex-a9",
+ "cortex-a15",
+ "cortex-a53",
+ "cortex-a53-a57",
+ "cortex-a55",
+ "cortex-a72",
+ "cortex-a73",
+ "cortex-a75",
+ "cortex-a76",
+ "krait",
+ "kryo",
+ "kryo385",
+ "exynos-m1",
+ "exynos-m2",
+ },
+ Arm64: {
+ "armv8_a",
+ "armv8_2a",
+ "armv8-2a-dotprod",
+ "cortex-a53",
+ "cortex-a55",
+ "cortex-a72",
+ "cortex-a73",
+ "cortex-a75",
+ "cortex-a76",
+ "kryo",
+ "kryo385",
+ "exynos-m1",
+ "exynos-m2",
+ },
+ X86: {
+ "amberlake",
+ "atom",
+ "broadwell",
+ "haswell",
+ "icelake",
+ "ivybridge",
+ "kabylake",
+ "sandybridge",
+ "silvermont",
+ "skylake",
+ "stoneyridge",
+ "tigerlake",
+ "whiskeylake",
+ "x86_64",
+ },
+ X86_64: {
+ "amberlake",
+ "broadwell",
+ "haswell",
+ "icelake",
+ "ivybridge",
+ "kabylake",
+ "sandybridge",
+ "silvermont",
+ "skylake",
+ "stoneyridge",
+ "tigerlake",
+ "whiskeylake",
+ },
+}
+
+var archFeatures = map[ArchType][]string{
+ Arm: {
+ "neon",
+ },
+ Arm64: {
+ "dotprod",
+ },
+ X86: {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "aes_ni",
+ "avx",
+ "avx2",
+ "avx512",
+ "popcnt",
+ "movbe",
+ },
+ X86_64: {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "aes_ni",
+ "avx",
+ "avx2",
+ "avx512",
+ "popcnt",
+ },
+}
+
+var archFeatureMap = map[ArchType]map[string][]string{
+ Arm: {
+ "armv7-a-neon": {
+ "neon",
+ },
+ "armv8-a": {
+ "neon",
+ },
+ "armv8-2a": {
+ "neon",
+ },
+ },
+ Arm64: {
+ "armv8-2a-dotprod": {
+ "dotprod",
+ },
+ },
+ X86: {
+ "amberlake": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "avx",
+ "avx2",
+ "aes_ni",
+ "popcnt",
+ },
+ "atom": {
+ "ssse3",
+ "movbe",
+ },
+ "broadwell": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "avx",
+ "avx2",
+ "aes_ni",
+ "popcnt",
+ },
+ "haswell": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "aes_ni",
+ "avx",
+ "popcnt",
+ "movbe",
+ },
+ "icelake": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "avx",
+ "avx2",
+ "avx512",
+ "aes_ni",
+ "popcnt",
+ },
+ "ivybridge": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "aes_ni",
+ "avx",
+ "popcnt",
+ },
+ "kabylake": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "avx",
+ "avx2",
+ "aes_ni",
+ "popcnt",
+ },
+ "sandybridge": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "popcnt",
+ },
+ "silvermont": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "aes_ni",
+ "popcnt",
+ "movbe",
+ },
+ "skylake": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "avx",
+ "avx2",
+ "avx512",
+ "aes_ni",
+ "popcnt",
+ },
+ "stoneyridge": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "aes_ni",
+ "avx",
+ "avx2",
+ "popcnt",
+ "movbe",
+ },
+ "tigerlake": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "avx",
+ "avx2",
+ "avx512",
+ "aes_ni",
+ "popcnt",
+ },
+ "whiskeylake": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "avx",
+ "avx2",
+ "avx512",
+ "aes_ni",
+ "popcnt",
+ },
+ "x86_64": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "popcnt",
+ },
+ },
+ X86_64: {
+ "amberlake": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "avx",
+ "avx2",
+ "aes_ni",
+ "popcnt",
+ },
+ "broadwell": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "avx",
+ "avx2",
+ "aes_ni",
+ "popcnt",
+ },
+ "haswell": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "aes_ni",
+ "avx",
+ "popcnt",
+ },
+ "icelake": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "avx",
+ "avx2",
+ "avx512",
+ "aes_ni",
+ "popcnt",
+ },
+ "ivybridge": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "aes_ni",
+ "avx",
+ "popcnt",
+ },
+ "kabylake": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "avx",
+ "avx2",
+ "aes_ni",
+ "popcnt",
+ },
+ "sandybridge": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "popcnt",
+ },
+ "silvermont": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "aes_ni",
+ "popcnt",
+ },
+ "skylake": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "avx",
+ "avx2",
+ "avx512",
+ "aes_ni",
+ "popcnt",
+ },
+ "stoneyridge": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "aes_ni",
+ "avx",
+ "avx2",
+ "popcnt",
+ },
+ "tigerlake": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "avx",
+ "avx2",
+ "avx512",
+ "aes_ni",
+ "popcnt",
+ },
+ "whiskeylake": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "avx",
+ "avx2",
+ "avx512",
+ "aes_ni",
+ "popcnt",
+ },
+ },
+}
+
+var defaultArchFeatureMap = map[OsType]map[ArchType][]string{}
+
+// RegisterDefaultArchVariantFeatures is called by files that define Toolchains to specify the
+// arch features that are available for the default arch variant. It must be called from an
+// init() function.
+func RegisterDefaultArchVariantFeatures(os OsType, arch ArchType, features ...string) {
+ checkCalledFromInit()
+
+ for _, feature := range features {
+ if !InList(feature, archFeatures[arch]) {
+ panic(fmt.Errorf("Invalid feature %q for arch %q variant \"\"", feature, arch))
+ }
+ }
+
+ if defaultArchFeatureMap[os] == nil {
+ defaultArchFeatureMap[os] = make(map[ArchType][]string)
+ }
+ defaultArchFeatureMap[os][arch] = features
+}
diff --git a/android/config.go b/android/config.go
index d833c5c..10e7b24 100644
--- a/android/config.go
+++ b/android/config.go
@@ -112,7 +112,9 @@
envDeps map[string]string
envFrozen bool
- inMake bool
+ // Changes behavior based on whether Kati runs after soong_build, or if soong_build
+ // runs standalone.
+ katiEnabled bool
captureBuild bool // true for tests, saves build parameters for each module
ignoreEnvironment bool // true for tests, returns empty from all Getenv calls
@@ -263,10 +265,6 @@
config.mockFileSystem(bp, fs)
- if err := config.fromEnv(); err != nil {
- panic(err)
- }
-
return Config{config}
}
@@ -390,9 +388,9 @@
return Config{}, err
}
- inMakeFile := filepath.Join(buildDir, ".soong.in_make")
- if _, err := os.Stat(absolutePath(inMakeFile)); err == nil {
- config.inMake = true
+ KatiEnabledMarkerFile := filepath.Join(buildDir, ".soong.kati_enabled")
+ if _, err := os.Stat(absolutePath(KatiEnabledMarkerFile)); err == nil {
+ config.katiEnabled = true
}
targets, err := decodeTargetProductVariables(config)
@@ -436,10 +434,6 @@
config.AndroidFirstDeviceTarget = firstTarget(config.Targets[Android], "lib64", "lib32")[0]
}
- if err := config.fromEnv(); err != nil {
- return Config{}, err
- }
-
if Bool(config.productVariables.GcovCoverage) && Bool(config.productVariables.ClangCoverage) {
return Config{}, fmt.Errorf("GcovCoverage and ClangCoverage cannot both be set")
}
@@ -487,17 +481,6 @@
c.mockBpList = blueprint.MockModuleListFile
}
-func (c *config) fromEnv() error {
- switch c.Getenv("EXPERIMENTAL_JAVA_LANGUAGE_LEVEL_9") {
- case "", "true":
- // Do nothing
- default:
- return fmt.Errorf("The environment variable EXPERIMENTAL_JAVA_LANGUAGE_LEVEL_9 is no longer supported. Java language level 9 is now the global default.")
- }
-
- return nil
-}
-
func (c *config) StopBefore() bootstrap.StopBefore {
return c.stopBefore
}
@@ -619,8 +602,8 @@
return c.envDeps
}
-func (c *config) EmbeddedInMake() bool {
- return c.inMake
+func (c *config) KatiEnabled() bool {
+ return c.katiEnabled
}
func (c *config) BuildId() string {
diff --git a/android/defaults.go b/android/defaults.go
index eb013d7..44753ce 100644
--- a/android/defaults.go
+++ b/android/defaults.go
@@ -183,7 +183,7 @@
initAndroidModuleBase(module)
initProductVariableModule(module)
- InitArchModule(module)
+ initArchModule(module)
InitDefaultableModule(module)
// Add properties that will not have defaults applied to them.
diff --git a/android/filegroup.go b/android/filegroup.go
index 68311e3..9425616 100644
--- a/android/filegroup.go
+++ b/android/filegroup.go
@@ -15,6 +15,7 @@
package android
import (
+ "android/soong/bazel"
"strings"
)
@@ -37,6 +38,9 @@
// Create a make variable with the specified name that contains the list of files in the
// filegroup, relative to the root of the source tree.
Export_to_make_var *string
+
+ // Properties for Bazel migration purposes.
+ bazel.Properties
}
type fileGroup struct {
diff --git a/android/makevars.go b/android/makevars.go
index f784395..5101436 100644
--- a/android/makevars.go
+++ b/android/makevars.go
@@ -213,7 +213,7 @@
}
func (s *makeVarsSingleton) GenerateBuildActions(ctx SingletonContext) {
- if !ctx.Config().EmbeddedInMake() {
+ if !ctx.Config().KatiEnabled() {
return
}
diff --git a/android/module.go b/android/module.go
index eeb4fae..7a05096 100644
--- a/android/module.go
+++ b/android/module.go
@@ -717,7 +717,9 @@
DebugMutators []string `blueprint:"mutated"`
DebugVariations []string `blueprint:"mutated"`
- // set by ImageMutator
+ // ImageVariation is set by ImageMutator to specify which image this variation is for,
+ // for example "" for core or "recovery" for recovery. It will often be set to one of the
+ // constants in image.go, but can also be set to a custom value by individual module types.
ImageVariation string `blueprint:"mutated"`
}
@@ -825,6 +827,8 @@
m.base().module = m
}
+// InitAndroidModule initializes the Module as an Android module that is not architecture-specific.
+// It adds the common properties, for example "name" and "enabled".
func InitAndroidModule(m Module) {
initAndroidModuleBase(m)
base := m.base()
@@ -844,6 +848,12 @@
setPrimaryVisibilityProperty(m, "visibility", &base.commonProperties.Visibility)
}
+// InitAndroidArchModule initializes the Module as an Android module that is architecture-specific.
+// It adds the common properties, for example "name" and "enabled", as well as runtime generated
+// property structs for architecture-specific versions of generic properties tagged with
+// `android:"arch_variant"`.
+//
+// InitAndroidModule should not be called if InitAndroidArchModule was called.
func InitAndroidArchModule(m Module, hod HostOrDeviceSupported, defaultMultilib Multilib) {
InitAndroidModule(m)
@@ -857,16 +867,33 @@
m.AddProperties(&base.hostAndDeviceProperties)
}
- InitArchModule(m)
+ initArchModule(m)
}
+// InitAndroidMultiTargetsArchModule initializes the Module as an Android module that is
+// architecture-specific, but will only have a single variant per OS that handles all the
+// architectures simultaneously. The list of Targets that it must handle will be available from
+// ModuleContext.MultiTargets. It adds the common properties, for example "name" and "enabled", as
+// well as runtime generated property structs for architecture-specific versions of generic
+// properties tagged with `android:"arch_variant"`.
+//
+// InitAndroidModule or InitAndroidArchModule should not be called if
+// InitAndroidMultiTargetsArchModule was called.
func InitAndroidMultiTargetsArchModule(m Module, hod HostOrDeviceSupported, defaultMultilib Multilib) {
InitAndroidArchModule(m, hod, defaultMultilib)
m.base().commonProperties.UseTargetVariants = false
}
-// As InitAndroidMultiTargetsArchModule except it creates an additional CommonOS variant that
-// has dependencies on all the OsType specific variants.
+// InitCommonOSAndroidMultiTargetsArchModule initializes the Module as an Android module that is
+// architecture-specific, but will only have a single variant per OS that handles all the
+// architectures simultaneously, and will also have an additional CommonOS variant that has
+// dependencies on all the OS-specific variants. The list of Targets that it must handle will be
+// available from ModuleContext.MultiTargets. It adds the common properties, for example "name" and
+// "enabled", as well as runtime generated property structs for architecture-specific versions of
+// generic properties tagged with `android:"arch_variant"`.
+//
+// InitAndroidModule, InitAndroidArchModule or InitAndroidMultiTargetsArchModule should not be
+// called if InitCommonOSAndroidMultiTargetsArchModule was called.
func InitCommonOSAndroidMultiTargetsArchModule(m Module, hod HostOrDeviceSupported, defaultMultilib Multilib) {
InitAndroidArchModule(m, hod, defaultMultilib)
m.base().commonProperties.UseTargetVariants = false
@@ -1413,7 +1440,7 @@
if len(deps) > 0 {
suffix := ""
- if ctx.Config().EmbeddedInMake() {
+ if ctx.Config().KatiEnabled() {
suffix = "-soong"
}
@@ -2293,7 +2320,7 @@
}
if m.Device() {
- if m.Config().EmbeddedInMake() && !m.InstallBypassMake() {
+ if m.Config().KatiEnabled() && !m.InstallBypassMake() {
return true
}
@@ -2346,7 +2373,7 @@
Input: srcPath,
Implicits: implicitDeps,
OrderOnly: orderOnlyDeps,
- Default: !m.Config().EmbeddedInMake(),
+ Default: !m.Config().KatiEnabled(),
})
m.installFiles = append(m.installFiles, fullInstallPath)
@@ -2378,7 +2405,7 @@
Description: "install symlink " + fullInstallPath.Base(),
Output: fullInstallPath,
Input: srcPath,
- Default: !m.Config().EmbeddedInMake(),
+ Default: !m.Config().KatiEnabled(),
Args: map[string]string{
"fromPath": relPath,
},
@@ -2409,7 +2436,7 @@
Rule: Symlink,
Description: "install symlink " + fullInstallPath.Base() + " -> " + absPath,
Output: fullInstallPath,
- Default: !m.Config().EmbeddedInMake(),
+ Default: !m.Config().KatiEnabled(),
Args: map[string]string{
"fromPath": absPath,
},
@@ -2551,6 +2578,15 @@
return nil, fmt.Errorf("failed to get output files from module %q", pathContextName(ctx, module))
}
return paths, nil
+ } else if sourceFileProducer, ok := module.(SourceFileProducer); ok {
+ if tag != "" {
+ return nil, fmt.Errorf("module %q is a SourceFileProducer, not an OutputFileProducer, and so does not support tag %q", pathContextName(ctx, module), tag)
+ }
+ paths := sourceFileProducer.Srcs()
+ if len(paths) == 0 {
+ return nil, fmt.Errorf("failed to get output files from module %q", pathContextName(ctx, module))
+ }
+ return paths, nil
} else {
return nil, fmt.Errorf("module %q is not an OutputFileProducer", pathContextName(ctx, module))
}
@@ -2638,7 +2674,7 @@
})
suffix := ""
- if ctx.Config().EmbeddedInMake() {
+ if ctx.Config().KatiEnabled() {
suffix = "-soong"
}
@@ -2646,7 +2682,7 @@
ctx.Phony("checkbuild"+suffix, checkbuildDeps...)
// Make will generate the MODULES-IN-* targets
- if ctx.Config().EmbeddedInMake() {
+ if ctx.Config().KatiEnabled() {
return
}
diff --git a/android/path_properties.go b/android/path_properties.go
index 6b1cdb3..4bb706a 100644
--- a/android/path_properties.go
+++ b/android/path_properties.go
@@ -21,27 +21,31 @@
"github.com/google/blueprint/proptools"
)
+// This file implements support for automatically adding dependencies on any module referenced
+// with the ":module" module reference syntax in a property that is annotated with `android:"path"`.
+// The dependency is used by android.PathForModuleSrc to convert the module reference into the path
+// to the output file of the referenced module.
+
func registerPathDepsMutator(ctx RegisterMutatorsContext) {
ctx.BottomUp("pathdeps", pathDepsMutator).Parallel()
}
-// The pathDepsMutator automatically adds dependencies on any module that is listed with ":module" syntax in a
-// property that is tagged with android:"path".
+// The pathDepsMutator automatically adds dependencies on any module that is listed with the
+// ":module" module reference syntax in a property that is tagged with `android:"path"`.
func pathDepsMutator(ctx BottomUpMutatorContext) {
- m := ctx.Module().(Module)
- if m == nil {
- return
- }
+ props := ctx.Module().base().generalProperties
- props := m.base().generalProperties
-
+ // Iterate through each property struct of the module extracting the contents of all properties
+ // tagged with `android:"path"`.
var pathProperties []string
for _, ps := range props {
- pathProperties = append(pathProperties, pathPropertiesForPropertyStruct(ctx, ps)...)
+ pathProperties = append(pathProperties, pathPropertiesForPropertyStruct(ps)...)
}
+ // Remove duplicates to avoid multiple dependencies.
pathProperties = FirstUniqueStrings(pathProperties)
+ // Add dependencies to anything that is a module reference.
for _, s := range pathProperties {
if m, t := SrcIsModuleWithTag(s); m != "" {
ctx.AddDependency(ctx.Module(), sourceOrOutputDepTag(t), m)
@@ -49,34 +53,45 @@
}
}
-// pathPropertiesForPropertyStruct uses the indexes of properties that are tagged with android:"path" to extract
-// all their values from a property struct, returning them as a single slice of strings..
-func pathPropertiesForPropertyStruct(ctx BottomUpMutatorContext, ps interface{}) []string {
+// pathPropertiesForPropertyStruct uses the indexes of properties that are tagged with
+// android:"path" to extract all their values from a property struct, returning them as a single
+// slice of strings.
+func pathPropertiesForPropertyStruct(ps interface{}) []string {
v := reflect.ValueOf(ps)
if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct {
panic(fmt.Errorf("type %s is not a pointer to a struct", v.Type()))
}
+
+ // If the property struct is a nil pointer it can't have any paths set in it.
if v.IsNil() {
return nil
}
+
+ // v is now the reflect.Value for the concrete property struct.
v = v.Elem()
+ // Get or create the list of indexes of properties that are tagged with `android:"path"`.
pathPropertyIndexes := pathPropertyIndexesForPropertyStruct(ps)
var ret []string
for _, i := range pathPropertyIndexes {
+ // Turn an index into a field.
sv := fieldByIndex(v, i)
if !sv.IsValid() {
+ // Skip properties inside a nil pointer.
continue
}
+ // If the field is a non-nil pointer step into it.
if sv.Kind() == reflect.Ptr {
if sv.IsNil() {
continue
}
sv = sv.Elem()
}
+
+ // Collect paths from all strings and slices of strings.
switch sv.Kind() {
case reflect.String:
ret = append(ret, sv.String())
@@ -91,8 +106,8 @@
return ret
}
-// fieldByIndex is like reflect.Value.FieldByIndex, but returns an invalid reflect.Value when traversing a nil pointer
-// to a struct.
+// fieldByIndex is like reflect.Value.FieldByIndex, but returns an invalid reflect.Value when
+// traversing a nil pointer to a struct.
func fieldByIndex(v reflect.Value, index []int) reflect.Value {
if len(index) == 1 {
return v.Field(index[0])
@@ -111,9 +126,9 @@
var pathPropertyIndexesCache OncePer
-// pathPropertyIndexesForPropertyStruct returns a list of all of the indexes of properties in property struct type that
-// are tagged with android:"path". Each index is a []int suitable for passing to reflect.Value.FieldByIndex. The value
-// is cached in a global cache by type.
+// pathPropertyIndexesForPropertyStruct returns a list of all of the indexes of properties in
+// property struct type that are tagged with `android:"path"`. Each index is a []int suitable for
+// passing to reflect.Value.FieldByIndex. The value is cached in a global cache by type.
func pathPropertyIndexesForPropertyStruct(ps interface{}) [][]int {
key := NewCustomOnceKey(reflect.TypeOf(ps))
return pathPropertyIndexesCache.Once(key, func() interface{} {
diff --git a/android/paths.go b/android/paths.go
index b13979d..a62c9e3 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -1312,7 +1312,7 @@
ret := pathForInstall(ctx, os, arch, partition, ctx.Debug(), pathComponents...)
- if ctx.InstallBypassMake() && ctx.Config().EmbeddedInMake() {
+ if ctx.InstallBypassMake() && ctx.Config().KatiEnabled() {
ret = ret.ToMakePath()
}
diff --git a/android/phony.go b/android/phony.go
index f8e5a44..0adbb55 100644
--- a/android/phony.go
+++ b/android/phony.go
@@ -53,7 +53,7 @@
p.phonyMap[phony] = SortedUniquePaths(p.phonyMap[phony])
}
- if !ctx.Config().EmbeddedInMake() {
+ if !ctx.Config().KatiEnabled() {
for _, phony := range p.phonyList {
ctx.Build(pctx, BuildParams{
Rule: blueprint.Phony,
diff --git a/android/testing.go b/android/testing.go
index 1e2ae13..6539063 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -424,8 +424,8 @@
}
-func SetInMakeForTests(config Config) {
- config.inMake = true
+func SetKatiEnabledForTests(config Config) {
+ config.katiEnabled = true
}
func AndroidMkEntriesForTest(t *testing.T, config Config, bpPath string, mod blueprint.Module) []AndroidMkEntries {
diff --git a/androidmk/androidmk/androidmk.go b/androidmk/androidmk/androidmk.go
index 03cf74d..f4e5fa0 100644
--- a/androidmk/androidmk/androidmk.go
+++ b/androidmk/androidmk/androidmk.go
@@ -137,7 +137,14 @@
switch x := node.(type) {
case *mkparser.Comment:
- file.insertComment("//" + x.Comment)
+ // Split the comment on escaped newlines and then
+ // add each chunk separately.
+ chunks := strings.Split(x.Comment, "\\\n")
+ file.insertComment("//" + chunks[0])
+ for i := 1; i < len(chunks); i++ {
+ file.bpPos.Line++
+ file.insertComment("//" + chunks[i])
+ }
case *mkparser.Assignment:
handleAssignment(file, x, assignmentCond)
case *mkparser.Directive:
diff --git a/androidmk/androidmk/androidmk_test.go b/androidmk/androidmk/androidmk_test.go
index 16cb138..f32ff2a 100644
--- a/androidmk/androidmk/androidmk_test.go
+++ b/androidmk/androidmk/androidmk_test.go
@@ -1260,10 +1260,10 @@
desc: "comment with ESC",
in: `
# Comment line 1 \
-# Comment line 2
+ Comment line 2
`,
expected: `
-// Comment line 1 \
+// Comment line 1
// Comment line 2
`,
},
diff --git a/androidmk/parser/parser.go b/androidmk/parser/parser.go
index e61241b..c14910a 100644
--- a/androidmk/parser/parser.go
+++ b/androidmk/parser/parser.go
@@ -497,12 +497,6 @@
switch p.tok {
case '\\':
p.parseEscape()
- if p.tok == '\n' {
- // Special case: '\' does not "escape" newline in comment (b/127521510)
- comment += "\\"
- p.accept(p.tok)
- break loop
- }
comment += "\\" + p.scanner.TokenText()
p.accept(p.tok)
case '\n':
diff --git a/apex/androidmk.go b/apex/androidmk.go
index 993260c..fe89b73 100644
--- a/apex/androidmk.go
+++ b/apex/androidmk.go
@@ -36,6 +36,33 @@
return a.androidMkForType()
}
+// nameInMake converts apexFileClass into the corresponding class name in Make.
+func (class apexFileClass) nameInMake() string {
+ switch class {
+ case etc:
+ return "ETC"
+ case nativeSharedLib:
+ return "SHARED_LIBRARIES"
+ case nativeExecutable, shBinary, pyBinary, goBinary:
+ return "EXECUTABLES"
+ case javaSharedLib:
+ return "JAVA_LIBRARIES"
+ case nativeTest:
+ return "NATIVE_TESTS"
+ case app, appSet:
+ // b/142537672 Why isn't this APP? We want to have full control over
+ // the paths and file names of the apk file under the flattend APEX.
+ // If this is set to APP, then the paths and file names are modified
+ // by the Make build system. For example, it is installed to
+ // /system/apex/<apexname>/app/<Appname>/<apexname>.<Appname>/ instead of
+ // /system/apex/<apexname>/app/<Appname> because the build system automatically
+ // appends module name (which is <apexname>.<Appname> to the path.
+ return "ETC"
+ default:
+ panic(fmt.Errorf("unknown class %d", class))
+ }
+}
+
func (a *apexBundle) androidMkForFiles(w io.Writer, apexBundleName, apexName, moduleDir string,
apexAndroidMkData android.AndroidMkData) []string {
@@ -68,10 +95,10 @@
var postInstallCommands []string
for _, fi := range a.filesInfo {
- if a.linkToSystemLib && fi.transitiveDep && fi.AvailableToPlatform() {
+ if a.linkToSystemLib && fi.transitiveDep && fi.availableToPlatform() {
// TODO(jiyong): pathOnDevice should come from fi.module, not being calculated here
- linkTarget := filepath.Join("/system", fi.Path())
- linkPath := filepath.Join(a.installDir.ToMakePath().String(), apexBundleName, fi.Path())
+ linkTarget := filepath.Join("/system", fi.path())
+ linkPath := filepath.Join(a.installDir.ToMakePath().String(), apexBundleName, fi.path())
mkdirCmd := "mkdir -p " + filepath.Dir(linkPath)
linkCmd := "ln -sfn " + linkTarget + " " + linkPath
postInstallCommands = append(postInstallCommands, mkdirCmd, linkCmd)
@@ -85,7 +112,7 @@
continue
}
- linkToSystemLib := a.linkToSystemLib && fi.transitiveDep && fi.AvailableToPlatform()
+ linkToSystemLib := a.linkToSystemLib && fi.transitiveDep && fi.availableToPlatform()
var moduleName string
if linkToSystemLib {
@@ -151,7 +178,7 @@
fmt.Fprintln(w, "LOCAL_NO_NOTICE_FILE := true")
}
fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", fi.builtFile.String())
- fmt.Fprintln(w, "LOCAL_MODULE_CLASS :=", fi.class.NameInMake())
+ fmt.Fprintln(w, "LOCAL_MODULE_CLASS :=", fi.class.nameInMake())
if fi.module != nil {
archStr := fi.module.Target().Arch.ArchType.String()
host := false
@@ -189,7 +216,7 @@
// soong_java_prebuilt.mk sets LOCAL_MODULE_SUFFIX := .jar Therefore
// we need to remove the suffix from LOCAL_MODULE_STEM, otherwise
// we will have foo.jar.jar
- fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", strings.TrimSuffix(fi.Stem(), ".jar"))
+ fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", strings.TrimSuffix(fi.stem(), ".jar"))
if javaModule, ok := fi.module.(java.ApexDependency); ok {
fmt.Fprintln(w, "LOCAL_SOONG_CLASSES_JAR :=", javaModule.ImplementationAndResourcesJars()[0].String())
fmt.Fprintln(w, "LOCAL_SOONG_HEADER_JAR :=", javaModule.HeaderJars()[0].String())
@@ -205,7 +232,7 @@
// soong_app_prebuilt.mk sets LOCAL_MODULE_SUFFIX := .apk Therefore
// we need to remove the suffix from LOCAL_MODULE_STEM, otherwise
// we will have foo.apk.apk
- fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", strings.TrimSuffix(fi.Stem(), ".apk"))
+ fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", strings.TrimSuffix(fi.stem(), ".apk"))
if app, ok := fi.module.(*java.AndroidApp); ok {
if jniCoverageOutputs := app.JniCoverageOutputs(); len(jniCoverageOutputs) > 0 {
fmt.Fprintln(w, "LOCAL_PREBUILT_COVERAGE_ARCHIVE :=", strings.Join(jniCoverageOutputs.Strings(), " "))
@@ -224,7 +251,7 @@
fmt.Fprintln(w, "LOCAL_APKCERTS_FILE :=", as.APKCertsFile().String())
fmt.Fprintln(w, "include $(BUILD_SYSTEM)/soong_android_app_set.mk")
case nativeSharedLib, nativeExecutable, nativeTest:
- fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", fi.Stem())
+ fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", fi.stem())
if ccMod, ok := fi.module.(*cc.Module); ok {
if ccMod.UnstrippedOutputFile() != nil {
fmt.Fprintln(w, "LOCAL_SOONG_UNSTRIPPED_BINARY :=", ccMod.UnstrippedOutputFile().String())
@@ -236,7 +263,7 @@
}
fmt.Fprintln(w, "include $(BUILD_SYSTEM)/soong_cc_prebuilt.mk")
default:
- fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", fi.Stem())
+ fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", fi.stem())
if fi.builtFile == a.manifestPbOut && apexType == flattenedApex {
if a.primaryApexType {
// To install companion files (init_rc, vintf_fragments)
diff --git a/apex/apex.go b/apex/apex.go
index 91770f4..a645b06 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+// package apex implements build rules for creating the APEX files which are container for
+// lower-level system components. See https://source.android.com/devices/tech/ota/apex
package apex
import (
@@ -33,11 +35,937 @@
"android/soong/sh"
)
+func init() {
+ android.RegisterModuleType("apex", BundleFactory)
+ android.RegisterModuleType("apex_test", testApexBundleFactory)
+ android.RegisterModuleType("apex_vndk", vndkApexBundleFactory)
+ android.RegisterModuleType("apex_defaults", defaultsFactory)
+ android.RegisterModuleType("prebuilt_apex", PrebuiltFactory)
+ android.RegisterModuleType("override_apex", overrideApexFactory)
+ android.RegisterModuleType("apex_set", apexSetFactory)
+
+ android.PreDepsMutators(RegisterPreDepsMutators)
+ android.PostDepsMutators(RegisterPostDepsMutators)
+}
+
+func RegisterPreDepsMutators(ctx android.RegisterMutatorsContext) {
+ ctx.TopDown("apex_vndk", apexVndkMutator).Parallel()
+ ctx.BottomUp("apex_vndk_deps", apexVndkDepsMutator).Parallel()
+}
+
+func RegisterPostDepsMutators(ctx android.RegisterMutatorsContext) {
+ ctx.TopDown("apex_deps", apexDepsMutator).Parallel()
+ ctx.BottomUp("apex_unique", apexUniqueVariationsMutator).Parallel()
+ ctx.BottomUp("apex_test_for_deps", apexTestForDepsMutator).Parallel()
+ ctx.BottomUp("apex_test_for", apexTestForMutator).Parallel()
+ ctx.BottomUp("apex", apexMutator).Parallel()
+ ctx.BottomUp("apex_directly_in_any", apexDirectlyInAnyMutator).Parallel()
+ ctx.BottomUp("apex_flattened", apexFlattenedMutator).Parallel()
+ ctx.BottomUp("mark_platform_availability", markPlatformAvailability).Parallel()
+}
+
+type apexBundleProperties struct {
+ // Json manifest file describing meta info of this APEX bundle. Refer to
+ // system/apex/proto/apex_manifest.proto for the schema. Default: "apex_manifest.json"
+ Manifest *string `android:"path"`
+
+ // AndroidManifest.xml file used for the zip container of this APEX bundle. If unspecified,
+ // a default one is automatically generated.
+ AndroidManifest *string `android:"path"`
+
+ // Canonical name of this APEX bundle. Used to determine the path to the activated APEX on
+ // device (/apex/<apex_name>). If unspecified, follows the name property.
+ Apex_name *string
+
+ // Determines the file contexts file for setting the security contexts to files in this APEX
+ // bundle. For platform APEXes, this should points to a file under /system/sepolicy Default:
+ // /system/sepolicy/apex/<module_name>_file_contexts.
+ File_contexts *string `android:"path"`
+
+ ApexNativeDependencies
+
+ Multilib apexMultilibProperties
+
+ // List of java libraries that are embedded inside this APEX bundle.
+ Java_libs []string
+
+ // List of prebuilt files that are embedded inside this APEX bundle.
+ Prebuilts []string
+
+ // List of BPF programs inside this APEX bundle.
+ Bpfs []string
+
+ // Name of the apex_key module that provides the private key to sign this APEX bundle.
+ Key *string
+
+ // Specifies the certificate and the private key to sign the zip container of this APEX. If
+ // this is "foo", foo.x509.pem and foo.pk8 under PRODUCT_DEFAULT_DEV_CERTIFICATE are used
+ // as the certificate and the private key, respectively. If this is ":module", then the
+ // certificate and the private key are provided from the android_app_certificate module
+ // named "module".
+ Certificate *string
+
+ // The minimum SDK version that this APEX must support at minimum. This is usually set to
+ // the SDK version that the APEX was first introduced.
+ Min_sdk_version *string
+
+ // Whether this APEX is considered updatable or not. When set to true, this will enforce
+ // additional rules for making sure that the APEX is truly updatable. To be updatable,
+ // min_sdk_version should be set as well. This will also disable the size optimizations like
+ // symlinking to the system libs. Default is false.
+ Updatable *bool
+
+ // Whether this APEX is installable to one of the partitions like system, vendor, etc.
+ // Default: true.
+ Installable *bool
+
+ // For native libraries and binaries, use the vendor variant instead of the core (platform)
+ // variant. Default is false. DO NOT use this for APEXes that are installed to the system or
+ // system_ext partition.
+ Use_vendor *bool
+
+ // If set true, VNDK libs are considered as stable libs and are not included in this APEX.
+ // Should be only used in non-system apexes (e.g. vendor: true). Default is false.
+ Use_vndk_as_stable *bool
+
+ // List of SDKs that are used to build this APEX. A reference to an SDK should be either
+ // `name#version` or `name` which is an alias for `name#current`. If left empty,
+ // `platform#current` is implied. This value affects all modules included in this APEX. In
+ // other words, they are also built with the SDKs specified here.
+ Uses_sdks []string
+
+ // The type of APEX to build. Controls what the APEX payload is. Either 'image', 'zip' or
+ // 'both'. When set to image, contents are stored in a filesystem image inside a zip
+ // container. When set to zip, contents are stored in a zip container directly. This type is
+ // mostly for host-side debugging. When set to both, the two types are both built. Default
+ // is 'image'.
+ Payload_type *string
+
+ // The type of filesystem to use when the payload_type is 'image'. Either 'ext4' or 'f2fs'.
+ // Default 'ext4'.
+ Payload_fs_type *string
+
+ // For telling the APEX to ignore special handling for system libraries such as bionic.
+ // Default is false.
+ Ignore_system_library_special_case *bool
+
+ // Whenever apex_payload.img of the APEX should include dm-verity hashtree. Should be only
+ // used in tests.
+ Test_only_no_hashtree *bool
+
+ // Whenever apex_payload.img of the APEX should not be dm-verity signed. Should be only
+ // used in tests.
+ Test_only_unsigned_payload *bool
+
+ IsCoverageVariant bool `blueprint:"mutated"`
+
+ // List of sanitizer names that this APEX is enabled for
+ SanitizerNames []string `blueprint:"mutated"`
+
+ PreventInstall bool `blueprint:"mutated"`
+
+ HideFromMake bool `blueprint:"mutated"`
+
+ // Internal package method for this APEX. When payload_type is image, this can be either
+ // imageApex or flattenedApex depending on Config.FlattenApex(). When payload_type is zip,
+ // this becomes zipApex.
+ ApexType apexPackaging `blueprint:"mutated"`
+}
+
+type ApexNativeDependencies struct {
+ // List of native libraries that are embedded inside this APEX.
+ Native_shared_libs []string
+
+ // List of JNI libraries that are embedded inside this APEX.
+ Jni_libs []string
+
+ // List of native executables that are embedded inside this APEX.
+ Binaries []string
+
+ // List of native tests that are embedded inside this APEX.
+ Tests []string
+}
+
+type apexMultilibProperties struct {
+ // Native dependencies whose compile_multilib is "first"
+ First ApexNativeDependencies
+
+ // Native dependencies whose compile_multilib is "both"
+ Both ApexNativeDependencies
+
+ // Native dependencies whose compile_multilib is "prefer32"
+ Prefer32 ApexNativeDependencies
+
+ // Native dependencies whose compile_multilib is "32"
+ Lib32 ApexNativeDependencies
+
+ // Native dependencies whose compile_multilib is "64"
+ Lib64 ApexNativeDependencies
+}
+
+type apexTargetBundleProperties struct {
+ Target struct {
+ // Multilib properties only for android.
+ Android struct {
+ Multilib apexMultilibProperties
+ }
+
+ // Multilib properties only for host.
+ Host struct {
+ Multilib apexMultilibProperties
+ }
+
+ // Multilib properties only for host linux_bionic.
+ Linux_bionic struct {
+ Multilib apexMultilibProperties
+ }
+
+ // Multilib properties only for host linux_glibc.
+ Linux_glibc struct {
+ Multilib apexMultilibProperties
+ }
+ }
+}
+
+// These properties can be used in override_apex to override the corresponding properties in the
+// base apex.
+type overridableProperties struct {
+ // List of APKs that are embedded inside this APEX.
+ Apps []string
+
+ // List of runtime resource overlays (RROs) that are embedded inside this APEX.
+ Rros []string
+
+ // Names of modules to be overridden. Listed modules can only be other binaries (in Make or
+ // Soong). This does not completely prevent installation of the overridden binaries, but if
+ // both binaries would be installed by default (in PRODUCT_PACKAGES) the other binary will
+ // be removed from PRODUCT_PACKAGES.
+ Overrides []string
+
+ // Logging parent value.
+ Logging_parent string
+
+ // Apex Container package name. Override value for attribute package:name in
+ // AndroidManifest.xml
+ Package_name string
+
+ // A txt file containing list of files that are allowed to be included in this APEX.
+ Allowed_files *string `android:"path"`
+}
+
+type apexBundle struct {
+ // Inherited structs
+ android.ModuleBase
+ android.DefaultableModuleBase
+ android.OverridableModuleBase
+ android.SdkBase
+
+ // Properties
+ properties apexBundleProperties
+ targetProperties apexTargetBundleProperties
+ overridableProperties overridableProperties
+ vndkProperties apexVndkProperties // only for apex_vndk modules
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // Inputs
+
+ // Keys for apex_paylaod.img
+ public_key_file android.Path
+ private_key_file android.Path
+
+ // Cert/priv-key for the zip container
+ container_certificate_file android.Path
+ container_private_key_file android.Path
+
+ // Flags for special variants of APEX
+ testApex bool
+ vndkApex bool
+ artApex bool
+
+ // Tells whether this variant of the APEX bundle is the primary one or not. Only the primary
+ // one gets installed to the device.
+ primaryApexType bool
+
+ // Suffix of module name in Android.mk ".flattened", ".apex", ".zipapex", or ""
+ suffix string
+
+ // File system type of apex_payload.img
+ payloadFsType fsType
+
+ // Whether to create symlink to the system file instead of having a file inside the apex or
+ // not
+ linkToSystemLib bool
+
+ // List of files to be included in this APEX. This is filled in the first part of
+ // GenerateAndroidBuildActions.
+ filesInfo []apexFile
+
+ // List of other module names that should be installed when this APEX gets installed.
+ requiredDeps []string
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // Outputs (final and intermediates)
+
+ // Processed apex manifest in JSONson format (for Q)
+ manifestJsonOut android.WritablePath
+
+ // Processed apex manifest in PB format (for R+)
+ manifestPbOut android.WritablePath
+
+ // Processed file_contexts files
+ fileContexts android.WritablePath
+
+ // Struct holding the merged notice file paths in different formats
+ mergedNotices android.NoticeOutputs
+
+ // The built APEX file. This is the main product.
+ outputFile android.WritablePath
+
+ // The built APEX file in app bundle format. This file is not directly installed to the
+ // device. For an APEX, multiple app bundles are created each of which is for a specific ABI
+ // like arm, arm64, x86, etc. Then they are processed again (outside of the Android build
+ // system) to be merged into a single app bundle file that Play accepts. See
+ // vendor/google/build/build_unbundled_mainline_module.sh for more detail.
+ bundleModuleFile android.WritablePath
+
+ // Target path to install this APEX. Usually out/target/product/<device>/<partition>/apex.
+ installDir android.InstallPath
+
+ // List of commands to create symlinks for backward compatibility. These commands will be
+ // attached as LOCAL_POST_INSTALL_CMD to apex package itself (for unflattened build) or
+ // apex_manifest (for flattened build) so that compat symlinks are always installed
+ // regardless of TARGET_FLATTEN_APEX setting.
+ compatSymlinks []string
+
+ // Text file having the list of individual files that are included in this APEX. Used for
+ // debugging purpose.
+ installedFilesFile android.WritablePath
+
+ // List of module names that this APEX is including (to be shown via *-deps-info target).
+ // Used for debugging purpose.
+ android.ApexBundleDepsInfo
+
+ // Optional list of lint report zip files for apexes that contain java or app modules
+ lintReports android.Paths
+
+ prebuiltFileToDelete string
+
+ distFiles android.TaggedDistFiles
+}
+
+// apexFileClass represents a type of file that can be included in APEX.
+type apexFileClass int
+
const (
+ app apexFileClass = iota
+ appSet
+ etc
+ goBinary
+ javaSharedLib
+ nativeExecutable
+ nativeSharedLib
+ nativeTest
+ pyBinary
+ shBinary
+)
+
+// apexFile represents a file in an APEX bundle. This is created during the first half of
+// GenerateAndroidBuildActions by traversing the dependencies of the APEX. Then in the second half
+// of the function, this is used to create commands that copies the files into a staging directory,
+// where they are packaged into the APEX file. This struct is also used for creating Make modules
+// for each of the files in case when the APEX is flattened.
+type apexFile struct {
+ // buildFile is put in the installDir inside the APEX.
+ builtFile android.Path
+ noticeFiles android.Paths
+ installDir string
+ customStem string
+ symlinks []string // additional symlinks
+
+ // Info for Android.mk Module name of `module` in AndroidMk. Note the generated AndroidMk
+ // module for apexFile is named something like <AndroidMk module name>.<apex name>[<apex
+ // suffix>]
+ androidMkModuleName string // becomes LOCAL_MODULE
+ class apexFileClass // becomes LOCAL_MODULE_CLASS
+ moduleDir string // becomes LOCAL_PATH
+ requiredModuleNames []string // becomes LOCAL_REQUIRED_MODULES
+ targetRequiredModuleNames []string // becomes LOCAL_TARGET_REQUIRED_MODULES
+ hostRequiredModuleNames []string // becomes LOCAL_HOST_REQUIRED_MODULES
+ dataPaths []android.DataPath // becomes LOCAL_TEST_DATA
+
+ jacocoReportClassesFile android.Path // only for javalibs and apps
+ lintDepSets java.LintDepSets // only for javalibs and apps
+ certificate java.Certificate // only for apps
+ overriddenPackageName string // only for apps
+
+ transitiveDep bool
+ isJniLib bool
+
+ // TODO(jiyong): remove this
+ module android.Module
+}
+
+// TODO(jiyong): shorten the arglist using an option struct
+func newApexFile(ctx android.BaseModuleContext, builtFile android.Path, androidMkModuleName string, installDir string, class apexFileClass, module android.Module) apexFile {
+ ret := apexFile{
+ builtFile: builtFile,
+ installDir: installDir,
+ androidMkModuleName: androidMkModuleName,
+ class: class,
+ module: module,
+ }
+ if module != nil {
+ ret.noticeFiles = module.NoticeFiles()
+ ret.moduleDir = ctx.OtherModuleDir(module)
+ ret.requiredModuleNames = module.RequiredModuleNames()
+ ret.targetRequiredModuleNames = module.TargetRequiredModuleNames()
+ ret.hostRequiredModuleNames = module.HostRequiredModuleNames()
+ }
+ return ret
+}
+
+func (af *apexFile) ok() bool {
+ return af.builtFile != nil && af.builtFile.String() != ""
+}
+
+// apexRelativePath returns the relative path of the given path from the install directory of this
+// apexFile.
+// TODO(jiyong): rename this
+func (af *apexFile) apexRelativePath(path string) string {
+ return filepath.Join(af.installDir, path)
+}
+
+// path returns path of this apex file relative to the APEX root
+func (af *apexFile) path() string {
+ return af.apexRelativePath(af.stem())
+}
+
+// stem returns the base filename of this apex file
+func (af *apexFile) stem() string {
+ if af.customStem != "" {
+ return af.customStem
+ }
+ return af.builtFile.Base()
+}
+
+// symlinkPaths returns paths of the symlinks (if any) relative to the APEX root
+func (af *apexFile) symlinkPaths() []string {
+ var ret []string
+ for _, symlink := range af.symlinks {
+ ret = append(ret, af.apexRelativePath(symlink))
+ }
+ return ret
+}
+
+// availableToPlatform tests whether this apexFile is from a module that can be installed to the
+// platform.
+func (af *apexFile) availableToPlatform() bool {
+ if af.module == nil {
+ return false
+ }
+ if am, ok := af.module.(android.ApexModule); ok {
+ return am.AvailableFor(android.AvailableToPlatform)
+ }
+ return false
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Mutators
+//
+// Brief description about mutators for APEX. The following three mutators are the most important
+// ones.
+//
+// 1) DepsMutator: from the properties like native_shared_libs, java_libs, etc., modules are added
+// to the (direct) dependencies of this APEX bundle.
+//
+// 2) apexDepsMutator: this is a post-deps mutator, so runs after DepsMutator. Its goal is to
+// collect modules that are direct and transitive dependencies of each APEX bundle. The collected
+// modules are marked as being included in the APEX via BuildForApex().
+//
+// 3) apexMutator: this is a post-deps mutator that runs after apexDepsMutator. For each module that
+// are marked by the apexDepsMutator, apex variations are created using CreateApexVariations().
+
+type dependencyTag struct {
+ blueprint.BaseDependencyTag
+ name string
+
+ // Determines if the dependent will be part of the APEX payload. Can be false for the
+ // dependencies to the signing key module, etc.
+ payload bool
+}
+
+var (
+ androidAppTag = dependencyTag{name: "androidApp", payload: true}
+ bpfTag = dependencyTag{name: "bpf", payload: true}
+ certificateTag = dependencyTag{name: "certificate"}
+ executableTag = dependencyTag{name: "executable", payload: true}
+ javaLibTag = dependencyTag{name: "javaLib", payload: true}
+ jniLibTag = dependencyTag{name: "jniLib", payload: true}
+ keyTag = dependencyTag{name: "key"}
+ prebuiltTag = dependencyTag{name: "prebuilt", payload: true}
+ rroTag = dependencyTag{name: "rro", payload: true}
+ sharedLibTag = dependencyTag{name: "sharedLib", payload: true}
+ testForTag = dependencyTag{name: "test for"}
+ testTag = dependencyTag{name: "test", payload: true}
+)
+
+// TODO(jiyong): shorten this function signature
+func addDependenciesForNativeModules(ctx android.BottomUpMutatorContext, nativeModules ApexNativeDependencies, target android.Target, imageVariation string) {
+ binVariations := target.Variations()
+ libVariations := append(target.Variations(), blueprint.Variation{Mutator: "link", Variation: "shared"})
+
+ if ctx.Device() {
+ binVariations = append(binVariations, blueprint.Variation{Mutator: "image", Variation: imageVariation})
+ libVariations = append(libVariations,
+ blueprint.Variation{Mutator: "image", Variation: imageVariation},
+ blueprint.Variation{Mutator: "version", Variation: ""}, // "" is the non-stub variant
+ )
+ }
+
+ // Use *FarVariation* to be able to depend on modules having conflicting variations with
+ // this module. This is required since arch variant of an APEX bundle is 'common' but it is
+ // 'arm' or 'arm64' for native shared libs.
+ ctx.AddFarVariationDependencies(binVariations, executableTag, nativeModules.Binaries...)
+ ctx.AddFarVariationDependencies(binVariations, testTag, nativeModules.Tests...)
+ ctx.AddFarVariationDependencies(libVariations, jniLibTag, nativeModules.Jni_libs...)
+ ctx.AddFarVariationDependencies(libVariations, sharedLibTag, nativeModules.Native_shared_libs...)
+}
+
+func (a *apexBundle) combineProperties(ctx android.BottomUpMutatorContext) {
+ if ctx.Device() {
+ proptools.AppendProperties(&a.properties.Multilib, &a.targetProperties.Target.Android.Multilib, nil)
+ } else {
+ proptools.AppendProperties(&a.properties.Multilib, &a.targetProperties.Target.Host.Multilib, nil)
+ if ctx.Os().Bionic() {
+ proptools.AppendProperties(&a.properties.Multilib, &a.targetProperties.Target.Linux_bionic.Multilib, nil)
+ } else {
+ proptools.AppendProperties(&a.properties.Multilib, &a.targetProperties.Target.Linux_glibc.Multilib, nil)
+ }
+ }
+}
+
+// getImageVariation returns the image variant name for this apexBundle. In most cases, it's simply
+// android.CoreVariation, but gets complicated for the vendor APEXes and the VNDK APEX.
+func (a *apexBundle) getImageVariation(ctx android.BottomUpMutatorContext) string {
+ deviceConfig := ctx.DeviceConfig()
+ if a.vndkApex {
+ return cc.VendorVariationPrefix + a.vndkVersion(deviceConfig)
+ }
+
+ var prefix string
+ var vndkVersion string
+ if deviceConfig.VndkVersion() != "" {
+ if proptools.Bool(a.properties.Use_vendor) {
+ prefix = cc.VendorVariationPrefix
+ vndkVersion = deviceConfig.PlatformVndkVersion()
+ } else if a.SocSpecific() || a.DeviceSpecific() {
+ prefix = cc.VendorVariationPrefix
+ vndkVersion = deviceConfig.VndkVersion()
+ } else if a.ProductSpecific() {
+ prefix = cc.ProductVariationPrefix
+ vndkVersion = deviceConfig.ProductVndkVersion()
+ }
+ }
+ if vndkVersion == "current" {
+ vndkVersion = deviceConfig.PlatformVndkVersion()
+ }
+ if vndkVersion != "" {
+ return prefix + vndkVersion
+ }
+
+ return android.CoreVariation // The usual case
+}
+
+func (a *apexBundle) DepsMutator(ctx android.BottomUpMutatorContext) {
+ // TODO(jiyong): move this kind of checks to GenerateAndroidBuildActions?
+ checkUseVendorProperty(ctx, a)
+
+ // apexBundle is a multi-arch targets module. Arch variant of apexBundle is set to 'common'.
+ // arch-specific targets are enabled by the compile_multilib setting of the apex bundle. For
+ // each target os/architectures, appropriate dependencies are selected by their
+ // target.<os>.multilib.<type> groups and are added as (direct) dependencies.
+ targets := ctx.MultiTargets()
+ config := ctx.DeviceConfig()
+ imageVariation := a.getImageVariation(ctx)
+
+ a.combineProperties(ctx)
+
+ has32BitTarget := false
+ for _, target := range targets {
+ if target.Arch.ArchType.Multilib == "lib32" {
+ has32BitTarget = true
+ }
+ }
+ for i, target := range targets {
+ // Don't include artifacts for the host cross targets because there is no way for us
+ // to run those artifacts natively on host
+ if target.HostCross {
+ continue
+ }
+
+ var depsList []ApexNativeDependencies
+
+ // Add native modules targeting both ABIs. When multilib.* is omitted for
+ // native_shared_libs/jni_libs/tests, it implies multilib.both
+ depsList = append(depsList, a.properties.Multilib.Both)
+ depsList = append(depsList, ApexNativeDependencies{
+ Native_shared_libs: a.properties.Native_shared_libs,
+ Tests: a.properties.Tests,
+ Jni_libs: a.properties.Jni_libs,
+ Binaries: nil,
+ })
+
+ // Add native modules targeting the first ABI When multilib.* is omitted for
+ // binaries, it implies multilib.first
+ isPrimaryAbi := i == 0
+ if isPrimaryAbi {
+ depsList = append(depsList, a.properties.Multilib.First)
+ depsList = append(depsList, ApexNativeDependencies{
+ Native_shared_libs: nil,
+ Tests: nil,
+ Jni_libs: nil,
+ Binaries: a.properties.Binaries,
+ })
+ }
+
+ // Add native modules targeting either 32-bit or 64-bit ABI
+ switch target.Arch.ArchType.Multilib {
+ case "lib32":
+ depsList = append(depsList, a.properties.Multilib.Lib32)
+ depsList = append(depsList, a.properties.Multilib.Prefer32)
+ case "lib64":
+ depsList = append(depsList, a.properties.Multilib.Lib64)
+ if !has32BitTarget {
+ depsList = append(depsList, a.properties.Multilib.Prefer32)
+ }
+ }
+
+ for _, d := range depsList {
+ addDependenciesForNativeModules(ctx, d, target, imageVariation)
+ }
+ }
+
+ // For prebuilt_etc, use the first variant (64 on 64/32bit device, 32 on 32bit device)
+ // regardless of the TARGET_PREFER_* setting. See b/144532908
+ archForPrebuiltEtc := config.Arches()[0]
+ for _, arch := range config.Arches() {
+ // Prefer 64-bit arch if there is any
+ if arch.ArchType.Multilib == "lib64" {
+ archForPrebuiltEtc = arch
+ break
+ }
+ }
+ ctx.AddFarVariationDependencies([]blueprint.Variation{
+ {Mutator: "os", Variation: ctx.Os().String()},
+ {Mutator: "arch", Variation: archForPrebuiltEtc.String()},
+ }, prebuiltTag, a.properties.Prebuilts...)
+
+ // Common-arch dependencies come next
+ commonVariation := ctx.Config().AndroidCommonTarget.Variations()
+ ctx.AddFarVariationDependencies(commonVariation, javaLibTag, a.properties.Java_libs...)
+ ctx.AddFarVariationDependencies(commonVariation, bpfTag, a.properties.Bpfs...)
+
+ // With EMMA_INSTRUMENT_FRAMEWORK=true the ART boot image includes jacoco library.
+ if a.artApex && ctx.Config().IsEnvTrue("EMMA_INSTRUMENT_FRAMEWORK") {
+ ctx.AddFarVariationDependencies(commonVariation, javaLibTag, "jacocoagent")
+ }
+
+ // Dependencies for signing
+ if String(a.properties.Key) == "" {
+ ctx.PropertyErrorf("key", "missing")
+ return
+ }
+ ctx.AddDependency(ctx.Module(), keyTag, String(a.properties.Key))
+
+ cert := android.SrcIsModule(a.getCertString(ctx))
+ if cert != "" {
+ ctx.AddDependency(ctx.Module(), certificateTag, cert)
+ // empty cert is not an error. Cert and private keys will be directly found under
+ // PRODUCT_DEFAULT_DEV_CERTIFICATE
+ }
+
+ // Marks that this APEX (in fact all the modules in it) has to be built with the given SDKs.
+ // This field currently isn't used.
+ // TODO(jiyong): consider dropping this feature
+ // TODO(jiyong): ensure that all apexes are with non-empty uses_sdks
+ if len(a.properties.Uses_sdks) > 0 {
+ sdkRefs := []android.SdkRef{}
+ for _, str := range a.properties.Uses_sdks {
+ parsed := android.ParseSdkRef(ctx, str, "uses_sdks")
+ sdkRefs = append(sdkRefs, parsed)
+ }
+ a.BuildWithSdks(sdkRefs)
+ }
+}
+
+// DepsMutator for the overridden properties.
+func (a *apexBundle) OverridablePropertiesDepsMutator(ctx android.BottomUpMutatorContext) {
+ if a.overridableProperties.Allowed_files != nil {
+ android.ExtractSourceDeps(ctx, a.overridableProperties.Allowed_files)
+ }
+
+ commonVariation := ctx.Config().AndroidCommonTarget.Variations()
+ ctx.AddFarVariationDependencies(commonVariation, androidAppTag, a.overridableProperties.Apps...)
+ ctx.AddFarVariationDependencies(commonVariation, rroTag, a.overridableProperties.Rros...)
+}
+
+type ApexBundleInfo struct {
+ Contents *android.ApexContents
+}
+
+var ApexBundleInfoProvider = blueprint.NewMutatorProvider(ApexBundleInfo{}, "apex_deps")
+
+// apexDepsMutator is responsible for collecting modules that need to have apex variants. They are
+// identified by doing a graph walk starting from an apexBundle. Basically, all the (direct and
+// indirect) dependencies are collected. But a few types of modules that shouldn't be included in
+// the apexBundle (e.g. stub libraries) are not collected. Note that a single module can be depended
+// on by multiple apexBundles. In that case, the module is collected for all of the apexBundles.
+func apexDepsMutator(mctx android.TopDownMutatorContext) {
+ if !mctx.Module().Enabled() {
+ return
+ }
+
+ a, ok := mctx.Module().(*apexBundle)
+ if !ok {
+ return
+ }
+
+ // The VNDK APEX is special. For the APEX, the membership is described in a very different
+ // way. There is no dependency from the VNDK APEX to the VNDK libraries. Instead, VNDK
+ // libraries are self-identified by their vndk.enabled properties. There is no need to run
+ // this mutator for the APEX as nothing will be collected. So, let's return fast.
+ if a.vndkApex {
+ return
+ }
+
+ // Special casing for APEXes on non-system (e.g., vendor, odm, etc.) partitions. They are
+ // provided with a property named use_vndk_as_stable, which when set to true doesn't collect
+ // VNDK libraries as transitive dependencies. This option is useful for reducing the size of
+ // the non-system APEXes because the VNDK libraries won't be included (and duped) in the
+ // APEX, but shared across APEXes via the VNDK APEX.
+ useVndk := a.SocSpecific() || a.DeviceSpecific() || (a.ProductSpecific() && mctx.Config().EnforceProductPartitionInterface())
+ excludeVndkLibs := useVndk && proptools.Bool(a.properties.Use_vndk_as_stable)
+ if !useVndk && proptools.Bool(a.properties.Use_vndk_as_stable) {
+ mctx.PropertyErrorf("use_vndk_as_stable", "not supported for system/system_ext APEXes")
+ return
+ }
+
+ continueApexDepsWalk := func(child, parent android.Module) bool {
+ am, ok := child.(android.ApexModule)
+ if !ok || !am.CanHaveApexVariants() {
+ return false
+ }
+ if !parent.(android.DepIsInSameApex).DepIsInSameApex(mctx, child) {
+ return false
+ }
+ if excludeVndkLibs {
+ if c, ok := child.(*cc.Module); ok && c.IsVndk() {
+ return false
+ }
+ }
+ // By default, all the transitive dependencies are collected, unless filtered out
+ // above.
+ return true
+ }
+
+ // Records whether a certain module is included in this apexBundle via direct dependency or
+ // inndirect dependency.
+ contents := make(map[string]android.ApexMembership)
+ mctx.WalkDeps(func(child, parent android.Module) bool {
+ if !continueApexDepsWalk(child, parent) {
+ return false
+ }
+ // If the parent is apexBundle, this child is directly depended.
+ _, directDep := parent.(*apexBundle)
+ depName := mctx.OtherModuleName(child)
+ contents[depName] = contents[depName].Add(directDep)
+ return true
+ })
+
+ // The membership information is saved for later access
+ apexContents := android.NewApexContents(contents)
+ mctx.SetProvider(ApexBundleInfoProvider, ApexBundleInfo{
+ Contents: apexContents,
+ })
+
+ // This is the main part of this mutator. Mark the collected dependencies that they need to
+ // be built for this apexBundle.
+ apexInfo := android.ApexInfo{
+ ApexVariationName: mctx.ModuleName(),
+ MinSdkVersionStr: a.minSdkVersion(mctx).String(),
+ RequiredSdks: a.RequiredSdks(),
+ Updatable: a.Updatable(),
+ InApexes: []string{mctx.ModuleName()},
+ ApexContents: []*android.ApexContents{apexContents},
+ }
+ mctx.WalkDeps(func(child, parent android.Module) bool {
+ if !continueApexDepsWalk(child, parent) {
+ return false
+ }
+ child.(android.ApexModule).BuildForApex(apexInfo) // leave a mark!
+ return true
+ })
+}
+
+// apexUniqueVariationsMutator checks if any dependencies use unique apex variations. If so, use
+// unique apex variations for this module. See android/apex.go for more about unique apex variant.
+// TODO(jiyong): move this to android/apex.go?
+func apexUniqueVariationsMutator(mctx android.BottomUpMutatorContext) {
+ if !mctx.Module().Enabled() {
+ return
+ }
+ if am, ok := mctx.Module().(android.ApexModule); ok {
+ android.UpdateUniqueApexVariationsForDeps(mctx, am)
+ }
+}
+
+// apexTestForDepsMutator checks if this module is a test for an apex. If so, add a dependency on
+// the apex in order to retrieve its contents later.
+// TODO(jiyong): move this to android/apex.go?
+func apexTestForDepsMutator(mctx android.BottomUpMutatorContext) {
+ if !mctx.Module().Enabled() {
+ return
+ }
+ if am, ok := mctx.Module().(android.ApexModule); ok {
+ if testFor := am.TestFor(); len(testFor) > 0 {
+ mctx.AddFarVariationDependencies([]blueprint.Variation{
+ {Mutator: "os", Variation: am.Target().OsVariation()},
+ {"arch", "common"},
+ }, testForTag, testFor...)
+ }
+ }
+}
+
+// TODO(jiyong): move this to android/apex.go?
+func apexTestForMutator(mctx android.BottomUpMutatorContext) {
+ if !mctx.Module().Enabled() {
+ return
+ }
+ if _, ok := mctx.Module().(android.ApexModule); ok {
+ var contents []*android.ApexContents
+ for _, testFor := range mctx.GetDirectDepsWithTag(testForTag) {
+ abInfo := mctx.OtherModuleProvider(testFor, ApexBundleInfoProvider).(ApexBundleInfo)
+ contents = append(contents, abInfo.Contents)
+ }
+ mctx.SetProvider(android.ApexTestForInfoProvider, android.ApexTestForInfo{
+ ApexContents: contents,
+ })
+ }
+}
+
+// markPlatformAvailability marks whether or not a module can be available to platform. A module
+// cannot be available to platform if 1) it is explicitly marked as not available (i.e.
+// "//apex_available:platform" is absent) or 2) it depends on another module that isn't (or can't
+// be) available to platform
+// TODO(jiyong): move this to android/apex.go?
+func markPlatformAvailability(mctx android.BottomUpMutatorContext) {
+ // Host and recovery are not considered as platform
+ if mctx.Host() || mctx.Module().InstallInRecovery() {
+ return
+ }
+
+ am, ok := mctx.Module().(android.ApexModule)
+ if !ok {
+ return
+ }
+
+ availableToPlatform := am.AvailableFor(android.AvailableToPlatform)
+
+ // If any of the dep is not available to platform, this module is also considered as being
+ // not available to platform even if it has "//apex_available:platform"
+ mctx.VisitDirectDeps(func(child android.Module) {
+ if !am.DepIsInSameApex(mctx, child) {
+ // if the dependency crosses apex boundary, don't consider it
+ return
+ }
+ if dep, ok := child.(android.ApexModule); ok && dep.NotAvailableForPlatform() {
+ availableToPlatform = false
+ // TODO(b/154889534) trigger an error when 'am' has
+ // "//apex_available:platform"
+ }
+ })
+
+ // Exception 1: stub libraries and native bridge libraries are always available to platform
+ if cc, ok := mctx.Module().(*cc.Module); ok &&
+ (cc.IsStubs() || cc.Target().NativeBridge == android.NativeBridgeEnabled) {
+ availableToPlatform = true
+ }
+
+ // Exception 2: bootstrap bionic libraries are also always available to platform
+ if cc.InstallToBootstrap(mctx.ModuleName(), mctx.Config()) {
+ availableToPlatform = true
+ }
+
+ if !availableToPlatform {
+ am.SetNotAvailableForPlatform()
+ }
+}
+
+// apexMutator visits each module and creates apex variations if the module was marked in the
+// previous run of apexDepsMutator.
+func apexMutator(mctx android.BottomUpMutatorContext) {
+ if !mctx.Module().Enabled() {
+ return
+ }
+
+ // This is the usual path.
+ if am, ok := mctx.Module().(android.ApexModule); ok && am.CanHaveApexVariants() {
+ android.CreateApexVariations(mctx, am)
+ return
+ }
+
+ // apexBundle itself is mutated so that it and its dependencies have the same apex variant.
+ // TODO(jiyong): document the reason why the VNDK APEX is an exception here.
+ if a, ok := mctx.Module().(*apexBundle); ok && !a.vndkApex {
+ apexBundleName := mctx.ModuleName()
+ mctx.CreateVariations(apexBundleName)
+ } else if o, ok := mctx.Module().(*OverrideApex); ok {
+ apexBundleName := o.GetOverriddenModuleName()
+ if apexBundleName == "" {
+ mctx.ModuleErrorf("base property is not set")
+ return
+ }
+ mctx.CreateVariations(apexBundleName)
+ }
+}
+
+// See android.UpdateDirectlyInAnyApex
+// TODO(jiyong): move this to android/apex.go?
+func apexDirectlyInAnyMutator(mctx android.BottomUpMutatorContext) {
+ if !mctx.Module().Enabled() {
+ return
+ }
+ if am, ok := mctx.Module().(android.ApexModule); ok {
+ android.UpdateDirectlyInAnyApex(mctx, am)
+ }
+}
+
+// apexPackaging represents a specific packaging method for an APEX.
+type apexPackaging int
+
+const (
+ // imageApex is a packaging method where contents are included in a filesystem image which
+ // is then included in a zip container. This is the most typical way of packaging.
+ imageApex apexPackaging = iota
+
+ // zipApex is a packaging method where contents are directly included in the zip container.
+ // This is used for host-side testing - because the contents are easily accessible by
+ // unzipping the container.
+ zipApex
+
+ // flattendApex is a packaging method where contents are not included in the APEX file, but
+ // installed to /apex/<apexname> directory on the device. This packaging method is used for
+ // old devices where the filesystem-based APEX file can't be supported.
+ flattenedApex
+)
+
+const (
+ // File extensions of an APEX for different packaging methods
imageApexSuffix = ".apex"
zipApexSuffix = ".zipapex"
flattenedSuffix = ".flattened"
+ // variant names each of which is for a packaging method
imageApexType = "image"
zipApexType = "zip"
flattenedApexType = "flattened"
@@ -46,33 +974,1111 @@
f2fsFsType = "f2fs"
)
-type dependencyTag struct {
- blueprint.BaseDependencyTag
- name string
+// The suffix for the output "file", not the module
+func (a apexPackaging) suffix() string {
+ switch a {
+ case imageApex:
+ return imageApexSuffix
+ case zipApex:
+ return zipApexSuffix
+ default:
+ panic(fmt.Errorf("unknown APEX type %d", a))
+ }
+}
- // determines if the dependent will be part of the APEX payload
- payload bool
+func (a apexPackaging) name() string {
+ switch a {
+ case imageApex:
+ return imageApexType
+ case zipApex:
+ return zipApexType
+ default:
+ panic(fmt.Errorf("unknown APEX type %d", a))
+ }
+}
+
+// apexFlattenedMutator creates one or more variations each of which is for a packaging method.
+// TODO(jiyong): give a better name to this mutator
+func apexFlattenedMutator(mctx android.BottomUpMutatorContext) {
+ if !mctx.Module().Enabled() {
+ return
+ }
+ if ab, ok := mctx.Module().(*apexBundle); ok {
+ var variants []string
+ switch proptools.StringDefault(ab.properties.Payload_type, "image") {
+ case "image":
+ // This is the normal case. Note that both image and flattend APEXes are
+ // created. The image type is installed to the system partition, while the
+ // flattened APEX is (optionally) installed to the system_ext partition.
+ // This is mostly for GSI which has to support wide range of devices. If GSI
+ // is installed on a newer (APEX-capable) device, the image APEX in the
+ // system will be used. However, if the same GSI is installed on an old
+ // device which can't support image APEX, the flattened APEX in the
+ // system_ext partion (which still is part of GSI) is used instead.
+ variants = append(variants, imageApexType, flattenedApexType)
+ case "zip":
+ variants = append(variants, zipApexType)
+ case "both":
+ variants = append(variants, imageApexType, zipApexType, flattenedApexType)
+ default:
+ mctx.PropertyErrorf("payload_type", "%q is not one of \"image\", \"zip\", or \"both\".", *ab.properties.Payload_type)
+ return
+ }
+
+ modules := mctx.CreateLocalVariations(variants...)
+
+ for i, v := range variants {
+ switch v {
+ case imageApexType:
+ modules[i].(*apexBundle).properties.ApexType = imageApex
+ case zipApexType:
+ modules[i].(*apexBundle).properties.ApexType = zipApex
+ case flattenedApexType:
+ modules[i].(*apexBundle).properties.ApexType = flattenedApex
+ // See the comment above for why system_ext.
+ if !mctx.Config().FlattenApex() && ab.Platform() {
+ modules[i].(*apexBundle).MakeAsSystemExt()
+ }
+ }
+ }
+ } else if _, ok := mctx.Module().(*OverrideApex); ok {
+ // payload_type is forcibly overridden to "image"
+ // TODO(jiyong): is this the right decision?
+ mctx.CreateVariations(imageApexType, flattenedApexType)
+ }
+}
+
+// checkUseVendorProperty checks if the use of `use_vendor` property is allowed for the given APEX.
+// When use_vendor is used, native modules are built with __ANDROID_VNDK__ and __ANDROID_APEX__,
+// which may cause compatibility issues. (e.g. libbinder) Even though libbinder restricts its
+// availability via 'apex_available' property and relies on yet another macro
+// __ANDROID_APEX_<NAME>__, we restrict usage of "use_vendor:" from other APEX modules to avoid
+// similar problems.
+func checkUseVendorProperty(ctx android.BottomUpMutatorContext, a *apexBundle) {
+ if proptools.Bool(a.properties.Use_vendor) && !android.InList(a.Name(), useVendorAllowList(ctx.Config())) {
+ ctx.PropertyErrorf("use_vendor", "not allowed to set use_vendor: true")
+ }
}
var (
- sharedLibTag = dependencyTag{name: "sharedLib", payload: true}
- jniLibTag = dependencyTag{name: "jniLib", payload: true}
- executableTag = dependencyTag{name: "executable", payload: true}
- javaLibTag = dependencyTag{name: "javaLib", payload: true}
- prebuiltTag = dependencyTag{name: "prebuilt", payload: true}
- testTag = dependencyTag{name: "test", payload: true}
- keyTag = dependencyTag{name: "key"}
- certificateTag = dependencyTag{name: "certificate"}
- androidAppTag = dependencyTag{name: "androidApp", payload: true}
- rroTag = dependencyTag{name: "rro", payload: true}
- bpfTag = dependencyTag{name: "bpf", payload: true}
- testForTag = dependencyTag{name: "test for"}
+ useVendorAllowListKey = android.NewOnceKey("useVendorAllowList")
+)
- apexAvailBaseline = makeApexAvailableBaseline()
+func useVendorAllowList(config android.Config) []string {
+ return config.Once(useVendorAllowListKey, func() interface{} {
+ return []string{
+ // swcodec uses "vendor" variants for smaller size
+ "com.android.media.swcodec",
+ "test_com.android.media.swcodec",
+ }
+ }).([]string)
+}
+// setUseVendorAllowListForTest overrides useVendorAllowList and must be called before the first
+// call to useVendorAllowList()
+func setUseVendorAllowListForTest(config android.Config, allowList []string) {
+ config.Once(useVendorAllowListKey, func() interface{} {
+ return allowList
+ })
+}
+
+var _ android.DepIsInSameApex = (*apexBundle)(nil)
+
+// Implements android.DepInInSameApex
+func (a *apexBundle) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
+ // direct deps of an APEX bundle are all part of the APEX bundle
+ // TODO(jiyong): shouldn't we look into the payload field of the dependencyTag?
+ return true
+}
+
+var _ android.OutputFileProducer = (*apexBundle)(nil)
+
+// Implements android.OutputFileProducer
+func (a *apexBundle) OutputFiles(tag string) (android.Paths, error) {
+ switch tag {
+ case "":
+ return android.Paths{a.outputFile}, nil
+ default:
+ return nil, fmt.Errorf("unsupported module reference tag %q", tag)
+ }
+}
+
+var _ cc.Coverage = (*apexBundle)(nil)
+
+// Implements cc.Coverage
+func (a *apexBundle) IsNativeCoverageNeeded(ctx android.BaseModuleContext) bool {
+ return ctx.Device() && ctx.DeviceConfig().NativeCoverageEnabled()
+}
+
+// Implements cc.Coverage
+func (a *apexBundle) PreventInstall() {
+ a.properties.PreventInstall = true
+}
+
+// Implements cc.Coverage
+func (a *apexBundle) HideFromMake() {
+ a.properties.HideFromMake = true
+}
+
+// Implements cc.Coverage
+func (a *apexBundle) MarkAsCoverageVariant(coverage bool) {
+ a.properties.IsCoverageVariant = coverage
+}
+
+// Implements cc.Coverage
+func (a *apexBundle) EnableCoverageIfNeeded() {}
+
+var _ android.ApexBundleDepsInfoIntf = (*apexBundle)(nil)
+
+// Implements android.ApexBudleDepsInfoIntf
+func (a *apexBundle) Updatable() bool {
+ return proptools.Bool(a.properties.Updatable)
+}
+
+// getCertString returns the name of the cert that should be used to sign this APEX. This is
+// basically from the "certificate" property, but could be overridden by the device config.
+func (a *apexBundle) getCertString(ctx android.BaseModuleContext) string {
+ moduleName := ctx.ModuleName()
+ // VNDK APEXes share the same certificate. To avoid adding a new VNDK version to the
+ // OVERRIDE_* list, we check with the pseudo module name to see if its certificate is
+ // overridden.
+ if a.vndkApex {
+ moduleName = vndkApexName
+ }
+ certificate, overridden := ctx.DeviceConfig().OverrideCertificateFor(moduleName)
+ if overridden {
+ return ":" + certificate
+ }
+ return String(a.properties.Certificate)
+}
+
+// See the installable property
+func (a *apexBundle) installable() bool {
+ return !a.properties.PreventInstall && (a.properties.Installable == nil || proptools.Bool(a.properties.Installable))
+}
+
+// See the test_only_no_hashtree property
+func (a *apexBundle) testOnlyShouldSkipHashtreeGeneration() bool {
+ return proptools.Bool(a.properties.Test_only_no_hashtree)
+}
+
+// See the test_only_unsigned_payload property
+func (a *apexBundle) testOnlyShouldSkipPayloadSign() bool {
+ return proptools.Bool(a.properties.Test_only_unsigned_payload)
+}
+
+// These functions are interfacing with cc/sanitizer.go. The entire APEX (along with all of its
+// members) can be sanitized, either forcibly, or by the global configuration. For some of the
+// sanitizers, extra dependencies can be forcibly added as well.
+
+func (a *apexBundle) EnableSanitizer(sanitizerName string) {
+ if !android.InList(sanitizerName, a.properties.SanitizerNames) {
+ a.properties.SanitizerNames = append(a.properties.SanitizerNames, sanitizerName)
+ }
+}
+
+func (a *apexBundle) IsSanitizerEnabled(ctx android.BaseModuleContext, sanitizerName string) bool {
+ if android.InList(sanitizerName, a.properties.SanitizerNames) {
+ return true
+ }
+
+ // Then follow the global setting
+ globalSanitizerNames := []string{}
+ if a.Host() {
+ globalSanitizerNames = ctx.Config().SanitizeHost()
+ } else {
+ arches := ctx.Config().SanitizeDeviceArch()
+ if len(arches) == 0 || android.InList(a.Arch().ArchType.Name, arches) {
+ globalSanitizerNames = ctx.Config().SanitizeDevice()
+ }
+ }
+ return android.InList(sanitizerName, globalSanitizerNames)
+}
+
+func (a *apexBundle) AddSanitizerDependencies(ctx android.BottomUpMutatorContext, sanitizerName string) {
+ // TODO(jiyong): move this info (the sanitizer name, the lib name, etc.) to cc/sanitize.go
+ // Keep only the mechanism here.
+ if ctx.Device() && sanitizerName == "hwaddress" && strings.HasPrefix(a.Name(), "com.android.runtime") {
+ imageVariation := a.getImageVariation(ctx)
+ for _, target := range ctx.MultiTargets() {
+ if target.Arch.ArchType.Multilib == "lib64" {
+ addDependenciesForNativeModules(ctx, ApexNativeDependencies{
+ Native_shared_libs: []string{"libclang_rt.hwasan-aarch64-android"},
+ Tests: nil,
+ Jni_libs: nil,
+ Binaries: nil,
+ }, target, imageVariation)
+ break
+ }
+ }
+ }
+}
+
+// apexFileFor<Type> functions below create an apexFile struct for a given Soong module. The
+// returned apexFile saves information about the Soong module that will be used for creating the
+// build rules.
+func apexFileForNativeLibrary(ctx android.BaseModuleContext, ccMod *cc.Module, handleSpecialLibs bool) apexFile {
+ // Decide the APEX-local directory by the multilib of the library In the future, we may
+ // query this to the module.
+ // TODO(jiyong): use the new PackagingSpec
+ var dirInApex string
+ switch ccMod.Arch().ArchType.Multilib {
+ case "lib32":
+ dirInApex = "lib"
+ case "lib64":
+ dirInApex = "lib64"
+ }
+ if ccMod.Target().NativeBridge == android.NativeBridgeEnabled {
+ dirInApex = filepath.Join(dirInApex, ccMod.Target().NativeBridgeRelativePath)
+ }
+ dirInApex = filepath.Join(dirInApex, ccMod.RelativeInstallPath())
+ if handleSpecialLibs && cc.InstallToBootstrap(ccMod.BaseModuleName(), ctx.Config()) {
+ // Special case for Bionic libs and other libs installed with them. This is to
+ // prevent those libs from being included in the search path
+ // /apex/com.android.runtime/${LIB}. This exclusion is required because those libs
+ // in the Runtime APEX are available via the legacy paths in /system/lib/. By the
+ // init process, the libs in the APEX are bind-mounted to the legacy paths and thus
+ // will be loaded into the default linker namespace (aka "platform" namespace). If
+ // the libs are directly in /apex/com.android.runtime/${LIB} then the same libs will
+ // be loaded again into the runtime linker namespace, which will result in double
+ // loading of them, which isn't supported.
+ dirInApex = filepath.Join(dirInApex, "bionic")
+ }
+
+ fileToCopy := ccMod.OutputFile().Path()
+ androidMkModuleName := ccMod.BaseModuleName() + ccMod.Properties.SubName
+ return newApexFile(ctx, fileToCopy, androidMkModuleName, dirInApex, nativeSharedLib, ccMod)
+}
+
+func apexFileForExecutable(ctx android.BaseModuleContext, cc *cc.Module) apexFile {
+ dirInApex := "bin"
+ if cc.Target().NativeBridge == android.NativeBridgeEnabled {
+ dirInApex = filepath.Join(dirInApex, cc.Target().NativeBridgeRelativePath)
+ }
+ dirInApex = filepath.Join(dirInApex, cc.RelativeInstallPath())
+ fileToCopy := cc.OutputFile().Path()
+ androidMkModuleName := cc.BaseModuleName() + cc.Properties.SubName
+ af := newApexFile(ctx, fileToCopy, androidMkModuleName, dirInApex, nativeExecutable, cc)
+ af.symlinks = cc.Symlinks()
+ af.dataPaths = cc.DataPaths()
+ return af
+}
+
+func apexFileForPyBinary(ctx android.BaseModuleContext, py *python.Module) apexFile {
+ dirInApex := "bin"
+ fileToCopy := py.HostToolPath().Path()
+ return newApexFile(ctx, fileToCopy, py.BaseModuleName(), dirInApex, pyBinary, py)
+}
+
+func apexFileForGoBinary(ctx android.BaseModuleContext, depName string, gb bootstrap.GoBinaryTool) apexFile {
+ dirInApex := "bin"
+ s, err := filepath.Rel(android.PathForOutput(ctx).String(), gb.InstallPath())
+ if err != nil {
+ ctx.ModuleErrorf("Unable to use compiled binary at %s", gb.InstallPath())
+ return apexFile{}
+ }
+ fileToCopy := android.PathForOutput(ctx, s)
+ // NB: Since go binaries are static we don't need the module for anything here, which is
+ // good since the go tool is a blueprint.Module not an android.Module like we would
+ // normally use.
+ return newApexFile(ctx, fileToCopy, depName, dirInApex, goBinary, nil)
+}
+
+func apexFileForShBinary(ctx android.BaseModuleContext, sh *sh.ShBinary) apexFile {
+ dirInApex := filepath.Join("bin", sh.SubDir())
+ fileToCopy := sh.OutputFile()
+ af := newApexFile(ctx, fileToCopy, sh.BaseModuleName(), dirInApex, shBinary, sh)
+ af.symlinks = sh.Symlinks()
+ return af
+}
+
+func apexFileForPrebuiltEtc(ctx android.BaseModuleContext, prebuilt prebuilt_etc.PrebuiltEtcModule, depName string) apexFile {
+ dirInApex := filepath.Join(prebuilt.BaseDir(), prebuilt.SubDir())
+ fileToCopy := prebuilt.OutputFile()
+ return newApexFile(ctx, fileToCopy, depName, dirInApex, etc, prebuilt)
+}
+
+func apexFileForCompatConfig(ctx android.BaseModuleContext, config java.PlatformCompatConfigIntf, depName string) apexFile {
+ dirInApex := filepath.Join("etc", config.SubDir())
+ fileToCopy := config.CompatConfig()
+ return newApexFile(ctx, fileToCopy, depName, dirInApex, etc, config)
+}
+
+// javaModule is an interface to handle all Java modules (java_library, dex_import, etc) in the same
+// way.
+type javaModule interface {
+ android.Module
+ BaseModuleName() string
+ DexJarBuildPath() android.Path
+ JacocoReportClassesFile() android.Path
+ LintDepSets() java.LintDepSets
+ Stem() string
+}
+
+var _ javaModule = (*java.Library)(nil)
+var _ javaModule = (*java.SdkLibrary)(nil)
+var _ javaModule = (*java.DexImport)(nil)
+var _ javaModule = (*java.SdkLibraryImport)(nil)
+
+func apexFileForJavaModule(ctx android.BaseModuleContext, module javaModule) apexFile {
+ dirInApex := "javalib"
+ fileToCopy := module.DexJarBuildPath()
+ af := newApexFile(ctx, fileToCopy, module.BaseModuleName(), dirInApex, javaSharedLib, module)
+ af.jacocoReportClassesFile = module.JacocoReportClassesFile()
+ af.lintDepSets = module.LintDepSets()
+ af.customStem = module.Stem() + ".jar"
+ return af
+}
+
+// androidApp is an interface to handle all app modules (android_app, android_app_import, etc.) in
+// the same way.
+type androidApp interface {
+ android.Module
+ Privileged() bool
+ InstallApkName() string
+ OutputFile() android.Path
+ JacocoReportClassesFile() android.Path
+ Certificate() java.Certificate
+ BaseModuleName() string
+}
+
+var _ androidApp = (*java.AndroidApp)(nil)
+var _ androidApp = (*java.AndroidAppImport)(nil)
+
+func apexFileForAndroidApp(ctx android.BaseModuleContext, aapp androidApp) apexFile {
+ appDir := "app"
+ if aapp.Privileged() {
+ appDir = "priv-app"
+ }
+ dirInApex := filepath.Join(appDir, aapp.InstallApkName())
+ fileToCopy := aapp.OutputFile()
+ af := newApexFile(ctx, fileToCopy, aapp.BaseModuleName(), dirInApex, app, aapp)
+ af.jacocoReportClassesFile = aapp.JacocoReportClassesFile()
+ af.certificate = aapp.Certificate()
+
+ if app, ok := aapp.(interface {
+ OverriddenManifestPackageName() string
+ }); ok {
+ af.overriddenPackageName = app.OverriddenManifestPackageName()
+ }
+ return af
+}
+
+func apexFileForRuntimeResourceOverlay(ctx android.BaseModuleContext, rro java.RuntimeResourceOverlayModule) apexFile {
+ rroDir := "overlay"
+ dirInApex := filepath.Join(rroDir, rro.Theme())
+ fileToCopy := rro.OutputFile()
+ af := newApexFile(ctx, fileToCopy, rro.Name(), dirInApex, app, rro)
+ af.certificate = rro.Certificate()
+
+ if a, ok := rro.(interface {
+ OverriddenManifestPackageName() string
+ }); ok {
+ af.overriddenPackageName = a.OverriddenManifestPackageName()
+ }
+ return af
+}
+
+func apexFileForBpfProgram(ctx android.BaseModuleContext, builtFile android.Path, bpfProgram bpf.BpfModule) apexFile {
+ dirInApex := filepath.Join("etc", "bpf")
+ return newApexFile(ctx, builtFile, builtFile.Base(), dirInApex, etc, bpfProgram)
+}
+
+// WalyPayloadDeps visits dependencies that contributes to the payload of this APEX. For each of the
+// visited module, the `do` callback is executed. Returning true in the callback continues the visit
+// to the child modules. Returning false makes the visit to continue in the sibling or the parent
+// modules. This is used in check* functions below.
+func (a *apexBundle) WalkPayloadDeps(ctx android.ModuleContext, do android.PayloadDepsCallback) {
+ ctx.WalkDeps(func(child, parent android.Module) bool {
+ am, ok := child.(android.ApexModule)
+ if !ok || !am.CanHaveApexVariants() {
+ return false
+ }
+
+ // Filter-out unwanted depedendencies
+ depTag := ctx.OtherModuleDependencyTag(child)
+ if _, ok := depTag.(android.ExcludeFromApexContentsTag); ok {
+ return false
+ }
+ if dt, ok := depTag.(dependencyTag); ok && !dt.payload {
+ return false
+ }
+
+ ai := ctx.OtherModuleProvider(child, android.ApexInfoProvider).(android.ApexInfo)
+ externalDep := !android.InList(ctx.ModuleName(), ai.InApexes)
+
+ // Visit actually
+ return do(ctx, parent, am, externalDep)
+ })
+}
+
+// filesystem type of the apex_payload.img inside the APEX. Currently, ext4 and f2fs are supported.
+type fsType int
+
+const (
+ ext4 fsType = iota
+ f2fs
+)
+
+func (f fsType) string() string {
+ switch f {
+ case ext4:
+ return ext4FsType
+ case f2fs:
+ return f2fsFsType
+ default:
+ panic(fmt.Errorf("unknown APEX payload type %d", f))
+ }
+}
+
+// Creates build rules for an APEX. It consists of the following major steps:
+//
+// 1) do some validity checks such as apex_available, min_sdk_version, etc.
+// 2) traverse the dependency tree to collect apexFile structs from them.
+// 3) some fields in apexBundle struct are configured
+// 4) generate the build rules to create the APEX. This is mostly done in builder.go.
+func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ ////////////////////////////////////////////////////////////////////////////////////////////
+ // 1) do some validity checks such as apex_available, min_sdk_version, etc.
+ a.checkApexAvailability(ctx)
+ a.checkUpdatable(ctx)
+ a.checkMinSdkVersion(ctx)
+ a.checkStaticLinkingToStubLibraries(ctx)
+ if len(a.properties.Tests) > 0 && !a.testApex {
+ ctx.PropertyErrorf("tests", "property allowed only in apex_test module type")
+ return
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////
+ // 2) traverse the dependency tree to collect apexFile structs from them.
+
+ // all the files that will be included in this APEX
+ var filesInfo []apexFile
+
+ // native lib dependencies
+ var provideNativeLibs []string
+ var requireNativeLibs []string
+
+ handleSpecialLibs := !android.Bool(a.properties.Ignore_system_library_special_case)
+
+ // TODO(jiyong): do this using WalkPayloadDeps
+ // TODO(jiyong): make this clean!!!
+ ctx.WalkDepsBlueprint(func(child, parent blueprint.Module) bool {
+ depTag := ctx.OtherModuleDependencyTag(child)
+ if _, ok := depTag.(android.ExcludeFromApexContentsTag); ok {
+ return false
+ }
+ depName := ctx.OtherModuleName(child)
+ if _, isDirectDep := parent.(*apexBundle); isDirectDep {
+ switch depTag {
+ case sharedLibTag, jniLibTag:
+ isJniLib := depTag == jniLibTag
+ if c, ok := child.(*cc.Module); ok {
+ fi := apexFileForNativeLibrary(ctx, c, handleSpecialLibs)
+ fi.isJniLib = isJniLib
+ filesInfo = append(filesInfo, fi)
+ // Collect the list of stub-providing libs except:
+ // - VNDK libs are only for vendors
+ // - bootstrap bionic libs are treated as provided by system
+ if c.HasStubsVariants() && !a.vndkApex && !cc.InstallToBootstrap(c.BaseModuleName(), ctx.Config()) {
+ provideNativeLibs = append(provideNativeLibs, fi.stem())
+ }
+ return true // track transitive dependencies
+ } else {
+ propertyName := "native_shared_libs"
+ if isJniLib {
+ propertyName = "jni_libs"
+ }
+ ctx.PropertyErrorf(propertyName, "%q is not a cc_library or cc_library_shared module", depName)
+ }
+ case executableTag:
+ if cc, ok := child.(*cc.Module); ok {
+ filesInfo = append(filesInfo, apexFileForExecutable(ctx, cc))
+ return true // track transitive dependencies
+ } else if sh, ok := child.(*sh.ShBinary); ok {
+ filesInfo = append(filesInfo, apexFileForShBinary(ctx, sh))
+ } else if py, ok := child.(*python.Module); ok && py.HostToolPath().Valid() {
+ filesInfo = append(filesInfo, apexFileForPyBinary(ctx, py))
+ } else if gb, ok := child.(bootstrap.GoBinaryTool); ok && a.Host() {
+ filesInfo = append(filesInfo, apexFileForGoBinary(ctx, depName, gb))
+ } else {
+ ctx.PropertyErrorf("binaries", "%q is neither cc_binary, (embedded) py_binary, (host) blueprint_go_binary, (host) bootstrap_go_binary, nor sh_binary", depName)
+ }
+ case javaLibTag:
+ switch child.(type) {
+ case *java.Library, *java.SdkLibrary, *java.DexImport, *java.SdkLibraryImport:
+ af := apexFileForJavaModule(ctx, child.(javaModule))
+ if !af.ok() {
+ ctx.PropertyErrorf("java_libs", "%q is not configured to be compiled into dex", depName)
+ return false
+ }
+ filesInfo = append(filesInfo, af)
+ return true // track transitive dependencies
+ default:
+ ctx.PropertyErrorf("java_libs", "%q of type %q is not supported", depName, ctx.OtherModuleType(child))
+ }
+ case androidAppTag:
+ if ap, ok := child.(*java.AndroidApp); ok {
+ filesInfo = append(filesInfo, apexFileForAndroidApp(ctx, ap))
+ return true // track transitive dependencies
+ } else if ap, ok := child.(*java.AndroidAppImport); ok {
+ filesInfo = append(filesInfo, apexFileForAndroidApp(ctx, ap))
+ } else if ap, ok := child.(*java.AndroidTestHelperApp); ok {
+ filesInfo = append(filesInfo, apexFileForAndroidApp(ctx, ap))
+ } else if ap, ok := child.(*java.AndroidAppSet); ok {
+ appDir := "app"
+ if ap.Privileged() {
+ appDir = "priv-app"
+ }
+ af := newApexFile(ctx, ap.OutputFile(), ap.BaseModuleName(),
+ filepath.Join(appDir, ap.BaseModuleName()), appSet, ap)
+ af.certificate = java.PresignedCertificate
+ filesInfo = append(filesInfo, af)
+ } else {
+ ctx.PropertyErrorf("apps", "%q is not an android_app module", depName)
+ }
+ case rroTag:
+ if rro, ok := child.(java.RuntimeResourceOverlayModule); ok {
+ filesInfo = append(filesInfo, apexFileForRuntimeResourceOverlay(ctx, rro))
+ } else {
+ ctx.PropertyErrorf("rros", "%q is not an runtime_resource_overlay module", depName)
+ }
+ case bpfTag:
+ if bpfProgram, ok := child.(bpf.BpfModule); ok {
+ filesToCopy, _ := bpfProgram.OutputFiles("")
+ for _, bpfFile := range filesToCopy {
+ filesInfo = append(filesInfo, apexFileForBpfProgram(ctx, bpfFile, bpfProgram))
+ }
+ } else {
+ ctx.PropertyErrorf("bpfs", "%q is not a bpf module", depName)
+ }
+ case prebuiltTag:
+ if prebuilt, ok := child.(prebuilt_etc.PrebuiltEtcModule); ok {
+ filesInfo = append(filesInfo, apexFileForPrebuiltEtc(ctx, prebuilt, depName))
+ } else if prebuilt, ok := child.(java.PlatformCompatConfigIntf); ok {
+ filesInfo = append(filesInfo, apexFileForCompatConfig(ctx, prebuilt, depName))
+ } else {
+ ctx.PropertyErrorf("prebuilts", "%q is not a prebuilt_etc and not a platform_compat_config module", depName)
+ }
+ case testTag:
+ if ccTest, ok := child.(*cc.Module); ok {
+ if ccTest.IsTestPerSrcAllTestsVariation() {
+ // Multiple-output test module (where `test_per_src: true`).
+ //
+ // `ccTest` is the "" ("all tests") variation of a `test_per_src` module.
+ // We do not add this variation to `filesInfo`, as it has no output;
+ // however, we do add the other variations of this module as indirect
+ // dependencies (see below).
+ } else {
+ // Single-output test module (where `test_per_src: false`).
+ af := apexFileForExecutable(ctx, ccTest)
+ af.class = nativeTest
+ filesInfo = append(filesInfo, af)
+ }
+ return true // track transitive dependencies
+ } else {
+ ctx.PropertyErrorf("tests", "%q is not a cc module", depName)
+ }
+ case keyTag:
+ if key, ok := child.(*apexKey); ok {
+ a.private_key_file = key.private_key_file
+ a.public_key_file = key.public_key_file
+ } else {
+ ctx.PropertyErrorf("key", "%q is not an apex_key module", depName)
+ }
+ return false
+ case certificateTag:
+ if dep, ok := child.(*java.AndroidAppCertificate); ok {
+ a.container_certificate_file = dep.Certificate.Pem
+ a.container_private_key_file = dep.Certificate.Key
+ } else {
+ ctx.ModuleErrorf("certificate dependency %q must be an android_app_certificate module", depName)
+ }
+ case android.PrebuiltDepTag:
+ // If the prebuilt is force disabled, remember to delete the prebuilt file
+ // that might have been installed in the previous builds
+ if prebuilt, ok := child.(prebuilt); ok && prebuilt.isForceDisabled() {
+ a.prebuiltFileToDelete = prebuilt.InstallFilename()
+ }
+ }
+ } else if !a.vndkApex {
+ // indirect dependencies
+ if am, ok := child.(android.ApexModule); ok {
+ // We cannot use a switch statement on `depTag` here as the checked
+ // tags used below are private (e.g. `cc.sharedDepTag`).
+ if cc.IsSharedDepTag(depTag) || cc.IsRuntimeDepTag(depTag) {
+ if cc, ok := child.(*cc.Module); ok {
+ if cc.UseVndk() && proptools.Bool(a.properties.Use_vndk_as_stable) && cc.IsVndk() {
+ requireNativeLibs = append(requireNativeLibs, ":vndk")
+ return false
+ }
+ af := apexFileForNativeLibrary(ctx, cc, handleSpecialLibs)
+ af.transitiveDep = true
+ abInfo := ctx.Provider(ApexBundleInfoProvider).(ApexBundleInfo)
+ if !a.Host() && !abInfo.Contents.DirectlyInApex(depName) && (cc.IsStubs() || cc.HasStubsVariants()) {
+ // If the dependency is a stubs lib, don't include it in this APEX,
+ // but make sure that the lib is installed on the device.
+ // In case no APEX is having the lib, the lib is installed to the system
+ // partition.
+ //
+ // Always include if we are a host-apex however since those won't have any
+ // system libraries.
+ if !am.DirectlyInAnyApex() {
+ // we need a module name for Make
+ name := cc.ImplementationModuleName(ctx)
+
+ if !proptools.Bool(a.properties.Use_vendor) {
+ // we don't use subName(.vendor) for a "use_vendor: true" apex
+ // which is supposed to be installed in /system
+ name += cc.Properties.SubName
+ }
+ if !android.InList(name, a.requiredDeps) {
+ a.requiredDeps = append(a.requiredDeps, name)
+ }
+ }
+ requireNativeLibs = append(requireNativeLibs, af.stem())
+ // Don't track further
+ return false
+ }
+ filesInfo = append(filesInfo, af)
+ return true // track transitive dependencies
+ }
+ } else if cc.IsTestPerSrcDepTag(depTag) {
+ if cc, ok := child.(*cc.Module); ok {
+ af := apexFileForExecutable(ctx, cc)
+ // Handle modules created as `test_per_src` variations of a single test module:
+ // use the name of the generated test binary (`fileToCopy`) instead of the name
+ // of the original test module (`depName`, shared by all `test_per_src`
+ // variations of that module).
+ af.androidMkModuleName = filepath.Base(af.builtFile.String())
+ // these are not considered transitive dep
+ af.transitiveDep = false
+ filesInfo = append(filesInfo, af)
+ return true // track transitive dependencies
+ }
+ } else if java.IsJniDepTag(depTag) {
+ // Because APK-in-APEX embeds jni_libs transitively, we don't need to track transitive deps
+ return false
+ } else if java.IsXmlPermissionsFileDepTag(depTag) {
+ if prebuilt, ok := child.(prebuilt_etc.PrebuiltEtcModule); ok {
+ filesInfo = append(filesInfo, apexFileForPrebuiltEtc(ctx, prebuilt, depName))
+ }
+ } else if _, ok := depTag.(android.CopyDirectlyInAnyApexTag); ok {
+ // nothing
+ } else if am.CanHaveApexVariants() && am.IsInstallableToApex() {
+ ctx.ModuleErrorf("unexpected tag %s for indirect dependency %q", android.PrettyPrintTag(depTag), depName)
+ }
+ }
+ }
+ return false
+ })
+ if a.private_key_file == nil {
+ ctx.PropertyErrorf("key", "private_key for %q could not be found", String(a.properties.Key))
+ return
+ }
+
+ // Specific to the ART apex: dexpreopt artifacts for libcore Java libraries. Build rules are
+ // generated by the dexpreopt singleton, and here we access build artifacts via the global
+ // boot image config.
+ if a.artApex {
+ for arch, files := range java.DexpreoptedArtApexJars(ctx) {
+ dirInApex := filepath.Join("javalib", arch.String())
+ for _, f := range files {
+ localModule := "javalib_" + arch.String() + "_" + filepath.Base(f.String())
+ af := newApexFile(ctx, f, localModule, dirInApex, etc, nil)
+ filesInfo = append(filesInfo, af)
+ }
+ }
+ }
+
+ // Remove duplicates in filesInfo
+ removeDup := func(filesInfo []apexFile) []apexFile {
+ encountered := make(map[string]apexFile)
+ for _, f := range filesInfo {
+ dest := filepath.Join(f.installDir, f.builtFile.Base())
+ if e, ok := encountered[dest]; !ok {
+ encountered[dest] = f
+ } else {
+ // If a module is directly included and also transitively depended on
+ // consider it as directly included.
+ e.transitiveDep = e.transitiveDep && f.transitiveDep
+ encountered[dest] = e
+ }
+ }
+ var result []apexFile
+ for _, v := range encountered {
+ result = append(result, v)
+ }
+ return result
+ }
+ filesInfo = removeDup(filesInfo)
+
+ // Sort to have consistent build rules
+ sort.Slice(filesInfo, func(i, j int) bool {
+ return filesInfo[i].builtFile.String() < filesInfo[j].builtFile.String()
+ })
+
+ ////////////////////////////////////////////////////////////////////////////////////////////
+ // 3) some fields in apexBundle struct are configured
+ a.installDir = android.PathForModuleInstall(ctx, "apex")
+ a.filesInfo = filesInfo
+
+ // Set suffix and primaryApexType depending on the ApexType
+ buildFlattenedAsDefault := ctx.Config().FlattenApex() && !ctx.Config().UnbundledBuildApps()
+ switch a.properties.ApexType {
+ case imageApex:
+ if buildFlattenedAsDefault {
+ a.suffix = imageApexSuffix
+ } else {
+ a.suffix = ""
+ a.primaryApexType = true
+
+ if ctx.Config().InstallExtraFlattenedApexes() {
+ a.requiredDeps = append(a.requiredDeps, a.Name()+flattenedSuffix)
+ }
+ }
+ case zipApex:
+ if proptools.String(a.properties.Payload_type) == "zip" {
+ a.suffix = ""
+ a.primaryApexType = true
+ } else {
+ a.suffix = zipApexSuffix
+ }
+ case flattenedApex:
+ if buildFlattenedAsDefault {
+ a.suffix = ""
+ a.primaryApexType = true
+ } else {
+ a.suffix = flattenedSuffix
+ }
+ }
+
+ switch proptools.StringDefault(a.properties.Payload_fs_type, ext4FsType) {
+ case ext4FsType:
+ a.payloadFsType = ext4
+ case f2fsFsType:
+ a.payloadFsType = f2fs
+ default:
+ ctx.PropertyErrorf("payload_fs_type", "%q is not a valid filesystem for apex [ext4, f2fs]", *a.properties.Payload_fs_type)
+ }
+
+ // Optimization. If we are building bundled APEX, for the files that are gathered due to the
+ // transitive dependencies, don't place them inside the APEX, but place a symlink pointing
+ // the same library in the system partition, thus effectively sharing the same libraries
+ // across the APEX boundary. For unbundled APEX, all the gathered files are actually placed
+ // in the APEX.
+ a.linkToSystemLib = !ctx.Config().UnbundledBuild() && a.installable() && !proptools.Bool(a.properties.Use_vendor)
+
+ // APEXes targeting other than system/system_ext partitions use vendor/product variants.
+ // So we can't link them to /system/lib libs which are core variants.
+ if a.SocSpecific() || a.DeviceSpecific() || (a.ProductSpecific() && ctx.Config().EnforceProductPartitionInterface()) {
+ a.linkToSystemLib = false
+ }
+
+ // We don't need the optimization for updatable APEXes, as it might give false signal
+ // to the system health when the APEXes are still bundled (b/149805758)
+ if a.Updatable() && a.properties.ApexType == imageApex {
+ a.linkToSystemLib = false
+ }
+
+ // We also don't want the optimization for host APEXes, because it doesn't make sense.
+ if ctx.Host() {
+ a.linkToSystemLib = false
+ }
+
+ a.setCertificateAndPrivateKey(ctx)
+
+ a.compatSymlinks = makeCompatSymlinks(a.BaseModuleName(), ctx)
+
+ ////////////////////////////////////////////////////////////////////////////////////////////
+ // 4) generate the build rules to create the APEX. This is done in builder.go.
+ a.buildManifest(ctx, provideNativeLibs, requireNativeLibs)
+ a.buildFileContexts(ctx)
+ if a.properties.ApexType == flattenedApex {
+ a.buildFlattenedApex(ctx)
+ } else {
+ a.buildUnflattenedApex(ctx)
+ }
+ a.buildApexDependencyInfo(ctx)
+ a.buildLintReports(ctx)
+ a.distFiles = a.GenerateTaggedDistFiles(ctx)
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Factory functions
+//
+
+func newApexBundle() *apexBundle {
+ module := &apexBundle{}
+
+ module.AddProperties(&module.properties)
+ module.AddProperties(&module.targetProperties)
+ module.AddProperties(&module.overridableProperties)
+
+ android.InitAndroidMultiTargetsArchModule(module, android.HostAndDeviceSupported, android.MultilibCommon)
+ android.InitDefaultableModule(module)
+ android.InitSdkAwareModule(module)
+ android.InitOverridableModule(module, &module.overridableProperties.Overrides)
+ return module
+}
+
+func ApexBundleFactory(testApex bool, artApex bool) android.Module {
+ bundle := newApexBundle()
+ bundle.testApex = testApex
+ bundle.artApex = artApex
+ return bundle
+}
+
+// apex_test is an APEX for testing. The difference from the ordinary apex module type is that
+// certain compatibility checks such as apex_available are not done for apex_test.
+func testApexBundleFactory() android.Module {
+ bundle := newApexBundle()
+ bundle.testApex = true
+ return bundle
+}
+
+// apex packages other modules into an APEX file which is a packaging format for system-level
+// components like binaries, shared libraries, etc.
+func BundleFactory() android.Module {
+ return newApexBundle()
+}
+
+type Defaults struct {
+ android.ModuleBase
+ android.DefaultsModuleBase
+}
+
+// apex_defaults provides defaultable properties to other apex modules.
+func defaultsFactory() android.Module {
+ return DefaultsFactory()
+}
+
+func DefaultsFactory(props ...interface{}) android.Module {
+ module := &Defaults{}
+
+ module.AddProperties(props...)
+ module.AddProperties(
+ &apexBundleProperties{},
+ &apexTargetBundleProperties{},
+ &overridableProperties{},
+ )
+
+ android.InitDefaultsModule(module)
+ return module
+}
+
+type OverrideApex struct {
+ android.ModuleBase
+ android.OverrideModuleBase
+}
+
+func (o *OverrideApex) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ // All the overrides happen in the base module.
+}
+
+// override_apex is used to create an apex module based on another apex module by overriding some of
+// its properties.
+func overrideApexFactory() android.Module {
+ m := &OverrideApex{}
+
+ m.AddProperties(&overridableProperties{})
+
+ android.InitAndroidMultiTargetsArchModule(m, android.DeviceSupported, android.MultilibCommon)
+ android.InitOverrideModule(m)
+ return m
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Vality check routines
+//
+// These are called in at the very beginning of GenerateAndroidBuildActions to flag an error when
+// certain conditions are not met.
+//
+// TODO(jiyong): move these checks to a separate go file.
+
+// Entures that min_sdk_version of the included modules are equal or less than the min_sdk_version
+// of this apexBundle.
+func (a *apexBundle) checkMinSdkVersion(ctx android.ModuleContext) {
+ if a.testApex || a.vndkApex {
+ return
+ }
+ // Meaningless to check min_sdk_version when building use_vendor modules against non-Trebleized targets
+ if proptools.Bool(a.properties.Use_vendor) && ctx.DeviceConfig().VndkVersion() == "" {
+ return
+ }
+ // apexBundle::minSdkVersion reports its own errors.
+ minSdkVersion := a.minSdkVersion(ctx)
+ android.CheckMinSdkVersion(a, ctx, minSdkVersion)
+}
+
+func (a *apexBundle) minSdkVersion(ctx android.BaseModuleContext) android.ApiLevel {
+ ver := proptools.String(a.properties.Min_sdk_version)
+ if ver == "" {
+ return android.FutureApiLevel
+ }
+ apiLevel, err := android.ApiLevelFromUser(ctx, ver)
+ if err != nil {
+ ctx.PropertyErrorf("min_sdk_version", "%s", err.Error())
+ return android.NoneApiLevel
+ }
+ if apiLevel.IsPreview() {
+ // All codenames should build against "current".
+ return android.FutureApiLevel
+ }
+ return apiLevel
+}
+
+// Ensures that a lib providing stub isn't statically linked
+func (a *apexBundle) checkStaticLinkingToStubLibraries(ctx android.ModuleContext) {
+ // Practically, we only care about regular APEXes on the device.
+ if ctx.Host() || a.testApex || a.vndkApex {
+ return
+ }
+
+ abInfo := ctx.Provider(ApexBundleInfoProvider).(ApexBundleInfo)
+
+ a.WalkPayloadDeps(ctx, func(ctx android.ModuleContext, from blueprint.Module, to android.ApexModule, externalDep bool) bool {
+ if ccm, ok := to.(*cc.Module); ok {
+ apexName := ctx.ModuleName()
+ fromName := ctx.OtherModuleName(from)
+ toName := ctx.OtherModuleName(to)
+
+ // If `to` is not actually in the same APEX as `from` then it does not need
+ // apex_available and neither do any of its dependencies.
+ if am, ok := from.(android.DepIsInSameApex); ok && !am.DepIsInSameApex(ctx, to) {
+ // As soon as the dependency graph crosses the APEX boundary, don't go further.
+ return false
+ }
+
+ // The dynamic linker and crash_dump tool in the runtime APEX is the only
+ // exception to this rule. It can't make the static dependencies dynamic
+ // because it can't do the dynamic linking for itself.
+ if apexName == "com.android.runtime" && (fromName == "linker" || fromName == "crash_dump") {
+ return false
+ }
+
+ isStubLibraryFromOtherApex := ccm.HasStubsVariants() && !abInfo.Contents.DirectlyInApex(toName)
+ if isStubLibraryFromOtherApex && !externalDep {
+ ctx.ModuleErrorf("%q required by %q is a native library providing stub. "+
+ "It shouldn't be included in this APEX via static linking. Dependency path: %s", to.String(), fromName, ctx.GetPathString(false))
+ }
+
+ }
+ return true
+ })
+}
+
+// Enforce that Java deps of the apex are using stable SDKs to compile
+func (a *apexBundle) checkUpdatable(ctx android.ModuleContext) {
+ if a.Updatable() {
+ if String(a.properties.Min_sdk_version) == "" {
+ ctx.PropertyErrorf("updatable", "updatable APEXes should set min_sdk_version as well")
+ }
+ a.checkJavaStableSdkVersion(ctx)
+ }
+}
+
+func (a *apexBundle) checkJavaStableSdkVersion(ctx android.ModuleContext) {
+ // Visit direct deps only. As long as we guarantee top-level deps are using stable SDKs,
+ // java's checkLinkType guarantees correct usage for transitive deps
+ ctx.VisitDirectDepsBlueprint(func(module blueprint.Module) {
+ tag := ctx.OtherModuleDependencyTag(module)
+ switch tag {
+ case javaLibTag, androidAppTag:
+ if m, ok := module.(interface{ CheckStableSdkVersion() error }); ok {
+ if err := m.CheckStableSdkVersion(); err != nil {
+ ctx.ModuleErrorf("cannot depend on \"%v\": %v", ctx.OtherModuleName(module), err)
+ }
+ }
+ }
+ })
+}
+
+// Ensures that the all the dependencies are marked as available for this APEX
+func (a *apexBundle) checkApexAvailability(ctx android.ModuleContext) {
+ // Let's be practical. Availability for test, host, and the VNDK apex isn't important
+ if ctx.Host() || a.testApex || a.vndkApex {
+ return
+ }
+
+ // Because APEXes targeting other than system/system_ext partitions can't set
+ // apex_available, we skip checks for these APEXes
+ if a.SocSpecific() || a.DeviceSpecific() || (a.ProductSpecific() && ctx.Config().EnforceProductPartitionInterface()) {
+ return
+ }
+
+ // Coverage build adds additional dependencies for the coverage-only runtime libraries.
+ // Requiring them and their transitive depencies with apex_available is not right
+ // because they just add noise.
+ if ctx.Config().IsEnvTrue("EMMA_INSTRUMENT") || a.IsNativeCoverageNeeded(ctx) {
+ return
+ }
+
+ a.WalkPayloadDeps(ctx, func(ctx android.ModuleContext, from blueprint.Module, to android.ApexModule, externalDep bool) bool {
+ // As soon as the dependency graph crosses the APEX boundary, don't go further.
+ if externalDep {
+ return false
+ }
+
+ apexName := ctx.ModuleName()
+ fromName := ctx.OtherModuleName(from)
+ toName := ctx.OtherModuleName(to)
+
+ // If `to` is not actually in the same APEX as `from` then it does not need
+ // apex_available and neither do any of its dependencies.
+ if am, ok := from.(android.DepIsInSameApex); ok && !am.DepIsInSameApex(ctx, to) {
+ // As soon as the dependency graph crosses the APEX boundary, don't go
+ // further.
+ return false
+ }
+
+ if to.AvailableFor(apexName) || baselineApexAvailable(apexName, toName) {
+ return true
+ }
+ ctx.ModuleErrorf("%q requires %q that doesn't list the APEX under 'apex_available'. Dependency path:%s",
+ fromName, toName, ctx.GetPathString(true))
+ // Visit this module's dependencies to check and report any issues with their availability.
+ return true
+ })
+}
+
+var (
+ apexAvailBaseline = makeApexAvailableBaseline()
inverseApexAvailBaseline = invertApexBaseline(apexAvailBaseline)
)
+func baselineApexAvailable(apex, moduleName string) bool {
+ key := apex
+ moduleName = normalizeModuleName(moduleName)
+
+ if val, ok := apexAvailBaseline[key]; ok && android.InList(moduleName, val) {
+ return true
+ }
+
+ key = android.AvailableToAnyApex
+ if val, ok := apexAvailBaseline[key]; ok && android.InList(moduleName, val) {
+ return true
+ }
+
+ return false
+}
+
+func normalizeModuleName(moduleName string) string {
+ // Prebuilt modules (e.g. java_import, etc.) have "prebuilt_" prefix added by the build
+ // system. Trim the prefix for the check since they are confusing
+ moduleName = strings.TrimPrefix(moduleName, "prebuilt_")
+ if strings.HasPrefix(moduleName, "libclang_rt.") {
+ // This module has many arch variants that depend on the product being built.
+ // We don't want to list them all
+ moduleName = "libclang_rt"
+ }
+ if strings.HasPrefix(moduleName, "androidx.") {
+ // TODO(b/156996905) Set apex_available/min_sdk_version for androidx support libraries
+ moduleName = "androidx"
+ }
+ return moduleName
+}
+
// Transform the map of apex -> modules to module -> apexes.
func invertApexBaseline(m map[string][]string) map[string][]string {
r := make(map[string][]string)
@@ -89,9 +2095,8 @@
return inverseApexAvailBaseline[normalizeModuleName(moduleName)]
}
-// This is a map from apex to modules, which overrides the
-// apex_available setting for that particular module to make
-// it available for the apex regardless of its setting.
+// This is a map from apex to modules, which overrides the apex_available setting for that
+// particular module to make it available for the apex regardless of its setting.
// TODO(b/147364041): remove this
func makeApexAvailableBaseline() map[string][]string {
// The "Module separator"s below are employed to minimize merge conflicts.
@@ -670,6 +2675,26 @@
return m
}
+func init() {
+ android.AddNeverAllowRules(createApexPermittedPackagesRules(qModulesPackages())...)
+ android.AddNeverAllowRules(createApexPermittedPackagesRules(rModulesPackages())...)
+}
+
+func createApexPermittedPackagesRules(modules_packages map[string][]string) []android.Rule {
+ rules := make([]android.Rule, 0, len(modules_packages))
+ for module_name, module_packages := range modules_packages {
+ permitted_packages_rule := android.NeverAllow().
+ BootclasspathJar().
+ With("apex_available", module_name).
+ WithMatcher("permitted_packages", android.NotInList(module_packages)).
+ Because("jars that are part of the " + module_name +
+ " module may only allow these packages: " + strings.Join(module_packages, ",") +
+ ". Please jarjar or move code around.")
+ rules = append(rules, permitted_packages_rule)
+ }
+ return rules
+}
+
// DO NOT EDIT! These are the package prefixes that are exempted from being AOT'ed by ART.
// Adding code to the bootclasspath in new packages will cause issues on module update.
func qModulesPackages() map[string][]string {
@@ -718,1900 +2743,3 @@
},
}
}
-
-func init() {
- android.RegisterModuleType("apex", BundleFactory)
- android.RegisterModuleType("apex_test", testApexBundleFactory)
- android.RegisterModuleType("apex_vndk", vndkApexBundleFactory)
- android.RegisterModuleType("apex_defaults", defaultsFactory)
- android.RegisterModuleType("prebuilt_apex", PrebuiltFactory)
- android.RegisterModuleType("override_apex", overrideApexFactory)
- android.RegisterModuleType("apex_set", apexSetFactory)
-
- android.PreDepsMutators(RegisterPreDepsMutators)
- android.PostDepsMutators(RegisterPostDepsMutators)
-
- android.AddNeverAllowRules(createApexPermittedPackagesRules(qModulesPackages())...)
- android.AddNeverAllowRules(createApexPermittedPackagesRules(rModulesPackages())...)
-}
-
-func createApexPermittedPackagesRules(modules_packages map[string][]string) []android.Rule {
- rules := make([]android.Rule, 0, len(modules_packages))
- for module_name, module_packages := range modules_packages {
- permitted_packages_rule := android.NeverAllow().
- BootclasspathJar().
- With("apex_available", module_name).
- WithMatcher("permitted_packages", android.NotInList(module_packages)).
- Because("jars that are part of the " + module_name +
- " module may only allow these packages: " + strings.Join(module_packages, ",") +
- ". Please jarjar or move code around.")
- rules = append(rules, permitted_packages_rule)
- }
- return rules
-}
-
-func RegisterPreDepsMutators(ctx android.RegisterMutatorsContext) {
- ctx.TopDown("apex_vndk", apexVndkMutator).Parallel()
- ctx.BottomUp("apex_vndk_deps", apexVndkDepsMutator).Parallel()
-}
-
-func RegisterPostDepsMutators(ctx android.RegisterMutatorsContext) {
- ctx.TopDown("apex_deps", apexDepsMutator).Parallel()
- ctx.BottomUp("apex_unique", apexUniqueVariationsMutator).Parallel()
- ctx.BottomUp("apex_test_for_deps", apexTestForDepsMutator).Parallel()
- ctx.BottomUp("apex_test_for", apexTestForMutator).Parallel()
- ctx.BottomUp("apex", apexMutator).Parallel()
- ctx.BottomUp("apex_directly_in_any", apexDirectlyInAnyMutator).Parallel()
- ctx.BottomUp("apex_flattened", apexFlattenedMutator).Parallel()
- ctx.BottomUp("mark_platform_availability", markPlatformAvailability).Parallel()
-}
-
-// Mark the direct and transitive dependencies of apex bundles so that they
-// can be built for the apex bundles.
-func apexDepsMutator(mctx android.TopDownMutatorContext) {
- if !mctx.Module().Enabled() {
- return
- }
- a, ok := mctx.Module().(*apexBundle)
- if !ok || a.vndkApex {
- return
- }
-
- useVndk := a.SocSpecific() || a.DeviceSpecific() || (a.ProductSpecific() && mctx.Config().EnforceProductPartitionInterface())
- excludeVndkLibs := useVndk && proptools.Bool(a.properties.Use_vndk_as_stable)
- if !useVndk && proptools.Bool(a.properties.Use_vndk_as_stable) {
- mctx.PropertyErrorf("use_vndk_as_stable", "not supported for system/system_ext APEXes")
- return
- }
-
- contents := make(map[string]android.ApexMembership)
-
- continueApexDepsWalk := func(child, parent android.Module) bool {
- am, ok := child.(android.ApexModule)
- if !ok || !am.CanHaveApexVariants() {
- return false
- }
- if !parent.(android.DepIsInSameApex).DepIsInSameApex(mctx, child) {
- return false
- }
- if excludeVndkLibs {
- if c, ok := child.(*cc.Module); ok && c.IsVndk() {
- return false
- }
- }
- return true
- }
-
- mctx.WalkDeps(func(child, parent android.Module) bool {
- if !continueApexDepsWalk(child, parent) {
- return false
- }
-
- depName := mctx.OtherModuleName(child)
- // If the parent is apexBundle, this child is directly depended.
- _, directDep := parent.(*apexBundle)
- contents[depName] = contents[depName].Add(directDep)
- return true
- })
-
- apexContents := android.NewApexContents(mctx.ModuleName(), contents)
- mctx.SetProvider(ApexBundleInfoProvider, ApexBundleInfo{
- Contents: apexContents,
- })
-
- apexInfo := android.ApexInfo{
- ApexVariationName: mctx.ModuleName(),
- MinSdkVersionStr: a.minSdkVersion(mctx).String(),
- RequiredSdks: a.RequiredSdks(),
- Updatable: a.Updatable(),
- InApexes: []string{mctx.ModuleName()},
- ApexContents: []*android.ApexContents{apexContents},
- }
-
- mctx.WalkDeps(func(child, parent android.Module) bool {
- if !continueApexDepsWalk(child, parent) {
- return false
- }
-
- child.(android.ApexModule).BuildForApex(apexInfo)
- return true
- })
-}
-
-func apexUniqueVariationsMutator(mctx android.BottomUpMutatorContext) {
- if !mctx.Module().Enabled() {
- return
- }
- if am, ok := mctx.Module().(android.ApexModule); ok {
- // Check if any dependencies use unique apex variations. If so, use unique apex variations
- // for this module.
- android.UpdateUniqueApexVariationsForDeps(mctx, am)
- }
-}
-
-func apexTestForDepsMutator(mctx android.BottomUpMutatorContext) {
- if !mctx.Module().Enabled() {
- return
- }
- // Check if this module is a test for an apex. If so, add a dependency on the apex
- // in order to retrieve its contents later.
- if am, ok := mctx.Module().(android.ApexModule); ok {
- if testFor := am.TestFor(); len(testFor) > 0 {
- mctx.AddFarVariationDependencies([]blueprint.Variation{
- {Mutator: "os", Variation: am.Target().OsVariation()},
- {"arch", "common"},
- }, testForTag, testFor...)
- }
- }
-}
-
-func apexTestForMutator(mctx android.BottomUpMutatorContext) {
- if !mctx.Module().Enabled() {
- return
- }
-
- if _, ok := mctx.Module().(android.ApexModule); ok {
- var contents []*android.ApexContents
- for _, testFor := range mctx.GetDirectDepsWithTag(testForTag) {
- abInfo := mctx.OtherModuleProvider(testFor, ApexBundleInfoProvider).(ApexBundleInfo)
- contents = append(contents, abInfo.Contents)
- }
- mctx.SetProvider(android.ApexTestForInfoProvider, android.ApexTestForInfo{
- ApexContents: contents,
- })
- }
-}
-
-// mark if a module cannot be available to platform. A module cannot be available
-// to platform if 1) it is explicitly marked as not available (i.e. "//apex_available:platform"
-// is absent) or 2) it depends on another module that isn't (or can't be) available to platform
-func markPlatformAvailability(mctx android.BottomUpMutatorContext) {
- // Host and recovery are not considered as platform
- if mctx.Host() || mctx.Module().InstallInRecovery() {
- return
- }
-
- if am, ok := mctx.Module().(android.ApexModule); ok {
- availableToPlatform := am.AvailableFor(android.AvailableToPlatform)
-
- // If any of the dep is not available to platform, this module is also considered
- // as being not available to platform even if it has "//apex_available:platform"
- mctx.VisitDirectDeps(func(child android.Module) {
- if !am.DepIsInSameApex(mctx, child) {
- // if the dependency crosses apex boundary, don't consider it
- return
- }
- if dep, ok := child.(android.ApexModule); ok && dep.NotAvailableForPlatform() {
- availableToPlatform = false
- // TODO(b/154889534) trigger an error when 'am' has "//apex_available:platform"
- }
- })
-
- // Exception 1: stub libraries and native bridge libraries are always available to platform
- if cc, ok := mctx.Module().(*cc.Module); ok &&
- (cc.IsStubs() || cc.Target().NativeBridge == android.NativeBridgeEnabled) {
- availableToPlatform = true
- }
-
- // Exception 2: bootstrap bionic libraries are also always available to platform
- if cc.InstallToBootstrap(mctx.ModuleName(), mctx.Config()) {
- availableToPlatform = true
- }
-
- if !availableToPlatform {
- am.SetNotAvailableForPlatform()
- }
- }
-}
-
-// If a module in an APEX depends on a module from an SDK then it needs an APEX
-// specific variant created for it. Refer to sdk.sdkDepsReplaceMutator.
-func inAnySdk(module android.Module) bool {
- if sa, ok := module.(android.SdkAware); ok {
- return sa.IsInAnySdk()
- }
-
- return false
-}
-
-// Create apex variations if a module is included in APEX(s).
-func apexMutator(mctx android.BottomUpMutatorContext) {
- if !mctx.Module().Enabled() {
- return
- }
-
- if am, ok := mctx.Module().(android.ApexModule); ok && am.CanHaveApexVariants() {
- android.CreateApexVariations(mctx, am)
- } else if a, ok := mctx.Module().(*apexBundle); ok && !a.vndkApex {
- // apex bundle itself is mutated so that it and its modules have same
- // apex variant.
- apexBundleName := mctx.ModuleName()
- mctx.CreateVariations(apexBundleName)
- } else if o, ok := mctx.Module().(*OverrideApex); ok {
- apexBundleName := o.GetOverriddenModuleName()
- if apexBundleName == "" {
- mctx.ModuleErrorf("base property is not set")
- return
- }
- mctx.CreateVariations(apexBundleName)
- }
-
-}
-
-func apexDirectlyInAnyMutator(mctx android.BottomUpMutatorContext) {
- if !mctx.Module().Enabled() {
- return
- }
- if am, ok := mctx.Module().(android.ApexModule); ok {
- android.UpdateDirectlyInAnyApex(mctx, am)
- }
-}
-
-func apexFlattenedMutator(mctx android.BottomUpMutatorContext) {
- if !mctx.Module().Enabled() {
- return
- }
- if ab, ok := mctx.Module().(*apexBundle); ok {
- var variants []string
- switch proptools.StringDefault(ab.properties.Payload_type, "image") {
- case "image":
- variants = append(variants, imageApexType, flattenedApexType)
- case "zip":
- variants = append(variants, zipApexType)
- case "both":
- variants = append(variants, imageApexType, zipApexType, flattenedApexType)
- default:
- mctx.PropertyErrorf("type", "%q is not one of \"image\", \"zip\", or \"both\".", *ab.properties.Payload_type)
- return
- }
-
- modules := mctx.CreateLocalVariations(variants...)
-
- for i, v := range variants {
- switch v {
- case imageApexType:
- modules[i].(*apexBundle).properties.ApexType = imageApex
- case zipApexType:
- modules[i].(*apexBundle).properties.ApexType = zipApex
- case flattenedApexType:
- modules[i].(*apexBundle).properties.ApexType = flattenedApex
- if !mctx.Config().FlattenApex() && ab.Platform() {
- modules[i].(*apexBundle).MakeAsSystemExt()
- }
- }
- }
- } else if _, ok := mctx.Module().(*OverrideApex); ok {
- mctx.CreateVariations(imageApexType, flattenedApexType)
- }
-}
-
-var (
- useVendorAllowListKey = android.NewOnceKey("useVendorAllowList")
-)
-
-// useVendorAllowList returns the list of APEXes which are allowed to use_vendor.
-// When use_vendor is used, native modules are built with __ANDROID_VNDK__ and __ANDROID_APEX__,
-// which may cause compatibility issues. (e.g. libbinder)
-// Even though libbinder restricts its availability via 'apex_available' property and relies on
-// yet another macro __ANDROID_APEX_<NAME>__, we restrict usage of "use_vendor:" from other APEX modules
-// to avoid similar problems.
-func useVendorAllowList(config android.Config) []string {
- return config.Once(useVendorAllowListKey, func() interface{} {
- return []string{
- // swcodec uses "vendor" variants for smaller size
- "com.android.media.swcodec",
- "test_com.android.media.swcodec",
- }
- }).([]string)
-}
-
-// setUseVendorAllowListForTest overrides useVendorAllowList and must be
-// called before the first call to useVendorAllowList()
-func setUseVendorAllowListForTest(config android.Config, allowList []string) {
- config.Once(useVendorAllowListKey, func() interface{} {
- return allowList
- })
-}
-
-type ApexNativeDependencies struct {
- // List of native libraries
- Native_shared_libs []string
-
- // List of JNI libraries
- Jni_libs []string
-
- // List of native executables
- Binaries []string
-
- // List of native tests
- Tests []string
-}
-
-type apexMultilibProperties struct {
- // Native dependencies whose compile_multilib is "first"
- First ApexNativeDependencies
-
- // Native dependencies whose compile_multilib is "both"
- Both ApexNativeDependencies
-
- // Native dependencies whose compile_multilib is "prefer32"
- Prefer32 ApexNativeDependencies
-
- // Native dependencies whose compile_multilib is "32"
- Lib32 ApexNativeDependencies
-
- // Native dependencies whose compile_multilib is "64"
- Lib64 ApexNativeDependencies
-}
-
-type apexBundleProperties struct {
- // Json manifest file describing meta info of this APEX bundle. Default:
- // "apex_manifest.json"
- Manifest *string `android:"path"`
-
- // AndroidManifest.xml file used for the zip container of this APEX bundle.
- // If unspecified, a default one is automatically generated.
- AndroidManifest *string `android:"path"`
-
- // Canonical name of the APEX bundle. Used to determine the path to the activated APEX on
- // device (/apex/<apex_name>).
- // If unspecified, defaults to the value of name.
- Apex_name *string
-
- // Determines the file contexts file for setting security context to each file in this APEX bundle.
- // For platform APEXes, this should points to a file under /system/sepolicy
- // Default: /system/sepolicy/apex/<module_name>_file_contexts.
- File_contexts *string `android:"path"`
-
- ApexNativeDependencies
-
- // List of java libraries that are embedded inside this APEX bundle
- Java_libs []string
-
- // List of prebuilt files that are embedded inside this APEX bundle
- Prebuilts []string
-
- // List of BPF programs inside APEX
- Bpfs []string
-
- // Name of the apex_key module that provides the private key to sign APEX
- Key *string
-
- // The type of APEX to build. Controls what the APEX payload is. Either
- // 'image', 'zip' or 'both'. Default: 'image'.
- Payload_type *string
-
- // The name of a certificate in the default certificate directory, blank to use the default product certificate,
- // or an android_app_certificate module name in the form ":module".
- Certificate *string
-
- // Whether this APEX is installable to one of the partitions. Default: true.
- Installable *bool
-
- // For native libraries and binaries, use the vendor variant instead of the core (platform) variant.
- // Default is false.
- Use_vendor *bool
-
- // For telling the apex to ignore special handling for system libraries such as bionic. Default is false.
- Ignore_system_library_special_case *bool
-
- Multilib apexMultilibProperties
-
- // List of sanitizer names that this APEX is enabled for
- SanitizerNames []string `blueprint:"mutated"`
-
- PreventInstall bool `blueprint:"mutated"`
-
- HideFromMake bool `blueprint:"mutated"`
-
- // package format of this apex variant; could be non-flattened, flattened, or zip.
- // imageApex, zipApex or flattened
- ApexType apexPackaging `blueprint:"mutated"`
-
- // List of SDKs that are used to build this APEX. A reference to an SDK should be either
- // `name#version` or `name` which is an alias for `name#current`. If left empty, `platform#current`
- // is implied. This value affects all modules included in this APEX. In other words, they are
- // also built with the SDKs specified here.
- Uses_sdks []string
-
- // Whenever apex_payload.img of the APEX should include dm-verity hashtree.
- // Should be only used in tests#.
- Test_only_no_hashtree *bool
-
- // Whenever apex_payload.img of the APEX should not be dm-verity signed.
- // Should be only used in tests#.
- Test_only_unsigned_payload *bool
-
- IsCoverageVariant bool `blueprint:"mutated"`
-
- // Whether this APEX is considered updatable or not. When set to true, this will enforce additional
- // rules for making sure that the APEX is truly updatable.
- // - To be updatable, min_sdk_version should be set as well
- // This will also disable the size optimizations like symlinking to the system libs.
- // Default is false.
- Updatable *bool
-
- // The minimum SDK version that this apex must be compatibile with.
- Min_sdk_version *string
-
- // If set true, VNDK libs are considered as stable libs and are not included in this apex.
- // Should be only used in non-system apexes (e.g. vendor: true).
- // Default is false.
- Use_vndk_as_stable *bool
-
- // The type of filesystem to use for an image apex. Either 'ext4' or 'f2fs'.
- // Default 'ext4'.
- Payload_fs_type *string
-}
-
-type ApexBundleInfo struct {
- Contents *android.ApexContents
-}
-
-var ApexBundleInfoProvider = blueprint.NewMutatorProvider(ApexBundleInfo{}, "apex_deps")
-
-type apexTargetBundleProperties struct {
- Target struct {
- // Multilib properties only for android.
- Android struct {
- Multilib apexMultilibProperties
- }
-
- // Multilib properties only for host.
- Host struct {
- Multilib apexMultilibProperties
- }
-
- // Multilib properties only for host linux_bionic.
- Linux_bionic struct {
- Multilib apexMultilibProperties
- }
-
- // Multilib properties only for host linux_glibc.
- Linux_glibc struct {
- Multilib apexMultilibProperties
- }
- }
-}
-
-type overridableProperties struct {
- // List of APKs to package inside APEX
- Apps []string
-
- // List of runtime resource overlays (RROs) inside APEX
- Rros []string
-
- // Names of modules to be overridden. Listed modules can only be other binaries
- // (in Make or Soong).
- // This does not completely prevent installation of the overridden binaries, but if both
- // binaries would be installed by default (in PRODUCT_PACKAGES) the other binary will be removed
- // from PRODUCT_PACKAGES.
- Overrides []string
-
- // Logging Parent value
- Logging_parent string
-
- // Apex Container Package Name.
- // Override value for attribute package:name in AndroidManifest.xml
- Package_name string
-
- // A txt file containing list of files that are allowed to be included in this APEX.
- Allowed_files *string `android:"path"`
-}
-
-type apexPackaging int
-
-const (
- imageApex apexPackaging = iota
- zipApex
- flattenedApex
-)
-
-// The suffix for the output "file", not the module
-func (a apexPackaging) suffix() string {
- switch a {
- case imageApex:
- return imageApexSuffix
- case zipApex:
- return zipApexSuffix
- default:
- panic(fmt.Errorf("unknown APEX type %d", a))
- }
-}
-
-func (a apexPackaging) name() string {
- switch a {
- case imageApex:
- return imageApexType
- case zipApex:
- return zipApexType
- default:
- panic(fmt.Errorf("unknown APEX type %d", a))
- }
-}
-
-type apexFileClass int
-
-const (
- etc apexFileClass = iota
- nativeSharedLib
- nativeExecutable
- shBinary
- pyBinary
- goBinary
- javaSharedLib
- nativeTest
- app
- appSet
-)
-
-func (class apexFileClass) NameInMake() string {
- switch class {
- case etc:
- return "ETC"
- case nativeSharedLib:
- return "SHARED_LIBRARIES"
- case nativeExecutable, shBinary, pyBinary, goBinary:
- return "EXECUTABLES"
- case javaSharedLib:
- return "JAVA_LIBRARIES"
- case nativeTest:
- return "NATIVE_TESTS"
- case app, appSet:
- // b/142537672 Why isn't this APP? We want to have full control over
- // the paths and file names of the apk file under the flattend APEX.
- // If this is set to APP, then the paths and file names are modified
- // by the Make build system. For example, it is installed to
- // /system/apex/<apexname>/app/<Appname>/<apexname>.<Appname>/ instead of
- // /system/apex/<apexname>/app/<Appname> because the build system automatically
- // appends module name (which is <apexname>.<Appname> to the path.
- return "ETC"
- default:
- panic(fmt.Errorf("unknown class %d", class))
- }
-}
-
-// apexFile represents a file in an APEX bundle
-type apexFile struct {
- builtFile android.Path
- stem string
- // Module name of `module` in AndroidMk. Note the generated AndroidMk module for
- // apexFile is named something like <AndroidMk module name>.<apex name>[<apex suffix>]
- androidMkModuleName string
- installDir string
- class apexFileClass
- module android.Module
- // list of symlinks that will be created in installDir that point to this apexFile
- symlinks []string
- dataPaths []android.DataPath
- transitiveDep bool
- moduleDir string
-
- requiredModuleNames []string
- targetRequiredModuleNames []string
- hostRequiredModuleNames []string
-
- jacocoReportClassesFile android.Path // only for javalibs and apps
- lintDepSets java.LintDepSets // only for javalibs and apps
- certificate java.Certificate // only for apps
- overriddenPackageName string // only for apps
-
- isJniLib bool
-
- noticeFiles android.Paths
-}
-
-func newApexFile(ctx android.BaseModuleContext, builtFile android.Path, androidMkModuleName string, installDir string, class apexFileClass, module android.Module) apexFile {
- ret := apexFile{
- builtFile: builtFile,
- androidMkModuleName: androidMkModuleName,
- installDir: installDir,
- class: class,
- module: module,
- }
- if module != nil {
- ret.moduleDir = ctx.OtherModuleDir(module)
- ret.requiredModuleNames = module.RequiredModuleNames()
- ret.targetRequiredModuleNames = module.TargetRequiredModuleNames()
- ret.hostRequiredModuleNames = module.HostRequiredModuleNames()
- ret.noticeFiles = module.NoticeFiles()
- }
- return ret
-}
-
-func (af *apexFile) Ok() bool {
- return af.builtFile != nil && af.builtFile.String() != ""
-}
-
-func (af *apexFile) apexRelativePath(path string) string {
- return filepath.Join(af.installDir, path)
-}
-
-// Path() returns path of this apex file relative to the APEX root
-func (af *apexFile) Path() string {
- return af.apexRelativePath(af.Stem())
-}
-
-func (af *apexFile) Stem() string {
- if af.stem != "" {
- return af.stem
- }
- return af.builtFile.Base()
-}
-
-// SymlinkPaths() returns paths of the symlinks (if any) relative to the APEX root
-func (af *apexFile) SymlinkPaths() []string {
- var ret []string
- for _, symlink := range af.symlinks {
- ret = append(ret, af.apexRelativePath(symlink))
- }
- return ret
-}
-
-func (af *apexFile) AvailableToPlatform() bool {
- if af.module == nil {
- return false
- }
- if am, ok := af.module.(android.ApexModule); ok {
- return am.AvailableFor(android.AvailableToPlatform)
- }
- return false
-}
-
-type fsType int
-
-const (
- ext4 fsType = iota
- f2fs
-)
-
-func (f fsType) string() string {
- switch f {
- case ext4:
- return ext4FsType
- case f2fs:
- return f2fsFsType
- default:
- panic(fmt.Errorf("unknown APEX payload type %d", f))
- }
-}
-
-type apexBundle struct {
- android.ModuleBase
- android.DefaultableModuleBase
- android.OverridableModuleBase
- android.SdkBase
-
- properties apexBundleProperties
- targetProperties apexTargetBundleProperties
- overridableProperties overridableProperties
-
- // specific to apex_vndk modules
- vndkProperties apexVndkProperties
-
- bundleModuleFile android.WritablePath
- outputFile android.WritablePath
- installDir android.InstallPath
-
- prebuiltFileToDelete string
-
- public_key_file android.Path
- private_key_file android.Path
-
- container_certificate_file android.Path
- container_private_key_file android.Path
-
- fileContexts android.WritablePath
-
- // list of files to be included in this apex
- filesInfo []apexFile
-
- // list of module names that should be installed along with this APEX
- requiredDeps []string
-
- // list of module names that this APEX is including (to be shown via *-deps-info target)
- android.ApexBundleDepsInfo
-
- testApex bool
- vndkApex bool
- artApex bool
- primaryApexType bool
-
- manifestJsonOut android.WritablePath
- manifestPbOut android.WritablePath
-
- // list of commands to create symlinks for backward compatibility.
- // these commands will be attached as LOCAL_POST_INSTALL_CMD to
- // apex package itself(for unflattened build) or apex_manifest(for flattened build)
- // so that compat symlinks are always installed regardless of TARGET_FLATTEN_APEX setting.
- compatSymlinks []string
-
- // Suffix of module name in Android.mk
- // ".flattened", ".apex", ".zipapex", or ""
- suffix string
-
- installedFilesFile android.WritablePath
-
- // Whether to create symlink to the system file instead of having a file
- // inside the apex or not
- linkToSystemLib bool
-
- // Struct holding the merged notice file paths in different formats
- mergedNotices android.NoticeOutputs
-
- // Optional list of lint report zip files for apexes that contain java or app modules
- lintReports android.Paths
-
- payloadFsType fsType
-
- distFiles android.TaggedDistFiles
-}
-
-func addDependenciesForNativeModules(ctx android.BottomUpMutatorContext,
- nativeModules ApexNativeDependencies,
- target android.Target, imageVariation string) {
- // Use *FarVariation* to be able to depend on modules having
- // conflicting variations with this module. This is required since
- // arch variant of an APEX bundle is 'common' but it is 'arm' or 'arm64'
- // for native shared libs.
-
- binVariations := target.Variations()
- libVariations := append(target.Variations(),
- blueprint.Variation{Mutator: "link", Variation: "shared"})
-
- if ctx.Device() {
- binVariations = append(binVariations,
- blueprint.Variation{Mutator: "image", Variation: imageVariation})
- libVariations = append(libVariations,
- blueprint.Variation{Mutator: "image", Variation: imageVariation},
- blueprint.Variation{Mutator: "version", Variation: ""}) // "" is the non-stub variant
- }
-
- ctx.AddFarVariationDependencies(libVariations, sharedLibTag, nativeModules.Native_shared_libs...)
-
- ctx.AddFarVariationDependencies(libVariations, jniLibTag, nativeModules.Jni_libs...)
-
- ctx.AddFarVariationDependencies(binVariations, executableTag, nativeModules.Binaries...)
-
- ctx.AddFarVariationDependencies(binVariations, testTag, nativeModules.Tests...)
-}
-
-func (a *apexBundle) combineProperties(ctx android.BottomUpMutatorContext) {
- if ctx.Os().Class == android.Device {
- proptools.AppendProperties(&a.properties.Multilib, &a.targetProperties.Target.Android.Multilib, nil)
- } else {
- proptools.AppendProperties(&a.properties.Multilib, &a.targetProperties.Target.Host.Multilib, nil)
- if ctx.Os().Bionic() {
- proptools.AppendProperties(&a.properties.Multilib, &a.targetProperties.Target.Linux_bionic.Multilib, nil)
- } else {
- proptools.AppendProperties(&a.properties.Multilib, &a.targetProperties.Target.Linux_glibc.Multilib, nil)
- }
- }
-}
-
-func (a *apexBundle) DepsMutator(ctx android.BottomUpMutatorContext) {
- if proptools.Bool(a.properties.Use_vendor) && !android.InList(a.Name(), useVendorAllowList(ctx.Config())) {
- ctx.PropertyErrorf("use_vendor", "not allowed to set use_vendor: true")
- }
-
- targets := ctx.MultiTargets()
- config := ctx.DeviceConfig()
- imageVariation := a.getImageVariation(ctx)
-
- a.combineProperties(ctx)
-
- has32BitTarget := false
- for _, target := range targets {
- if target.Arch.ArchType.Multilib == "lib32" {
- has32BitTarget = true
- }
- }
- for i, target := range targets {
- if target.HostCross {
- // Don't include artifats for the host cross targets because there is no way
- // for us to run those artifacts natively on host
- continue
- }
-
- // When multilib.* is omitted for native_shared_libs/jni_libs/tests, it implies
- // multilib.both
- addDependenciesForNativeModules(ctx,
- ApexNativeDependencies{
- Native_shared_libs: a.properties.Native_shared_libs,
- Tests: a.properties.Tests,
- Jni_libs: a.properties.Jni_libs,
- Binaries: nil,
- },
- target, imageVariation)
-
- // Add native modules targetting both ABIs
- addDependenciesForNativeModules(ctx,
- a.properties.Multilib.Both,
- target,
- imageVariation)
-
- isPrimaryAbi := i == 0
- if isPrimaryAbi {
- // When multilib.* is omitted for binaries, it implies
- // multilib.first
- addDependenciesForNativeModules(ctx,
- ApexNativeDependencies{
- Native_shared_libs: nil,
- Tests: nil,
- Jni_libs: nil,
- Binaries: a.properties.Binaries,
- },
- target, imageVariation)
-
- // Add native modules targetting the first ABI
- addDependenciesForNativeModules(ctx,
- a.properties.Multilib.First,
- target,
- imageVariation)
- }
-
- switch target.Arch.ArchType.Multilib {
- case "lib32":
- // Add native modules targetting 32-bit ABI
- addDependenciesForNativeModules(ctx,
- a.properties.Multilib.Lib32,
- target,
- imageVariation)
-
- addDependenciesForNativeModules(ctx,
- a.properties.Multilib.Prefer32,
- target,
- imageVariation)
- case "lib64":
- // Add native modules targetting 64-bit ABI
- addDependenciesForNativeModules(ctx,
- a.properties.Multilib.Lib64,
- target,
- imageVariation)
-
- if !has32BitTarget {
- addDependenciesForNativeModules(ctx,
- a.properties.Multilib.Prefer32,
- target,
- imageVariation)
- }
- }
- }
-
- // For prebuilt_etc, use the first variant (64 on 64/32bit device,
- // 32 on 32bit device) regardless of the TARGET_PREFER_* setting.
- // b/144532908
- archForPrebuiltEtc := config.Arches()[0]
- for _, arch := range config.Arches() {
- // Prefer 64-bit arch if there is any
- if arch.ArchType.Multilib == "lib64" {
- archForPrebuiltEtc = arch
- break
- }
- }
- ctx.AddFarVariationDependencies([]blueprint.Variation{
- {Mutator: "os", Variation: ctx.Os().String()},
- {Mutator: "arch", Variation: archForPrebuiltEtc.String()},
- }, prebuiltTag, a.properties.Prebuilts...)
-
- ctx.AddFarVariationDependencies(ctx.Config().AndroidCommonTarget.Variations(),
- javaLibTag, a.properties.Java_libs...)
-
- ctx.AddFarVariationDependencies(ctx.Config().AndroidCommonTarget.Variations(),
- bpfTag, a.properties.Bpfs...)
-
- // With EMMA_INSTRUMENT_FRAMEWORK=true the ART boot image includes jacoco library.
- if a.artApex && ctx.Config().IsEnvTrue("EMMA_INSTRUMENT_FRAMEWORK") {
- ctx.AddFarVariationDependencies(ctx.Config().AndroidCommonTarget.Variations(),
- javaLibTag, "jacocoagent")
- }
-
- if String(a.properties.Key) == "" {
- ctx.ModuleErrorf("key is missing")
- return
- }
- ctx.AddDependency(ctx.Module(), keyTag, String(a.properties.Key))
-
- cert := android.SrcIsModule(a.getCertString(ctx))
- if cert != "" {
- ctx.AddDependency(ctx.Module(), certificateTag, cert)
- }
-
- // TODO(jiyong): ensure that all apexes are with non-empty uses_sdks
- if len(a.properties.Uses_sdks) > 0 {
- sdkRefs := []android.SdkRef{}
- for _, str := range a.properties.Uses_sdks {
- parsed := android.ParseSdkRef(ctx, str, "uses_sdks")
- sdkRefs = append(sdkRefs, parsed)
- }
- a.BuildWithSdks(sdkRefs)
- }
-}
-
-func (a *apexBundle) OverridablePropertiesDepsMutator(ctx android.BottomUpMutatorContext) {
- if a.overridableProperties.Allowed_files != nil {
- android.ExtractSourceDeps(ctx, a.overridableProperties.Allowed_files)
- }
- ctx.AddFarVariationDependencies(ctx.Config().AndroidCommonTarget.Variations(),
- androidAppTag, a.overridableProperties.Apps...)
- ctx.AddFarVariationDependencies(ctx.Config().AndroidCommonTarget.Variations(),
- rroTag, a.overridableProperties.Rros...)
-}
-
-func (a *apexBundle) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
- // direct deps of an APEX bundle are all part of the APEX bundle
- return true
-}
-
-func (a *apexBundle) getCertString(ctx android.BaseModuleContext) string {
- moduleName := ctx.ModuleName()
- // VNDK APEXes share the same certificate. To avoid adding a new VNDK version to the OVERRIDE_* list,
- // we check with the pseudo module name to see if its certificate is overridden.
- if a.vndkApex {
- moduleName = vndkApexName
- }
- certificate, overridden := ctx.DeviceConfig().OverrideCertificateFor(moduleName)
- if overridden {
- return ":" + certificate
- }
- return String(a.properties.Certificate)
-}
-
-func (a *apexBundle) OutputFiles(tag string) (android.Paths, error) {
- switch tag {
- case "":
- return android.Paths{a.outputFile}, nil
- default:
- return nil, fmt.Errorf("unsupported module reference tag %q", tag)
- }
-}
-
-func (a *apexBundle) installable() bool {
- return !a.properties.PreventInstall && (a.properties.Installable == nil || proptools.Bool(a.properties.Installable))
-}
-
-func (a *apexBundle) testOnlyShouldSkipHashtreeGeneration() bool {
- return proptools.Bool(a.properties.Test_only_no_hashtree)
-}
-
-func (a *apexBundle) testOnlyShouldSkipPayloadSign() bool {
- return proptools.Bool(a.properties.Test_only_unsigned_payload)
-}
-
-func (a *apexBundle) getImageVariation(ctx android.BottomUpMutatorContext) string {
- deviceConfig := ctx.DeviceConfig()
- if a.vndkApex {
- return cc.VendorVariationPrefix + a.vndkVersion(deviceConfig)
- }
-
- var prefix string
- var vndkVersion string
- if deviceConfig.VndkVersion() != "" {
- if proptools.Bool(a.properties.Use_vendor) {
- prefix = cc.VendorVariationPrefix
- vndkVersion = deviceConfig.PlatformVndkVersion()
- } else if a.SocSpecific() || a.DeviceSpecific() {
- prefix = cc.VendorVariationPrefix
- vndkVersion = deviceConfig.VndkVersion()
- } else if a.ProductSpecific() {
- prefix = cc.ProductVariationPrefix
- vndkVersion = deviceConfig.ProductVndkVersion()
- }
- }
- if vndkVersion == "current" {
- vndkVersion = deviceConfig.PlatformVndkVersion()
- }
- if vndkVersion != "" {
- return prefix + vndkVersion
- }
- return android.CoreVariation
-}
-
-func (a *apexBundle) EnableSanitizer(sanitizerName string) {
- if !android.InList(sanitizerName, a.properties.SanitizerNames) {
- a.properties.SanitizerNames = append(a.properties.SanitizerNames, sanitizerName)
- }
-}
-
-func (a *apexBundle) IsSanitizerEnabled(ctx android.BaseModuleContext, sanitizerName string) bool {
- if android.InList(sanitizerName, a.properties.SanitizerNames) {
- return true
- }
-
- // Then follow the global setting
- globalSanitizerNames := []string{}
- if a.Host() {
- globalSanitizerNames = ctx.Config().SanitizeHost()
- } else {
- arches := ctx.Config().SanitizeDeviceArch()
- if len(arches) == 0 || android.InList(a.Arch().ArchType.Name, arches) {
- globalSanitizerNames = ctx.Config().SanitizeDevice()
- }
- }
- return android.InList(sanitizerName, globalSanitizerNames)
-}
-
-func (a *apexBundle) AddSanitizerDependencies(ctx android.BottomUpMutatorContext, sanitizerName string) {
- if ctx.Device() && sanitizerName == "hwaddress" && strings.HasPrefix(a.Name(), "com.android.runtime") {
- for _, target := range ctx.MultiTargets() {
- if target.Arch.ArchType.Multilib == "lib64" {
- ctx.AddFarVariationDependencies(append(target.Variations(), []blueprint.Variation{
- {Mutator: "image", Variation: a.getImageVariation(ctx)},
- {Mutator: "link", Variation: "shared"},
- {Mutator: "version", Variation: ""}, // "" is the non-stub variant
- }...), sharedLibTag, "libclang_rt.hwasan-aarch64-android")
- break
- }
- }
- }
-}
-
-var _ cc.Coverage = (*apexBundle)(nil)
-
-func (a *apexBundle) IsNativeCoverageNeeded(ctx android.BaseModuleContext) bool {
- return ctx.Device() && ctx.DeviceConfig().NativeCoverageEnabled()
-}
-
-func (a *apexBundle) PreventInstall() {
- a.properties.PreventInstall = true
-}
-
-func (a *apexBundle) HideFromMake() {
- a.properties.HideFromMake = true
-}
-
-func (a *apexBundle) MarkAsCoverageVariant(coverage bool) {
- a.properties.IsCoverageVariant = coverage
-}
-
-func (a *apexBundle) EnableCoverageIfNeeded() {}
-
-// TODO(jiyong) move apexFileFor* close to the apexFile type definition
-func apexFileForNativeLibrary(ctx android.BaseModuleContext, ccMod *cc.Module, handleSpecialLibs bool) apexFile {
- // Decide the APEX-local directory by the multilib of the library
- // In the future, we may query this to the module.
- var dirInApex string
- switch ccMod.Arch().ArchType.Multilib {
- case "lib32":
- dirInApex = "lib"
- case "lib64":
- dirInApex = "lib64"
- }
- if ccMod.Target().NativeBridge == android.NativeBridgeEnabled {
- dirInApex = filepath.Join(dirInApex, ccMod.Target().NativeBridgeRelativePath)
- }
- dirInApex = filepath.Join(dirInApex, ccMod.RelativeInstallPath())
- if handleSpecialLibs && cc.InstallToBootstrap(ccMod.BaseModuleName(), ctx.Config()) {
- // Special case for Bionic libs and other libs installed with them. This is
- // to prevent those libs from being included in the search path
- // /apex/com.android.runtime/${LIB}. This exclusion is required because
- // those libs in the Runtime APEX are available via the legacy paths in
- // /system/lib/. By the init process, the libs in the APEX are bind-mounted
- // to the legacy paths and thus will be loaded into the default linker
- // namespace (aka "platform" namespace). If the libs are directly in
- // /apex/com.android.runtime/${LIB} then the same libs will be loaded again
- // into the runtime linker namespace, which will result in double loading of
- // them, which isn't supported.
- dirInApex = filepath.Join(dirInApex, "bionic")
- }
-
- fileToCopy := ccMod.OutputFile().Path()
- androidMkModuleName := ccMod.BaseModuleName() + ccMod.Properties.SubName
- return newApexFile(ctx, fileToCopy, androidMkModuleName, dirInApex, nativeSharedLib, ccMod)
-}
-
-func apexFileForExecutable(ctx android.BaseModuleContext, cc *cc.Module) apexFile {
- dirInApex := "bin"
- if cc.Target().NativeBridge == android.NativeBridgeEnabled {
- dirInApex = filepath.Join(dirInApex, cc.Target().NativeBridgeRelativePath)
- }
- dirInApex = filepath.Join(dirInApex, cc.RelativeInstallPath())
- fileToCopy := cc.OutputFile().Path()
- androidMkModuleName := cc.BaseModuleName() + cc.Properties.SubName
- af := newApexFile(ctx, fileToCopy, androidMkModuleName, dirInApex, nativeExecutable, cc)
- af.symlinks = cc.Symlinks()
- af.dataPaths = cc.DataPaths()
- return af
-}
-
-func apexFileForPyBinary(ctx android.BaseModuleContext, py *python.Module) apexFile {
- dirInApex := "bin"
- fileToCopy := py.HostToolPath().Path()
- return newApexFile(ctx, fileToCopy, py.BaseModuleName(), dirInApex, pyBinary, py)
-}
-func apexFileForGoBinary(ctx android.BaseModuleContext, depName string, gb bootstrap.GoBinaryTool) apexFile {
- dirInApex := "bin"
- s, err := filepath.Rel(android.PathForOutput(ctx).String(), gb.InstallPath())
- if err != nil {
- ctx.ModuleErrorf("Unable to use compiled binary at %s", gb.InstallPath())
- return apexFile{}
- }
- fileToCopy := android.PathForOutput(ctx, s)
- // NB: Since go binaries are static we don't need the module for anything here, which is
- // good since the go tool is a blueprint.Module not an android.Module like we would
- // normally use.
- return newApexFile(ctx, fileToCopy, depName, dirInApex, goBinary, nil)
-}
-
-func apexFileForShBinary(ctx android.BaseModuleContext, sh *sh.ShBinary) apexFile {
- dirInApex := filepath.Join("bin", sh.SubDir())
- fileToCopy := sh.OutputFile()
- af := newApexFile(ctx, fileToCopy, sh.BaseModuleName(), dirInApex, shBinary, sh)
- af.symlinks = sh.Symlinks()
- return af
-}
-
-type javaModule interface {
- android.Module
- BaseModuleName() string
- DexJarBuildPath() android.Path
- JacocoReportClassesFile() android.Path
- LintDepSets() java.LintDepSets
-
- Stem() string
-}
-
-var _ javaModule = (*java.Library)(nil)
-var _ javaModule = (*java.SdkLibrary)(nil)
-var _ javaModule = (*java.DexImport)(nil)
-var _ javaModule = (*java.SdkLibraryImport)(nil)
-
-func apexFileForJavaLibrary(ctx android.BaseModuleContext, module javaModule) apexFile {
- dirInApex := "javalib"
- fileToCopy := module.DexJarBuildPath()
- af := newApexFile(ctx, fileToCopy, module.BaseModuleName(), dirInApex, javaSharedLib, module)
- af.jacocoReportClassesFile = module.JacocoReportClassesFile()
- af.lintDepSets = module.LintDepSets()
- af.stem = module.Stem() + ".jar"
- return af
-}
-
-func apexFileForPrebuiltEtc(ctx android.BaseModuleContext, prebuilt prebuilt_etc.PrebuiltEtcModule, depName string) apexFile {
- dirInApex := filepath.Join(prebuilt.BaseDir(), prebuilt.SubDir())
- fileToCopy := prebuilt.OutputFile()
- return newApexFile(ctx, fileToCopy, depName, dirInApex, etc, prebuilt)
-}
-
-func apexFileForCompatConfig(ctx android.BaseModuleContext, config java.PlatformCompatConfigIntf, depName string) apexFile {
- dirInApex := filepath.Join("etc", config.SubDir())
- fileToCopy := config.CompatConfig()
- return newApexFile(ctx, fileToCopy, depName, dirInApex, etc, config)
-}
-
-func apexFileForAndroidApp(ctx android.BaseModuleContext, aapp interface {
- android.Module
- Privileged() bool
- InstallApkName() string
- OutputFile() android.Path
- JacocoReportClassesFile() android.Path
- Certificate() java.Certificate
- BaseModuleName() string
-}) apexFile {
- appDir := "app"
- if aapp.Privileged() {
- appDir = "priv-app"
- }
- dirInApex := filepath.Join(appDir, aapp.InstallApkName())
- fileToCopy := aapp.OutputFile()
- af := newApexFile(ctx, fileToCopy, aapp.BaseModuleName(), dirInApex, app, aapp)
- af.jacocoReportClassesFile = aapp.JacocoReportClassesFile()
- af.certificate = aapp.Certificate()
-
- if app, ok := aapp.(interface {
- OverriddenManifestPackageName() string
- }); ok {
- af.overriddenPackageName = app.OverriddenManifestPackageName()
- }
- return af
-}
-
-func apexFileForRuntimeResourceOverlay(ctx android.BaseModuleContext, rro java.RuntimeResourceOverlayModule) apexFile {
- rroDir := "overlay"
- dirInApex := filepath.Join(rroDir, rro.Theme())
- fileToCopy := rro.OutputFile()
- af := newApexFile(ctx, fileToCopy, rro.Name(), dirInApex, app, rro)
- af.certificate = rro.Certificate()
-
- if a, ok := rro.(interface {
- OverriddenManifestPackageName() string
- }); ok {
- af.overriddenPackageName = a.OverriddenManifestPackageName()
- }
- return af
-}
-
-func apexFileForBpfProgram(ctx android.BaseModuleContext, builtFile android.Path, bpfProgram bpf.BpfModule) apexFile {
- dirInApex := filepath.Join("etc", "bpf")
- return newApexFile(ctx, builtFile, builtFile.Base(), dirInApex, etc, bpfProgram)
-}
-
-// Context "decorator", overriding the InstallBypassMake method to always reply `true`.
-type flattenedApexContext struct {
- android.ModuleContext
-}
-
-func (c *flattenedApexContext) InstallBypassMake() bool {
- return true
-}
-
-// Visit dependencies that contributes to the payload of this APEX
-func (a *apexBundle) WalkPayloadDeps(ctx android.ModuleContext, do android.PayloadDepsCallback) {
- ctx.WalkDeps(func(child, parent android.Module) bool {
- am, ok := child.(android.ApexModule)
- if !ok || !am.CanHaveApexVariants() {
- return false
- }
-
- childApexInfo := ctx.OtherModuleProvider(child, android.ApexInfoProvider).(android.ApexInfo)
-
- dt := ctx.OtherModuleDependencyTag(child)
-
- if _, ok := dt.(android.ExcludeFromApexContentsTag); ok {
- return false
- }
-
- // Check for the direct dependencies that contribute to the payload
- if adt, ok := dt.(dependencyTag); ok {
- if adt.payload {
- return do(ctx, parent, am, false /* externalDep */)
- }
- // As soon as the dependency graph crosses the APEX boundary, don't go further.
- return false
- }
-
- // Check for the indirect dependencies if it is considered as part of the APEX
- if android.InList(ctx.ModuleName(), childApexInfo.InApexes) {
- return do(ctx, parent, am, false /* externalDep */)
- }
-
- return do(ctx, parent, am, true /* externalDep */)
- })
-}
-
-func (a *apexBundle) minSdkVersion(ctx android.BaseModuleContext) android.ApiLevel {
- ver := proptools.String(a.properties.Min_sdk_version)
- if ver == "" {
- return android.FutureApiLevel
- }
- apiLevel, err := android.ApiLevelFromUser(ctx, ver)
- if err != nil {
- ctx.PropertyErrorf("min_sdk_version", "%s", err.Error())
- return android.NoneApiLevel
- }
- if apiLevel.IsPreview() {
- // All codenames should build against "current".
- return android.FutureApiLevel
- }
- return apiLevel
-}
-
-func (a *apexBundle) Updatable() bool {
- return proptools.Bool(a.properties.Updatable)
-}
-
-var _ android.ApexBundleDepsInfoIntf = (*apexBundle)(nil)
-
-// Ensures that the dependencies are marked as available for this APEX
-func (a *apexBundle) checkApexAvailability(ctx android.ModuleContext) {
- // Let's be practical. Availability for test, host, and the VNDK apex isn't important
- if ctx.Host() || a.testApex || a.vndkApex {
- return
- }
-
- // Because APEXes targeting other than system/system_ext partitions
- // can't set apex_available, we skip checks for these APEXes
- if a.SocSpecific() || a.DeviceSpecific() ||
- (a.ProductSpecific() && ctx.Config().EnforceProductPartitionInterface()) {
- return
- }
-
- // Coverage build adds additional dependencies for the coverage-only runtime libraries.
- // Requiring them and their transitive depencies with apex_available is not right
- // because they just add noise.
- if ctx.Config().IsEnvTrue("EMMA_INSTRUMENT") || a.IsNativeCoverageNeeded(ctx) {
- return
- }
-
- a.WalkPayloadDeps(ctx, func(ctx android.ModuleContext, from blueprint.Module, to android.ApexModule, externalDep bool) bool {
- if externalDep {
- // As soon as the dependency graph crosses the APEX boundary, don't go further.
- return false
- }
-
- apexName := ctx.ModuleName()
- fromName := ctx.OtherModuleName(from)
- toName := ctx.OtherModuleName(to)
-
- // If `to` is not actually in the same APEX as `from` then it does not need apex_available and neither
- // do any of its dependencies.
- if am, ok := from.(android.DepIsInSameApex); ok && !am.DepIsInSameApex(ctx, to) {
- // As soon as the dependency graph crosses the APEX boundary, don't go further.
- return false
- }
-
- if to.AvailableFor(apexName) || baselineApexAvailable(apexName, toName) {
- return true
- }
- ctx.ModuleErrorf("%q requires %q that doesn't list the APEX under 'apex_available'. Dependency path:%s", fromName, toName, ctx.GetPathString(true))
- // Visit this module's dependencies to check and report any issues with their availability.
- return true
- })
-}
-
-func (a *apexBundle) checkUpdatable(ctx android.ModuleContext) {
- if a.Updatable() {
- if String(a.properties.Min_sdk_version) == "" {
- ctx.PropertyErrorf("updatable", "updatable APEXes should set min_sdk_version as well")
- }
-
- a.checkJavaStableSdkVersion(ctx)
- }
-}
-
-func (a *apexBundle) checkMinSdkVersion(ctx android.ModuleContext) {
- if a.testApex || a.vndkApex {
- return
- }
- // Meaningless to check min_sdk_version when building use_vendor modules against non-Trebleized targets
- if proptools.Bool(a.properties.Use_vendor) && ctx.DeviceConfig().VndkVersion() == "" {
- return
- }
- // apexBundle::minSdkVersion reports its own errors.
- minSdkVersion := a.minSdkVersion(ctx)
- android.CheckMinSdkVersion(a, ctx, minSdkVersion)
-}
-
-// Ensures that a lib providing stub isn't statically linked
-func (a *apexBundle) checkStaticLinkingToStubLibraries(ctx android.ModuleContext) {
- // Practically, we only care about regular APEXes on the device.
- if ctx.Host() || a.testApex || a.vndkApex {
- return
- }
-
- abInfo := ctx.Provider(ApexBundleInfoProvider).(ApexBundleInfo)
-
- a.WalkPayloadDeps(ctx, func(ctx android.ModuleContext, from blueprint.Module, to android.ApexModule, externalDep bool) bool {
- if ccm, ok := to.(*cc.Module); ok {
- apexName := ctx.ModuleName()
- fromName := ctx.OtherModuleName(from)
- toName := ctx.OtherModuleName(to)
-
- // If `to` is not actually in the same APEX as `from` then it does not need apex_available and neither
- // do any of its dependencies.
- if am, ok := from.(android.DepIsInSameApex); ok && !am.DepIsInSameApex(ctx, to) {
- // As soon as the dependency graph crosses the APEX boundary, don't go further.
- return false
- }
-
- // The dynamic linker and crash_dump tool in the runtime APEX is the only exception to this rule.
- // It can't make the static dependencies dynamic because it can't
- // do the dynamic linking for itself.
- if apexName == "com.android.runtime" && (fromName == "linker" || fromName == "crash_dump") {
- return false
- }
-
- isStubLibraryFromOtherApex := ccm.HasStubsVariants() && !abInfo.Contents.DirectlyInApex(toName)
- if isStubLibraryFromOtherApex && !externalDep {
- ctx.ModuleErrorf("%q required by %q is a native library providing stub. "+
- "It shouldn't be included in this APEX via static linking. Dependency path: %s", to.String(), fromName, ctx.GetPathString(false))
- }
-
- }
- return true
- })
-}
-
-func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- buildFlattenedAsDefault := ctx.Config().FlattenApex() && !ctx.Config().UnbundledBuildApps()
- switch a.properties.ApexType {
- case imageApex:
- if buildFlattenedAsDefault {
- a.suffix = imageApexSuffix
- } else {
- a.suffix = ""
- a.primaryApexType = true
-
- if ctx.Config().InstallExtraFlattenedApexes() {
- a.requiredDeps = append(a.requiredDeps, a.Name()+flattenedSuffix)
- }
- }
- case zipApex:
- if proptools.String(a.properties.Payload_type) == "zip" {
- a.suffix = ""
- a.primaryApexType = true
- } else {
- a.suffix = zipApexSuffix
- }
- case flattenedApex:
- if buildFlattenedAsDefault {
- a.suffix = ""
- a.primaryApexType = true
- } else {
- a.suffix = flattenedSuffix
- }
- }
-
- if len(a.properties.Tests) > 0 && !a.testApex {
- ctx.PropertyErrorf("tests", "property not allowed in apex module type")
- return
- }
-
- a.checkApexAvailability(ctx)
- a.checkUpdatable(ctx)
- a.checkMinSdkVersion(ctx)
- a.checkStaticLinkingToStubLibraries(ctx)
-
- handleSpecialLibs := !android.Bool(a.properties.Ignore_system_library_special_case)
-
- // native lib dependencies
- var provideNativeLibs []string
- var requireNativeLibs []string
-
- var filesInfo []apexFile
- // TODO(jiyong) do this using WalkPayloadDeps
- ctx.WalkDepsBlueprint(func(child, parent blueprint.Module) bool {
- depTag := ctx.OtherModuleDependencyTag(child)
- if _, ok := depTag.(android.ExcludeFromApexContentsTag); ok {
- return false
- }
- depName := ctx.OtherModuleName(child)
- if _, isDirectDep := parent.(*apexBundle); isDirectDep {
- switch depTag {
- case sharedLibTag, jniLibTag:
- isJniLib := depTag == jniLibTag
- if c, ok := child.(*cc.Module); ok {
- fi := apexFileForNativeLibrary(ctx, c, handleSpecialLibs)
- fi.isJniLib = isJniLib
- filesInfo = append(filesInfo, fi)
- // Collect the list of stub-providing libs except:
- // - VNDK libs are only for vendors
- // - bootstrap bionic libs are treated as provided by system
- if c.HasStubsVariants() && !a.vndkApex && !cc.InstallToBootstrap(c.BaseModuleName(), ctx.Config()) {
- provideNativeLibs = append(provideNativeLibs, fi.Stem())
- }
- return true // track transitive dependencies
- } else {
- propertyName := "native_shared_libs"
- if isJniLib {
- propertyName = "jni_libs"
- }
- ctx.PropertyErrorf(propertyName, "%q is not a cc_library or cc_library_shared module", depName)
- }
- case executableTag:
- if cc, ok := child.(*cc.Module); ok {
- filesInfo = append(filesInfo, apexFileForExecutable(ctx, cc))
- return true // track transitive dependencies
- } else if sh, ok := child.(*sh.ShBinary); ok {
- filesInfo = append(filesInfo, apexFileForShBinary(ctx, sh))
- } else if py, ok := child.(*python.Module); ok && py.HostToolPath().Valid() {
- filesInfo = append(filesInfo, apexFileForPyBinary(ctx, py))
- } else if gb, ok := child.(bootstrap.GoBinaryTool); ok && a.Host() {
- filesInfo = append(filesInfo, apexFileForGoBinary(ctx, depName, gb))
- } else {
- ctx.PropertyErrorf("binaries", "%q is neither cc_binary, (embedded) py_binary, (host) blueprint_go_binary, (host) bootstrap_go_binary, nor sh_binary", depName)
- }
- case javaLibTag:
- switch child.(type) {
- case *java.Library, *java.SdkLibrary, *java.DexImport, *java.SdkLibraryImport:
- af := apexFileForJavaLibrary(ctx, child.(javaModule))
- if !af.Ok() {
- ctx.PropertyErrorf("java_libs", "%q is not configured to be compiled into dex", depName)
- return false
- }
- filesInfo = append(filesInfo, af)
- return true // track transitive dependencies
- default:
- ctx.PropertyErrorf("java_libs", "%q of type %q is not supported", depName, ctx.OtherModuleType(child))
- }
- case androidAppTag:
- if ap, ok := child.(*java.AndroidApp); ok {
- filesInfo = append(filesInfo, apexFileForAndroidApp(ctx, ap))
- return true // track transitive dependencies
- } else if ap, ok := child.(*java.AndroidAppImport); ok {
- filesInfo = append(filesInfo, apexFileForAndroidApp(ctx, ap))
- } else if ap, ok := child.(*java.AndroidTestHelperApp); ok {
- filesInfo = append(filesInfo, apexFileForAndroidApp(ctx, ap))
- } else if ap, ok := child.(*java.AndroidAppSet); ok {
- appDir := "app"
- if ap.Privileged() {
- appDir = "priv-app"
- }
- af := newApexFile(ctx, ap.OutputFile(), ap.BaseModuleName(),
- filepath.Join(appDir, ap.BaseModuleName()), appSet, ap)
- af.certificate = java.PresignedCertificate
- filesInfo = append(filesInfo, af)
- } else {
- ctx.PropertyErrorf("apps", "%q is not an android_app module", depName)
- }
- case rroTag:
- if rro, ok := child.(java.RuntimeResourceOverlayModule); ok {
- filesInfo = append(filesInfo, apexFileForRuntimeResourceOverlay(ctx, rro))
- } else {
- ctx.PropertyErrorf("rros", "%q is not an runtime_resource_overlay module", depName)
- }
- case bpfTag:
- if bpfProgram, ok := child.(bpf.BpfModule); ok {
- filesToCopy, _ := bpfProgram.OutputFiles("")
- for _, bpfFile := range filesToCopy {
- filesInfo = append(filesInfo, apexFileForBpfProgram(ctx, bpfFile, bpfProgram))
- }
- } else {
- ctx.PropertyErrorf("bpfs", "%q is not a bpf module", depName)
- }
- case prebuiltTag:
- if prebuilt, ok := child.(prebuilt_etc.PrebuiltEtcModule); ok {
- filesInfo = append(filesInfo, apexFileForPrebuiltEtc(ctx, prebuilt, depName))
- } else if prebuilt, ok := child.(java.PlatformCompatConfigIntf); ok {
- filesInfo = append(filesInfo, apexFileForCompatConfig(ctx, prebuilt, depName))
- } else {
- ctx.PropertyErrorf("prebuilts", "%q is not a prebuilt_etc and not a platform_compat_config module", depName)
- }
- case testTag:
- if ccTest, ok := child.(*cc.Module); ok {
- if ccTest.IsTestPerSrcAllTestsVariation() {
- // Multiple-output test module (where `test_per_src: true`).
- //
- // `ccTest` is the "" ("all tests") variation of a `test_per_src` module.
- // We do not add this variation to `filesInfo`, as it has no output;
- // however, we do add the other variations of this module as indirect
- // dependencies (see below).
- } else {
- // Single-output test module (where `test_per_src: false`).
- af := apexFileForExecutable(ctx, ccTest)
- af.class = nativeTest
- filesInfo = append(filesInfo, af)
- }
- return true // track transitive dependencies
- } else {
- ctx.PropertyErrorf("tests", "%q is not a cc module", depName)
- }
- case keyTag:
- if key, ok := child.(*apexKey); ok {
- a.private_key_file = key.private_key_file
- a.public_key_file = key.public_key_file
- } else {
- ctx.PropertyErrorf("key", "%q is not an apex_key module", depName)
- }
- return false
- case certificateTag:
- if dep, ok := child.(*java.AndroidAppCertificate); ok {
- a.container_certificate_file = dep.Certificate.Pem
- a.container_private_key_file = dep.Certificate.Key
- } else {
- ctx.ModuleErrorf("certificate dependency %q must be an android_app_certificate module", depName)
- }
- case android.PrebuiltDepTag:
- // If the prebuilt is force disabled, remember to delete the prebuilt file
- // that might have been installed in the previous builds
- if prebuilt, ok := child.(prebuilt); ok && prebuilt.isForceDisabled() {
- a.prebuiltFileToDelete = prebuilt.InstallFilename()
- }
- }
- } else if !a.vndkApex {
- // indirect dependencies
- if am, ok := child.(android.ApexModule); ok {
- // We cannot use a switch statement on `depTag` here as the checked
- // tags used below are private (e.g. `cc.sharedDepTag`).
- if cc.IsSharedDepTag(depTag) || cc.IsRuntimeDepTag(depTag) {
- if cc, ok := child.(*cc.Module); ok {
- if cc.UseVndk() && proptools.Bool(a.properties.Use_vndk_as_stable) && cc.IsVndk() {
- requireNativeLibs = append(requireNativeLibs, ":vndk")
- return false
- }
- af := apexFileForNativeLibrary(ctx, cc, handleSpecialLibs)
- af.transitiveDep = true
- abInfo := ctx.Provider(ApexBundleInfoProvider).(ApexBundleInfo)
- if !a.Host() && !abInfo.Contents.DirectlyInApex(depName) && (cc.IsStubs() || cc.HasStubsVariants()) {
- // If the dependency is a stubs lib, don't include it in this APEX,
- // but make sure that the lib is installed on the device.
- // In case no APEX is having the lib, the lib is installed to the system
- // partition.
- //
- // Always include if we are a host-apex however since those won't have any
- // system libraries.
- if !am.DirectlyInAnyApex() {
- // we need a module name for Make
- name := cc.ImplementationModuleName(ctx)
-
- if !proptools.Bool(a.properties.Use_vendor) {
- // we don't use subName(.vendor) for a "use_vendor: true" apex
- // which is supposed to be installed in /system
- name += cc.Properties.SubName
- }
- if !android.InList(name, a.requiredDeps) {
- a.requiredDeps = append(a.requiredDeps, name)
- }
- }
- requireNativeLibs = append(requireNativeLibs, af.Stem())
- // Don't track further
- return false
- }
- filesInfo = append(filesInfo, af)
- return true // track transitive dependencies
- }
- } else if cc.IsTestPerSrcDepTag(depTag) {
- if cc, ok := child.(*cc.Module); ok {
- af := apexFileForExecutable(ctx, cc)
- // Handle modules created as `test_per_src` variations of a single test module:
- // use the name of the generated test binary (`fileToCopy`) instead of the name
- // of the original test module (`depName`, shared by all `test_per_src`
- // variations of that module).
- af.androidMkModuleName = filepath.Base(af.builtFile.String())
- // these are not considered transitive dep
- af.transitiveDep = false
- filesInfo = append(filesInfo, af)
- return true // track transitive dependencies
- }
- } else if java.IsJniDepTag(depTag) {
- // Because APK-in-APEX embeds jni_libs transitively, we don't need to track transitive deps
- return false
- } else if java.IsXmlPermissionsFileDepTag(depTag) {
- if prebuilt, ok := child.(prebuilt_etc.PrebuiltEtcModule); ok {
- filesInfo = append(filesInfo, apexFileForPrebuiltEtc(ctx, prebuilt, depName))
- }
- } else if _, ok := depTag.(android.CopyDirectlyInAnyApexTag); ok {
- // nothing
- } else if am.CanHaveApexVariants() && am.IsInstallableToApex() {
- ctx.ModuleErrorf("unexpected tag %s for indirect dependency %q", android.PrettyPrintTag(depTag), depName)
- }
- }
- }
- return false
- })
-
- // Specific to the ART apex: dexpreopt artifacts for libcore Java libraries.
- // Build rules are generated by the dexpreopt singleton, and here we access build artifacts
- // via the global boot image config.
- if a.artApex {
- for arch, files := range java.DexpreoptedArtApexJars(ctx) {
- dirInApex := filepath.Join("javalib", arch.String())
- for _, f := range files {
- localModule := "javalib_" + arch.String() + "_" + filepath.Base(f.String())
- af := newApexFile(ctx, f, localModule, dirInApex, etc, nil)
- filesInfo = append(filesInfo, af)
- }
- }
- }
-
- if a.private_key_file == nil {
- ctx.PropertyErrorf("key", "private_key for %q could not be found", String(a.properties.Key))
- return
- }
-
- // remove duplicates in filesInfo
- removeDup := func(filesInfo []apexFile) []apexFile {
- encountered := make(map[string]apexFile)
- for _, f := range filesInfo {
- dest := filepath.Join(f.installDir, f.builtFile.Base())
- if e, ok := encountered[dest]; !ok {
- encountered[dest] = f
- } else {
- // If a module is directly included and also transitively depended on
- // consider it as directly included.
- e.transitiveDep = e.transitiveDep && f.transitiveDep
- encountered[dest] = e
- }
- }
- var result []apexFile
- for _, v := range encountered {
- result = append(result, v)
- }
- return result
- }
- filesInfo = removeDup(filesInfo)
-
- // to have consistent build rules
- sort.Slice(filesInfo, func(i, j int) bool {
- return filesInfo[i].builtFile.String() < filesInfo[j].builtFile.String()
- })
-
- a.installDir = android.PathForModuleInstall(ctx, "apex")
- a.filesInfo = filesInfo
-
- switch proptools.StringDefault(a.properties.Payload_fs_type, ext4FsType) {
- case ext4FsType:
- a.payloadFsType = ext4
- case f2fsFsType:
- a.payloadFsType = f2fs
- default:
- ctx.PropertyErrorf("payload_fs_type", "%q is not a valid filesystem for apex [ext4, f2fs]", *a.properties.Payload_fs_type)
- }
-
- // Optimization. If we are building bundled APEX, for the files that are gathered due to the
- // transitive dependencies, don't place them inside the APEX, but place a symlink pointing
- // the same library in the system partition, thus effectively sharing the same libraries
- // across the APEX boundary. For unbundled APEX, all the gathered files are actually placed
- // in the APEX.
- a.linkToSystemLib = !ctx.Config().UnbundledBuild() &&
- a.installable() &&
- !proptools.Bool(a.properties.Use_vendor)
-
- // APEXes targeting other than system/system_ext partitions use vendor/product variants.
- // So we can't link them to /system/lib libs which are core variants.
- if a.SocSpecific() || a.DeviceSpecific() ||
- (a.ProductSpecific() && ctx.Config().EnforceProductPartitionInterface()) {
- a.linkToSystemLib = false
- }
-
- // We don't need the optimization for updatable APEXes, as it might give false signal
- // to the system health when the APEXes are still bundled (b/149805758)
- if a.Updatable() && a.properties.ApexType == imageApex {
- a.linkToSystemLib = false
- }
-
- // We also don't want the optimization for host APEXes, because it doesn't make sense.
- if ctx.Host() {
- a.linkToSystemLib = false
- }
-
- // prepare apex_manifest.json
- a.buildManifest(ctx, provideNativeLibs, requireNativeLibs)
-
- a.buildFileContexts(ctx)
-
- a.setCertificateAndPrivateKey(ctx)
- if a.properties.ApexType == flattenedApex {
- a.buildFlattenedApex(ctx)
- } else {
- a.buildUnflattenedApex(ctx)
- }
-
- a.compatSymlinks = makeCompatSymlinks(a.BaseModuleName(), ctx)
-
- a.buildApexDependencyInfo(ctx)
-
- a.buildLintReports(ctx)
-
- a.distFiles = a.GenerateTaggedDistFiles(ctx)
-}
-
-// Enforce that Java deps of the apex are using stable SDKs to compile
-func (a *apexBundle) checkJavaStableSdkVersion(ctx android.ModuleContext) {
- // Visit direct deps only. As long as we guarantee top-level deps are using
- // stable SDKs, java's checkLinkType guarantees correct usage for transitive deps
- ctx.VisitDirectDepsBlueprint(func(module blueprint.Module) {
- tag := ctx.OtherModuleDependencyTag(module)
- switch tag {
- case javaLibTag, androidAppTag:
- if m, ok := module.(interface{ CheckStableSdkVersion() error }); ok {
- if err := m.CheckStableSdkVersion(); err != nil {
- ctx.ModuleErrorf("cannot depend on \"%v\": %v", ctx.OtherModuleName(module), err)
- }
- }
- }
- })
-}
-
-func baselineApexAvailable(apex, moduleName string) bool {
- key := apex
- moduleName = normalizeModuleName(moduleName)
-
- if val, ok := apexAvailBaseline[key]; ok && android.InList(moduleName, val) {
- return true
- }
-
- key = android.AvailableToAnyApex
- if val, ok := apexAvailBaseline[key]; ok && android.InList(moduleName, val) {
- return true
- }
-
- return false
-}
-
-func normalizeModuleName(moduleName string) string {
- // Prebuilt modules (e.g. java_import, etc.) have "prebuilt_" prefix added by the build
- // system. Trim the prefix for the check since they are confusing
- moduleName = strings.TrimPrefix(moduleName, "prebuilt_")
- if strings.HasPrefix(moduleName, "libclang_rt.") {
- // This module has many arch variants that depend on the product being built.
- // We don't want to list them all
- moduleName = "libclang_rt"
- }
- if strings.HasPrefix(moduleName, "androidx.") {
- // TODO(b/156996905) Set apex_available/min_sdk_version for androidx support libraries
- moduleName = "androidx"
- }
- return moduleName
-}
-
-func newApexBundle() *apexBundle {
- module := &apexBundle{}
- module.AddProperties(&module.properties)
- module.AddProperties(&module.targetProperties)
- module.AddProperties(&module.overridableProperties)
- android.InitAndroidMultiTargetsArchModule(module, android.HostAndDeviceSupported, android.MultilibCommon)
- android.InitDefaultableModule(module)
- android.InitSdkAwareModule(module)
- android.InitOverridableModule(module, &module.overridableProperties.Overrides)
- return module
-}
-
-func ApexBundleFactory(testApex bool, artApex bool) android.Module {
- bundle := newApexBundle()
- bundle.testApex = testApex
- bundle.artApex = artApex
- return bundle
-}
-
-// apex_test is an APEX for testing. The difference from the ordinary apex module type is that
-// certain compatibility checks such as apex_available are not done for apex_test.
-func testApexBundleFactory() android.Module {
- bundle := newApexBundle()
- bundle.testApex = true
- return bundle
-}
-
-// apex packages other modules into an APEX file which is a packaging format for system-level
-// components like binaries, shared libraries, etc.
-func BundleFactory() android.Module {
- return newApexBundle()
-}
-
-//
-// Defaults
-//
-type Defaults struct {
- android.ModuleBase
- android.DefaultsModuleBase
-}
-
-func defaultsFactory() android.Module {
- return DefaultsFactory()
-}
-
-func DefaultsFactory(props ...interface{}) android.Module {
- module := &Defaults{}
-
- module.AddProperties(props...)
- module.AddProperties(
- &apexBundleProperties{},
- &apexTargetBundleProperties{},
- &overridableProperties{},
- )
-
- android.InitDefaultsModule(module)
- return module
-}
-
-//
-// OverrideApex
-//
-type OverrideApex struct {
- android.ModuleBase
- android.OverrideModuleBase
-}
-
-func (o *OverrideApex) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- // All the overrides happen in the base module.
-}
-
-// override_apex is used to create an apex module based on another apex module
-// by overriding some of its properties.
-func overrideApexFactory() android.Module {
- m := &OverrideApex{}
- m.AddProperties(&overridableProperties{})
-
- android.InitAndroidMultiTargetsArchModule(m, android.DeviceSupported, android.MultilibCommon)
- android.InitOverrideModule(m)
- return m
-}
diff --git a/apex/builder.go b/apex/builder.go
index acfb8c5..6f27dd1 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -195,8 +195,8 @@
// collect jniLibs. Notice that a.filesInfo is already sorted
var jniLibs []string
for _, fi := range a.filesInfo {
- if fi.isJniLib && !android.InList(fi.Stem(), jniLibs) {
- jniLibs = append(jniLibs, fi.Stem())
+ if fi.isJniLib && !android.InList(fi.stem(), jniLibs) {
+ jniLibs = append(jniLibs, fi.stem())
}
}
if len(jniLibs) > 0 {
@@ -363,7 +363,7 @@
config.Apex_config.Apex_embedded_apk_config,
ApkConfig{
Package_name: packageName,
- Apk_path: fi.Path(),
+ Apk_path: fi.path(),
})
}
}
@@ -396,15 +396,15 @@
// TODO(jiyong): construct the copy rules using RuleBuilder
var copyCommands []string
for _, fi := range a.filesInfo {
- destPath := android.PathForModuleOut(ctx, "image"+suffix, fi.Path()).String()
+ destPath := android.PathForModuleOut(ctx, "image"+suffix, fi.path()).String()
destPathDir := filepath.Dir(destPath)
if fi.class == appSet {
copyCommands = append(copyCommands, "rm -rf "+destPathDir)
}
copyCommands = append(copyCommands, "mkdir -p "+destPathDir)
- if a.linkToSystemLib && fi.transitiveDep && fi.AvailableToPlatform() {
+ if a.linkToSystemLib && fi.transitiveDep && fi.availableToPlatform() {
// TODO(jiyong): pathOnDevice should come from fi.module, not being calculated here
- pathOnDevice := filepath.Join("/system", fi.Path())
+ pathOnDevice := filepath.Join("/system", fi.path())
copyCommands = append(copyCommands, "ln -sfn "+pathOnDevice+" "+destPath)
} else {
if fi.class == appSet {
@@ -416,7 +416,7 @@
implicitInputs = append(implicitInputs, fi.builtFile)
}
// create additional symlinks pointing the file inside the APEX
- for _, symlinkPath := range fi.SymlinkPaths() {
+ for _, symlinkPath := range fi.symlinkPaths() {
symlinkDest := android.PathForModuleOut(ctx, "image"+suffix, symlinkPath).String()
copyCommands = append(copyCommands, "ln -sfn "+filepath.Base(destPath)+" "+symlinkDest)
}
@@ -444,7 +444,7 @@
emitCommands = append(emitCommands, "echo ./apex_manifest.json >> "+imageContentFile.String())
}
for _, fi := range a.filesInfo {
- emitCommands = append(emitCommands, "echo './"+fi.Path()+"' >> "+imageContentFile.String())
+ emitCommands = append(emitCommands, "echo './"+fi.path()+"' >> "+imageContentFile.String())
}
emitCommands = append(emitCommands, "sort -o "+imageContentFile.String()+" "+imageContentFile.String())
implicitInputs = append(implicitInputs, a.manifestPbOut)
@@ -489,7 +489,7 @@
var extractedAppSetPaths android.Paths
var extractedAppSetDirs []string
for _, f := range a.filesInfo {
- pathInApex := f.Path()
+ pathInApex := f.path()
if f.installDir == "bin" || strings.HasPrefix(f.installDir, "bin/") {
executablePaths = append(executablePaths, pathInApex)
for _, d := range f.dataPaths {
@@ -697,6 +697,15 @@
a.installedFilesFile = a.buildInstalledFilesFile(ctx, a.outputFile, imageDir)
}
+// Context "decorator", overriding the InstallBypassMake method to always reply `true`.
+type flattenedApexContext struct {
+ android.ModuleContext
+}
+
+func (c *flattenedApexContext) InstallBypassMake() bool {
+ return true
+}
+
func (a *apexBundle) buildFlattenedApex(ctx android.ModuleContext) {
// Temporarily wrap the original `ctx` into a `flattenedApexContext` to have it
// reply true to `InstallBypassMake()` (thus making the call
@@ -742,7 +751,7 @@
apexBundleName := a.Name()
for _, fi := range a.filesInfo {
dir := filepath.Join("apex", apexBundleName, fi.installDir)
- target := ctx.InstallFile(android.PathForModuleInstall(ctx, dir), fi.Stem(), fi.builtFile)
+ target := ctx.InstallFile(android.PathForModuleInstall(ctx, dir), fi.stem(), fi.builtFile)
for _, sym := range fi.symlinks {
ctx.InstallSymlink(android.PathForModuleInstall(ctx, dir), sym, target)
}
diff --git a/bazel/Android.bp b/bazel/Android.bp
new file mode 100644
index 0000000..0113726
--- /dev/null
+++ b/bazel/Android.bp
@@ -0,0 +1,10 @@
+bootstrap_go_package {
+ name: "soong-bazel",
+ pkgPath: "android/soong/bazel",
+ srcs: [
+ "properties.go",
+ ],
+ pluginFor: [
+ "soong_build",
+ ],
+}
diff --git a/bazel/properties.go b/bazel/properties.go
new file mode 100644
index 0000000..8bb1956
--- /dev/null
+++ b/bazel/properties.go
@@ -0,0 +1,27 @@
+// Copyright 2020 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 bazel
+
+type bazelModuleProperties struct {
+ // The label of the Bazel target replacing this Soong module.
+ Label string
+}
+
+// Properties contains common module properties for migration purposes.
+type Properties struct {
+ // In USE_BAZEL_ANALYSIS=1 mode, this represents the Bazel target replacing
+ // this Soong module.
+ Bazel_module bazelModuleProperties
+}
diff --git a/cc/prebuilt_test.go b/cc/prebuilt_test.go
index 5bf334e..ee4de6e 100644
--- a/cc/prebuilt_test.go
+++ b/cc/prebuilt_test.go
@@ -32,7 +32,7 @@
// * Configure that we are inside make
// * Add CommonOS to ensure that androidmk processing works.
android.RegisterAndroidMkBuildComponents(ctx)
- android.SetInMakeForTests(config)
+ android.SetKatiEnabledForTests(config)
for _, handler := range handlers {
handler(config)
diff --git a/cc/sanitize.go b/cc/sanitize.go
index eb61525..dbc52a5 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -172,6 +172,12 @@
No_recover []string
}
+ // Sanitizers to run with flag configuration specified
+ Config struct {
+ // Enables CFI support flags for assembly-heavy libraries
+ Cfi_assembly_support *bool `android:"arch_variant"`
+ }
+
// value to pass to -fsanitize-recover=
Recover []string
@@ -543,6 +549,9 @@
flags.Local.CFlags = append(flags.Local.CFlags, cfiCflags...)
flags.Local.AsFlags = append(flags.Local.AsFlags, cfiAsflags...)
+ if Bool(sanitize.Properties.Sanitize.Config.Cfi_assembly_support) {
+ flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize-cfi-canonical-jump-tables")
+ }
// Only append the default visibility flag if -fvisibility has not already been set
// to hidden.
if !inList("-fvisibility=hidden", flags.Local.CFlags) {
diff --git a/cc/strip.go b/cc/strip.go
index 18150dc..f8814ea 100644
--- a/cc/strip.go
+++ b/cc/strip.go
@@ -35,8 +35,8 @@
}
func (stripper *Stripper) NeedsStrip(actx android.ModuleContext) bool {
- // TODO(ccross): enable host stripping when embedded in make? Make never had support for stripping host binaries.
- return (!actx.Config().EmbeddedInMake() || actx.Device()) && !Bool(stripper.StripProperties.Strip.None)
+ // TODO(ccross): enable host stripping when Kati is enabled? Make never had support for stripping host binaries.
+ return (!actx.Config().KatiEnabled() || actx.Device()) && !Bool(stripper.StripProperties.Strip.None)
}
func (stripper *Stripper) strip(actx android.ModuleContext, in android.Path, out android.ModuleOutPath,
diff --git a/etc/prebuilt_etc.go b/etc/prebuilt_etc.go
index a6d1fcf..850c8f9 100644
--- a/etc/prebuilt_etc.go
+++ b/etc/prebuilt_etc.go
@@ -56,16 +56,19 @@
// Source file of this prebuilt. Can reference a genrule type module with the ":module" syntax.
Src *string `android:"path,arch_variant"`
- // optional subdirectory under which this file is installed into, cannot be specified with relative_install_path, prefer relative_install_path
+ // Optional subdirectory under which this file is installed into, cannot be specified with
+ // relative_install_path, prefer relative_install_path.
Sub_dir *string `android:"arch_variant"`
- // optional subdirectory under which this file is installed into, cannot be specified with sub_dir
+ // Optional subdirectory under which this file is installed into, cannot be specified with
+ // sub_dir.
Relative_install_path *string `android:"arch_variant"`
- // optional name for the installed file. If unspecified, name of the module is used as the file name
+ // Optional name for the installed file. If unspecified, name of the module is used as the file
+ // name.
Filename *string `android:"arch_variant"`
- // when set to true, and filename property is not set, the name for the installed file
+ // When set to true, and filename property is not set, the name for the installed file
// is the same as the file name of the source file.
Filename_from_src *bool `android:"arch_variant"`
@@ -95,8 +98,15 @@
type PrebuiltEtcModule interface {
android.Module
+
+ // Returns the base install directory, such as "etc", "usr/share".
BaseDir() string
+
+ // Returns the sub install directory relative to BaseDir().
SubDir() string
+
+ // Returns an android.OutputPath to the intermeidate file, which is the renamed prebuilt source
+ // file.
OutputFile() android.OutputPath
}
@@ -109,7 +119,8 @@
outputFilePath android.OutputPath
// The base install location, e.g. "etc" for prebuilt_etc, "usr/share" for prebuilt_usr_share.
installDirBase string
- // The base install location when soc_specific property is set to true, e.g. "firmware" for prebuilt_firmware.
+ // The base install location when soc_specific property is set to true, e.g. "firmware" for
+ // prebuilt_firmware.
socInstallDirBase string
installDirPath android.InstallPath
additionalDependencies *android.Paths
@@ -179,14 +190,8 @@
func (p *PrebuiltEtc) SetImageVariation(ctx android.BaseModuleContext, variation string, module android.Module) {
}
-func (p *PrebuiltEtc) DepsMutator(ctx android.BottomUpMutatorContext) {
- if p.properties.Src == nil {
- ctx.PropertyErrorf("src", "missing prebuilt source file")
- }
-}
-
func (p *PrebuiltEtc) SourceFilePath(ctx android.ModuleContext) android.Path {
- return android.PathForModuleSrc(ctx, android.String(p.properties.Src))
+ return android.PathForModuleSrc(ctx, proptools.String(p.properties.Src))
}
func (p *PrebuiltEtc) InstallDirPath() android.InstallPath {
@@ -215,36 +220,41 @@
}
func (p *PrebuiltEtc) Installable() bool {
- return p.properties.Installable == nil || android.Bool(p.properties.Installable)
+ return p.properties.Installable == nil || proptools.Bool(p.properties.Installable)
}
func (p *PrebuiltEtc) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- p.sourceFilePath = android.PathForModuleSrc(ctx, android.String(p.properties.Src))
+ if p.properties.Src == nil {
+ ctx.PropertyErrorf("src", "missing prebuilt source file")
+ return
+ }
+ p.sourceFilePath = android.PathForModuleSrc(ctx, proptools.String(p.properties.Src))
// Determine the output file basename.
// If Filename is set, use the name specified by the property.
// If Filename_from_src is set, use the source file name.
// Otherwise use the module name.
- filename := android.String(p.properties.Filename)
- filename_from_src := android.Bool(p.properties.Filename_from_src)
- if filename == "" {
- if filename_from_src {
- filename = p.sourceFilePath.Base()
- } else {
- filename = ctx.ModuleName()
+ filename := proptools.String(p.properties.Filename)
+ filenameFromSrc := proptools.Bool(p.properties.Filename_from_src)
+ if filename != "" {
+ if filenameFromSrc {
+ ctx.PropertyErrorf("filename_from_src", "filename is set. filename_from_src can't be true")
+ return
}
- } else if filename_from_src {
- ctx.PropertyErrorf("filename_from_src", "filename is set. filename_from_src can't be true")
- return
+ } else if filenameFromSrc {
+ filename = p.sourceFilePath.Base()
+ } else {
+ filename = ctx.ModuleName()
}
p.outputFilePath = android.PathForModuleOut(ctx, filename).OutputPath
+ // Check that `sub_dir` and `relative_install_path` are not set at the same time.
if p.properties.Sub_dir != nil && p.properties.Relative_install_path != nil {
ctx.PropertyErrorf("sub_dir", "relative_install_path is set. Cannot set sub_dir")
}
- // If soc install dir was specified and SOC specific is set, set the installDirPath to the specified
- // socInstallDirBase.
+ // If soc install dir was specified and SOC specific is set, set the installDirPath to the
+ // specified socInstallDirBase.
installBaseDir := p.installDirBase
if p.SocSpecific() && p.socInstallDirBase != "" {
installBaseDir = p.socInstallDirBase
@@ -353,9 +363,10 @@
return module
}
-// prebuilt_firmware installs a firmware file to <partition>/etc/firmware directory for system image.
-// If soc_specific property is set to true, the firmware file is installed to the vendor <partition>/firmware
-// directory for vendor image.
+// prebuilt_firmware installs a firmware file to <partition>/etc/firmware directory for system
+// image.
+// If soc_specific property is set to true, the firmware file is installed to the
+// vendor <partition>/firmware directory for vendor image.
func PrebuiltFirmwareFactory() android.Module {
module := &PrebuiltEtc{}
module.socInstallDirBase = "firmware"
@@ -366,8 +377,8 @@
}
// prebuilt_dsp installs a DSP related file to <partition>/etc/dsp directory for system image.
-// If soc_specific property is set to true, the DSP related file is installed to the vendor <partition>/dsp
-// directory for vendor image.
+// If soc_specific property is set to true, the DSP related file is installed to the
+// vendor <partition>/dsp directory for vendor image.
func PrebuiltDSPFactory() android.Module {
module := &PrebuiltEtc{}
module.socInstallDirBase = "dsp"
diff --git a/genrule/Android.bp b/genrule/Android.bp
index ff543a6..9690007 100644
--- a/genrule/Android.bp
+++ b/genrule/Android.bp
@@ -6,6 +6,7 @@
"blueprint-pathtools",
"soong",
"soong-android",
+ "soong-bazel",
"soong-shared",
],
srcs: [
diff --git a/genrule/genrule.go b/genrule/genrule.go
index f85146c..0743e67 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -26,6 +26,7 @@
"github.com/google/blueprint/proptools"
"android/soong/android"
+ "android/soong/bazel"
)
func init() {
@@ -78,13 +79,6 @@
blueprint.BaseDependencyTag
label string
}
-
-// TODO(cparsons): Move to a common location when there is more than just
-// genrule with a bazel_module property.
-type bazelModuleProperties struct {
- Label string
-}
-
type generatorProperties struct {
// The command to run on one or more input files. Cmd supports substitution of a few variables
//
@@ -118,8 +112,8 @@
// input files to exclude
Exclude_srcs []string `android:"path,arch_variant"`
- // in bazel-enabled mode, the bazel label to evaluate instead of this module
- Bazel_module bazelModuleProperties
+ // Properties for Bazel migration purposes.
+ bazel.Properties
}
type Module struct {
android.ModuleBase
diff --git a/java/hiddenapi_singleton.go b/java/hiddenapi_singleton.go
index 1f80e77..e57f323 100644
--- a/java/hiddenapi_singleton.go
+++ b/java/hiddenapi_singleton.go
@@ -255,7 +255,7 @@
FlagWithInput("--max-target-p ",
android.PathForSource(ctx, "frameworks/base/config/hiddenapi-max-target-p.txt")).
FlagWithInput("--max-target-o ", android.PathForSource(
- ctx, "frameworks/base/config/hiddenapi-max-target-o.txt")).Flag("--ignore-conflicts ").
+ ctx, "frameworks/base/config/hiddenapi-max-target-o.txt")).Flag("--ignore-conflicts ").FlagWithArg("--tag ", "lo-prio").
FlagWithInput("--blocked ",
android.PathForSource(ctx, "frameworks/base/config/hiddenapi-force-blocked.txt")).
FlagWithInput("--blocked ",
diff --git a/java/java.go b/java/java.go
index c9a3819..3c9c92d 100644
--- a/java/java.go
+++ b/java/java.go
@@ -2358,7 +2358,7 @@
func (j *Test) GenerateAndroidBuildActions(ctx android.ModuleContext) {
j.testConfig = tradefed.AutoGenJavaTestConfig(ctx, j.testProperties.Test_config, j.testProperties.Test_config_template,
- j.testProperties.Test_suites, j.testProperties.Auto_gen_config)
+ j.testProperties.Test_suites, j.testProperties.Auto_gen_config, j.testProperties.Test_options.Unit_test)
j.data = android.PathsForModuleSrc(ctx, j.testProperties.Data)
@@ -2377,7 +2377,7 @@
func (j *JavaTestImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
j.testConfig = tradefed.AutoGenJavaTestConfig(ctx, j.prebuiltTestProperties.Test_config, nil,
- j.prebuiltTestProperties.Test_suites, nil)
+ j.prebuiltTestProperties.Test_suites, nil, nil)
j.Import.GenerateAndroidBuildActions(ctx)
}
diff --git a/java/lint.go b/java/lint.go
index 3df582f..11f92e5 100644
--- a/java/lint.go
+++ b/java/lint.go
@@ -447,7 +447,7 @@
var outputs []*lintOutputs
var dirs []string
ctx.VisitAllModules(func(m android.Module) {
- if ctx.Config().EmbeddedInMake() && !m.ExportedToMake() {
+ if ctx.Config().KatiEnabled() && !m.ExportedToMake() {
return
}
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 4369ffc..603c808 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -435,8 +435,23 @@
// If set to true, the path of dist files is apistubs/core. Defaults to false.
Core_lib *bool
- // don't create dist rules.
- No_dist *bool `blueprint:"mutated"`
+ // If set to true then don't create dist rules.
+ No_dist *bool
+
+ // The stem for the artifacts that are copied to the dist, if not specified
+ // then defaults to the base module name.
+ //
+ // For each scope the following artifacts are copied to the apistubs/<scope>
+ // directory in the dist.
+ // * stubs impl jar -> <dist-stem>.jar
+ // * API specification file -> api/<dist-stem>.txt
+ // * Removed API specification file -> api/<dist-stem>-removed.txt
+ //
+ // Also used to construct the name of the filegroup (created by prebuilt_apis)
+ // that references the latest released API and remove API specification files.
+ // * API specification filegroup -> <dist-stem>.api.<scope>.latest
+ // * Removed API specification filegroup -> <dist-stem>-removed.api.<scope>.latest
+ Dist_stem *string
// indicates whether system and test apis should be generated.
Generate_system_and_test_apis bool `blueprint:"mutated"`
@@ -1117,12 +1132,16 @@
}
}
+func (module *SdkLibrary) distStem() string {
+ return proptools.StringDefault(module.sdkLibraryProperties.Dist_stem, module.BaseModuleName())
+}
+
func (module *SdkLibrary) latestApiFilegroupName(apiScope *apiScope) string {
- return ":" + module.BaseModuleName() + ".api." + apiScope.name + ".latest"
+ return ":" + module.distStem() + ".api." + apiScope.name + ".latest"
}
func (module *SdkLibrary) latestRemovedApiFilegroupName(apiScope *apiScope) string {
- return ":" + module.BaseModuleName() + "-removed.api." + apiScope.name + ".latest"
+ return ":" + module.distStem() + "-removed.api." + apiScope.name + ".latest"
}
func childModuleVisibility(childVisibility []string) []string {
@@ -1228,7 +1247,7 @@
// Dist the class jar artifact for sdk builds.
if !Bool(module.sdkLibraryProperties.No_dist) {
props.Dist.Targets = []string{"sdk", "win_sdk"}
- props.Dist.Dest = proptools.StringPtr(fmt.Sprintf("%v.jar", module.BaseModuleName()))
+ props.Dist.Dest = proptools.StringPtr(fmt.Sprintf("%v.jar", module.distStem()))
props.Dist.Dir = proptools.StringPtr(module.apiDistPath(apiScope))
props.Dist.Tag = proptools.StringPtr(".jar")
}
@@ -1377,7 +1396,7 @@
// Dist the api txt artifact for sdk builds.
if !Bool(module.sdkLibraryProperties.No_dist) {
props.Dist.Targets = []string{"sdk", "win_sdk"}
- props.Dist.Dest = proptools.StringPtr(fmt.Sprintf("%v.txt", module.BaseModuleName()))
+ props.Dist.Dest = proptools.StringPtr(fmt.Sprintf("%v.txt", module.distStem()))
props.Dist.Dir = proptools.StringPtr(path.Join(module.apiDistPath(apiScope), "api"))
}
@@ -1482,10 +1501,6 @@
return module.sdkJars(ctx, sdkVersion, false /*headerJars*/)
}
-func (module *SdkLibrary) SetNoDist() {
- module.sdkLibraryProperties.No_dist = proptools.BoolPtr(true)
-}
-
var javaSdkLibrariesKey = android.NewOnceKey("javaSdkLibraries")
func javaSdkLibraries(config android.Config) *[]string {
@@ -1513,12 +1528,10 @@
}
// If this builds against standard libraries (i.e. is not part of the core libraries)
- // then assume it provides both system and test apis. Otherwise, assume it does not and
- // also assume it does not contribute to the dist build.
+ // then assume it provides both system and test apis.
sdkDep := decodeSdkDep(mctx, sdkContext(&module.Library))
hasSystemAndTestApis := sdkDep.hasStandardLibs()
module.sdkLibraryProperties.Generate_system_and_test_apis = hasSystemAndTestApis
- module.sdkLibraryProperties.No_dist = proptools.BoolPtr(!hasSystemAndTestApis)
missing_current_api := false
diff --git a/python/python.go b/python/python.go
index e4c8e94..d7b1bba 100644
--- a/python/python.go
+++ b/python/python.go
@@ -34,7 +34,7 @@
}
func RegisterPythonPreDepsMutators(ctx android.RegisterMutatorsContext) {
- ctx.BottomUp("version_split", versionSplitMutator()).Parallel()
+ ctx.BottomUp("python_version", versionSplitMutator()).Parallel()
}
// the version properties that apply to python libraries and binaries.
@@ -86,6 +86,9 @@
// the test. the file extension can be arbitrary except for (.py).
Data []string `android:"path,arch_variant"`
+ // list of java modules that provide data that should be installed alongside the test.
+ Java_data []string
+
// list of the Python libraries compatible both with Python2 and Python3.
Libs []string `android:"arch_variant"`
@@ -216,6 +219,7 @@
var (
pythonLibTag = dependencyTag{name: "pythonLib"}
+ javaDataTag = dependencyTag{name: "javaData"}
launcherTag = dependencyTag{name: "launcher"}
launcherSharedLibTag = dependencyTag{name: "launcherSharedLib"}
pyIdentifierRegexp = regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_-]*$`)
@@ -248,7 +252,7 @@
versionNames = append(versionNames, pyVersion2)
versionProps = append(versionProps, base.properties.Version.Py2)
}
- modules := mctx.CreateVariations(versionNames...)
+ modules := mctx.CreateLocalVariations(versionNames...)
if len(versionNames) > 0 {
mctx.AliasVariation(versionNames[0])
}
@@ -306,16 +310,20 @@
func (p *Module) DepsMutator(ctx android.BottomUpMutatorContext) {
android.ProtoDeps(ctx, &p.protoProperties)
- if p.hasSrcExt(ctx, protoExt) && p.Name() != "libprotobuf-python" {
- ctx.AddVariationDependencies(nil, pythonLibTag, "libprotobuf-python")
+ versionVariation := []blueprint.Variation{
+ {"python_version", p.properties.Actual_version},
}
- ctx.AddVariationDependencies(nil, pythonLibTag, android.LastUniqueStrings(p.properties.Libs)...)
+
+ if p.hasSrcExt(ctx, protoExt) && p.Name() != "libprotobuf-python" {
+ ctx.AddVariationDependencies(versionVariation, pythonLibTag, "libprotobuf-python")
+ }
+ ctx.AddVariationDependencies(versionVariation, pythonLibTag, android.LastUniqueStrings(p.properties.Libs)...)
switch p.properties.Actual_version {
case pyVersion2:
if p.bootstrapper != nil && p.isEmbeddedLauncherEnabled() {
- ctx.AddVariationDependencies(nil, pythonLibTag, "py2-stdlib")
+ ctx.AddVariationDependencies(versionVariation, pythonLibTag, "py2-stdlib")
launcherModule := "py2-launcher"
if p.bootstrapper.autorun() {
@@ -338,7 +346,7 @@
case pyVersion3:
if p.bootstrapper != nil && p.isEmbeddedLauncherEnabled() {
- ctx.AddVariationDependencies(nil, pythonLibTag, "py3-stdlib")
+ ctx.AddVariationDependencies(versionVariation, pythonLibTag, "py3-stdlib")
launcherModule := "py3-launcher"
if p.bootstrapper.autorun() {
@@ -366,6 +374,11 @@
panic(fmt.Errorf("unknown Python Actual_version: %q for module: %q.",
p.properties.Actual_version, ctx.ModuleName()))
}
+
+ // Emulate the data property for java_data but with the arch variation overridden to "common"
+ // so that it can point to java modules.
+ javaDataVariation := []blueprint.Variation{{"arch", android.Common.String()}}
+ ctx.AddVariationDependencies(javaDataVariation, javaDataTag, p.properties.Java_data...)
}
func (p *Module) GenerateAndroidBuildActions(ctx android.ModuleContext) {
@@ -412,6 +425,11 @@
// expand data files from "data" property.
expandedData := android.PathsForModuleSrc(ctx, p.properties.Data)
+ // Emulate the data property for java_data dependencies.
+ for _, javaData := range ctx.GetDirectDepsWithTag(javaDataTag) {
+ expandedData = append(expandedData, android.OutputFilesForModule(ctx, javaData, "")...)
+ }
+
// sanitize pkg_path.
pkgPath := String(p.properties.Pkg_path)
if pkgPath != "" {
diff --git a/python/test.go b/python/test.go
index f9baa46..4df71c1 100644
--- a/python/test.go
+++ b/python/test.go
@@ -45,6 +45,9 @@
// the test
Data []string `android:"path,arch_variant"`
+ // list of java modules that provide data that should be installed alongside the test.
+ Java_data []string
+
// Test options.
Test_options TestOptions
}
@@ -80,6 +83,13 @@
for _, dataSrcPath := range dataSrcPaths {
test.data = append(test.data, android.DataPath{SrcPath: dataSrcPath})
}
+
+ // Emulate the data property for java_data dependencies.
+ for _, javaData := range ctx.GetDirectDepsWithTag(javaDataTag) {
+ for _, javaDataSrcPath := range android.OutputFilesForModule(ctx, javaData, "") {
+ test.data = append(test.data, android.DataPath{SrcPath: javaDataSrcPath})
+ }
+ }
}
func NewTest(hod android.HostOrDeviceSupported) *Module {
diff --git a/scripts/build-aml-prebuilts.sh b/scripts/build-aml-prebuilts.sh
index 89fb1a5..0c868ea 100755
--- a/scripts/build-aml-prebuilts.sh
+++ b/scripts/build-aml-prebuilts.sh
@@ -12,7 +12,7 @@
export OUT_DIR=${OUT_DIR:-out}
-if [ -e ${OUT_DIR}/soong/.soong.in_make ]; then
+if [ -e ${OUT_DIR}/soong/.soong.kati_enabled ]; then
# If ${OUT_DIR} has been created without --skip-make, Soong will create an
# ${OUT_DIR}/soong/build.ninja that leaves out many targets which are
# expected to be supplied by the .mk files, and that might cause errors in
@@ -32,8 +32,8 @@
my_get_build_var() {
# get_build_var will run Soong in normal in-make mode where it creates
- # .soong.in_make. That would clobber our real out directory, so we need to
- # run it in a different one.
+ # .soong.kati_enabled. That would clobber our real out directory, so we need
+ # to run it in a different one.
OUT_DIR=${OUT_DIR}/get_build_var get_build_var "$@"
}
diff --git a/sdk/testing.go b/sdk/testing.go
index 5f520e5..91aa879 100644
--- a/sdk/testing.go
+++ b/sdk/testing.go
@@ -87,7 +87,7 @@
// * Configure that we are inside make
// * Add CommonOS to ensure that androidmk processing works.
android.RegisterAndroidMkBuildComponents(ctx)
- android.SetInMakeForTests(config)
+ android.SetKatiEnabledForTests(config)
config.Targets[android.CommonOS] = []android.Target{
{android.CommonOS, android.Arch{ArchType: android.Common}, android.NativeBridgeDisabled, "", "", true},
}
diff --git a/tradefed/autogen.go b/tradefed/autogen.go
index b35f831..27d71e8 100644
--- a/tradefed/autogen.go
+++ b/tradefed/autogen.go
@@ -188,7 +188,7 @@
}
func AutoGenJavaTestConfig(ctx android.ModuleContext, testConfigProp *string, testConfigTemplateProp *string,
- testSuites []string, autoGenConfig *bool) android.Path {
+ testSuites []string, autoGenConfig *bool, unitTest *bool) android.Path {
path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig, testConfigTemplateProp)
if autogenPath != nil {
templatePath := getTestConfigTemplate(ctx, testConfigTemplateProp)
@@ -198,7 +198,11 @@
if ctx.Device() {
autogenTemplate(ctx, autogenPath, "${JavaTestConfigTemplate}", nil, "")
} else {
- autogenTemplate(ctx, autogenPath, "${JavaHostTestConfigTemplate}", nil, "")
+ if Bool(unitTest) {
+ autogenTemplate(ctx, autogenPath, "${JavaHostUnitTestConfigTemplate}", nil, "")
+ } else {
+ autogenTemplate(ctx, autogenPath, "${JavaHostTestConfigTemplate}", nil, "")
+ }
}
}
return autogenPath
diff --git a/tradefed/config.go b/tradefed/config.go
index f7e8349..f3566a8 100644
--- a/tradefed/config.go
+++ b/tradefed/config.go
@@ -27,6 +27,7 @@
pctx.SourcePathVariable("InstrumentationTestConfigTemplate", "build/make/core/instrumentation_test_config_template.xml")
pctx.SourcePathVariable("JavaTestConfigTemplate", "build/make/core/java_test_config_template.xml")
pctx.SourcePathVariable("JavaHostTestConfigTemplate", "build/make/core/java_host_test_config_template.xml")
+ pctx.SourcePathVariable("JavaHostUnitTestConfigTemplate", "build/make/core/java_host_unit_test_config_template.xml")
pctx.SourcePathVariable("NativeBenchmarkTestConfigTemplate", "build/make/core/native_benchmark_test_config_template.xml")
pctx.SourcePathVariable("NativeHostTestConfigTemplate", "build/make/core/native_host_test_config_template.xml")
pctx.SourcePathVariable("NativeTestConfigTemplate", "build/make/core/native_test_config_template.xml")
diff --git a/ui/build/build.go b/ui/build/build.go
index 1cf2023..e9196a9 100644
--- a/ui/build/build.go
+++ b/ui/build/build.go
@@ -23,13 +23,20 @@
"android/soong/ui/metrics"
)
-// Ensures the out directory exists, and has the proper files to prevent kati
-// from recursing into it.
+// SetupOutDir ensures the out directory exists, and has the proper files to
+// prevent kati from recursing into it.
func SetupOutDir(ctx Context, config Config) {
ensureEmptyFileExists(ctx, filepath.Join(config.OutDir(), "Android.mk"))
ensureEmptyFileExists(ctx, filepath.Join(config.OutDir(), "CleanSpec.mk"))
if !config.SkipMake() {
- ensureEmptyFileExists(ctx, filepath.Join(config.SoongOutDir(), ".soong.in_make"))
+ // Run soong_build with Kati for a hybrid build, e.g. running the
+ // AndroidMk singleton and postinstall commands. Communicate this to
+ // soong_build by writing an empty .soong.kati_enabled marker file in the
+ // soong_build output directory for the soong_build primary builder to
+ // know if the user wants to run Kati after.
+ //
+ // This does not preclude running Kati for *product configuration purposes*.
+ ensureEmptyFileExists(ctx, filepath.Join(config.SoongOutDir(), ".soong.kati_enabled"))
}
// The ninja_build file is used by our buildbots to understand that the output
// can be parsed as ninja output.
diff --git a/ui/build/kati.go b/ui/build/kati.go
index f6c0f52..06ec646 100644
--- a/ui/build/kati.go
+++ b/ui/build/kati.go
@@ -33,12 +33,18 @@
const katiCleanspecSuffix = "-cleanspec"
const katiPackageSuffix = "-package"
-// genKatiSuffix creates a suffix for kati-generated files so that we can cache
-// them based on their inputs. So this should encode all common changes to Kati
-// inputs. Currently that includes the TARGET_PRODUCT, kati-processed command
-// line arguments, and the directories specified by mm/mmm.
+// genKatiSuffix creates a filename suffix for kati-generated files so that we
+// can cache them based on their inputs. Such files include the generated Ninja
+// files and env.sh environment variable setup files.
+//
+// The filename suffix should encode all common changes to Kati inputs.
+// Currently that includes the TARGET_PRODUCT and kati-processed command line
+// arguments.
func genKatiSuffix(ctx Context, config Config) {
+ // Construct the base suffix.
katiSuffix := "-" + config.TargetProduct()
+
+ // Append kati arguments to the suffix.
if args := config.KatiArgs(); len(args) > 0 {
katiSuffix += "-" + spaceSlashReplacer.Replace(strings.Join(args, "_"))
}
@@ -60,51 +66,101 @@
}
}
+// Base function to construct and run the Kati command line with additional
+// arguments, and a custom function closure to mutate the environment Kati runs
+// in.
func runKati(ctx Context, config Config, extraSuffix string, args []string, envFunc func(*Environment)) {
executable := config.PrebuiltBuildTool("ckati")
+ // cKati arguments.
args = append([]string{
+ // Instead of executing commands directly, generate a Ninja file.
"--ninja",
+ // Generate Ninja files in the output directory.
"--ninja_dir=" + config.OutDir(),
+ // Filename suffix of the generated Ninja file.
"--ninja_suffix=" + config.KatiSuffix() + extraSuffix,
+ // Remove common parts at the beginning of a Ninja file, like build_dir,
+ // local_pool and _kati_always_build_. Allows Kati to be run multiple
+ // times, with generated Ninja files combined in a single invocation
+ // using 'include'.
"--no_ninja_prelude",
+ // Support declaring phony outputs in AOSP Ninja.
"--use_ninja_phony_output",
+ // Support declaring symlink outputs in AOSP Ninja.
"--use_ninja_symlink_outputs",
+ // Regenerate the Ninja file if environment inputs have changed. e.g.
+ // CLI flags, .mk file timestamps, env vars, $(wildcard ..) and some
+ // $(shell ..) results.
"--regen",
+ // Skip '-include' directives starting with the specified path. Used to
+ // ignore generated .mk files.
"--ignore_optional_include=" + filepath.Join(config.OutDir(), "%.P"),
+ // Detect the use of $(shell echo ...).
"--detect_android_echo",
+ // Colorful ANSI-based warning and error messages.
"--color_warnings",
+ // Generate all targets, not just the top level requested ones.
"--gen_all_targets",
+ // Use the built-in emulator of GNU find for better file finding
+ // performance. Used with $(shell find ...).
"--use_find_emulator",
+ // Fail when the find emulator encounters problems.
"--werror_find_emulator",
+ // Do not provide any built-in rules.
"--no_builtin_rules",
+ // Fail when suffix rules are used.
"--werror_suffix_rules",
- "--warn_real_to_phony",
- "--warn_phony_looks_real",
+ // Fail when a real target depends on a phony target.
"--werror_real_to_phony",
- "--werror_phony_looks_real",
- "--werror_writable",
+ // Makes real_to_phony checks assume that any top-level or leaf
+ // dependencies that does *not* have a '/' in it is a phony target.
"--top_level_phony",
+ // Fail when a phony target contains slashes.
+ "--werror_phony_looks_real",
+ // Fail when writing to a read-only directory.
+ "--werror_writable",
+ // Print Kati's internal statistics, such as the number of variables,
+ // implicit/explicit/suffix rules, and so on.
"--kati_stats",
}, args...)
+ // Generate a minimal Ninja file.
+ //
+ // Used for build_test and multiproduct_kati, which runs Kati several
+ // hundred times for different configurations to test file generation logic.
+ // These can result in generating Ninja files reaching ~1GB or more,
+ // resulting in ~hundreds of GBs of writes.
+ //
+ // Since we don't care about executing the Ninja files in these test cases,
+ // generating the Ninja file content wastes time, so skip writing any
+ // information out with --empty_ninja_file.
+ //
+ // From https://github.com/google/kati/commit/87b8da7af2c8bea28b1d8ab17679453d859f96e5
if config.Environment().IsEnvTrue("EMPTY_NINJA_FILE") {
args = append(args, "--empty_ninja_file")
}
+ // Apply 'local_pool' to to all rules that don't specify a pool.
if config.UseRemoteBuild() {
args = append(args, "--default_pool=local_pool")
}
cmd := Command(ctx, config, "ckati", executable, args...)
+
+ // Set up the nsjail sandbox.
cmd.Sandbox = katiSandbox
+
+ // Set up stdout and stderr.
pipe, err := cmd.StdoutPipe()
if err != nil {
ctx.Fatalln("Error getting output pipe for ckati:", err)
}
cmd.Stderr = cmd.Stdout
+ // Apply the caller's function closure to mutate the environment variables.
envFunc(cmd.Environment)
+ // Pass on various build environment metadata to Kati.
if _, ok := cmd.Environment.Get("BUILD_USERNAME"); !ok {
username := "unknown"
if u, err := user.Current(); err == nil {
@@ -125,6 +181,8 @@
}
cmd.StartOrFatal()
+ // Set up the ToolStatus command line reader for Kati for a consistent UI
+ // for the user.
status.KatiReader(ctx.Status.StartTool(), pipe)
cmd.WaitOrFatal()
}
@@ -134,35 +192,57 @@
defer ctx.EndTrace()
args := []string{
+ // Mark the output directory as writable.
"--writable", config.OutDir() + "/",
+ // Fail when encountering implicit rules. e.g.
+ // %.foo: %.bar
+ // cp $< $@
"--werror_implicit_rules",
+ // Entry point for the Kati Ninja file generation.
"-f", "build/make/core/main.mk",
}
if !config.BuildBrokenDupRules() {
+ // Fail when redefining / duplicating a target.
args = append(args, "--werror_overriding_commands")
}
args = append(args, config.KatiArgs()...)
args = append(args,
+ // Location of the Make vars .mk file generated by Soong.
"SOONG_MAKEVARS_MK="+config.SoongMakeVarsMk(),
+ // Location of the Android.mk file generated by Soong. This
+ // file contains Soong modules represented as Kati modules,
+ // allowing Kati modules to depend on Soong modules.
"SOONG_ANDROID_MK="+config.SoongAndroidMk(),
+ // Directory containing outputs for the target device.
"TARGET_DEVICE_DIR="+config.TargetDeviceDir(),
+ // Directory containing .mk files for packaging purposes, such as
+ // the dist.mk file, containing dist-for-goals data.
"KATI_PACKAGE_MK_DIR="+config.KatiPackageMkDir())
runKati(ctx, config, katiBuildSuffix, args, func(env *Environment) {})
+ // compress and dist the main build ninja file.
distGzipFile(ctx, config, config.KatiBuildNinjaFile())
+ // Cleanup steps.
cleanCopyHeaders(ctx, config)
cleanOldInstalledFiles(ctx, config)
}
+// Clean out obsolete header files on the disk that were *not copied* during the
+// build with BUILD_COPY_HEADERS and LOCAL_COPY_HEADERS.
+//
+// These should be increasingly uncommon, as it's a deprecated feature and there
+// isn't an equivalent feature in Soong.
func cleanCopyHeaders(ctx Context, config Config) {
ctx.BeginTrace("clean", "clean copy headers")
defer ctx.EndTrace()
+ // Read and parse the list of copied headers from a file in the product
+ // output directory.
data, err := ioutil.ReadFile(filepath.Join(config.ProductOut(), ".copied_headers_list"))
if err != nil {
if os.IsNotExist(err) {
@@ -178,6 +258,8 @@
headerDir := headers[0]
headers = headers[1:]
+ // Walk the tree and remove any headers that are not in the list of copied
+ // headers in the current build.
filepath.Walk(headerDir,
func(path string, info os.FileInfo, err error) error {
if err != nil {
@@ -196,6 +278,8 @@
})
}
+// Clean out any previously installed files from the disk that are not installed
+// in the current build.
func cleanOldInstalledFiles(ctx Context, config Config) {
ctx.BeginTrace("clean", "clean old installed files")
defer ctx.EndTrace()
@@ -213,18 +297,27 @@
cleanOldFiles(ctx, config.HostOut(), ".installable_test_files")
}
+// Generate the Ninja file containing the packaging command lines for the dist
+// dir.
func runKatiPackage(ctx Context, config Config) {
ctx.BeginTrace(metrics.RunKati, "kati package")
defer ctx.EndTrace()
args := []string{
+ // Mark the dist dir as writable.
"--writable", config.DistDir() + "/",
+ // Fail when encountering implicit rules. e.g.
"--werror_implicit_rules",
+ // Fail when redefining / duplicating a target.
"--werror_overriding_commands",
+ // Entry point.
"-f", "build/make/packaging/main.mk",
+ // Directory containing .mk files for packaging purposes, such as
+ // the dist.mk file, containing dist-for-goals data.
"KATI_PACKAGE_MK_DIR=" + config.KatiPackageMkDir(),
}
+ // Run Kati against a restricted set of environment variables.
runKati(ctx, config, katiPackageSuffix, args, func(env *Environment) {
env.Allow([]string{
// Some generic basics
@@ -251,16 +344,21 @@
}
})
+ // Compress and dist the packaging Ninja file.
distGzipFile(ctx, config, config.KatiPackageNinjaFile())
}
+// Run Kati on the cleanspec files to clean the build.
func runKatiCleanSpec(ctx Context, config Config) {
ctx.BeginTrace(metrics.RunKati, "kati cleanspec")
defer ctx.EndTrace()
runKati(ctx, config, katiCleanspecSuffix, []string{
+ // Fail when encountering implicit rules. e.g.
"--werror_implicit_rules",
+ // Fail when redefining / duplicating a target.
"--werror_overriding_commands",
+ // Entry point.
"-f", "build/make/core/cleanbuild.mk",
"SOONG_MAKEVARS_MK=" + config.SoongMakeVarsMk(),
"TARGET_DEVICE_DIR=" + config.TargetDeviceDir(),
diff --git a/ui/build/ninja.go b/ui/build/ninja.go
index fa44cb1..ffd1ab9 100644
--- a/ui/build/ninja.go
+++ b/ui/build/ninja.go
@@ -27,10 +27,16 @@
"android/soong/ui/status"
)
+// Constructs and runs the Ninja command line with a restricted set of
+// environment variables. It's important to restrict the environment Ninja runs
+// for hermeticity reasons, and to avoid spurious rebuilds.
func runNinja(ctx Context, config Config) {
ctx.BeginTrace(metrics.PrimaryNinja, "ninja")
defer ctx.EndTrace()
+ // Sets up the FIFO status updater that reads the Ninja protobuf output, and
+ // translates it to the soong_ui status output, displaying real-time
+ // progress of the build.
fifo := filepath.Join(config.OutDir(), ".ninja_fifo")
nr := status.NewNinjaReader(ctx, ctx.Status.StartTool(), fifo)
defer nr.Close()
@@ -64,8 +70,12 @@
"-w", "missingdepfile=err")
cmd := Command(ctx, config, "ninja", executable, args...)
+
+ // Set up the nsjail sandbox Ninja runs in.
cmd.Sandbox = ninjaSandbox
if config.HasKatiSuffix() {
+ // Reads and executes a shell script from Kati that sets/unsets the
+ // environment Ninja runs in.
cmd.Environment.AppendFromKati(config.KatiEnvFile())
}
@@ -78,8 +88,8 @@
cmd.Args = append(cmd.Args, strings.Fields(extra)...)
}
- logPath := filepath.Join(config.OutDir(), ".ninja_log")
ninjaHeartbeatDuration := time.Minute * 5
+ // Get the ninja heartbeat interval from the environment before it's filtered away later.
if overrideText, ok := cmd.Environment.Get("NINJA_HEARTBEAT_INTERVAL"); ok {
// For example, "1m"
overrideDuration, err := time.ParseDuration(overrideText)
@@ -88,18 +98,22 @@
}
}
- // Filter the environment, as ninja does not rebuild files when environment variables change.
+ // Filter the environment, as ninja does not rebuild files when environment
+ // variables change.
//
- // Anything listed here must not change the output of rules/actions when the value changes,
- // otherwise incremental builds may be unsafe. Vars explicitly set to stable values
- // elsewhere in soong_ui are fine.
+ // Anything listed here must not change the output of rules/actions when the
+ // value changes, otherwise incremental builds may be unsafe. Vars
+ // explicitly set to stable values elsewhere in soong_ui are fine.
//
- // For the majority of cases, either Soong or the makefiles should be replicating any
- // necessary environment variables in the command line of each action that needs it.
+ // For the majority of cases, either Soong or the makefiles should be
+ // replicating any necessary environment variables in the command line of
+ // each action that needs it.
if cmd.Environment.IsEnvTrue("ALLOW_NINJA_ENV") {
ctx.Println("Allowing all environment variables during ninja; incremental builds may be unsafe.")
} else {
cmd.Environment.Allow(append([]string{
+ // Set the path to a symbolizer (e.g. llvm-symbolizer) so ASAN-based
+ // tools can symbolize crashes.
"ASAN_SYMBOLIZER_PATH",
"HOME",
"JAVA_HOME",
@@ -108,14 +122,19 @@
"OUT_DIR",
"PATH",
"PWD",
+ // https://docs.python.org/3/using/cmdline.html#envvar-PYTHONDONTWRITEBYTECODE
"PYTHONDONTWRITEBYTECODE",
"TMPDIR",
"USER",
// TODO: remove these carefully
+ // Options for the address sanitizer.
"ASAN_OPTIONS",
+ // The list of Android app modules to be built in an unbundled manner.
"TARGET_BUILD_APPS",
+ // The variant of the product being built. e.g. eng, userdebug, debug.
"TARGET_BUILD_VARIANT",
+ // The product name of the product being built, e.g. aosp_arm, aosp_flame.
"TARGET_PRODUCT",
// b/147197813 - used by art-check-debug-apex-gen
"EMMA_INSTRUMENT_FRAMEWORK",
@@ -162,6 +181,7 @@
cmd.Environment.Set("DIST_DIR", config.DistDir())
cmd.Environment.Set("SHELL", "/bin/bash")
+ // Print the environment variables that Ninja is operating in.
ctx.Verboseln("Ninja environment: ")
envVars := cmd.Environment.Environ()
sort.Strings(envVars)
@@ -169,17 +189,21 @@
ctx.Verbosef(" %s", envVar)
}
- // Poll the ninja log for updates; if it isn't updated enough, then we want to show some diagnostics
+ // Poll the Ninja log for updates regularly based on the heartbeat
+ // frequency. If it isn't updated enough, then we want to surface the
+ // possibility that Ninja is stuck, to the user.
done := make(chan struct{})
defer close(done)
ticker := time.NewTicker(ninjaHeartbeatDuration)
defer ticker.Stop()
- checker := &statusChecker{}
+ ninjaChecker := &ninjaStucknessChecker{
+ logPath: filepath.Join(config.OutDir(), ".ninja_log"),
+ }
go func() {
for {
select {
case <-ticker.C:
- checker.check(ctx, config, logPath)
+ ninjaChecker.check(ctx, config)
case <-done:
return
}
@@ -190,37 +214,36 @@
cmd.RunAndStreamOrFatal()
}
-type statusChecker struct {
- prevTime time.Time
+// A simple struct for checking if Ninja gets stuck, using timestamps.
+type ninjaStucknessChecker struct {
+ logPath string
+ prevModTime time.Time
}
-func (c *statusChecker) check(ctx Context, config Config, pathToCheck string) {
- info, err := os.Stat(pathToCheck)
- var newTime time.Time
+// Check that a file has been modified since the last time it was checked. If
+// the mod time hasn't changed, then assume that Ninja got stuck, and print
+// diagnostics for debugging.
+func (c *ninjaStucknessChecker) check(ctx Context, config Config) {
+ info, err := os.Stat(c.logPath)
+ var newModTime time.Time
if err == nil {
- newTime = info.ModTime()
+ newModTime = info.ModTime()
}
- if newTime == c.prevTime {
- // ninja may be stuck
- dumpStucknessDiagnostics(ctx, config, pathToCheck, newTime)
+ if newModTime == c.prevModTime {
+ // The Ninja file hasn't been modified since the last time it was
+ // checked, so Ninja could be stuck. Output some diagnostics.
+ ctx.Verbosef("ninja may be stuck; last update to %v was %v. dumping process tree...", c.logPath, newModTime)
+
+ // The "pstree" command doesn't exist on Mac, but "pstree" on Linux
+ // gives more convenient output than "ps" So, we try pstree first, and
+ // ps second
+ commandText := fmt.Sprintf("pstree -pal %v || ps -ef", os.Getpid())
+
+ cmd := Command(ctx, config, "dump process tree", "bash", "-c", commandText)
+ output := cmd.CombinedOutputOrFatal()
+ ctx.Verbose(string(output))
+
+ ctx.Verbosef("done\n")
}
- c.prevTime = newTime
-}
-
-// dumpStucknessDiagnostics gets called when it is suspected that Ninja is stuck and we want to output some diagnostics
-func dumpStucknessDiagnostics(ctx Context, config Config, statusPath string, lastUpdated time.Time) {
-
- ctx.Verbosef("ninja may be stuck; last update to %v was %v. dumping process tree...", statusPath, lastUpdated)
-
- // The "pstree" command doesn't exist on Mac, but "pstree" on Linux gives more convenient output than "ps"
- // So, we try pstree first, and ps second
- pstreeCommandText := fmt.Sprintf("pstree -pal %v", os.Getpid())
- psCommandText := "ps -ef"
- commandText := pstreeCommandText + " || " + psCommandText
-
- cmd := Command(ctx, config, "dump process tree", "bash", "-c", commandText)
- output := cmd.CombinedOutputOrFatal()
- ctx.Verbose(string(output))
-
- ctx.Verbosef("done\n")
+ c.prevModTime = newModTime
}
diff --git a/ui/build/path.go b/ui/build/path.go
index 6f5cf78..86e61c0 100644
--- a/ui/build/path.go
+++ b/ui/build/path.go
@@ -29,6 +29,9 @@
"android/soong/ui/metrics"
)
+// parsePathDir returns the list of filenames of readable files in a directory.
+// This does not recurse into subdirectories, and does not contain subdirectory
+// names in the list.
func parsePathDir(dir string) []string {
f, err := os.Open(dir)
if err != nil {
@@ -54,10 +57,12 @@
return ret
}
-// A "lite" version of SetupPath used for dumpvars, or other places that need
-// minimal overhead (but at the expense of logging). If tmpDir is empty, the
-// default TMPDIR is used from config.
+// SetupLitePath is the "lite" version of SetupPath used for dumpvars, or other
+// places that does not need the full logging capabilities of path_interposer,
+// wants the minimal performance overhead, and still get the benefits of $PATH
+// hermeticity.
func SetupLitePath(ctx Context, config Config, tmpDir string) {
+ // Don't replace the path twice.
if config.pathReplaced {
return
}
@@ -67,6 +72,7 @@
origPath, _ := config.Environment().Get("PATH")
+ // If tmpDir is empty, the default TMPDIR is used from config.
if tmpDir == "" {
tmpDir, _ = config.Environment().Get("TMPDIR")
}
@@ -74,8 +80,10 @@
ensureEmptyDirectoriesExist(ctx, myPath)
os.Setenv("PATH", origPath)
+ // Iterate over the ACL configuration of host tools for this build.
for name, pathConfig := range paths.Configuration {
if !pathConfig.Symlink {
+ // Excludes 'Forbidden' and 'LinuxOnlyPrebuilt' PathConfigs.
continue
}
@@ -88,6 +96,7 @@
continue
}
+ // Symlink allowed host tools into a directory for hermeticity.
err = os.Symlink(origExec, filepath.Join(myPath, name))
if err != nil {
ctx.Fatalln("Failed to create symlink:", err)
@@ -96,14 +105,26 @@
myPath, _ = filepath.Abs(myPath)
+ // Set up the checked-in prebuilts path directory for the current host OS.
prebuiltsPath, _ := filepath.Abs("prebuilts/build-tools/path/" + runtime.GOOS + "-x86")
myPath = prebuiltsPath + string(os.PathListSeparator) + myPath
+ // Set $PATH to be the directories containing the host tool symlinks, and
+ // the prebuilts directory for the current host OS.
config.Environment().Set("PATH", myPath)
config.pathReplaced = true
}
+// SetupPath uses the path_interposer to intercept calls to $PATH binaries, and
+// communicates with the interposer to validate allowed $PATH binaries at
+// runtime, using logs as a medium.
+//
+// This results in hermetic directories in $PATH containing only allowed host
+// tools for the build, and replaces $PATH to contain *only* these directories,
+// and enables an incremental restriction of tools allowed in the $PATH without
+// breaking existing use cases.
func SetupPath(ctx Context, config Config) {
+ // Don't replace $PATH twice.
if config.pathReplaced {
return
}
@@ -112,9 +133,11 @@
defer ctx.EndTrace()
origPath, _ := config.Environment().Get("PATH")
+ // The directory containing symlinks from binaries in $PATH to the interposer.
myPath := filepath.Join(config.OutDir(), ".path")
interposer := myPath + "_interposer"
+ // Bootstrap the path_interposer Go binary with microfactory.
var cfg microfactory.Config
cfg.Map("android/soong", "build/soong")
cfg.TrimPath, _ = filepath.Abs(".")
@@ -122,15 +145,20 @@
ctx.Fatalln("Failed to build path interposer:", err)
}
+ // Save the original $PATH in a file.
if err := ioutil.WriteFile(interposer+"_origpath", []byte(origPath), 0777); err != nil {
ctx.Fatalln("Failed to write original path:", err)
}
+ // Communication with the path interposer works over log entries. Set up the
+ // listener channel for the log entries here.
entries, err := paths.LogListener(ctx.Context, interposer+"_log")
if err != nil {
ctx.Fatalln("Failed to listen for path logs:", err)
}
+ // Loop over all log entry listener channels to validate usage of only
+ // allowed PATH tools at runtime.
go func() {
for log := range entries {
curPid := os.Getpid()
@@ -140,6 +168,8 @@
break
}
}
+ // Compute the error message along with the process tree, including
+ // parents, for this log line.
procPrints := []string{
"See https://android.googlesource.com/platform/build/+/master/Changes.md#PATH_Tools for more information.",
}
@@ -150,6 +180,7 @@
}
}
+ // Validate usage against disallowed or missing PATH tools.
config := paths.GetConfig(log.Basename)
if config.Error {
ctx.Printf("Disallowed PATH tool %q used: %#v", log.Basename, log.Args)
@@ -165,8 +196,10 @@
}
}()
+ // Create the .path directory.
ensureEmptyDirectoriesExist(ctx, myPath)
+ // Compute the full list of binaries available in the original $PATH.
var execs []string
for _, pathEntry := range filepath.SplitList(origPath) {
if pathEntry == "" {
@@ -185,8 +218,14 @@
ctx.Fatalln("TEMPORARY_DISABLE_PATH_RESTRICTIONS was a temporary migration method, and is now obsolete.")
}
+ // Create symlinks from the path_interposer binary to all binaries for each
+ // directory in the original $PATH. This ensures that during the build,
+ // every call to a binary that's expected to be in the $PATH will be
+ // intercepted by the path_interposer binary, and validated with the
+ // LogEntry listener above at build time.
for _, name := range execs {
if !paths.GetConfig(name).Symlink {
+ // Ignore host tools that shouldn't be symlinked.
continue
}
@@ -200,11 +239,13 @@
myPath, _ = filepath.Abs(myPath)
- // We put some prebuilts in $PATH, since it's infeasible to add dependencies for all of
- // them.
+ // We put some prebuilts in $PATH, since it's infeasible to add dependencies
+ // for all of them.
prebuiltsPath, _ := filepath.Abs("prebuilts/build-tools/path/" + runtime.GOOS + "-x86")
myPath = prebuiltsPath + string(os.PathListSeparator) + myPath
+ // Replace the $PATH variable with the path_interposer symlinks, and
+ // checked-in prebuilts.
config.Environment().Set("PATH", myPath)
config.pathReplaced = true
}