Merge "Revert "Revert "Allow codename.fingerprint format for minSdkVersion"""
diff --git a/Android.bp b/Android.bp
index 0382ee2..0f7ef9e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -473,6 +473,7 @@
],
testSrcs: [
"apex/apex_test.go",
+ "apex/vndk_test.go",
],
pluginFor: ["soong_build"],
}
@@ -548,6 +549,7 @@
name: "libatomic",
defaults: ["linux_bionic_supported"],
vendor_available: true,
+ ramdisk_available: true,
recovery_available: true,
native_bridge_supported: true,
@@ -594,8 +596,10 @@
name: "libgcc_stripped",
defaults: ["linux_bionic_supported"],
vendor_available: true,
+ ramdisk_available: true,
recovery_available: true,
native_bridge_supported: true,
+ sdk_version: "current",
arch: {
arm: {
diff --git a/android/apex.go b/android/apex.go
index 0b901ae..a4b6956 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -82,7 +82,10 @@
}
type ApexProperties struct {
- // Availability of this module in APEXes. Only the listed APEXes can include this module.
+ // 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".
// Default is ["//apex_available:platform", "//apex_available:anyapex"].
diff --git a/android/arch.go b/android/arch.go
index b5b8a8f..65833a8 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -838,8 +838,8 @@
osTargets = targets
}
- // only the primary arch in the recovery partition
- if os == Android && module.InstallInRecovery() {
+ // only the primary arch in the ramdisk / recovery partition
+ if os == Android && (module.InstallInRecovery() || module.InstallInRamdisk()) {
osTargets = []Target{osTargets[0]}
}
@@ -926,16 +926,31 @@
return targets
}
-// createArchType takes a reflect.Type that is either a struct or a pointer to a struct, and returns a list of
-// reflect.Type that contains the arch-variant properties inside structs for each architecture, os, target, multilib,
-// etc.
-func createArchType(props reflect.Type) []reflect.Type {
- propShards, _ := proptools.FilterPropertyStructSharded(props, filterArchStruct)
+type archPropTypeDesc struct {
+ arch, multilib, target reflect.Type
+}
+
+type archPropRoot struct {
+ Arch, Multilib, Target interface{}
+}
+
+// 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.
+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
+ // 97 times now, which may grow in the future, plus there is some overhead for the containing
+ // type. This number may need to be reduced if too many are added, but reducing it too far
+ // could cause problems if a single deeply nested property no longer fits in the name.
+ const maxArchTypeNameSize = 500
+
+ propShards, _ := proptools.FilterPropertyStructSharded(props, maxArchTypeNameSize, filterArchStruct)
if len(propShards) == 0 {
return nil
}
- var ret []reflect.Type
+ var ret []archPropTypeDesc
for _, props := range propShards {
variantFields := func(names []string) []reflect.StructField {
@@ -1011,20 +1026,12 @@
}
targetType := reflect.StructOf(variantFields(targets))
- ret = append(ret, reflect.StructOf([]reflect.StructField{
- {
- Name: "Arch",
- Type: archType,
- },
- {
- Name: "Multilib",
- Type: multilibType,
- },
- {
- Name: "Target",
- Type: targetType,
- },
- }))
+
+ ret = append(ret, archPropTypeDesc{
+ arch: reflect.PtrTo(archType),
+ multilib: reflect.PtrTo(multilibType),
+ target: reflect.PtrTo(targetType),
+ })
}
return ret
}
@@ -1036,11 +1043,20 @@
// 16-bit limit on structure name length. The name is constructed
// based on the Go source representation of the structure, so
// the tag names count towards that length.
- //
- // TODO: handle the uncommon case of other tags being involved
- if field.Tag == `android:"arch_variant"` {
- field.Tag = ""
+
+ androidTag := field.Tag.Get("android")
+ values := strings.Split(androidTag, ",")
+
+ if string(field.Tag) != `android:"`+strings.Join(values, ",")+`"` {
+ panic(fmt.Errorf("unexpected tag format %q", field.Tag))
}
+ // these tags don't need to be present in the runtime generated struct type.
+ values = RemoveListFromList(values, []string{"arch_variant", "variant_prepend", "path"})
+ if len(values) > 0 {
+ panic(fmt.Errorf("unknown tags %q in field %q", values, prefix+field.Name))
+ }
+
+ field.Tag = ""
return true, field
}
return false, field
@@ -1069,12 +1085,16 @@
}
archPropTypes := archPropTypeMap.Once(NewCustomOnceKey(t), func() interface{} {
- return createArchType(t)
- }).([]reflect.Type)
+ return createArchPropTypeDesc(t)
+ }).([]archPropTypeDesc)
var archProperties []interface{}
for _, t := range archPropTypes {
- archProperties = append(archProperties, reflect.New(t).Interface())
+ archProperties = append(archProperties, &archPropRoot{
+ Arch: reflect.Zero(t.arch).Interface(),
+ Multilib: reflect.Zero(t.multilib).Interface(),
+ Target: reflect.Zero(t.target).Interface(),
+ })
}
base.archProperties = append(base.archProperties, archProperties)
m.AddProperties(archProperties...)
@@ -1088,6 +1108,13 @@
func (m *ModuleBase) appendProperties(ctx BottomUpMutatorContext,
dst interface{}, src reflect.Value, field, srcPrefix string) reflect.Value {
+ if src.Kind() == reflect.Ptr {
+ if src.IsNil() {
+ return src
+ }
+ src = src.Elem()
+ }
+
src = src.FieldByName(field)
if !src.IsValid() {
ctx.ModuleErrorf("field %q does not exist", srcPrefix)
@@ -1134,7 +1161,7 @@
for _, archProperties := range m.archProperties[i] {
archPropValues := reflect.ValueOf(archProperties).Elem()
- targetProp := archPropValues.FieldByName("Target")
+ targetProp := archPropValues.FieldByName("Target").Elem()
// Handle host-specific properties in the form:
// target: {
@@ -1229,9 +1256,9 @@
for _, archProperties := range m.archProperties[i] {
archPropValues := reflect.ValueOf(archProperties).Elem()
- archProp := archPropValues.FieldByName("Arch")
- multilibProp := archPropValues.FieldByName("Multilib")
- targetProp := archPropValues.FieldByName("Target")
+ archProp := archPropValues.FieldByName("Arch").Elem()
+ multilibProp := archPropValues.FieldByName("Multilib").Elem()
+ targetProp := archPropValues.FieldByName("Target").Elem()
// Handle arch-specific properties in the form:
// arch: {
diff --git a/android/arch_test.go b/android/arch_test.go
index 98b0534..b987d56 100644
--- a/android/arch_test.go
+++ b/android/arch_test.go
@@ -55,6 +55,24 @@
filtered: true,
},
{
+ name: "tags",
+ in: &struct {
+ A *string `android:"arch_variant"`
+ B *string `android:"arch_variant,path"`
+ C *string `android:"arch_variant,path,variant_prepend"`
+ D *string `android:"path,variant_prepend,arch_variant"`
+ E *string `android:"path"`
+ F *string
+ }{},
+ out: &struct {
+ A *string
+ B *string
+ C *string
+ D *string
+ }{},
+ filtered: true,
+ },
+ {
name: "all filtered",
in: &struct {
A *string
diff --git a/android/config.go b/android/config.go
index 1cb543d..f907de6 100644
--- a/android/config.go
+++ b/android/config.go
@@ -827,6 +827,10 @@
return Bool(c.productVariables.UseRBE)
}
+func (c *config) UseRemoteBuild() bool {
+ return c.UseGoma() || c.UseRBE()
+}
+
func (c *config) RunErrorProne() bool {
return c.IsEnvTrue("RUN_ERROR_PRONE")
}
@@ -1027,6 +1031,10 @@
return Bool(c.config.productVariables.NativeCoverage)
}
+func (c *deviceConfig) ClangCoverageEnabled() bool {
+ return Bool(c.config.productVariables.ClangCoverage)
+}
+
func (c *deviceConfig) CoverageEnabledForPath(path string) bool {
coverage := false
if c.config.productVariables.CoveragePaths != nil {
@@ -1233,3 +1241,7 @@
func (c *deviceConfig) DeviceSecondaryArchVariant() string {
return String(c.config.productVariables.DeviceSecondaryArchVariant)
}
+
+func (c *deviceConfig) BoardUsesRecoveryAsBoot() bool {
+ return Bool(c.config.productVariables.BoardUsesRecoveryAsBoot)
+}
diff --git a/android/defs.go b/android/defs.go
index 4890c66..5c815e6 100644
--- a/android/defs.go
+++ b/android/defs.go
@@ -99,6 +99,9 @@
// Used only when USE_GOMA=true is set, to restrict non-goma jobs to the local parallelism value
localPool = blueprint.NewBuiltinPool("local_pool")
+
+ // Used for processes that need significant RAM to ensure there are not too many running in parallel.
+ highmemPool = blueprint.NewBuiltinPool("highmem_pool")
)
func init() {
diff --git a/android/image.go b/android/image.go
index 5291ce3..061bfa5 100644
--- a/android/image.go
+++ b/android/image.go
@@ -22,6 +22,10 @@
// CoreVariantNeeded should return true if the module needs a core variant (installed on the system image).
CoreVariantNeeded(ctx BaseModuleContext) bool
+ // RamdiskVariantNeeded should return true if the module needs a ramdisk variant (installed on the
+ // ramdisk partition).
+ RamdiskVariantNeeded(ctx BaseModuleContext) bool
+
// RecoveryVariantNeeded should return true if the module needs a recovery variant (installed on the
// recovery partition).
RecoveryVariantNeeded(ctx BaseModuleContext) bool
@@ -46,6 +50,9 @@
// RecoveryVariation means a module to be installed to recovery image.
RecoveryVariation string = "recovery"
+
+ // RamdiskVariation means a module to be installed to ramdisk image.
+ RamdiskVariation string = "ramdisk"
)
// imageMutator creates variants for modules that implement the ImageInterface that
@@ -63,6 +70,9 @@
if m.CoreVariantNeeded(ctx) {
variations = append(variations, CoreVariation)
}
+ if m.RamdiskVariantNeeded(ctx) {
+ variations = append(variations, RamdiskVariation)
+ }
if m.RecoveryVariantNeeded(ctx) {
variations = append(variations, RecoveryVariation)
}
diff --git a/android/module.go b/android/module.go
index 05115d6..4a72889 100644
--- a/android/module.go
+++ b/android/module.go
@@ -167,6 +167,7 @@
InstallInData() bool
InstallInTestcases() bool
InstallInSanitizerDir() bool
+ InstallInRamdisk() bool
InstallInRecovery() bool
InstallInRoot() bool
InstallBypassMake() bool
@@ -207,6 +208,7 @@
InstallInData() bool
InstallInTestcases() bool
InstallInSanitizerDir() bool
+ InstallInRamdisk() bool
InstallInRecovery() bool
InstallInRoot() bool
InstallBypassMake() bool
@@ -384,6 +386,9 @@
// Whether this module is installed to recovery partition
Recovery *bool
+ // Whether this module is installed to ramdisk
+ Ramdisk *bool
+
// Whether this module is built for non-native architecures (also known as native bridge binary)
Native_bridge_supported *bool `android:"arch_variant"`
@@ -867,6 +872,10 @@
return false
}
+func (m *ModuleBase) InstallInRamdisk() bool {
+ return Bool(m.commonProperties.Ramdisk)
+}
+
func (m *ModuleBase) InstallInRecovery() bool {
return Bool(m.commonProperties.Recovery)
}
@@ -898,6 +907,10 @@
}
}
+func (m *ModuleBase) InRamdisk() bool {
+ return m.base().commonProperties.ImageVariation == RamdiskVariation
+}
+
func (m *ModuleBase) InRecovery() bool {
return m.base().commonProperties.ImageVariation == RecoveryVariation
}
@@ -1338,7 +1351,7 @@
func (m *moduleContext) Rule(pctx PackageContext, name string, params blueprint.RuleParams,
argNames ...string) blueprint.Rule {
- if (m.config.UseGoma() || m.config.UseRBE()) && params.Pool == nil {
+ if m.config.UseRemoteBuild() && params.Pool == nil {
// When USE_GOMA=true or USE_RBE=true are set and the rule is not supported by goma/RBE, restrict
// jobs to the local parallelism value
params.Pool = localPool
@@ -1649,6 +1662,10 @@
return m.module.InstallInSanitizerDir()
}
+func (m *moduleContext) InstallInRamdisk() bool {
+ return m.module.InstallInRamdisk()
+}
+
func (m *moduleContext) InstallInRecovery() bool {
return m.module.InstallInRecovery()
}
diff --git a/android/mutator.go b/android/mutator.go
index f2f9663..3d1bb4f 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -27,6 +27,7 @@
// run Pre-deps mutators
// run depsMutator
// run PostDeps mutators
+// run FinalDeps mutators (CreateVariations disallowed in this phase)
// continue on to GenerateAndroidBuildActions
func registerMutatorsToContext(ctx *blueprint.Context, mutators []*mutator) {
@@ -43,7 +44,7 @@
}
}
-func registerMutators(ctx *blueprint.Context, preArch, preDeps, postDeps []RegisterMutatorFunc) {
+func registerMutators(ctx *blueprint.Context, preArch, preDeps, postDeps, finalDeps []RegisterMutatorFunc) {
mctx := ®isterMutatorsContext{}
register := func(funcs []RegisterMutatorFunc) {
@@ -60,11 +61,15 @@
register(postDeps)
+ mctx.finalPhase = true
+ register(finalDeps)
+
registerMutatorsToContext(ctx, mctx.mutators)
}
type registerMutatorsContext struct {
- mutators []*mutator
+ mutators []*mutator
+ finalPhase bool
}
type RegisterMutatorsContext interface {
@@ -102,6 +107,8 @@
RegisterOverridePostDepsMutators,
}
+var finalDeps = []RegisterMutatorFunc{}
+
func PreArchMutators(f RegisterMutatorFunc) {
preArch = append(preArch, f)
}
@@ -114,6 +121,10 @@
postDeps = append(postDeps, f)
}
+func FinalDepsMutators(f RegisterMutatorFunc) {
+ finalDeps = append(finalDeps, f)
+}
+
type TopDownMutator func(TopDownMutatorContext)
type TopDownMutatorContext interface {
@@ -156,14 +167,17 @@
type bottomUpMutatorContext struct {
bp blueprint.BottomUpMutatorContext
baseModuleContext
+ finalPhase bool
}
func (x *registerMutatorsContext) BottomUp(name string, m BottomUpMutator) MutatorHandle {
+ finalPhase := x.finalPhase
f := func(ctx blueprint.BottomUpMutatorContext) {
if a, ok := ctx.Module().(Module); ok {
actx := &bottomUpMutatorContext{
bp: ctx,
baseModuleContext: a.base().baseModuleContextFactory(ctx),
+ finalPhase: finalPhase,
}
m(actx)
}
@@ -285,6 +299,10 @@
}
func (b *bottomUpMutatorContext) CreateVariations(variations ...string) []Module {
+ if b.finalPhase {
+ panic("CreateVariations not allowed in FinalDepsMutators")
+ }
+
modules := b.bp.CreateVariations(variations...)
aModules := make([]Module, len(modules))
@@ -299,6 +317,10 @@
}
func (b *bottomUpMutatorContext) CreateLocalVariations(variations ...string) []Module {
+ if b.finalPhase {
+ panic("CreateLocalVariations not allowed in FinalDepsMutators")
+ }
+
modules := b.bp.CreateLocalVariations(variations...)
aModules := make([]Module, len(modules))
diff --git a/android/mutator_test.go b/android/mutator_test.go
index d179f9d..191b535 100644
--- a/android/mutator_test.go
+++ b/android/mutator_test.go
@@ -15,9 +15,12 @@
package android
import (
+ "fmt"
"reflect"
+ "strings"
"testing"
+ "github.com/google/blueprint"
"github.com/google/blueprint/proptools"
)
@@ -185,3 +188,110 @@
t.Errorf("want module String() values:\n%q\ngot:\n%q", want, moduleStrings)
}
}
+
+func TestFinalDepsPhase(t *testing.T) {
+ ctx := NewTestContext()
+
+ finalGot := map[string]int{}
+
+ dep1Tag := struct {
+ blueprint.BaseDependencyTag
+ }{}
+ dep2Tag := struct {
+ blueprint.BaseDependencyTag
+ }{}
+
+ ctx.PostDepsMutators(func(ctx RegisterMutatorsContext) {
+ ctx.BottomUp("far_deps_1", func(ctx BottomUpMutatorContext) {
+ if !strings.HasPrefix(ctx.ModuleName(), "common_dep") {
+ ctx.AddFarVariationDependencies([]blueprint.Variation{}, dep1Tag, "common_dep_1")
+ }
+ })
+ ctx.BottomUp("variant", func(ctx BottomUpMutatorContext) {
+ ctx.CreateLocalVariations("a", "b")
+ })
+ })
+
+ ctx.FinalDepsMutators(func(ctx RegisterMutatorsContext) {
+ ctx.BottomUp("far_deps_2", func(ctx BottomUpMutatorContext) {
+ if !strings.HasPrefix(ctx.ModuleName(), "common_dep") {
+ ctx.AddFarVariationDependencies([]blueprint.Variation{}, dep2Tag, "common_dep_2")
+ }
+ })
+ ctx.BottomUp("final", func(ctx BottomUpMutatorContext) {
+ finalGot[ctx.Module().String()] += 1
+ ctx.VisitDirectDeps(func(mod Module) {
+ finalGot[fmt.Sprintf("%s -> %s", ctx.Module().String(), mod)] += 1
+ })
+ })
+ })
+
+ ctx.RegisterModuleType("test", mutatorTestModuleFactory)
+
+ bp := `
+ test {
+ name: "common_dep_1",
+ }
+ test {
+ name: "common_dep_2",
+ }
+ test {
+ name: "foo",
+ }
+ `
+
+ config := TestConfig(buildDir, nil, bp, nil)
+ ctx.Register(config)
+
+ _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+ FailIfErrored(t, errs)
+ _, errs = ctx.PrepareBuildActions(config)
+ FailIfErrored(t, errs)
+
+ finalWant := map[string]int{
+ "common_dep_1{variant:a}": 1,
+ "common_dep_1{variant:b}": 1,
+ "common_dep_2{variant:a}": 1,
+ "common_dep_2{variant:b}": 1,
+ "foo{variant:a}": 1,
+ "foo{variant:a} -> common_dep_1{variant:a}": 1,
+ "foo{variant:a} -> common_dep_2{variant:a}": 1,
+ "foo{variant:b}": 1,
+ "foo{variant:b} -> common_dep_1{variant:b}": 1,
+ "foo{variant:b} -> common_dep_2{variant:a}": 1,
+ }
+
+ if !reflect.DeepEqual(finalWant, finalGot) {
+ t.Errorf("want:\n%q\ngot:\n%q", finalWant, finalGot)
+ }
+}
+
+func TestNoCreateVariationsInFinalDeps(t *testing.T) {
+ ctx := NewTestContext()
+
+ checkErr := func() {
+ if err := recover(); err == nil || !strings.Contains(fmt.Sprintf("%s", err), "not allowed in FinalDepsMutators") {
+ panic("Expected FinalDepsMutators consistency check to fail")
+ }
+ }
+
+ ctx.FinalDepsMutators(func(ctx RegisterMutatorsContext) {
+ ctx.BottomUp("vars", func(ctx BottomUpMutatorContext) {
+ defer checkErr()
+ ctx.CreateVariations("a", "b")
+ })
+ ctx.BottomUp("local_vars", func(ctx BottomUpMutatorContext) {
+ defer checkErr()
+ ctx.CreateLocalVariations("a", "b")
+ })
+ })
+
+ ctx.RegisterModuleType("test", mutatorTestModuleFactory)
+ config := TestConfig(buildDir, nil, `test {name: "foo"}`, nil)
+ ctx.Register(config)
+
+ _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+ FailIfErrored(t, errs)
+ _, errs = ctx.PrepareBuildActions(config)
+ FailIfErrored(t, errs)
+}
diff --git a/android/package_ctx.go b/android/package_ctx.go
index a228910..6350667 100644
--- a/android/package_ctx.go
+++ b/android/package_ctx.go
@@ -109,7 +109,7 @@
if len(ctx.errors) > 0 {
return params, ctx.errors[0]
}
- if (ctx.Config().UseGoma() || ctx.Config().UseRBE()) && params.Pool == nil {
+ if ctx.Config().UseRemoteBuild() && params.Pool == nil {
// When USE_GOMA=true or USE_RBE=true are set and the rule is not supported by
// goma/RBE, restrict jobs to the local parallelism value
params.Pool = localPool
diff --git a/android/paths.go b/android/paths.go
index 02f56d0..da579d5 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -49,6 +49,7 @@
InstallInData() bool
InstallInTestcases() bool
InstallInSanitizerDir() bool
+ InstallInRamdisk() bool
InstallInRecovery() bool
InstallInRoot() bool
InstallBypassMake() bool
@@ -1254,6 +1255,15 @@
partition = "data"
} else if ctx.InstallInTestcases() {
partition = "testcases"
+ } else if ctx.InstallInRamdisk() {
+ if ctx.DeviceConfig().BoardUsesRecoveryAsBoot() {
+ partition = "recovery/root/first_stage_ramdisk"
+ } else {
+ partition = "ramdisk"
+ }
+ if !ctx.InstallInRoot() {
+ partition += "/system"
+ }
} else if ctx.InstallInRecovery() {
if ctx.InstallInRoot() {
partition = "recovery/root"
diff --git a/android/paths_test.go b/android/paths_test.go
index 46e3e1f..7a32026 100644
--- a/android/paths_test.go
+++ b/android/paths_test.go
@@ -202,6 +202,7 @@
inData bool
inTestcases bool
inSanitizerDir bool
+ inRamdisk bool
inRecovery bool
inRoot bool
}
@@ -224,6 +225,10 @@
return m.inSanitizerDir
}
+func (m moduleInstallPathContextImpl) InstallInRamdisk() bool {
+ return m.inRamdisk
+}
+
func (m moduleInstallPathContextImpl) InstallInRecovery() bool {
return m.inRecovery
}
diff --git a/android/prebuilt_etc.go b/android/prebuilt_etc.go
index 388d17f..3dea6d8 100644
--- a/android/prebuilt_etc.go
+++ b/android/prebuilt_etc.go
@@ -41,6 +41,9 @@
// is the same as the file name of the source file.
Filename_from_src *bool `android:"arch_variant"`
+ // Make this module available when building for ramdisk.
+ Ramdisk_available *bool
+
// Make this module available when building for recovery.
Recovery_available *bool
@@ -69,6 +72,18 @@
additionalDependencies *Paths
}
+func (p *PrebuiltEtc) inRamdisk() bool {
+ return p.ModuleBase.InRamdisk() || p.ModuleBase.InstallInRamdisk()
+}
+
+func (p *PrebuiltEtc) onlyInRamdisk() bool {
+ return p.ModuleBase.InstallInRamdisk()
+}
+
+func (p *PrebuiltEtc) InstallInRamdisk() bool {
+ return p.inRamdisk()
+}
+
func (p *PrebuiltEtc) inRecovery() bool {
return p.ModuleBase.InRecovery() || p.ModuleBase.InstallInRecovery()
}
@@ -86,7 +101,11 @@
func (p *PrebuiltEtc) ImageMutatorBegin(ctx BaseModuleContext) {}
func (p *PrebuiltEtc) CoreVariantNeeded(ctx BaseModuleContext) bool {
- return !p.ModuleBase.InstallInRecovery()
+ return !p.ModuleBase.InstallInRecovery() && !p.ModuleBase.InstallInRamdisk()
+}
+
+func (p *PrebuiltEtc) RamdiskVariantNeeded(ctx BaseModuleContext) bool {
+ return Bool(p.properties.Ramdisk_available) || p.ModuleBase.InstallInRamdisk()
}
func (p *PrebuiltEtc) RecoveryVariantNeeded(ctx BaseModuleContext) bool {
@@ -167,6 +186,9 @@
func (p *PrebuiltEtc) AndroidMkEntries() []AndroidMkEntries {
nameSuffix := ""
+ if p.inRamdisk() && !p.onlyInRamdisk() {
+ nameSuffix = ".ramdisk"
+ }
if p.inRecovery() && !p.onlyInRecovery() {
nameSuffix = ".recovery"
}
diff --git a/android/register.go b/android/register.go
index d5aa1a5..ccfe01e 100644
--- a/android/register.go
+++ b/android/register.go
@@ -102,7 +102,7 @@
ctx.RegisterSingletonType(t.name, t.factory)
}
- registerMutators(ctx.Context, preArch, preDeps, postDeps)
+ registerMutators(ctx.Context, preArch, preDeps, postDeps, finalDeps)
// Register makevars after other singletons so they can export values through makevars
ctx.RegisterSingletonType("makevars", SingletonFactoryAdaptor(makeVarsSingletonFunc))
@@ -135,6 +135,7 @@
PreDepsMutators(f RegisterMutatorFunc)
PostDepsMutators(f RegisterMutatorFunc)
+ FinalDepsMutators(f RegisterMutatorFunc)
}
// Used to register build components from an init() method, e.g.
@@ -197,3 +198,7 @@
func (ctx *initRegistrationContext) PostDepsMutators(f RegisterMutatorFunc) {
PostDepsMutators(f)
}
+
+func (ctx *initRegistrationContext) FinalDepsMutators(f RegisterMutatorFunc) {
+ FinalDepsMutators(f)
+}
diff --git a/android/rule_builder.go b/android/rule_builder.go
index 6f04672..928ba53 100644
--- a/android/rule_builder.go
+++ b/android/rule_builder.go
@@ -33,6 +33,8 @@
temporariesSet map[WritablePath]bool
restat bool
sbox bool
+ highmem bool
+ remoteable RemoteRuleSupports
sboxOutDir WritablePath
missingDeps []string
}
@@ -87,6 +89,19 @@
return r
}
+// HighMem marks the rule as a high memory rule, which will limit how many run in parallel with other high memory
+// rules.
+func (r *RuleBuilder) HighMem() *RuleBuilder {
+ r.highmem = true
+ return r
+}
+
+// Remoteable marks the rule as supporting remote execution.
+func (r *RuleBuilder) Remoteable(supports RemoteRuleSupports) *RuleBuilder {
+ r.remoteable = supports
+ return r
+}
+
// Sbox marks the rule as needing to be wrapped by sbox. The WritablePath should point to the output
// directory that sbox will wipe. It should not be written to by any other rule. sbox will ensure
// that all outputs have been written, and will discard any output files that were not specified.
@@ -401,6 +416,17 @@
rspFileContent = "$in"
}
+ var pool blueprint.Pool
+ if ctx.Config().UseGoma() && r.remoteable&SUPPORTS_GOMA != 0 {
+ // When USE_GOMA=true is set and the rule is supported by goma, allow jobs to run outside the local pool.
+ } else if ctx.Config().UseRBE() && r.remoteable&SUPPORTS_RBE != 0 {
+ // When USE_GOMA=true is set and the rule is supported by RBE, allow jobs to run outside the local pool.
+ } else if r.highmem {
+ pool = highmemPool
+ } else if ctx.Config().UseRemoteBuild() {
+ pool = localPool
+ }
+
ctx.Build(pctx, BuildParams{
Rule: ctx.Rule(pctx, name, blueprint.RuleParams{
Command: commandString,
@@ -408,6 +434,7 @@
Restat: r.restat,
Rspfile: rspFile,
RspfileContent: rspFileContent,
+ Pool: pool,
}),
Inputs: rspFileInputs,
Implicits: r.Inputs(),
diff --git a/android/singleton.go b/android/singleton.go
index 91268ad..45a9b82 100644
--- a/android/singleton.go
+++ b/android/singleton.go
@@ -128,7 +128,7 @@
}
func (s *singletonContextAdaptor) Rule(pctx PackageContext, name string, params blueprint.RuleParams, argNames ...string) blueprint.Rule {
- if (s.Config().UseGoma() || s.Config().UseRBE()) && params.Pool == nil {
+ if s.Config().UseRemoteBuild() && params.Pool == nil {
// When USE_GOMA=true or USE_RBE=true are set and the rule is not supported by goma/RBE, restrict
// jobs to the local parallelism value
params.Pool = localPool
diff --git a/android/testing.go b/android/testing.go
index c07af7f..9aff039 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -50,9 +50,9 @@
type TestContext struct {
*Context
- preArch, preDeps, postDeps []RegisterMutatorFunc
- NameResolver *NameResolver
- config Config
+ preArch, preDeps, postDeps, finalDeps []RegisterMutatorFunc
+ NameResolver *NameResolver
+ config Config
}
func (ctx *TestContext) PreArchMutators(f RegisterMutatorFunc) {
@@ -72,12 +72,16 @@
ctx.postDeps = append(ctx.postDeps, f)
}
+func (ctx *TestContext) FinalDepsMutators(f RegisterMutatorFunc) {
+ ctx.finalDeps = append(ctx.finalDeps, f)
+}
+
func (ctx *TestContext) Register(config Config) {
ctx.SetFs(config.fs)
if config.mockBpList != "" {
ctx.SetModuleListFile(config.mockBpList)
}
- registerMutators(ctx.Context.Context, ctx.preArch, ctx.preDeps, ctx.postDeps)
+ registerMutators(ctx.Context.Context, ctx.preArch, ctx.preDeps, ctx.postDeps, ctx.finalDeps)
ctx.RegisterSingletonType("env", EnvSingleton)
diff --git a/android/variable.go b/android/variable.go
index 2bf84dd..7473491 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -243,6 +243,7 @@
TidyChecks *string `json:",omitempty"`
NativeCoverage *bool `json:",omitempty"`
+ ClangCoverage *bool `json:",omitempty"`
CoveragePaths []string `json:",omitempty"`
CoverageExcludePaths []string `json:",omitempty"`
@@ -316,6 +317,8 @@
EnforceProductPartitionInterface *bool `json:",omitempty"`
InstallExtraFlattenedApexes *bool `json:",omitempty"`
+
+ BoardUsesRecoveryAsBoot *bool `json:",omitempty"`
}
func boolPtr(v bool) *bool {
diff --git a/apex/androidmk.go b/apex/androidmk.go
index ad7d2f1..5fa5bf0 100644
--- a/apex/androidmk.go
+++ b/apex/androidmk.go
@@ -52,13 +52,39 @@
return moduleNames
}
+ var postInstallCommands []string
+ for _, fi := range a.filesInfo {
+ 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(), apexName, fi.Path())
+ mkdirCmd := "mkdir -p " + filepath.Dir(linkPath)
+ linkCmd := "ln -sfn " + linkTarget + " " + linkPath
+ postInstallCommands = append(postInstallCommands, mkdirCmd, linkCmd)
+ }
+ }
+
for _, fi := range a.filesInfo {
if cc, ok := fi.module.(*cc.Module); ok && cc.Properties.HideFromMake {
continue
}
- if !android.InList(fi.moduleName, moduleNames) {
- moduleNames = append(moduleNames, fi.moduleName)
+ linkToSystemLib := a.linkToSystemLib && fi.transitiveDep && fi.AvailableToPlatform()
+
+ var moduleName string
+ if linkToSystemLib {
+ moduleName = fi.moduleName
+ } else {
+ moduleName = fi.moduleName + "." + apexName + a.suffix
+ }
+
+ if !android.InList(moduleName, moduleNames) {
+ moduleNames = append(moduleNames, moduleName)
+ }
+
+ if linkToSystemLib {
+ // No need to copy the file since it's linked to the system file
+ continue
}
fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)")
@@ -67,7 +93,7 @@
} else {
fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
}
- fmt.Fprintln(w, "LOCAL_MODULE :=", fi.moduleName)
+ fmt.Fprintln(w, "LOCAL_MODULE :=", moduleName)
// /apex/<apex_name>/{lib|framework|...}
pathWhenActivated := filepath.Join("$(PRODUCT_OUT)", "apex", apexName, fi.installDir)
if apexType == flattenedApex {
@@ -151,18 +177,23 @@
fmt.Fprintln(w, "include $(BUILD_SYSTEM)/soong_cc_prebuilt.mk")
} else {
fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", fi.builtFile.Base())
- if a.primaryApexType && fi.builtFile == a.manifestPbOut {
- // Make apex_manifest.pb module for this APEX to override all other
- // modules in the APEXes being overridden by this APEX
- var patterns []string
- for _, o := range a.overridableProperties.Overrides {
- patterns = append(patterns, "%."+o+a.suffix)
- }
- fmt.Fprintln(w, "LOCAL_OVERRIDES_MODULES :=", strings.Join(patterns, " "))
+ if fi.builtFile == a.manifestPbOut {
+ if a.primaryApexType {
+ // Make apex_manifest.pb module for this APEX to override all other
+ // modules in the APEXes being overridden by this APEX
+ var patterns []string
+ for _, o := range a.overridableProperties.Overrides {
+ patterns = append(patterns, "%."+o+a.suffix)
+ }
+ fmt.Fprintln(w, "LOCAL_OVERRIDES_MODULES :=", strings.Join(patterns, " "))
- if len(a.compatSymlinks) > 0 {
- // For flattened apexes, compat symlinks are attached to apex_manifest.json which is guaranteed for every apex
- fmt.Fprintln(w, "LOCAL_POST_INSTALL_CMD :=", strings.Join(a.compatSymlinks, " && "))
+ if apexType == flattenedApex && len(a.compatSymlinks) > 0 {
+ // For flattened apexes, compat symlinks are attached to apex_manifest.json which is guaranteed for every apex
+ postInstallCommands = append(postInstallCommands, a.compatSymlinks...)
+ }
+ }
+ if len(postInstallCommands) > 0 {
+ fmt.Fprintln(w, "LOCAL_POST_INSTALL_CMD :=", strings.Join(postInstallCommands, " && "))
}
}
fmt.Fprintln(w, "include $(BUILD_PREBUILT)")
diff --git a/apex/apex.go b/apex/apex.go
index 33b1be3..f925066 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -333,7 +333,7 @@
// Mark the direct and transitive dependencies of apex bundles so that they
// can be built for the apex bundles.
func apexDepsMutator(mctx android.BottomUpMutatorContext) {
- if a, ok := mctx.Module().(*apexBundle); ok {
+ if a, ok := mctx.Module().(*apexBundle); ok && !a.vndkApex {
apexBundleName := mctx.ModuleName()
mctx.WalkDeps(func(child, parent android.Module) bool {
depName := mctx.OtherModuleName(child)
@@ -361,7 +361,7 @@
func apexMutator(mctx android.BottomUpMutatorContext) {
if am, ok := mctx.Module().(android.ApexModule); ok && am.CanHaveApexVariants() {
am.CreateApexVariations(mctx)
- } else if _, ok := mctx.Module().(*apexBundle); ok {
+ } 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()
@@ -733,6 +733,30 @@
return af.builtFile != nil && af.builtFile.String() != ""
}
+// Path() returns path of this apex file relative to the APEX root
+func (af *apexFile) Path() string {
+ return filepath.Join(af.installDir, 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, filepath.Join(af.installDir, 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 apexBundle struct {
android.ModuleBase
android.DefaultableModuleBase
@@ -790,6 +814,10 @@
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
}
func addDependenciesForNativeModules(ctx android.BottomUpMutatorContext,
@@ -1056,7 +1084,7 @@
}
func (a *apexBundle) IsNativeCoverageNeeded(ctx android.BaseModuleContext) bool {
- return ctx.Device() && ctx.DeviceConfig().NativeCoverageEnabled()
+ return ctx.Device() && (ctx.DeviceConfig().NativeCoverageEnabled() || ctx.DeviceConfig().ClangCoverageEnabled())
}
func (a *apexBundle) PreventInstall() {
@@ -1414,7 +1442,8 @@
// of the original test module (`depName`, shared by all `test_per_src`
// variations of that module).
af.moduleName = filepath.Base(af.builtFile.String())
- af.transitiveDep = true
+ // these are not considered transitive dep
+ af.transitiveDep = false
filesInfo = append(filesInfo, af)
return true // track transitive dependencies
}
@@ -1452,15 +1481,22 @@
// remove duplicates in filesInfo
removeDup := func(filesInfo []apexFile) []apexFile {
- encountered := make(map[string]bool)
- result := []apexFile{}
+ encountered := make(map[string]apexFile)
for _, f := range filesInfo {
dest := filepath.Join(f.installDir, f.builtFile.Base())
- if !encountered[dest] {
- encountered[dest] = true
- result = append(result, f)
+ 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)
@@ -1487,12 +1523,6 @@
}
}
- // prepend the name of this APEX to the module names. These names will be the names of
- // modules that will be defined if the APEX is flattened.
- for i := range filesInfo {
- filesInfo[i].moduleName = filesInfo[i].moduleName + "." + a.Name() + a.suffix
- }
-
a.installDir = android.PathForModuleInstall(ctx, "apex")
a.filesInfo = filesInfo
@@ -1512,6 +1542,14 @@
return
}
}
+ // 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)
// prepare apex_manifest.json
a.buildManifest(ctx, provideNativeLibs, requireNativeLibs)
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 2103b6e..b7dd7fb 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -91,6 +91,10 @@
config.TestProductVariables.Binder32bit = proptools.BoolPtr(true)
}
+func withUnbundledBuild(fs map[string][]byte, config android.Config) {
+ config.TestProductVariables.Unbundled_build = proptools.BoolPtr(true)
+}
+
func testApexContext(t *testing.T, bp string, handlers ...testCustomizer) (*android.TestContext, android.Config) {
android.ClearApexDependency()
@@ -287,6 +291,9 @@
ctx.RegisterModuleType("prebuilt_apex", PrebuiltFactory)
ctx.RegisterModuleType("override_apex", overrideApexFactory)
+ ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
+ ctx.PostDepsMutators(android.RegisterOverridePostDepsMutators)
+
cc.RegisterRequiredBuildComponentsForTest(ctx)
ctx.RegisterModuleType("cc_test", cc.TestFactory)
ctx.RegisterModuleType("vndk_prebuilt_shared", cc.VndkPrebuiltSharedFactory)
@@ -299,9 +306,7 @@
java.RegisterAppBuildComponents(ctx)
ctx.RegisterModuleType("java_sdk_library", java.SdkLibraryFactory)
- ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
ctx.PreDepsMutators(RegisterPreDepsMutators)
- ctx.PostDepsMutators(android.RegisterOverridePostDepsMutators)
ctx.PostDepsMutators(RegisterPostDepsMutators)
ctx.Register(config)
@@ -516,7 +521,7 @@
found_foo_link_64 := false
found_foo := false
for _, cmd := range strings.Split(copyCmds, " && ") {
- if strings.HasPrefix(cmd, "ln -s foo64") {
+ if strings.HasPrefix(cmd, "ln -sfn foo64") {
if strings.HasSuffix(cmd, "bin/foo") {
found_foo = true
} else if strings.HasSuffix(cmd, "bin/foo_link_64") {
@@ -595,7 +600,7 @@
apex_available: [ "myapex" ],
}
`)
- ensureExactContents(t, ctx, "myapex", []string{
+ ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{
"etc/myetc",
"javalib/myjar.jar",
"lib64/mylib.so",
@@ -760,7 +765,7 @@
// Ensure that genstub is invoked with --apex
ensureContains(t, "--apex", ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_static_3").Rule("genStubSrc").Args["flags"])
- ensureExactContents(t, ctx, "myapex", []string{
+ ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{
"lib64/mylib.so",
"lib64/mylib3.so",
"lib64/mylib4.so",
@@ -1597,46 +1602,72 @@
ensureContains(t, cFlags, "-Imy_include")
}
-func ensureExactContents(t *testing.T, ctx *android.TestContext, moduleName string, files []string) {
+type fileInApex struct {
+ path string // path in apex
+ src string // src path
+ isLink bool
+}
+
+func getFiles(t *testing.T, ctx *android.TestContext, moduleName, variant string) []fileInApex {
t.Helper()
- apexRule := ctx.ModuleForTests(moduleName, "android_common_"+moduleName+"_image").Rule("apexRule")
+ apexRule := ctx.ModuleForTests(moduleName, variant).Rule("apexRule")
copyCmds := apexRule.Args["copy_commands"]
imageApexDir := "/image.apex/"
- var failed bool
- var surplus []string
- filesMatched := make(map[string]bool)
- addContent := func(content string) {
- for _, expected := range files {
- if matched, _ := path.Match(expected, content); matched {
- filesMatched[expected] = true
- return
- }
- }
- surplus = append(surplus, content)
- }
+ var ret []fileInApex
for _, cmd := range strings.Split(copyCmds, "&&") {
cmd = strings.TrimSpace(cmd)
if cmd == "" {
continue
}
terms := strings.Split(cmd, " ")
+ var dst, src string
+ var isLink bool
switch terms[0] {
case "mkdir":
case "cp":
- if len(terms) != 3 {
+ if len(terms) != 3 && len(terms) != 4 {
t.Fatal("copyCmds contains invalid cp command", cmd)
}
- dst := terms[2]
+ dst = terms[len(terms)-1]
+ src = terms[len(terms)-2]
+ isLink = false
+ case "ln":
+ if len(terms) != 3 && len(terms) != 4 {
+ // ln LINK TARGET or ln -s LINK TARGET
+ t.Fatal("copyCmds contains invalid ln command", cmd)
+ }
+ dst = terms[len(terms)-1]
+ src = terms[len(terms)-2]
+ isLink = true
+ default:
+ t.Fatalf("copyCmds should contain mkdir/cp commands only: %q", cmd)
+ }
+ if dst != "" {
index := strings.Index(dst, imageApexDir)
if index == -1 {
t.Fatal("copyCmds should copy a file to image.apex/", cmd)
}
dstFile := dst[index+len(imageApexDir):]
- addContent(dstFile)
- default:
- t.Fatalf("copyCmds should contain mkdir/cp commands only: %q", cmd)
+ ret = append(ret, fileInApex{path: dstFile, src: src, isLink: isLink})
}
}
+ return ret
+}
+
+func ensureExactContents(t *testing.T, ctx *android.TestContext, moduleName, variant string, files []string) {
+ t.Helper()
+ var failed bool
+ var surplus []string
+ filesMatched := make(map[string]bool)
+ for _, file := range getFiles(t, ctx, moduleName, variant) {
+ for _, expected := range files {
+ if matched, _ := path.Match(expected, file.path); matched {
+ filesMatched[expected] = true
+ return
+ }
+ }
+ surplus = append(surplus, file.path)
+ }
if len(surplus) > 0 {
sort.Strings(surplus)
@@ -1699,7 +1730,7 @@
}
`+vndkLibrariesTxtFiles("current"))
- ensureExactContents(t, ctx, "myapex", []string{
+ ensureExactContents(t, ctx, "myapex", "android_common_image", []string{
"lib/libvndk.so",
"lib/libvndksp.so",
"lib64/libvndk.so",
@@ -1759,7 +1790,7 @@
"libvndk.arm.so": nil,
}))
- ensureExactContents(t, ctx, "myapex", []string{
+ ensureExactContents(t, ctx, "myapex", "android_common_image", []string{
"lib/libvndk.so",
"lib/libvndk.arm.so",
"lib64/libvndk.so",
@@ -1850,7 +1881,7 @@
"libvndk27_x86_64.so": nil,
}))
- ensureExactContents(t, ctx, "myapex_v27", []string{
+ ensureExactContents(t, ctx, "myapex_v27", "android_common_image", []string{
"lib/libvndk27_arm.so",
"lib64/libvndk27_arm64.so",
"etc/*",
@@ -1923,7 +1954,7 @@
}`+vndkLibrariesTxtFiles("28", "current"))
assertApexName := func(expected, moduleName string) {
- bundle := ctx.ModuleForTests(moduleName, "android_common_"+moduleName+"_image").Module().(*apexBundle)
+ bundle := ctx.ModuleForTests(moduleName, "android_common_image").Module().(*apexBundle)
actual := proptools.String(bundle.properties.Apex_name)
if !reflect.DeepEqual(actual, expected) {
t.Errorf("Got '%v', expected '%v'", actual, expected)
@@ -1971,7 +2002,7 @@
},
}))
- ensureExactContents(t, ctx, "myapex", []string{
+ ensureExactContents(t, ctx, "myapex", "android_common_image", []string{
"lib/libvndk.so",
"lib64/libvndk.so",
"etc/*",
@@ -2067,7 +2098,7 @@
}),
)
- ensureExactContents(t, ctx, "myapex_v27", []string{
+ ensureExactContents(t, ctx, "myapex_v27", "android_common_image", []string{
"lib/libvndk27binder32.so",
"etc/*",
})
@@ -3411,7 +3442,7 @@
}))
// java_sdk_library installs both impl jar and permission XML
- ensureExactContents(t, ctx, "myapex", []string{
+ ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{
"javalib/foo.jar",
"etc/permissions/foo.xml",
})
@@ -3481,6 +3512,106 @@
ensureContains(t, androidMk, "LOCAL_TARGET_REQUIRED_MODULES += e f\n")
}
+func TestSymlinksFromApexToSystem(t *testing.T) {
+ bp := `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ native_shared_libs: ["mylib"],
+ java_libs: ["myjar"],
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ cc_library {
+ name: "mylib",
+ srcs: ["mylib.cpp"],
+ shared_libs: ["myotherlib"],
+ system_shared_libs: [],
+ stl: "none",
+ apex_available: [
+ "myapex",
+ "//apex_available:platform",
+ ],
+ }
+
+ cc_library {
+ name: "myotherlib",
+ srcs: ["mylib.cpp"],
+ system_shared_libs: [],
+ stl: "none",
+ apex_available: [
+ "myapex",
+ "//apex_available:platform",
+ ],
+ }
+
+ java_library {
+ name: "myjar",
+ srcs: ["foo/bar/MyClass.java"],
+ sdk_version: "none",
+ system_modules: "none",
+ libs: ["myotherjar"],
+ compile_dex: true,
+ apex_available: [
+ "myapex",
+ "//apex_available:platform",
+ ],
+ }
+
+ java_library {
+ name: "myotherjar",
+ srcs: ["foo/bar/MyClass.java"],
+ sdk_version: "none",
+ system_modules: "none",
+ apex_available: [
+ "myapex",
+ "//apex_available:platform",
+ ],
+ }
+ `
+
+ ensureRealfileExists := func(t *testing.T, files []fileInApex, file string) {
+ for _, f := range files {
+ if f.path == file {
+ if f.isLink {
+ t.Errorf("%q is not a real file", file)
+ }
+ return
+ }
+ }
+ t.Errorf("%q is not found", file)
+ }
+
+ ensureSymlinkExists := func(t *testing.T, files []fileInApex, file string) {
+ for _, f := range files {
+ if f.path == file {
+ if !f.isLink {
+ t.Errorf("%q is not a symlink", file)
+ }
+ return
+ }
+ }
+ t.Errorf("%q is not found", file)
+ }
+
+ ctx, _ := testApex(t, bp, withUnbundledBuild)
+ files := getFiles(t, ctx, "myapex", "android_common_myapex_image")
+ ensureRealfileExists(t, files, "javalib/myjar.jar")
+ ensureRealfileExists(t, files, "lib64/mylib.so")
+ ensureRealfileExists(t, files, "lib64/myotherlib.so")
+
+ ctx, _ = testApex(t, bp)
+ files = getFiles(t, ctx, "myapex", "android_common_myapex_image")
+ ensureRealfileExists(t, files, "javalib/myjar.jar")
+ ensureRealfileExists(t, files, "lib64/mylib.so")
+ ensureSymlinkExists(t, files, "lib64/myotherlib.so") // this is symlink
+}
+
func TestMain(m *testing.M) {
run := func() int {
setUp()
diff --git a/apex/builder.go b/apex/builder.go
index 290c1ea..8ae2b5c 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -258,34 +258,40 @@
apexType := a.properties.ApexType
suffix := apexType.suffix()
+ var implicitInputs []android.Path
unsignedOutputFile := android.PathForModuleOut(ctx, a.Name()+suffix+".unsigned")
- filesToCopy := []android.Path{}
- for _, f := range a.filesInfo {
- filesToCopy = append(filesToCopy, f.builtFile)
+ // 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()
+ copyCommands = append(copyCommands, "mkdir -p "+filepath.Dir(destPath))
+ 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())
+ copyCommands = append(copyCommands, "ln -sfn "+pathOnDevice+" "+destPath)
+ } else {
+ copyCommands = append(copyCommands, "cp -f "+fi.builtFile.String()+" "+destPath)
+ implicitInputs = append(implicitInputs, fi.builtFile)
+ }
+ // create additional symlinks pointing the file inside the APEX
+ for _, symlinkPath := range fi.SymlinkPaths() {
+ symlinkDest := android.PathForModuleOut(ctx, "image"+suffix, symlinkPath).String()
+ copyCommands = append(copyCommands, "ln -sfn "+filepath.Base(destPath)+" "+symlinkDest)
+ }
}
- copyCommands := []string{}
- emitCommands := []string{}
- imageContentFile := android.PathForModuleOut(ctx, a.Name()+"-content.txt")
+ // TODO(jiyong): use RuleBuilder
+ var emitCommands []string
+ imageContentFile := android.PathForModuleOut(ctx, "content.txt")
emitCommands = append(emitCommands, "echo ./apex_manifest.pb >> "+imageContentFile.String())
if proptools.Bool(a.properties.Legacy_android10_support) {
emitCommands = append(emitCommands, "echo ./apex_manifest.json >> "+imageContentFile.String())
}
- for i, src := range filesToCopy {
- dest := filepath.Join(a.filesInfo[i].installDir, src.Base())
- emitCommands = append(emitCommands, "echo './"+dest+"' >> "+imageContentFile.String())
- dest_path := filepath.Join(android.PathForModuleOut(ctx, "image"+suffix).String(), dest)
- copyCommands = append(copyCommands, "mkdir -p "+filepath.Dir(dest_path))
- copyCommands = append(copyCommands, "cp "+src.String()+" "+dest_path)
- for _, sym := range a.filesInfo[i].symlinks {
- symlinkDest := filepath.Join(filepath.Dir(dest_path), sym)
- copyCommands = append(copyCommands, "ln -s "+filepath.Base(dest)+" "+symlinkDest)
- }
+ for _, fi := range a.filesInfo {
+ emitCommands = append(emitCommands, "echo './"+fi.Path()+"' >> "+imageContentFile.String())
}
emitCommands = append(emitCommands, "sort -o "+imageContentFile.String()+" "+imageContentFile.String())
-
- implicitInputs := append(android.Paths(nil), filesToCopy...)
implicitInputs = append(implicitInputs, a.manifestPbOut)
if a.properties.Whitelisted_files != nil {
@@ -532,7 +538,7 @@
if a.installable() {
// For flattened APEX, do nothing but make sure that APEX manifest and apex_pubkey are also copied along
// with other ordinary files.
- a.filesInfo = append(a.filesInfo, newApexFile(ctx, a.manifestPbOut, "apex_manifest.pb."+a.Name()+a.suffix, ".", etc, nil))
+ a.filesInfo = append(a.filesInfo, newApexFile(ctx, a.manifestPbOut, "apex_manifest.pb", ".", etc, nil))
// rename to apex_pubkey
copiedPubkey := android.PathForModuleOut(ctx, "apex_pubkey")
@@ -541,7 +547,7 @@
Input: a.public_key_file,
Output: copiedPubkey,
})
- a.filesInfo = append(a.filesInfo, newApexFile(ctx, copiedPubkey, "apex_pubkey."+a.Name()+a.suffix, ".", etc, nil))
+ a.filesInfo = append(a.filesInfo, newApexFile(ctx, copiedPubkey, "apex_pubkey", ".", etc, nil))
if a.properties.ApexType == flattenedApex {
apexName := proptools.StringDefault(a.properties.Apex_name, a.Name())
diff --git a/apex/vndk_test.go b/apex/vndk_test.go
new file mode 100644
index 0000000..391072e
--- /dev/null
+++ b/apex/vndk_test.go
@@ -0,0 +1,94 @@
+package apex
+
+import (
+ "testing"
+
+ "github.com/google/blueprint/proptools"
+
+ "android/soong/android"
+)
+
+func TestVndkApexUsesVendorVariant(t *testing.T) {
+ bp := `
+ apex_vndk {
+ name: "myapex",
+ key: "mykey",
+ }
+ apex_key {
+ name: "mykey",
+ }
+ cc_library {
+ name: "libfoo",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
+ system_shared_libs: [],
+ stl: "none",
+ notice: "custom_notice",
+ }
+ ` + vndkLibrariesTxtFiles("current")
+
+ ensureFileSrc := func(t *testing.T, files []fileInApex, path, src string) {
+ t.Helper()
+ for _, f := range files {
+ if f.path == path {
+ ensureContains(t, f.src, src)
+ return
+ }
+ }
+ t.Fail()
+ }
+
+ t.Run("VNDK lib doesn't have an apex variant", func(t *testing.T) {
+ ctx, _ := testApex(t, bp)
+
+ // libfoo doesn't have apex variants
+ for _, variant := range ctx.ModuleVariantsForTests("libfoo") {
+ ensureNotContains(t, variant, "_myapex")
+ }
+
+ // VNDK APEX doesn't create apex variant
+ files := getFiles(t, ctx, "myapex", "android_common_image")
+ ensureFileSrc(t, files, "lib/libfoo.so", "libfoo/android_vendor.VER_arm_armv7-a-neon_shared/libfoo.so")
+ })
+
+ t.Run("VNDK APEX gathers only vendor variants even if product variants are available", func(t *testing.T) {
+ ctx, _ := testApex(t, bp, func(fs map[string][]byte, config android.Config) {
+ // Now product variant is available
+ config.TestProductVariables.ProductVndkVersion = proptools.StringPtr("current")
+ })
+
+ files := getFiles(t, ctx, "myapex", "android_common_image")
+ ensureFileSrc(t, files, "lib/libfoo.so", "libfoo/android_vendor.VER_arm_armv7-a-neon_shared/libfoo.so")
+ })
+
+ t.Run("VNDK APEX supports coverage variants", func(t *testing.T) {
+ ctx, _ := testApex(t, bp+`
+ cc_library {
+ name: "libprofile-extras",
+ vendor_available: true,
+ native_coverage: false,
+ system_shared_libs: [],
+ stl: "none",
+ notice: "custom_notice",
+ }
+ cc_library {
+ name: "libprofile-clang-extras",
+ vendor_available: true,
+ native_coverage: false,
+ system_shared_libs: [],
+ stl: "none",
+ notice: "custom_notice",
+ }
+ `, func(fs map[string][]byte, config android.Config) {
+ config.TestProductVariables.NativeCoverage = proptools.BoolPtr(true)
+ })
+
+ files := getFiles(t, ctx, "myapex", "android_common_image")
+ ensureFileSrc(t, files, "lib/libfoo.so", "libfoo/android_vendor.VER_arm_armv7-a-neon_shared/libfoo.so")
+
+ files = getFiles(t, ctx, "myapex", "android_common_cov_image")
+ ensureFileSrc(t, files, "lib/libfoo.so", "libfoo/android_vendor.VER_arm_armv7-a-neon_shared_cov/libfoo.so")
+ })
+}
diff --git a/build_kzip.bash b/build_kzip.bash
index 329825a..008030f 100755
--- a/build_kzip.bash
+++ b/build_kzip.bash
@@ -14,6 +14,7 @@
: ${BUILD_NUMBER:=$(uuidgen)}
: ${KYTHE_KZIP_ENCODING:=proto}
+export KYTHE_KZIP_ENCODING
# The extraction might fail for some source files, so run with -k and then check that
# sufficiently many files were generated.
diff --git a/cc/androidmk.go b/cc/androidmk.go
index c9cd01c..137cb63 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -27,6 +27,7 @@
nativeBridgeSuffix = ".native_bridge"
productSuffix = ".product"
vendorSuffix = ".vendor"
+ ramdiskSuffix = ".ramdisk"
recoverySuffix = ".recovery"
)
@@ -40,6 +41,7 @@
UseVndk() bool
VndkVersion() string
static() bool
+ InRamdisk() bool
InRecovery() bool
}
@@ -233,7 +235,7 @@
})
}
if len(library.Properties.Stubs.Versions) > 0 &&
- android.DirectlyInAnyApex(ctx, ctx.Name()) && !ctx.InRecovery() && !ctx.UseVndk() &&
+ android.DirectlyInAnyApex(ctx, ctx.Name()) && !ctx.InRamdisk() && !ctx.InRecovery() && !ctx.UseVndk() &&
!ctx.static() {
if !library.buildStubs() {
ret.SubName = ".bootstrap"
diff --git a/cc/binary.go b/cc/binary.go
index ba6ed5f..280d17b 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -264,7 +264,7 @@
} else {
switch ctx.Os() {
case android.Android:
- if ctx.bootstrap() && !ctx.inRecovery() {
+ if ctx.bootstrap() && !ctx.inRecovery() && !ctx.inRamdisk() {
flags.DynamicLinker = "/system/bin/bootstrap/linker"
} else {
flags.DynamicLinker = "/system/bin/linker"
@@ -458,7 +458,7 @@
// The original path becomes a symlink to the corresponding file in the
// runtime APEX.
translatedArch := ctx.Target().NativeBridge == android.NativeBridgeEnabled
- if InstallToBootstrap(ctx.baseModuleName(), ctx.Config()) && !translatedArch && ctx.apexName() == "" && !ctx.inRecovery() {
+ if InstallToBootstrap(ctx.baseModuleName(), ctx.Config()) && !translatedArch && ctx.apexName() == "" && !ctx.inRamdisk() && !ctx.inRecovery() {
if ctx.Device() && isBionic(ctx.baseModuleName()) {
binary.installSymlinkToRuntimeApex(ctx, file)
}
diff --git a/cc/cc.go b/cc/cc.go
index 022e350..ce3a2ed 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -223,11 +223,15 @@
// file
Logtags []string
+ // Make this module available when building for ramdisk
+ Ramdisk_available *bool
+
// Make this module available when building for recovery
Recovery_available *bool
// Set by imageMutator
CoreVariantNeeded bool `blueprint:"mutated"`
+ RamdiskVariantNeeded bool `blueprint:"mutated"`
RecoveryVariantNeeded bool `blueprint:"mutated"`
ExtraVariants []string `blueprint:"mutated"`
@@ -290,6 +294,7 @@
isVndkExt() bool
inProduct() bool
inVendor() bool
+ inRamdisk() bool
inRecovery() bool
shouldCreateSourceAbiDump() bool
selectedStl() string
@@ -878,10 +883,18 @@
return c.Properties.ImageVariationPrefix == VendorVariationPrefix
}
+func (c *Module) InRamdisk() bool {
+ return c.ModuleBase.InRamdisk() || c.ModuleBase.InstallInRamdisk()
+}
+
func (c *Module) InRecovery() bool {
return c.ModuleBase.InRecovery() || c.ModuleBase.InstallInRecovery()
}
+func (c *Module) OnlyInRamdisk() bool {
+ return c.ModuleBase.InstallInRamdisk()
+}
+
func (c *Module) OnlyInRecovery() bool {
return c.ModuleBase.InstallInRecovery()
}
@@ -1018,7 +1031,7 @@
}
func (ctx *moduleContextImpl) useSdk() bool {
- if ctx.ctx.Device() && !ctx.useVndk() && !ctx.inRecovery() && !ctx.ctx.Fuchsia() {
+ if ctx.ctx.Device() && !ctx.useVndk() && !ctx.inRamdisk() && !ctx.inRecovery() && !ctx.ctx.Fuchsia() {
return String(ctx.mod.Properties.Sdk_version) != ""
}
return false
@@ -1090,6 +1103,10 @@
return ctx.mod.inVendor()
}
+func (ctx *moduleContextImpl) inRamdisk() bool {
+ return ctx.mod.InRamdisk()
+}
+
func (ctx *moduleContextImpl) inRecovery() bool {
return ctx.mod.InRecovery()
}
@@ -1335,6 +1352,8 @@
// .vendor suffix is added for backward compatibility with VNDK snapshot whose names with
// such suffixes are already hard-coded in prebuilts/vndk/.../Android.bp.
c.Properties.SubName += vendorSuffix
+ } else if c.InRamdisk() && !c.OnlyInRamdisk() {
+ c.Properties.SubName += ramdiskSuffix
} else if c.InRecovery() && !c.OnlyInRecovery() {
c.Properties.SubName += recoverySuffix
}
@@ -1444,7 +1463,7 @@
// (unless it is explicitly referenced via .bootstrap suffix or the
// module is marked with 'bootstrap: true').
if c.HasStubsVariants() &&
- android.DirectlyInAnyApex(ctx, ctx.baseModuleName()) &&
+ android.DirectlyInAnyApex(ctx, ctx.baseModuleName()) && !c.InRamdisk() &&
!c.InRecovery() && !c.UseVndk() && !c.static() && !c.isCoverageVariant() &&
c.IsStubs() {
c.Properties.HideFromMake = false // unhide
@@ -1732,7 +1751,7 @@
addSharedLibDependencies := func(depTag DependencyTag, name string, version string) {
var variations []blueprint.Variation
variations = append(variations, blueprint.Variation{Mutator: "link", Variation: "shared"})
- versionVariantAvail := !ctx.useVndk() && !c.InRecovery()
+ versionVariantAvail := !ctx.useVndk() && !c.InRecovery() && !c.InRamdisk()
if version != "" && versionVariantAvail {
// Version is explicitly specified. i.e. libFoo#30
variations = append(variations, blueprint.Variation{Mutator: "version", Variation: version})
@@ -1863,6 +1882,10 @@
// Platform code can link to anything
return
}
+ if from.InRamdisk() {
+ // Ramdisk code is not NDK
+ return
+ }
if from.InRecovery() {
// Recovery code is not NDK
return
@@ -2123,8 +2146,8 @@
// If not building for APEX, use stubs only when it is from
// an APEX (and not from platform)
useThisDep = (depInPlatform != depIsStubs)
- if c.InRecovery() || c.bootstrap() {
- // However, for recovery or bootstrap modules,
+ if c.InRamdisk() || c.InRecovery() || c.bootstrap() {
+ // However, for ramdisk, recovery or bootstrap modules,
// always link to non-stub variant
useThisDep = !depIsStubs
}
@@ -2275,7 +2298,7 @@
isVendorPublicLib := inList(libName, *vendorPublicLibraries)
bothVendorAndCoreVariantsExist := ccDep.HasVendorVariant() || isLLndk
- if ctx.DeviceConfig().VndkUseCoreVariant() && ccDep.IsVndk() && !ccDep.MustUseVendorVariant() && !c.InRecovery() {
+ if ctx.DeviceConfig().VndkUseCoreVariant() && ccDep.IsVndk() && !ccDep.MustUseVendorVariant() && !c.InRamdisk() && !c.InRecovery() {
// The vendor module is a no-vendor-variant VNDK library. Depend on the
// core module instead.
return libName
@@ -2285,6 +2308,8 @@
return libName + c.getNameSuffixWithVndkVersion(ctx)
} else if (ctx.Platform() || ctx.ProductSpecific()) && isVendorPublicLib {
return libName + vendorPublicLibrarySuffix
+ } else if ccDep.InRamdisk() && !ccDep.OnlyInRamdisk() {
+ return libName + ramdiskSuffix
} else if ccDep.InRecovery() && !ccDep.OnlyInRecovery() {
return libName + recoverySuffix
} else if ccDep.Module().Target().NativeBridge == android.NativeBridgeEnabled {
@@ -2369,6 +2394,10 @@
return c.installer.inSanitizerDir()
}
+func (c *Module) InstallInRamdisk() bool {
+ return c.InRamdisk()
+}
+
func (c *Module) InstallInRecovery() bool {
return c.InRecovery()
}
@@ -2441,6 +2470,8 @@
return "native:product"
}
return "native:vendor"
+ } else if c.InRamdisk() {
+ return "native:ramdisk"
} else if c.InRecovery() {
return "native:recovery"
} else if c.Target().Os == android.Android && String(c.Properties.Sdk_version) != "" {
@@ -2647,6 +2678,7 @@
}
var coreVariantNeeded bool = false
+ var ramdiskVariantNeeded bool = false
var recoveryVariantNeeded bool = false
var vendorVariants []string
@@ -2729,6 +2761,15 @@
productVariants = []string{}
}
+ if Bool(m.Properties.Ramdisk_available) {
+ ramdiskVariantNeeded = true
+ }
+
+ if m.ModuleBase.InstallInRamdisk() {
+ ramdiskVariantNeeded = true
+ coreVariantNeeded = false
+ }
+
if Bool(m.Properties.Recovery_available) {
recoveryVariantNeeded = true
}
@@ -2746,6 +2787,7 @@
m.Properties.ExtraVariants = append(m.Properties.ExtraVariants, ProductVariationPrefix+variant)
}
+ m.Properties.RamdiskVariantNeeded = ramdiskVariantNeeded
m.Properties.RecoveryVariantNeeded = recoveryVariantNeeded
m.Properties.CoreVariantNeeded = coreVariantNeeded
}
@@ -2754,6 +2796,10 @@
return c.Properties.CoreVariantNeeded
}
+func (c *Module) RamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+ return c.Properties.RamdiskVariantNeeded
+}
+
func (c *Module) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool {
return c.Properties.RecoveryVariantNeeded
}
@@ -2764,7 +2810,9 @@
func (c *Module) SetImageVariation(ctx android.BaseModuleContext, variant string, module android.Module) {
m := module.(*Module)
- if variant == android.RecoveryVariation {
+ if variant == android.RamdiskVariation {
+ m.MakeAsPlatform()
+ } else if variant == android.RecoveryVariation {
m.MakeAsPlatform()
squashRecoverySrcs(m)
} else if strings.HasPrefix(variant, VendorVariationPrefix) {
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 4d02f4f..332cc45 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -2303,13 +2303,13 @@
// Check the shared version of lib2.
variant := "android_arm64_armv8-a_shared"
module := ctx.ModuleForTests("lib2", variant).Module().(*Module)
- checkStaticLibs(t, []string{"lib1", "libc++demangle", "libclang_rt.builtins-aarch64-android", "libatomic", "libgcc_stripped"}, module)
+ checkStaticLibs(t, []string{"lib1", "libc++demangle", "libclang_rt.builtins-aarch64-android", "libatomic"}, module)
// Check the static version of lib2.
variant = "android_arm64_armv8-a_static"
module = ctx.ModuleForTests("lib2", variant).Module().(*Module)
// libc++_static is linked additionally.
- checkStaticLibs(t, []string{"lib1", "libc++_static", "libc++demangle", "libclang_rt.builtins-aarch64-android", "libatomic", "libgcc_stripped"}, module)
+ checkStaticLibs(t, []string{"lib1", "libc++_static", "libc++demangle", "libclang_rt.builtins-aarch64-android", "libatomic"}, module)
}
var compilerFlagsTestCases = []struct {
diff --git a/cc/config/global.go b/cc/config/global.go
index bae5555..87314dc 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -88,6 +88,7 @@
"-Wl,--no-undefined-version",
"-Wl,--exclude-libs,libgcc.a",
"-Wl,--exclude-libs,libgcc_stripped.a",
+ "-Wl,--exclude-libs,libunwind_llvm.a",
}
deviceGlobalLldflags = append(ClangFilterUnknownLldflags(deviceGlobalLdflags),
diff --git a/cc/config/vndk.go b/cc/config/vndk.go
index 3b16c78..52bd9f0 100644
--- a/cc/config/vndk.go
+++ b/cc/config/vndk.go
@@ -18,56 +18,20 @@
// For these libraries, the vendor variants must be installed even if the device
// has VndkUseCoreVariant set.
var VndkMustUseVendorVariantList = []string{
- "android.frameworks.sensorservice@1.0",
- "android.hardware.atrace@1.0",
- "android.hardware.audio.common@5.0",
- "android.hardware.audio.effect@2.0",
- "android.hardware.audio.effect@4.0",
- "android.hardware.audio.effect@5.0",
- "android.hardware.audio@2.0",
- "android.hardware.audio@4.0",
- "android.hardware.audio@5.0",
"android.hardware.automotive.evs@1.0",
"android.hardware.automotive.vehicle@2.0",
- "android.hardware.bluetooth.audio@2.0",
- "android.hardware.boot@1.0",
- "android.hardware.broadcastradio@1.0",
- "android.hardware.broadcastradio@1.1",
"android.hardware.broadcastradio@2.0",
"android.hardware.camera.device@1.0",
"android.hardware.camera.device@3.2",
"android.hardware.camera.device@3.3",
"android.hardware.camera.device@3.4",
"android.hardware.camera.provider@2.4",
- "android.hardware.cas.native@1.0",
- "android.hardware.cas@1.0",
- "android.hardware.configstore@1.0",
- "android.hardware.configstore@1.1",
- "android.hardware.contexthub@1.0",
- "android.hardware.drm@1.0",
- "android.hardware.drm@1.1",
"android.hardware.fastboot@1.0",
- "android.hardware.gatekeeper@1.0",
- "android.hardware.gnss@1.0",
- "android.hardware.graphics.allocator@2.0",
- "android.hardware.graphics.bufferqueue@1.0",
- "android.hardware.graphics.composer@2.1",
- "android.hardware.graphics.composer@2.2",
- "android.hardware.health@1.0",
- "android.hardware.health@2.0",
- "android.hardware.ir@1.0",
- "android.hardware.keymaster@3.0",
- "android.hardware.keymaster@4.0",
- "android.hardware.light@2.0",
"android.hardware.media.bufferpool@1.0",
- "android.hardware.media.omx@1.0",
- "android.hardware.memtrack@1.0",
"android.hardware.neuralnetworks@1.0",
"android.hardware.neuralnetworks@1.1",
"android.hardware.neuralnetworks@1.2",
"android.hardware.neuralnetworks@1.3",
- "android.hardware.nfc@1.0",
- "android.hardware.nfc@1.1",
"android.hardware.nfc@1.2",
"android.hardware.oemlock@1.0",
"android.hardware.power.stats@1.0",
@@ -82,51 +46,31 @@
"android.hardware.soundtrigger@2.1",
"android.hardware.tetheroffload.config@1.0",
"android.hardware.tetheroffload.control@1.0",
- "android.hardware.thermal@1.0",
- "android.hardware.tv.cec@1.0",
- "android.hardware.tv.input@1.0",
"android.hardware.vibrator-ndk_platform",
- "android.hardware.vibrator@1.0",
- "android.hardware.vibrator@1.1",
- "android.hardware.vibrator@1.2",
"android.hardware.weaver@1.0",
"android.hardware.wifi.hostapd@1.0",
"android.hardware.wifi.offload@1.0",
"android.hardware.wifi.supplicant@1.0",
"android.hardware.wifi.supplicant@1.1",
- "android.hardware.wifi@1.0",
"android.hardware.wifi@1.1",
"android.hardware.wifi@1.2",
"android.hardwareundtrigger@2.0",
"android.hardwareundtrigger@2.0-core",
"android.hardwareundtrigger@2.1",
- "android.hidl.allocator@1.0",
- "android.hidl.token@1.0",
- "android.hidl.token@1.0-utils",
- "android.system.net.netd@1.0",
- "android.system.wifi.keystore@1.0",
"libaudioroute",
- "libaudioutils",
"libbinder",
"libcamera_metadata",
"libcrypto",
- "libdiskconfig",
- "libdumpstateutil",
"libexpat",
- "libfmq",
"libgatekeeper",
"libgui",
"libhidlcache",
"libkeymaster_messages",
"libkeymaster_portable",
- "libmedia_helper",
"libmedia_omx",
- "libmemtrack",
- "libnetutils",
"libprotobuf-cpp-full",
"libprotobuf-cpp-lite",
"libpuresoftkeymasterdevice",
- "libradio_metadata",
"libselinux",
"libsoftkeymasterdevice",
"libsqlite",
@@ -161,11 +105,8 @@
"libstagefright_soft_vpxdec",
"libstagefright_soft_vpxenc",
"libstagefright_xmlparser",
- "libsysutils",
- "libtinyxml2",
"libui",
"libvorbisidec",
"libxml2",
"libyuv",
- "libziparchive",
}
diff --git a/cc/coverage.go b/cc/coverage.go
index b6451ee..b94b628 100644
--- a/cc/coverage.go
+++ b/cc/coverage.go
@@ -43,7 +43,7 @@
return []interface{}{&cov.Properties}
}
-func getProfileLibraryName(ctx ModuleContextIntf) string {
+func getGcovProfileLibraryName(ctx ModuleContextIntf) string {
// This function should only ever be called for a cc.Module, so the
// following statement should always succeed.
if ctx.useSdk() {
@@ -53,28 +53,47 @@
}
}
+func getClangProfileLibraryName(ctx ModuleContextIntf) string {
+ if ctx.useSdk() {
+ return "libprofile-clang-extras_ndk"
+ } else {
+ return "libprofile-clang-extras"
+ }
+}
+
func (cov *coverage) deps(ctx DepsContext, deps Deps) Deps {
if cov.Properties.NeedCoverageVariant {
ctx.AddVariationDependencies([]blueprint.Variation{
{Mutator: "link", Variation: "static"},
- }, coverageDepTag, getProfileLibraryName(ctx))
+ }, coverageDepTag, getGcovProfileLibraryName(ctx))
+ ctx.AddVariationDependencies([]blueprint.Variation{
+ {Mutator: "link", Variation: "static"},
+ }, coverageDepTag, getClangProfileLibraryName(ctx))
}
return deps
}
func (cov *coverage) flags(ctx ModuleContext, flags Flags, deps PathDeps) (Flags, PathDeps) {
- if !ctx.DeviceConfig().NativeCoverageEnabled() {
+ gcovCoverage := ctx.DeviceConfig().NativeCoverageEnabled()
+ clangCoverage := ctx.DeviceConfig().ClangCoverageEnabled()
+
+ if !gcovCoverage && !clangCoverage {
return flags, deps
}
if cov.Properties.CoverageEnabled {
flags.Coverage = true
- flags.Local.CommonFlags = append(flags.Local.CommonFlags, "--coverage", "-O0")
cov.linkCoverage = true
- // Override -Wframe-larger-than and non-default optimization
- // flags that the module may use.
- flags.Local.CFlags = append(flags.Local.CFlags, "-Wno-frame-larger-than=", "-O0")
+ if gcovCoverage {
+ flags.Local.CommonFlags = append(flags.Local.CommonFlags, "--coverage", "-O0")
+
+ // Override -Wframe-larger-than and non-default optimization
+ // flags that the module may use.
+ flags.Local.CFlags = append(flags.Local.CFlags, "-Wno-frame-larger-than=", "-O0")
+ } else if clangCoverage {
+ flags.Local.CommonFlags = append(flags.Local.CommonFlags, "-fprofile-instr-generate", "-fcoverage-mapping")
+ }
}
// Even if we don't have coverage enabled, if any of our object files were compiled
@@ -112,12 +131,19 @@
}
if cov.linkCoverage {
- flags.Local.LdFlags = append(flags.Local.LdFlags, "--coverage")
+ if gcovCoverage {
+ flags.Local.LdFlags = append(flags.Local.LdFlags, "--coverage")
- coverage := ctx.GetDirectDepWithTag(getProfileLibraryName(ctx), coverageDepTag).(*Module)
- deps.WholeStaticLibs = append(deps.WholeStaticLibs, coverage.OutputFile().Path())
+ coverage := ctx.GetDirectDepWithTag(getGcovProfileLibraryName(ctx), coverageDepTag).(*Module)
+ deps.WholeStaticLibs = append(deps.WholeStaticLibs, coverage.OutputFile().Path())
- flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--wrap,getenv")
+ flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--wrap,getenv")
+ } else if clangCoverage {
+ flags.Local.LdFlags = append(flags.Local.LdFlags, "-fprofile-instr-generate")
+
+ coverage := ctx.GetDirectDepWithTag(getClangProfileLibraryName(ctx), coverageDepTag).(*Module)
+ deps.WholeStaticLibs = append(deps.WholeStaticLibs, coverage.OutputFile().Path())
+ }
}
return flags, deps
@@ -125,7 +151,7 @@
func (cov *coverage) begin(ctx BaseModuleContext) {
// Coverage is disabled globally
- if !ctx.DeviceConfig().NativeCoverageEnabled() {
+ if !ctx.DeviceConfig().NativeCoverageEnabled() && !ctx.DeviceConfig().ClangCoverageEnabled() {
return
}
diff --git a/cc/fuzz.go b/cc/fuzz.go
index 8b84be8..ee24300 100644
--- a/cc/fuzz.go
+++ b/cc/fuzz.go
@@ -355,10 +355,10 @@
return
}
- // Discard vendor-NDK-linked + recovery modules, they're duplicates of
+ // Discard vendor-NDK-linked + ramdisk + recovery modules, they're duplicates of
// fuzz targets we're going to package anyway.
if !ccModule.Enabled() || ccModule.Properties.PreventInstall ||
- ccModule.UseVndk() || ccModule.InRecovery() {
+ ccModule.UseVndk() || ccModule.InRamdisk() || ccModule.InRecovery() {
return
}
diff --git a/cc/genrule.go b/cc/genrule.go
index 548d5f2..155e410 100644
--- a/cc/genrule.go
+++ b/cc/genrule.go
@@ -25,6 +25,7 @@
type GenruleExtraProperties struct {
Vendor_available *bool
+ Ramdisk_available *bool
Recovery_available *bool
}
@@ -62,6 +63,10 @@
return Bool(g.Vendor_available) || !(ctx.SocSpecific() || ctx.DeviceSpecific())
}
+func (g *GenruleExtraProperties) RamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+ return Bool(g.Ramdisk_available)
+}
+
func (g *GenruleExtraProperties) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool {
return Bool(g.Recovery_available)
}
diff --git a/cc/library.go b/cc/library.go
index f29c4d0..99a3e16 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -757,6 +757,13 @@
deps.ReexportSharedLibHeaders = removeListFromList(deps.ReexportSharedLibHeaders, library.baseLinker.Properties.Target.Recovery.Exclude_shared_libs)
deps.ReexportStaticLibHeaders = removeListFromList(deps.ReexportStaticLibHeaders, library.baseLinker.Properties.Target.Recovery.Exclude_static_libs)
}
+ if ctx.inRamdisk() {
+ deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, library.baseLinker.Properties.Target.Ramdisk.Exclude_static_libs)
+ deps.SharedLibs = removeListFromList(deps.SharedLibs, library.baseLinker.Properties.Target.Ramdisk.Exclude_shared_libs)
+ deps.StaticLibs = removeListFromList(deps.StaticLibs, library.baseLinker.Properties.Target.Ramdisk.Exclude_static_libs)
+ deps.ReexportSharedLibHeaders = removeListFromList(deps.ReexportSharedLibHeaders, library.baseLinker.Properties.Target.Ramdisk.Exclude_shared_libs)
+ deps.ReexportStaticLibHeaders = removeListFromList(deps.ReexportStaticLibHeaders, library.baseLinker.Properties.Target.Ramdisk.Exclude_static_libs)
+ }
return deps
}
@@ -1040,7 +1047,7 @@
isVendor := ctx.useVndk()
isOwnerPlatform := Bool(library.Properties.Sysprop.Platform)
- if !ctx.inRecovery() && (isProduct || (isOwnerPlatform == isVendor)) {
+ if !ctx.inRamdisk() && !ctx.inRecovery() && (isProduct || (isOwnerPlatform == isVendor)) {
dir = android.PathForModuleGen(ctx, "sysprop/public", "include")
}
}
@@ -1118,7 +1125,7 @@
// The original path becomes a symlink to the corresponding file in the
// runtime APEX.
translatedArch := ctx.Target().NativeBridge == android.NativeBridgeEnabled
- if InstallToBootstrap(ctx.baseModuleName(), ctx.Config()) && !library.buildStubs() && !translatedArch && !ctx.inRecovery() {
+ if InstallToBootstrap(ctx.baseModuleName(), ctx.Config()) && !library.buildStubs() && !translatedArch && !ctx.inRamdisk() && !ctx.inRecovery() {
if ctx.Device() {
library.installSymlinkToRuntimeApex(ctx, file)
}
@@ -1133,7 +1140,7 @@
}
if Bool(library.Properties.Static_ndk_lib) && library.static() &&
- !ctx.useVndk() && !ctx.inRecovery() && ctx.Device() &&
+ !ctx.useVndk() && !ctx.inRamdisk() && !ctx.inRecovery() && ctx.Device() &&
library.baseLinker.sanitize.isUnsanitizedVariant() &&
!library.buildStubs() {
installPath := getNdkSysrootBase(ctx).Join(
diff --git a/cc/linkable.go b/cc/linkable.go
index 106092b..3c46d9d 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -38,6 +38,9 @@
Shared() bool
Toc() android.OptionalPath
+ InRamdisk() bool
+ OnlyInRamdisk() bool
+
InRecovery() bool
OnlyInRecovery() bool
diff --git a/cc/linker.go b/cc/linker.go
index 61ae757..c2b4a3a 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -141,6 +141,19 @@
// of the C/C++ module.
Exclude_header_libs []string
}
+ Ramdisk struct {
+ // list of static libs that only should be used to build the recovery
+ // variant of the C/C++ module.
+ Static_libs []string
+
+ // list of shared libs that should not be used to build
+ // the ramdisk variant of the C/C++ module.
+ Exclude_shared_libs []string
+
+ // list of static libs that should not be used to build
+ // the ramdisk variant of the C/C++ module.
+ Exclude_static_libs []string
+ }
}
// make android::build:GetBuildNumber() available containing the build ID.
@@ -223,12 +236,20 @@
deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, linker.Properties.Target.Recovery.Exclude_static_libs)
}
+ if ctx.inRamdisk() {
+ deps.SharedLibs = removeListFromList(deps.SharedLibs, linker.Properties.Target.Recovery.Exclude_shared_libs)
+ deps.ReexportSharedLibHeaders = removeListFromList(deps.ReexportSharedLibHeaders, linker.Properties.Target.Recovery.Exclude_shared_libs)
+ deps.StaticLibs = append(deps.StaticLibs, linker.Properties.Target.Recovery.Static_libs...)
+ deps.StaticLibs = removeListFromList(deps.StaticLibs, linker.Properties.Target.Recovery.Exclude_static_libs)
+ deps.ReexportStaticLibHeaders = removeListFromList(deps.ReexportStaticLibHeaders, linker.Properties.Target.Recovery.Exclude_static_libs)
+ deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, linker.Properties.Target.Recovery.Exclude_static_libs)
+ }
+
if ctx.toolchain().Bionic() {
- // libclang_rt.builtins, libgcc and libatomic have to be last on the command line
+ // libclang_rt.builtins and libatomic have to be last on the command line
if !Bool(linker.Properties.No_libcrt) {
deps.LateStaticLibs = append(deps.LateStaticLibs, config.BuiltinsRuntimeLibrary(ctx.toolchain()))
deps.LateStaticLibs = append(deps.LateStaticLibs, "libatomic")
- deps.LateStaticLibs = append(deps.LateStaticLibs, "libgcc_stripped")
}
systemSharedLibs := linker.Properties.System_shared_libs
diff --git a/cc/sanitize.go b/cc/sanitize.go
index c4aeb96..93c4b41 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -351,8 +351,8 @@
}
// HWASan ramdisk (which is built from recovery) goes over some bootloader limit.
- // Keep libc instrumented so that recovery can run hwasan-instrumented code if necessary.
- if ctx.inRecovery() && !strings.HasPrefix(ctx.ModuleDir(), "bionic/libc") {
+ // Keep libc instrumented so that ramdisk / recovery can run hwasan-instrumented code if necessary.
+ if (ctx.inRamdisk() || ctx.inRecovery()) && !strings.HasPrefix(ctx.ModuleDir(), "bionic/libc") {
s.Hwaddress = nil
}
diff --git a/cc/stl.go b/cc/stl.go
index 5ccd44a..af015f9 100644
--- a/cc/stl.go
+++ b/cc/stl.go
@@ -171,11 +171,13 @@
deps.StaticLibs = append(deps.StaticLibs, "libc++demangle")
}
if ctx.toolchain().Bionic() {
- if ctx.Arch().ArchType == android.Arm {
- deps.StaticLibs = append(deps.StaticLibs, "libunwind_llvm")
- }
if ctx.staticBinary() {
deps.StaticLibs = append(deps.StaticLibs, "libm", "libc")
+ if ctx.Arch().ArchType == android.Arm {
+ deps.StaticLibs = append(deps.StaticLibs, "libunwind_llvm")
+ } else {
+ deps.StaticLibs = append(deps.StaticLibs, "libgcc_stripped")
+ }
}
}
case "":
@@ -196,6 +198,8 @@
}
if ctx.Arch().ArchType == android.Arm {
deps.StaticLibs = append(deps.StaticLibs, "ndk_libunwind")
+ } else {
+ deps.StaticLibs = append(deps.StaticLibs, "libgcc_stripped")
}
default:
panic(fmt.Errorf("Unknown stl: %q", stl.Properties.SelectedStl))
diff --git a/cc/testing.go b/cc/testing.go
index 198a346..ba8ed95 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -136,6 +136,7 @@
name: "libc",
no_libcrt: true,
nocrt: true,
+ stl: "none",
system_shared_libs: [],
recovery_available: true,
}
@@ -147,6 +148,7 @@
name: "libm",
no_libcrt: true,
nocrt: true,
+ stl: "none",
system_shared_libs: [],
recovery_available: true,
}
@@ -158,6 +160,7 @@
name: "libdl",
no_libcrt: true,
nocrt: true,
+ stl: "none",
system_shared_libs: [],
recovery_available: true,
}
diff --git a/cc/vndk.go b/cc/vndk.go
index 872a473..ab73035 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -351,7 +351,7 @@
if lib, ok := m.linker.(libraryInterface); ok {
useCoreVariant := m.VndkVersion() == mctx.DeviceConfig().PlatformVndkVersion() &&
mctx.DeviceConfig().VndkUseCoreVariant() && !m.MustUseVendorVariant()
- return lib.shared() && m.UseVndk() && m.IsVndk() && !m.isVndkExt() && !useCoreVariant
+ return lib.shared() && m.inVendor() && m.IsVndk() && !m.isVndkExt() && !useCoreVariant
}
return false
}
@@ -670,7 +670,7 @@
if m.Target().NativeBridge == android.NativeBridgeEnabled {
return nil, "", false
}
- if !m.UseVndk() || !m.IsForPlatform() || !m.installable() || !m.inVendor() {
+ if !m.UseVndk() || !m.installable() || !m.inVendor() {
return nil, "", false
}
l, ok := m.linker.(vndkSnapshotLibraryInterface)
diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go
index 974c644..db61fba 100644
--- a/cmd/soong_ui/main.go
+++ b/cmd/soong_ui/main.go
@@ -174,6 +174,10 @@
stat.AddOutput(status.NewProtoErrorLog(log, filepath.Join(logsDir, c.logsPrefix+"build_error")))
stat.AddOutput(status.NewCriticalPath(log))
+ buildCtx.Verbosef("Detected %.3v GB total RAM", float32(config.TotalRAM())/(1024*1024*1024))
+ buildCtx.Verbosef("Parallelism (local/remote/highmem): %v/%v/%v",
+ config.Parallel(), config.RemoteParallel(), config.HighmemParallel())
+
defer met.Dump(filepath.Join(logsDir, c.logsPrefix+"soong_metrics"))
if start, ok := os.LookupEnv("TRACE_BEGIN_SOONG"); ok {
diff --git a/dexpreopt/Android.bp b/dexpreopt/Android.bp
index c5f24e2..b8f7ea6 100644
--- a/dexpreopt/Android.bp
+++ b/dexpreopt/Android.bp
@@ -4,6 +4,7 @@
srcs: [
"config.go",
"dexpreopt.go",
+ "testing.go",
],
testSrcs: [
"dexpreopt_test.go",
diff --git a/dexpreopt/config.go b/dexpreopt/config.go
index 2a929c5..72c01d0 100644
--- a/dexpreopt/config.go
+++ b/dexpreopt/config.go
@@ -16,14 +16,16 @@
import (
"encoding/json"
+ "fmt"
"strings"
+ "github.com/google/blueprint"
+
"android/soong/android"
)
// GlobalConfig stores the configuration for dex preopting. The fields are set
-// from product variables via dex_preopt_config.mk, except for SoongConfig
-// which come from CreateGlobalSoongConfig.
+// from product variables via dex_preopt_config.mk.
type GlobalConfig struct {
DisablePreopt bool // disable preopt for all modules
DisablePreoptModules []string // modules with preopt disabled by product-specific config
@@ -82,8 +84,6 @@
BootFlags string // extra flags to pass to dex2oat for the boot image
Dex2oatImageXmx string // max heap size for dex2oat for the boot image
Dex2oatImageXms string // initial heap size for dex2oat for the boot image
-
- SoongConfig GlobalSoongConfig // settings read from dexpreopt_soong.config
}
// GlobalSoongConfig contains the global config that is generated from Soong,
@@ -179,12 +179,9 @@
return constructPath(ctx, path).(android.WritablePath)
}
-// LoadGlobalConfig reads the global dexpreopt.config file into a GlobalConfig
-// struct, except the SoongConfig field which is set from the provided
-// soongConfig argument. LoadGlobalConfig is used directly in Soong and in
-// dexpreopt_gen called from Make to read the $OUT/dexpreopt.config written by
-// Make.
-func LoadGlobalConfig(ctx android.PathContext, data []byte, soongConfig GlobalSoongConfig) (GlobalConfig, error) {
+// ParseGlobalConfig parses the given data assumed to be read from the global
+// dexpreopt.config file into a GlobalConfig struct.
+func ParseGlobalConfig(ctx android.PathContext, data []byte) (GlobalConfig, error) {
type GlobalJSONConfig struct {
GlobalConfig
@@ -204,17 +201,68 @@
config.GlobalConfig.DirtyImageObjects = android.OptionalPathForPath(constructPath(ctx, config.DirtyImageObjects))
config.GlobalConfig.BootImageProfiles = constructPaths(ctx, config.BootImageProfiles)
- // Set this here to force the caller to provide a value for this struct (from
- // either CreateGlobalSoongConfig or LoadGlobalSoongConfig).
- config.GlobalConfig.SoongConfig = soongConfig
-
return config.GlobalConfig, nil
}
-// LoadModuleConfig reads a per-module dexpreopt.config file into a ModuleConfig struct. It is not used in Soong, which
-// receives a ModuleConfig struct directly from java/dexpreopt.go. It is used in dexpreopt_gen called from oMake to
-// read the module dexpreopt.config written by Make.
-func LoadModuleConfig(ctx android.PathContext, data []byte) (ModuleConfig, error) {
+type globalConfigAndRaw struct {
+ global GlobalConfig
+ data []byte
+}
+
+// GetGlobalConfig returns the global dexpreopt.config that's created in the
+// make config phase. It is loaded once the first time it is called for any
+// ctx.Config(), and returns the same data for all future calls with the same
+// ctx.Config(). A value can be inserted for tests using
+// setDexpreoptTestGlobalConfig.
+func GetGlobalConfig(ctx android.PathContext) GlobalConfig {
+ return getGlobalConfigRaw(ctx).global
+}
+
+// GetGlobalConfigRawData is the same as GetGlobalConfig, except that it returns
+// the literal content of dexpreopt.config.
+func GetGlobalConfigRawData(ctx android.PathContext) []byte {
+ return getGlobalConfigRaw(ctx).data
+}
+
+var globalConfigOnceKey = android.NewOnceKey("DexpreoptGlobalConfig")
+var testGlobalConfigOnceKey = android.NewOnceKey("TestDexpreoptGlobalConfig")
+
+func getGlobalConfigRaw(ctx android.PathContext) globalConfigAndRaw {
+ return ctx.Config().Once(globalConfigOnceKey, func() interface{} {
+ if data, err := ctx.Config().DexpreoptGlobalConfig(ctx); err != nil {
+ panic(err)
+ } else if data != nil {
+ globalConfig, err := ParseGlobalConfig(ctx, data)
+ if err != nil {
+ panic(err)
+ }
+ return globalConfigAndRaw{globalConfig, data}
+ }
+
+ // No global config filename set, see if there is a test config set
+ return ctx.Config().Once(testGlobalConfigOnceKey, func() interface{} {
+ // Nope, return a config with preopting disabled
+ return globalConfigAndRaw{GlobalConfig{
+ DisablePreopt: true,
+ DisableGenerateProfile: true,
+ }, nil}
+ })
+ }).(globalConfigAndRaw)
+}
+
+// SetTestGlobalConfig sets a GlobalConfig that future calls to GetGlobalConfig
+// will return. It must be called before the first call to GetGlobalConfig for
+// the config.
+func SetTestGlobalConfig(config android.Config, globalConfig GlobalConfig) {
+ config.Once(testGlobalConfigOnceKey, func() interface{} { return globalConfigAndRaw{globalConfig, nil} })
+}
+
+// ParseModuleConfig parses a per-module dexpreopt.config file into a
+// ModuleConfig struct. It is not used in Soong, which receives a ModuleConfig
+// struct directly from java/dexpreopt.go. It is used in dexpreopt_gen called
+// from Make to read the module dexpreopt.config written in the Make config
+// stage.
+func ParseModuleConfig(ctx android.PathContext, data []byte) (ModuleConfig, error) {
type ModuleJSONConfig struct {
ModuleConfig
@@ -253,21 +301,84 @@
return config.ModuleConfig, nil
}
-// CreateGlobalSoongConfig creates a GlobalSoongConfig from the current context.
-// Should not be used in dexpreopt_gen.
-func CreateGlobalSoongConfig(ctx android.PathContext) GlobalSoongConfig {
- // Default to debug version to help find bugs.
+// dex2oatModuleName returns the name of the module to use for the dex2oat host
+// tool. It should be a binary module with public visibility that is compiled
+// and installed for host.
+func dex2oatModuleName(config android.Config) string {
+ // Default to the debug variant of dex2oat to help find bugs.
// Set USE_DEX2OAT_DEBUG to false for only building non-debug versions.
- var dex2oatBinary string
- if ctx.Config().Getenv("USE_DEX2OAT_DEBUG") == "false" {
- dex2oatBinary = "dex2oat"
+ if config.Getenv("USE_DEX2OAT_DEBUG") == "false" {
+ return "dex2oat"
} else {
- dex2oatBinary = "dex2oatd"
+ return "dex2oatd"
+ }
+}
+
+var dex2oatDepTag = struct {
+ blueprint.BaseDependencyTag
+}{}
+
+type DexPreoptModule interface {
+ dexPreoptModuleSignature() // Not called - only for type detection.
+}
+
+// RegisterToolDepsMutator registers a mutator that adds the necessary
+// dependencies to binary modules for tools that are required later when
+// Get(Cached)GlobalSoongConfig is called. It should be passed to
+// android.RegistrationContext.FinalDepsMutators, and module types that need
+// dependencies also need to embed DexPreoptModule.
+func RegisterToolDepsMutator(ctx android.RegisterMutatorsContext) {
+ ctx.BottomUp("dexpreopt_tool_deps", toolDepsMutator).Parallel()
+}
+
+func toolDepsMutator(ctx android.BottomUpMutatorContext) {
+ if GetGlobalConfig(ctx).DisablePreopt {
+ // Only register dependencies if dexpreopting is enabled. Necessary to avoid
+ // them in non-platform builds where dex2oat etc isn't available.
+ //
+ // It would be nice to not register this mutator at all then, but
+ // RegisterMutatorsContext available at registration doesn't have the state
+ // necessary to pass as PathContext to constructPath etc.
+ return
+ }
+ if _, ok := ctx.Module().(DexPreoptModule); !ok {
+ return
+ }
+ dex2oatBin := dex2oatModuleName(ctx.Config())
+ v := ctx.Config().BuildOSTarget.Variations()
+ ctx.AddFarVariationDependencies(v, dex2oatDepTag, dex2oatBin)
+}
+
+func dex2oatPathFromDep(ctx android.ModuleContext) android.Path {
+ dex2oatBin := dex2oatModuleName(ctx.Config())
+
+ dex2oatModule := ctx.GetDirectDepWithTag(dex2oatBin, dex2oatDepTag)
+ if dex2oatModule == nil {
+ // If this happens there's probably a missing call to AddToolDeps in DepsMutator.
+ panic(fmt.Sprintf("Failed to lookup %s dependency", dex2oatBin))
+ }
+
+ dex2oatPath := dex2oatModule.(android.HostToolProvider).HostToolPath()
+ if !dex2oatPath.Valid() {
+ panic(fmt.Sprintf("Failed to find host tool path in %s", dex2oatModule))
+ }
+
+ return dex2oatPath.Path()
+}
+
+// createGlobalSoongConfig creates a GlobalSoongConfig from the current context.
+// Should not be used in dexpreopt_gen.
+func createGlobalSoongConfig(ctx android.ModuleContext) GlobalSoongConfig {
+ if ctx.Config().TestProductVariables != nil {
+ // If we're called in a test there'll be a confusing error from the path
+ // functions below that gets reported without a stack trace, so let's panic
+ // properly with a more helpful message.
+ panic("This should not be called from tests. Please call GlobalSoongConfigForTests somewhere in the test setup.")
}
return GlobalSoongConfig{
Profman: ctx.Config().HostToolPath(ctx, "profman"),
- Dex2oat: ctx.Config().HostToolPath(ctx, dex2oatBinary),
+ Dex2oat: dex2oatPathFromDep(ctx),
Aapt: ctx.Config().HostToolPath(ctx, "aapt"),
SoongZip: ctx.Config().HostToolPath(ctx, "soong_zip"),
Zip2zip: ctx.Config().HostToolPath(ctx, "zip2zip"),
@@ -276,6 +387,44 @@
}
}
+// The main reason for this Once cache for GlobalSoongConfig is to make the
+// dex2oat path available to singletons. In ordinary modules we get it through a
+// dex2oatDepTag dependency, but in singletons there's no simple way to do the
+// same thing and ensure the right variant is selected, hence this cache to make
+// the resolved path available to singletons. This means we depend on there
+// being at least one ordinary module with a dex2oatDepTag dependency.
+//
+// TODO(b/147613152): Implement a way to deal with dependencies from singletons,
+// and then possibly remove this cache altogether (but the use in
+// GlobalSoongConfigForTests also needs to be rethought).
+var globalSoongConfigOnceKey = android.NewOnceKey("DexpreoptGlobalSoongConfig")
+
+// GetGlobalSoongConfig creates a GlobalSoongConfig the first time it's called,
+// and later returns the same cached instance.
+func GetGlobalSoongConfig(ctx android.ModuleContext) GlobalSoongConfig {
+ globalSoong := ctx.Config().Once(globalSoongConfigOnceKey, func() interface{} {
+ return createGlobalSoongConfig(ctx)
+ }).(GlobalSoongConfig)
+
+ // Always resolve the tool path from the dependency, to ensure that every
+ // module has the dependency added properly.
+ myDex2oat := dex2oatPathFromDep(ctx)
+ if myDex2oat != globalSoong.Dex2oat {
+ panic(fmt.Sprintf("Inconsistent dex2oat path in cached config: expected %s, got %s", globalSoong.Dex2oat, myDex2oat))
+ }
+
+ return globalSoong
+}
+
+// GetCachedGlobalSoongConfig returns a cached GlobalSoongConfig created by an
+// earlier GetGlobalSoongConfig call. This function works with any context
+// compatible with a basic PathContext, since it doesn't try to create a
+// GlobalSoongConfig (which requires a full ModuleContext). It will panic if
+// called before the first GetGlobalSoongConfig call.
+func GetCachedGlobalSoongConfig(ctx android.PathContext) GlobalSoongConfig {
+ return ctx.Config().Get(globalSoongConfigOnceKey).(GlobalSoongConfig)
+}
+
type globalJsonSoongConfig struct {
Profman string
Dex2oat string
@@ -286,9 +435,10 @@
ConstructContext string
}
-// LoadGlobalSoongConfig reads the dexpreopt_soong.config file into a
-// GlobalSoongConfig struct. It is only used in dexpreopt_gen.
-func LoadGlobalSoongConfig(ctx android.PathContext, data []byte) (GlobalSoongConfig, error) {
+// ParseGlobalSoongConfig parses the given data assumed to be read from the
+// global dexpreopt_soong.config file into a GlobalSoongConfig struct. It is
+// only used in dexpreopt_gen.
+func ParseGlobalSoongConfig(ctx android.PathContext, data []byte) (GlobalSoongConfig, error) {
var jc globalJsonSoongConfig
err := json.Unmarshal(data, &jc)
@@ -310,7 +460,11 @@
}
func (s *globalSoongConfigSingleton) GenerateBuildActions(ctx android.SingletonContext) {
- config := CreateGlobalSoongConfig(ctx)
+ if GetGlobalConfig(ctx).DisablePreopt {
+ return
+ }
+
+ config := GetCachedGlobalSoongConfig(ctx)
jc := globalJsonSoongConfig{
Profman: config.Profman.String(),
Dex2oat: config.Dex2oat.String(),
@@ -337,7 +491,11 @@
}
func (s *globalSoongConfigSingleton) MakeVars(ctx android.MakeVarsContext) {
- config := CreateGlobalSoongConfig(ctx)
+ if GetGlobalConfig(ctx).DisablePreopt {
+ return
+ }
+
+ config := GetCachedGlobalSoongConfig(ctx)
ctx.Strict("DEX2OAT", config.Dex2oat.String())
ctx.Strict("DEXPREOPT_GEN_DEPS", strings.Join([]string{
@@ -390,7 +548,14 @@
BootFlags: "",
Dex2oatImageXmx: "",
Dex2oatImageXms: "",
- SoongConfig: GlobalSoongConfig{
+ }
+}
+
+func GlobalSoongConfigForTests(config android.Config) GlobalSoongConfig {
+ // Install the test GlobalSoongConfig in the Once cache so that later calls to
+ // Get(Cached)GlobalSoongConfig returns it without trying to create a real one.
+ return config.Once(globalSoongConfigOnceKey, func() interface{} {
+ return GlobalSoongConfig{
Profman: android.PathForTesting("profman"),
Dex2oat: android.PathForTesting("dex2oat"),
Aapt: android.PathForTesting("aapt"),
@@ -398,6 +563,6 @@
Zip2zip: android.PathForTesting("zip2zip"),
ManifestCheck: android.PathForTesting("manifest_check"),
ConstructContext: android.PathForTesting("construct_context.sh"),
- },
- }
+ }
+ }).(GlobalSoongConfig)
}
diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go
index ac5b691..a69498a 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -49,7 +49,7 @@
// GenerateDexpreoptRule generates a set of commands that will preopt a module based on a GlobalConfig and a
// ModuleConfig. The produced files and their install locations will be available through rule.Installs().
-func GenerateDexpreoptRule(ctx android.PathContext,
+func GenerateDexpreoptRule(ctx android.PathContext, globalSoong GlobalSoongConfig,
global GlobalConfig, module ModuleConfig) (rule *android.RuleBuilder, err error) {
defer func() {
@@ -72,10 +72,10 @@
var profile android.WritablePath
if generateProfile {
- profile = profileCommand(ctx, global, module, rule)
+ profile = profileCommand(ctx, globalSoong, global, module, rule)
}
if generateBootProfile {
- bootProfileCommand(ctx, global, module, rule)
+ bootProfileCommand(ctx, globalSoong, global, module, rule)
}
if !dexpreoptDisabled(global, module) {
@@ -87,7 +87,7 @@
generateDM := shouldGenerateDM(module, global)
for archIdx, _ := range module.Archs {
- dexpreoptCommand(ctx, global, module, rule, archIdx, profile, appImage, generateDM)
+ dexpreoptCommand(ctx, globalSoong, global, module, rule, archIdx, profile, appImage, generateDM)
}
}
}
@@ -119,8 +119,8 @@
return false
}
-func profileCommand(ctx android.PathContext, global GlobalConfig, module ModuleConfig,
- rule *android.RuleBuilder) android.WritablePath {
+func profileCommand(ctx android.PathContext, globalSoong GlobalSoongConfig, global GlobalConfig,
+ module ModuleConfig, rule *android.RuleBuilder) android.WritablePath {
profilePath := module.BuildPath.InSameDir(ctx, "profile.prof")
profileInstalledPath := module.DexLocation + ".prof"
@@ -131,7 +131,7 @@
cmd := rule.Command().
Text(`ANDROID_LOG_TAGS="*:e"`).
- Tool(global.SoongConfig.Profman)
+ Tool(globalSoong.Profman)
if module.ProfileIsTextListing {
// The profile is a test listing of classes (used for framework jars).
@@ -158,8 +158,8 @@
return profilePath
}
-func bootProfileCommand(ctx android.PathContext, global GlobalConfig, module ModuleConfig,
- rule *android.RuleBuilder) android.WritablePath {
+func bootProfileCommand(ctx android.PathContext, globalSoong GlobalSoongConfig, global GlobalConfig,
+ module ModuleConfig, rule *android.RuleBuilder) android.WritablePath {
profilePath := module.BuildPath.InSameDir(ctx, "profile.bprof")
profileInstalledPath := module.DexLocation + ".bprof"
@@ -170,7 +170,7 @@
cmd := rule.Command().
Text(`ANDROID_LOG_TAGS="*:e"`).
- Tool(global.SoongConfig.Profman)
+ Tool(globalSoong.Profman)
// The profile is a test listing of methods.
// We need to generate the actual binary profile.
@@ -190,8 +190,9 @@
return profilePath
}
-func dexpreoptCommand(ctx android.PathContext, global GlobalConfig, module ModuleConfig, rule *android.RuleBuilder,
- archIdx int, profile android.WritablePath, appImage bool, generateDM bool) {
+func dexpreoptCommand(ctx android.PathContext, globalSoong GlobalSoongConfig, global GlobalConfig,
+ module ModuleConfig, rule *android.RuleBuilder, archIdx int, profile android.WritablePath,
+ appImage bool, generateDM bool) {
arch := module.Archs[archIdx]
@@ -299,14 +300,14 @@
if module.EnforceUsesLibraries {
if module.ManifestPath != nil {
rule.Command().Text(`target_sdk_version="$(`).
- Tool(global.SoongConfig.ManifestCheck).
+ Tool(globalSoong.ManifestCheck).
Flag("--extract-target-sdk-version").
Input(module.ManifestPath).
Text(`)"`)
} else {
// No manifest to extract targetSdkVersion from, hope that DexJar is an APK
rule.Command().Text(`target_sdk_version="$(`).
- Tool(global.SoongConfig.Aapt).
+ Tool(globalSoong.Aapt).
Flag("dump badging").
Input(module.DexPath).
Text(`| grep "targetSdkVersion" | sed -n "s/targetSdkVersion:'\(.*\)'/\1/p"`).
@@ -327,7 +328,7 @@
Implicits(conditionalClassLoaderContextHost29)
rule.Command().Textf(`conditional_target_libs_29="%s"`,
strings.Join(conditionalClassLoaderContextTarget29, " "))
- rule.Command().Text("source").Tool(global.SoongConfig.ConstructContext).Input(module.DexPath)
+ rule.Command().Text("source").Tool(globalSoong.ConstructContext).Input(module.DexPath)
}
// Devices that do not have a product partition use a symlink from /product to /system/product.
@@ -340,7 +341,7 @@
cmd := rule.Command().
Text(`ANDROID_LOG_TAGS="*:e"`).
- Tool(global.SoongConfig.Dex2oat).
+ Tool(globalSoong.Dex2oat).
Flag("--avoid-storing-invocation").
FlagWithOutput("--write-invocation-to=", invocationPath).ImplicitOutput(invocationPath).
Flag("--runtime-arg").FlagWithArg("-Xms", global.Dex2oatXms).
@@ -409,7 +410,7 @@
dmInstalledPath := pathtools.ReplaceExtension(module.DexLocation, "dm")
tmpPath := module.BuildPath.InSameDir(ctx, "primary.vdex")
rule.Command().Text("cp -f").Input(vdexPath).Output(tmpPath)
- rule.Command().Tool(global.SoongConfig.SoongZip).
+ rule.Command().Tool(globalSoong.SoongZip).
FlagWithArg("-L", "9").
FlagWithOutput("-o", dmPath).
Flag("-j").
diff --git a/dexpreopt/dexpreopt_gen/dexpreopt_gen.go b/dexpreopt/dexpreopt_gen/dexpreopt_gen.go
index e2818bb..4da003e 100644
--- a/dexpreopt/dexpreopt_gen/dexpreopt_gen.go
+++ b/dexpreopt/dexpreopt_gen/dexpreopt_gen.go
@@ -80,13 +80,13 @@
globalSoongConfigData, err := ioutil.ReadFile(*globalSoongConfigPath)
if err != nil {
- fmt.Fprintf(os.Stderr, "error reading global config %q: %s\n", *globalSoongConfigPath, err)
+ fmt.Fprintf(os.Stderr, "error reading global Soong config %q: %s\n", *globalSoongConfigPath, err)
os.Exit(2)
}
- globalSoongConfig, err := dexpreopt.LoadGlobalSoongConfig(ctx, globalSoongConfigData)
+ globalSoongConfig, err := dexpreopt.ParseGlobalSoongConfig(ctx, globalSoongConfigData)
if err != nil {
- fmt.Fprintf(os.Stderr, "error loading global config %q: %s\n", *globalSoongConfigPath, err)
+ fmt.Fprintf(os.Stderr, "error parsing global Soong config %q: %s\n", *globalSoongConfigPath, err)
os.Exit(2)
}
@@ -96,9 +96,9 @@
os.Exit(2)
}
- globalConfig, err := dexpreopt.LoadGlobalConfig(ctx, globalConfigData, globalSoongConfig)
+ globalConfig, err := dexpreopt.ParseGlobalConfig(ctx, globalConfigData)
if err != nil {
- fmt.Fprintf(os.Stderr, "error parse global config %q: %s\n", *globalConfigPath, err)
+ fmt.Fprintf(os.Stderr, "error parsing global config %q: %s\n", *globalConfigPath, err)
os.Exit(2)
}
@@ -108,9 +108,9 @@
os.Exit(2)
}
- moduleConfig, err := dexpreopt.LoadModuleConfig(ctx, moduleConfigData)
+ moduleConfig, err := dexpreopt.ParseModuleConfig(ctx, moduleConfigData)
if err != nil {
- fmt.Fprintf(os.Stderr, "error loading module config %q: %s\n", *moduleConfigPath, err)
+ fmt.Fprintf(os.Stderr, "error parsing module config %q: %s\n", *moduleConfigPath, err)
os.Exit(2)
}
@@ -130,12 +130,12 @@
}
}()
- writeScripts(ctx, globalConfig, moduleConfig, *dexpreoptScriptPath)
+ writeScripts(ctx, globalSoongConfig, globalConfig, moduleConfig, *dexpreoptScriptPath)
}
-func writeScripts(ctx android.PathContext, global dexpreopt.GlobalConfig, module dexpreopt.ModuleConfig,
- dexpreoptScriptPath string) {
- dexpreoptRule, err := dexpreopt.GenerateDexpreoptRule(ctx, global, module)
+func writeScripts(ctx android.PathContext, globalSoong dexpreopt.GlobalSoongConfig,
+ global dexpreopt.GlobalConfig, module dexpreopt.ModuleConfig, dexpreoptScriptPath string) {
+ dexpreoptRule, err := dexpreopt.GenerateDexpreoptRule(ctx, globalSoong, global, module)
if err != nil {
panic(err)
}
@@ -150,7 +150,7 @@
dexpreoptRule.Command().Text("mkdir -p").Flag(filepath.Dir(installPath.String()))
dexpreoptRule.Command().Text("cp -f").Input(install.From).Output(installPath)
}
- dexpreoptRule.Command().Tool(global.SoongConfig.SoongZip).
+ dexpreoptRule.Command().Tool(globalSoong.SoongZip).
FlagWithArg("-o ", "$2").
FlagWithArg("-C ", installDir.String()).
FlagWithArg("-D ", installDir.String())
diff --git a/dexpreopt/dexpreopt_test.go b/dexpreopt/dexpreopt_test.go
index a128dc0..44bbbc2 100644
--- a/dexpreopt/dexpreopt_test.go
+++ b/dexpreopt/dexpreopt_test.go
@@ -61,10 +61,13 @@
}
func TestDexPreopt(t *testing.T) {
- ctx := android.PathContextForTesting(android.TestConfig("out", nil, "", nil))
- global, module := GlobalConfigForTests(ctx), testSystemModuleConfig(ctx, "test")
+ config := android.TestConfig("out", nil, "", nil)
+ ctx := android.PathContextForTesting(config)
+ globalSoong := GlobalSoongConfigForTests(config)
+ global := GlobalConfigForTests(ctx)
+ module := testSystemModuleConfig(ctx, "test")
- rule, err := GenerateDexpreoptRule(ctx, global, module)
+ rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module)
if err != nil {
t.Fatal(err)
}
@@ -80,7 +83,9 @@
}
func TestDexPreoptSystemOther(t *testing.T) {
- ctx := android.PathContextForTesting(android.TestConfig("out", nil, "", nil))
+ config := android.TestConfig("out", nil, "", nil)
+ ctx := android.PathContextForTesting(config)
+ globalSoong := GlobalSoongConfigForTests(config)
global := GlobalConfigForTests(ctx)
systemModule := testSystemModuleConfig(ctx, "Stest")
systemProductModule := testSystemProductModuleConfig(ctx, "SPtest")
@@ -118,7 +123,7 @@
for _, test := range tests {
global.PatternsOnSystemOther = test.patterns
for _, mt := range test.moduleTests {
- rule, err := GenerateDexpreoptRule(ctx, global, mt.module)
+ rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, mt.module)
if err != nil {
t.Fatal(err)
}
@@ -138,12 +143,15 @@
}
func TestDexPreoptProfile(t *testing.T) {
- ctx := android.PathContextForTesting(android.TestConfig("out", nil, "", nil))
- global, module := GlobalConfigForTests(ctx), testSystemModuleConfig(ctx, "test")
+ config := android.TestConfig("out", nil, "", nil)
+ ctx := android.PathContextForTesting(config)
+ globalSoong := GlobalSoongConfigForTests(config)
+ global := GlobalConfigForTests(ctx)
+ module := testSystemModuleConfig(ctx, "test")
module.ProfileClassListing = android.OptionalPathForPath(android.PathForTesting("profile"))
- rule, err := GenerateDexpreoptRule(ctx, global, module)
+ rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module)
if err != nil {
t.Fatal(err)
}
diff --git a/dexpreopt/testing.go b/dexpreopt/testing.go
new file mode 100644
index 0000000..b572eb3
--- /dev/null
+++ b/dexpreopt/testing.go
@@ -0,0 +1,47 @@
+// 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 dexpreopt
+
+import (
+ "android/soong/android"
+)
+
+type dummyToolBinary struct {
+ android.ModuleBase
+}
+
+func (m *dummyToolBinary) GenerateAndroidBuildActions(ctx android.ModuleContext) {}
+
+func (m *dummyToolBinary) HostToolPath() android.OptionalPath {
+ return android.OptionalPathForPath(android.PathForTesting("dex2oat"))
+}
+
+func dummyToolBinaryFactory() android.Module {
+ module := &dummyToolBinary{}
+ android.InitAndroidArchModule(module, android.HostSupported, android.MultilibFirst)
+ return module
+}
+
+func RegisterToolModulesForTest(ctx *android.TestContext) {
+ ctx.RegisterModuleType("dummy_tool_binary", dummyToolBinaryFactory)
+}
+
+func BpToolModulesForTest() string {
+ return `
+ dummy_tool_binary {
+ name: "dex2oatd",
+ }
+ `
+}
diff --git a/docs/perf.md b/docs/perf.md
index c3a2647..538adff 100644
--- a/docs/perf.md
+++ b/docs/perf.md
@@ -8,7 +8,7 @@
viewed. Just open `$OUT_DIR/build.trace.gz` in Chrome's <chrome://tracing>, or
with [catapult's trace viewer][catapult trace_viewer]. The last few traces are
stored in `build.trace.#.gz` (larger numbers are older). The associated logs
-are stored in `soong.#.log`.
+are stored in `soong.#.log` and `verbose.#.log.gz`.

@@ -31,29 +31,29 @@
In most cases, we've found that the fast-path is slow because all of the
`$(shell)` commands need to be re-executed to determine if their output changed.
-The `$OUT_DIR/soong.log` contains statistics from the regen check:
+The `$OUT_DIR/verbose.log.gz` contains statistics from the regen check:
```
-.../kati.go:127: *kati*: regen check time: 1.699207
-.../kati.go:127: *kati*: glob time (regen): 0.377193 / 33609
-.../kati.go:127: *kati*: shell time (regen): 1.313529 / 184
-.../kati.go:127: *kati*: 0.217 find device vendor -type f -name \*.pk8 -o -name verifiedboot\* -o -name \*.x509.pem -o -name oem\*.prop | sort
-.../kati.go:127: *kati*: 0.105 cd packages/apps/Dialer ; find -L . -type d -name "res"
-.../kati.go:127: *kati*: 0.035 find device vendor -maxdepth 4 -name '*_aux_variant_config.mk' -o -name '*_aux_os_config.mk' | sort
-.../kati.go:127: *kati*: 0.029 cd frameworks/base ; find -L core/java graphics/java location/java media/java media/mca/effect/java media/mca/filterfw/java media/mca/filterpacks/java drm/java opengl/java sax/java telecomm/java telephony/java wifi/java lowpan/java keystore/java rs/java ../opt/telephony/src/java/android/telephony ../opt/telephony/src/java/android/telephony/gsm ../opt/net/voip/src/java/android/net/rtp ../opt/net/voip/src/java/android/net/sip -name "*.html" -and -not -name ".*"
-.../kati.go:127: *kati*: 0.025 test -d device && find -L device -maxdepth 4 -path '*/marlin/BoardConfig.mk'
-.../kati.go:127: *kati*: 0.023 find packages/apps/Settings/tests/robotests -type f -name '*Test.java' | sed -e 's!.*\(com/google.*Test\)\.java!\1!' -e 's!.*\(com/android.*Test\)\.java!\1!' | sed 's!/!\.!g' | cat
-.../kati.go:127: *kati*: 0.022 test -d vendor && find -L vendor -maxdepth 4 -path '*/marlin/BoardConfig.mk'
-.../kati.go:127: *kati*: 0.017 cd cts/tests/tests/shortcutmanager/packages/launchermanifest ; find -L ../src -name "*.java" -and -not -name ".*"
-.../kati.go:127: *kati*: 0.016 cd cts/tests/tests/shortcutmanager/packages/launchermanifest ; find -L ../../common/src -name "*.java" -and -not -name ".*"
-.../kati.go:127: *kati*: 0.015 cd libcore && (find luni/src/test/java -name "*.java" 2> /dev/null) | grep -v -f java_tests_blacklist
-.../kati.go:127: *kati*: stat time (regen): 0.250384 / 4405
+verbose: *kati*: regen check time: 0.754030
+verbose: *kati*: glob time (regen): 0.545859 / 43840
+verbose: *kati*: shell time (regen): 0.278095 / 66 (59 unique)
+verbose: *kati*: 0.012 / 1 mkdir -p out/target/product/generic && echo Android/aosp_arm/generic:R/AOSP.MASTER/$(date -d @$(cat out/build_date.txt) +%m%d%H%M):eng/test-keys >out/target/product/generic/build_fingerprint.txt && grep " " out/target/product/generic/build_fingerprint.txt
+verbose: *kati*: 0.010 / 1 echo 'com.android.launcher3.config.FlagOverrideSampleTest com.android.launcher3.logging.FileLogTest com.android.launcher3.model.AddWorkspaceItemsTaskTest com.android.launcher3.model.CacheDataUpdatedTaskTest com.android.launcher3.model.DbDowngradeHelperTest com.android.launcher3.model.GridBackupTableTest com.android.launcher3.model.GridSizeMigrationTaskTest com.android.launcher3.model.PackageInstallStateChangedTaskTest com.android.launcher3.popup.PopupPopulatorTest com.android.launcher3.util.GridOccupancyTest com.android.launcher3.util.IntSetTest' | tr ' ' '\n' | cat
+verbose: *kati*: 0.010 / 1 cd cts/tests/framework/base/windowmanager ; find -L * -name "Components.java" -and -not -name ".*"
+verbose: *kati*: 0.010 / 1 git -C test/framework/build log -s -n 1 --format="%cd" --date=format:"%Y%m%d_%H%M%S" 2>/dev/null
+verbose: *kati*: 0.009 / 2 cd development/samples/ShortcutDemo/publisher ; find -L ../common/src -name "*.java" -and -not -name ".*"
+verbose: *kati*: 0.009 / 2 cd development/samples/ShortcutDemo/launcher ; find -L ../common/src -name "*.java" -and -not -name ".*"
+verbose: *kati*: 0.009 / 1 if ! cmp -s out/target/product/generic/obj/CONFIG/kati_packaging/dist.mk.tmp out/target/product/generic/obj/CONFIG/kati_packaging/dist.mk; then mv out/target/product/generic/obj/CONFIG/kati_packaging/dist.mk.tmp out/target/product/generic/obj/CONFIG/kati_packaging/dist.mk; else rm out/target/product/generic/obj/CONFIG/kati_packaging/dist.mk.tmp; fi
+verbose: *kati*: 0.008 / 1 mkdir -p out/target/product/generic && echo R/AOSP.MASTER/$(cat out/build_number.txt):eng/test-keys >out/target/product/generic/build_thumbprint.txt && grep " " out/target/product/generic/build_thumbprint.txt
+verbose: *kati*: 0.007 / 1 echo 'com.android.customization.model.clock.BaseClockManagerTest com.android.customization.model.clock.ClockManagerTest com.android.customization.model.grid.GridOptionsManagerTest com.android.customization.model.theme.ThemeManagerTest' | tr ' ' '\n' | cat
+verbose: *kati*: 0.007 / 1 uname -sm
+verbose: *kati*: stat time (regen): 0.361907 / 1241
```
-In this case, the total time spent checking was 1.69 seconds, even though the
+In this case, the total time spent checking was 0.75 seconds, even though the
other "(regen)" numbers add up to more than that (some parts are parallelized
-where possible). The biggest contributor is the `$(shell)` times -- 184
-executions took a total of 1.31 seconds. The top 10 longest shell functions are
+where possible). Often times, the biggest contributor is the `$(shell)` times
+-- in this case, 66 calls took 0.27s. The top 10 longest shell functions are
printed.
All the longest commands in this case are all variants of a call to `find`, but
@@ -96,7 +96,8 @@
$(sort $(shell find device vendor -type -f -a -name \*.pk8 -o -name verifiedboot\* -o -name \*.x509.pem -o -name oem\*.prop))
```
-Kati is learning about the implicit `-a` in [this change](https://github.com/google/kati/pull/132)
+Kati has now learned about the implicit `-a`, so this particular change is no
+longer necessary, but the basic concept holds.
#### Kati regens too often
@@ -113,6 +114,46 @@
is available when ckati is run with `--regen_debug`, but that can be a lot of
data to understand.
+#### Debugging the slow path
+
+Kati will now dump out information about which Makefiles took the most time to
+execute. This is also in the `verbose.log.gz` file:
+
+```
+verbose: *kati*: included makefiles: 73.640833 / 232810 (1066 unique)
+verbose: *kati*: 18.389 / 1 out/soong/Android-aosp_arm.mk
+verbose: *kati*: 13.137 / 20144 build/make/core/soong_cc_prebuilt.mk
+verbose: *kati*: 11.743 / 27666 build/make/core/base_rules.mk
+verbose: *kati*: 2.289 / 1 art/Android.mk
+verbose: *kati*: 2.054 / 1 art/build/Android.cpplint.mk
+verbose: *kati*: 1.955 / 28269 build/make/core/clear_vars.mk
+verbose: *kati*: 1.795 / 283 build/make/core/package.mk
+verbose: *kati*: 1.790 / 283 build/make/core/package_internal.mk
+verbose: *kati*: 1.757 / 17382 build/make/core/link_type.mk
+verbose: *kati*: 1.078 / 297 build/make/core/aapt2.mk
+```
+
+This shows that soong_cc_prebuilt.mk was included 20144 times, for a total time
+spent of 13.137 secounds. While Android-aosp_arm.mk was only included once, and
+took 18.389 seconds. In this case, Android-aosp_arm.mk is the only file that
+includes soong_cc_prebuilt.mk, so we can safely assume that 13 of the 18 seconds
+in Android-aosp_arm.mk was actually spent within soong_cc_prebuilt.mk (or
+something that it included, like base_rules.mk).
+
+By default this only includes the top 10 entries, but you can ask for the stats
+for any makefile to be printed with `$(KATI_profile_makefile)`:
+
+```
+$(KATI_profile_makefile build/make/core/product.mk)
+```
+
+With these primitives, it's possible to get the timing information for small
+chunks, or even single lines, of a makefile. Just move the lines you want to
+measure into a new makefile, and replace their use with an `include` of the
+new makefile. It's possible to analyze where the time is being spent by doing
+a binary search using this method, but you do need to be careful not to split
+conditionals across two files (the ifeq/else/endif must all be in the same file).
+
### Ninja
#### Understanding why something rebuilt
@@ -164,15 +205,14 @@
### Common
-#### mm
+### <= Android 10 (Q): mm
Soong always loads the entire module graph, so as modules convert from Make to
Soong, `mm` is becoming closer to `mma`. This produces more correct builds, but
does slow down builds, as we need to verify/produce/load a larger build graph.
-We're exploring a few options to speed up build startup, one being [an
-experimental set of ninja patches][ninja parse optimization],
-though that's not the current path we're working towards.
+As of Android Q, loading large build graphs is fast, and in Android R, `mm` is
+now an alias of `mma`.
### Android 8.1 (Oreo MR1)
diff --git a/genrule/genrule.go b/genrule/genrule.go
index 57ca9bc..a0008d3 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -30,10 +30,18 @@
)
func init() {
- android.RegisterModuleType("genrule_defaults", defaultsFactory)
+ registerGenruleBuildComponents(android.InitRegistrationContext)
+}
- android.RegisterModuleType("gensrcs", GenSrcsFactory)
- android.RegisterModuleType("genrule", GenRuleFactory)
+func registerGenruleBuildComponents(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("genrule_defaults", defaultsFactory)
+
+ ctx.RegisterModuleType("gensrcs", GenSrcsFactory)
+ ctx.RegisterModuleType("genrule", GenRuleFactory)
+
+ ctx.FinalDepsMutators(func(ctx android.RegisterMutatorsContext) {
+ ctx.BottomUp("genrule_tool_deps", toolDepsMutator).Parallel()
+ })
}
var (
@@ -166,7 +174,7 @@
return g.outputDeps
}
-func (g *Module) DepsMutator(ctx android.BottomUpMutatorContext) {
+func toolDepsMutator(ctx android.BottomUpMutatorContext) {
if g, ok := ctx.Module().(*Module); ok {
for _, tool := range g.properties.Tools {
tag := hostToolDependencyTag{label: tool}
@@ -542,6 +550,7 @@
func (x noopImageInterface) ImageMutatorBegin(android.BaseModuleContext) {}
func (x noopImageInterface) CoreVariantNeeded(android.BaseModuleContext) bool { return false }
+func (x noopImageInterface) RamdiskVariantNeeded(android.BaseModuleContext) bool { return false }
func (x noopImageInterface) RecoveryVariantNeeded(android.BaseModuleContext) bool { return false }
func (x noopImageInterface) ExtraImageVariations(ctx android.BaseModuleContext) []string { return nil }
func (x noopImageInterface) SetImageVariation(ctx android.BaseModuleContext, variation string, module android.Module) {
diff --git a/genrule/genrule_test.go b/genrule/genrule_test.go
index ea49e08..7eb43ac 100644
--- a/genrule/genrule_test.go
+++ b/genrule/genrule_test.go
@@ -55,10 +55,10 @@
ctx := android.NewTestArchContext()
ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
- ctx.RegisterModuleType("genrule", GenRuleFactory)
- ctx.RegisterModuleType("gensrcs", GenSrcsFactory)
- ctx.RegisterModuleType("genrule_defaults", defaultsFactory)
ctx.RegisterModuleType("tool", toolFactory)
+
+ registerGenruleBuildComponents(ctx)
+
ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
ctx.Register(config)
diff --git a/java/aar.go b/java/aar.go
index ae064e5..1ba65dc 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -15,11 +15,12 @@
package java
import (
- "android/soong/android"
"fmt"
"path/filepath"
"strings"
+ "android/soong/android"
+
"github.com/google/blueprint"
"github.com/google/blueprint/proptools"
)
diff --git a/java/androidmk.go b/java/androidmk.go
index 04bf15c..7c19180 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -695,3 +695,16 @@
}
entries.AddStrings("LOCAL_COMPATIBILITY_SUPPORT_FILES", testFiles...)
}
+
+func (r *RuntimeResourceOverlay) AndroidMkEntries() []android.AndroidMkEntries {
+ return []android.AndroidMkEntries{android.AndroidMkEntries{
+ Class: "ETC",
+ OutputFile: android.OptionalPathForPath(r.outputFile),
+ Include: "$(BUILD_SYSTEM)/soong_app_prebuilt.mk",
+ ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+ func(entries *android.AndroidMkEntries) {
+ entries.SetPath("LOCAL_MODULE_PATH", r.installDir.ToMakePath())
+ },
+ },
+ }}
+}
diff --git a/java/app.go b/java/app.go
index e9941f2..4f06087 100755
--- a/java/app.go
+++ b/java/app.go
@@ -27,6 +27,7 @@
"android/soong/android"
"android/soong/cc"
+ "android/soong/dexpreopt"
"android/soong/tradefed"
)
@@ -47,6 +48,7 @@
ctx.RegisterModuleType("override_android_test", OverrideAndroidTestModuleFactory)
ctx.RegisterModuleType("android_app_import", AndroidAppImportFactory)
ctx.RegisterModuleType("android_test_import", AndroidTestImportFactory)
+ ctx.RegisterModuleType("runtime_resource_overlay", RuntimeResourceOverlayFactory)
}
// AndroidManifest.xml merging
@@ -849,6 +851,7 @@
android.ModuleBase
android.DefaultableModuleBase
prebuilt android.Prebuilt
+ dexpreopt.DexPreoptModule
properties AndroidAppImportProperties
dpiVariants interface{}
@@ -1212,6 +1215,96 @@
return module
}
+type RuntimeResourceOverlay struct {
+ android.ModuleBase
+ android.DefaultableModuleBase
+ aapt
+
+ properties RuntimeResourceOverlayProperties
+
+ outputFile android.Path
+ installDir android.InstallPath
+}
+
+type RuntimeResourceOverlayProperties struct {
+ // the name of a certificate in the default certificate directory or an android_app_certificate
+ // module name in the form ":module".
+ Certificate *string
+
+ // optional theme name. If specified, the overlay package will be applied
+ // only when the ro.boot.vendor.overlay.theme system property is set to the same value.
+ Theme *string
+
+ // if not blank, set to the version of the sdk to compile against.
+ // Defaults to compiling against the current platform.
+ Sdk_version *string
+
+ // if not blank, set the minimum version of the sdk that the compiled artifacts will run against.
+ // Defaults to sdk_version if not set.
+ Min_sdk_version *string
+}
+
+func (r *RuntimeResourceOverlay) DepsMutator(ctx android.BottomUpMutatorContext) {
+ sdkDep := decodeSdkDep(ctx, sdkContext(r))
+ if sdkDep.hasFrameworkLibs() {
+ r.aapt.deps(ctx, sdkDep)
+ }
+
+ cert := android.SrcIsModule(String(r.properties.Certificate))
+ if cert != "" {
+ ctx.AddDependency(ctx.Module(), certificateTag, cert)
+ }
+}
+
+func (r *RuntimeResourceOverlay) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ // Compile and link resources
+ r.aapt.hasNoCode = true
+ // Do not remove resources without default values nor dedupe resource configurations with the same value
+ r.aapt.buildActions(ctx, r, "--no-resource-deduping", "--no-resource-removal")
+
+ // Sign the built package
+ _, certificates := collectAppDeps(ctx, false)
+ certificates = processMainCert(r.ModuleBase, String(r.properties.Certificate), certificates, ctx)
+ signed := android.PathForModuleOut(ctx, "signed", r.Name()+".apk")
+ SignAppPackage(ctx, signed, r.aapt.exportPackage, certificates)
+
+ r.outputFile = signed
+ r.installDir = android.PathForModuleInstall(ctx, "overlay", String(r.properties.Theme))
+ ctx.InstallFile(r.installDir, r.outputFile.Base(), r.outputFile)
+}
+
+func (r *RuntimeResourceOverlay) sdkVersion() string {
+ return String(r.properties.Sdk_version)
+}
+
+func (r *RuntimeResourceOverlay) systemModules() string {
+ return ""
+}
+
+func (r *RuntimeResourceOverlay) minSdkVersion() string {
+ if r.properties.Min_sdk_version != nil {
+ return *r.properties.Min_sdk_version
+ }
+ return r.sdkVersion()
+}
+
+func (r *RuntimeResourceOverlay) targetSdkVersion() string {
+ return r.sdkVersion()
+}
+
+// runtime_resource_overlay generates a resource-only apk file that can overlay application and
+// system resources at run time.
+func RuntimeResourceOverlayFactory() android.Module {
+ module := &RuntimeResourceOverlay{}
+ module.AddProperties(
+ &module.properties,
+ &module.aaptProperties)
+
+ InitJavaModule(module, android.DeviceSupported)
+
+ return module
+}
+
type UsesLibraryProperties struct {
// A list of shared library modules that will be listed in uses-library tags in the AndroidManifest.xml file.
Uses_libs []string
diff --git a/java/app_test.go b/java/app_test.go
index 6f89da4..2682682 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -2207,3 +2207,54 @@
}
}
}
+
+func TestRuntimeResourceOverlay(t *testing.T) {
+ ctx, config := testJava(t, `
+ runtime_resource_overlay {
+ name: "foo",
+ certificate: "platform",
+ product_specific: true,
+ aaptflags: ["--keep-raw-values"],
+ }
+
+ runtime_resource_overlay {
+ name: "foo_themed",
+ certificate: "platform",
+ product_specific: true,
+ theme: "faza",
+ }
+ `)
+
+ m := ctx.ModuleForTests("foo", "android_common")
+
+ // Check AAPT2 link flags.
+ aapt2Flags := m.Output("package-res.apk").Args["flags"]
+ expectedFlags := []string{"--keep-raw-values", "--no-resource-deduping", "--no-resource-removal"}
+ absentFlags := android.RemoveListFromList(expectedFlags, strings.Split(aapt2Flags, " "))
+ if len(absentFlags) > 0 {
+ t.Errorf("expected values, %q are missing in aapt2 link flags, %q", absentFlags, aapt2Flags)
+ }
+
+ // Check cert signing flag.
+ signedApk := m.Output("signed/foo.apk")
+ signingFlag := signedApk.Args["certificates"]
+ expected := "build/make/target/product/security/platform.x509.pem build/make/target/product/security/platform.pk8"
+ if expected != signingFlag {
+ t.Errorf("Incorrect signing flags, expected: %q, got: %q", expected, signingFlag)
+ }
+
+ // Check device location.
+ path := android.AndroidMkEntriesForTest(t, config, "", m.Module())[0].EntryMap["LOCAL_MODULE_PATH"]
+ expectedPath := []string{"/tmp/target/product/test_device/product/overlay"}
+ if !reflect.DeepEqual(path, expectedPath) {
+ t.Errorf("Unexpected LOCAL_MODULE_PATH value: %v, expected: %v", path, expectedPath)
+ }
+
+ // A themed module has a different device location
+ m = ctx.ModuleForTests("foo_themed", "android_common")
+ path = android.AndroidMkEntriesForTest(t, config, "", m.Module())[0].EntryMap["LOCAL_MODULE_PATH"]
+ expectedPath = []string{"/tmp/target/product/test_device/product/overlay/faza"}
+ if !reflect.DeepEqual(path, expectedPath) {
+ t.Errorf("Unexpected LOCAL_MODULE_PATH value: %v, expected: %v", path, expectedPath)
+ }
+}
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
index da68660..0fd1f28 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -59,7 +59,7 @@
}
func (d *dexpreopter) dexpreoptDisabled(ctx android.ModuleContext) bool {
- global := dexpreoptGlobalConfig(ctx)
+ global := dexpreopt.GetGlobalConfig(ctx)
if global.DisablePreopt {
return true
@@ -96,7 +96,7 @@
}
func odexOnSystemOther(ctx android.ModuleContext, installPath android.InstallPath) bool {
- return dexpreopt.OdexOnSystemOtherByName(ctx.ModuleName(), android.InstallPathToOnDevicePath(ctx, installPath), dexpreoptGlobalConfig(ctx))
+ return dexpreopt.OdexOnSystemOtherByName(ctx.ModuleName(), android.InstallPathToOnDevicePath(ctx, installPath), dexpreopt.GetGlobalConfig(ctx))
}
func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.ModuleOutPath) android.ModuleOutPath {
@@ -104,7 +104,8 @@
return dexJarFile
}
- global := dexpreoptGlobalConfig(ctx)
+ globalSoong := dexpreopt.GetGlobalSoongConfig(ctx)
+ global := dexpreopt.GetGlobalConfig(ctx)
bootImage := defaultBootImageConfig(ctx)
if global.UseApexImage {
bootImage = frameworkJZBootImageConfig(ctx)
@@ -189,7 +190,7 @@
PresignedPrebuilt: d.isPresignedPrebuilt,
}
- dexpreoptRule, err := dexpreopt.GenerateDexpreoptRule(ctx, global, dexpreoptConfig)
+ dexpreoptRule, err := dexpreopt.GenerateDexpreoptRule(ctx, globalSoong, global, dexpreoptConfig)
if err != nil {
ctx.ModuleErrorf("error generating dexpreopt rule: %s", err.Error())
return dexJarFile
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index 66840b5..0082d03 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -162,7 +162,7 @@
}
func skipDexpreoptBootJars(ctx android.PathContext) bool {
- if dexpreoptGlobalConfig(ctx).DisablePreopt {
+ if dexpreopt.GetGlobalConfig(ctx).DisablePreopt {
return true
}
@@ -190,7 +190,18 @@
if skipDexpreoptBootJars(ctx) {
return nil
}
- return artBootImageConfig(ctx).imagesDeps
+
+ // Include dexpreopt files for the primary boot image.
+ files := artBootImageConfig(ctx).imagesDeps
+
+ // For JIT-zygote config, also include dexpreopt files for the primary JIT-zygote image.
+ if dexpreopt.GetGlobalConfig(ctx).UseApexImage {
+ for arch, paths := range artJZBootImageConfig(ctx).imagesDeps {
+ files[arch] = append(files[arch], paths...)
+ }
+ }
+
+ return files
}
// dexpreoptBoot singleton rules
@@ -202,7 +213,7 @@
d.dexpreoptConfigForMake = android.PathForOutput(ctx, ctx.Config().DeviceName(), "dexpreopt.config")
writeGlobalConfigForMake(ctx, d.dexpreoptConfigForMake)
- global := dexpreoptGlobalConfig(ctx)
+ global := dexpreopt.GetGlobalConfig(ctx)
// Skip recompiling the boot image for the second sanitization phase. We'll get separate paths
// and invalidate first-stage artifacts which are crucial to SANITIZE_LITE builds.
@@ -293,7 +304,8 @@
func buildBootImageRuleForArch(ctx android.SingletonContext, image *bootImage,
arch android.ArchType, profile android.Path, missingDeps []string) android.WritablePaths {
- global := dexpreoptGlobalConfig(ctx)
+ globalSoong := dexpreopt.GetCachedGlobalSoongConfig(ctx)
+ global := dexpreopt.GetGlobalConfig(ctx)
symbolsDir := image.symbolsDir.Join(ctx, image.installSubdir, arch.String())
symbolsFile := symbolsDir.Join(ctx, image.stem+".oat")
@@ -328,7 +340,7 @@
invocationPath := outputPath.ReplaceExtension(ctx, "invocation")
- cmd.Tool(global.SoongConfig.Dex2oat).
+ cmd.Tool(globalSoong.Dex2oat).
Flag("--avoid-storing-invocation").
FlagWithOutput("--write-invocation-to=", invocationPath).ImplicitOutput(invocationPath).
Flag("--runtime-arg").FlagWithArg("-Xms", global.Dex2oatImageXms).
@@ -431,7 +443,8 @@
Rebuild with ART_BOOT_IMAGE_EXTRA_ARGS="--runtime-arg -verbose:verifier" to see verification errors.`
func bootImageProfileRule(ctx android.SingletonContext, image *bootImage, missingDeps []string) android.WritablePath {
- global := dexpreoptGlobalConfig(ctx)
+ globalSoong := dexpreopt.GetCachedGlobalSoongConfig(ctx)
+ global := dexpreopt.GetGlobalConfig(ctx)
if global.DisableGenerateProfile || ctx.Config().IsPdkBuild() || ctx.Config().UnbundledBuild() {
return nil
@@ -462,7 +475,7 @@
rule.Command().
Text(`ANDROID_LOG_TAGS="*:e"`).
- Tool(global.SoongConfig.Profman).
+ Tool(globalSoong.Profman).
FlagWithInput("--create-profile-from=", bootImageProfile).
FlagForEachInput("--apk=", image.dexPathsDeps.Paths()).
FlagForEachArg("--dex-location=", image.dexLocationsDeps).
@@ -485,7 +498,8 @@
var bootImageProfileRuleKey = android.NewOnceKey("bootImageProfileRule")
func bootFrameworkProfileRule(ctx android.SingletonContext, image *bootImage, missingDeps []string) android.WritablePath {
- global := dexpreoptGlobalConfig(ctx)
+ globalSoong := dexpreopt.GetCachedGlobalSoongConfig(ctx)
+ global := dexpreopt.GetGlobalConfig(ctx)
if global.DisableGenerateProfile || ctx.Config().IsPdkBuild() || ctx.Config().UnbundledBuild() {
return nil
@@ -511,7 +525,7 @@
rule.Command().
Text(`ANDROID_LOG_TAGS="*:e"`).
- Tool(global.SoongConfig.Profman).
+ Tool(globalSoong.Profman).
Flag("--generate-boot-profile").
FlagWithInput("--create-profile-from=", bootFrameworkProfile).
FlagForEachInput("--apk=", image.dexPathsDeps.Paths()).
@@ -573,7 +587,7 @@
}
func writeGlobalConfigForMake(ctx android.SingletonContext, path android.WritablePath) {
- data := dexpreoptGlobalConfigRaw(ctx).data
+ data := dexpreopt.GetGlobalConfigRawData(ctx)
ctx.Build(pctx, android.BuildParams{
Rule: android.WriteFile,
diff --git a/java/dexpreopt_bootjars_test.go b/java/dexpreopt_bootjars_test.go
index 4ce30f6..c3b2133 100644
--- a/java/dexpreopt_bootjars_test.go
+++ b/java/dexpreopt_bootjars_test.go
@@ -49,7 +49,7 @@
pathCtx := android.PathContextForTesting(config)
dexpreoptConfig := dexpreopt.GlobalConfigForTests(pathCtx)
dexpreoptConfig.BootJars = []string{"foo", "bar", "baz"}
- setDexpreoptTestGlobalConfig(config, dexpreoptConfig)
+ dexpreopt.SetTestGlobalConfig(config, dexpreoptConfig)
ctx := testContext()
diff --git a/java/dexpreopt_config.go b/java/dexpreopt_config.go
index 31bec93..a1a9a76 100644
--- a/java/dexpreopt_config.go
+++ b/java/dexpreopt_config.go
@@ -22,57 +22,12 @@
"android/soong/dexpreopt"
)
-// dexpreoptGlobalConfig returns the global dexpreopt.config. It is loaded once the first time it is called for any
-// ctx.Config(), and returns the same data for all future calls with the same ctx.Config(). A value can be inserted
-// for tests using setDexpreoptTestGlobalConfig.
-func dexpreoptGlobalConfig(ctx android.PathContext) dexpreopt.GlobalConfig {
- return dexpreoptGlobalConfigRaw(ctx).global
-}
-
-type globalConfigAndRaw struct {
- global dexpreopt.GlobalConfig
- data []byte
-}
-
-func dexpreoptGlobalConfigRaw(ctx android.PathContext) globalConfigAndRaw {
- return ctx.Config().Once(dexpreoptGlobalConfigKey, func() interface{} {
- if data, err := ctx.Config().DexpreoptGlobalConfig(ctx); err != nil {
- panic(err)
- } else if data != nil {
- soongConfig := dexpreopt.CreateGlobalSoongConfig(ctx)
- globalConfig, err := dexpreopt.LoadGlobalConfig(ctx, data, soongConfig)
- if err != nil {
- panic(err)
- }
- return globalConfigAndRaw{globalConfig, data}
- }
-
- // No global config filename set, see if there is a test config set
- return ctx.Config().Once(dexpreoptTestGlobalConfigKey, func() interface{} {
- // Nope, return a config with preopting disabled
- return globalConfigAndRaw{dexpreopt.GlobalConfig{
- DisablePreopt: true,
- DisableGenerateProfile: true,
- }, nil}
- })
- }).(globalConfigAndRaw)
-}
-
-// setDexpreoptTestGlobalConfig sets a GlobalConfig that future calls to dexpreoptGlobalConfig will return. It must
-// be called before the first call to dexpreoptGlobalConfig for the config.
-func setDexpreoptTestGlobalConfig(config android.Config, globalConfig dexpreopt.GlobalConfig) {
- config.Once(dexpreoptTestGlobalConfigKey, func() interface{} { return globalConfigAndRaw{globalConfig, nil} })
-}
-
-var dexpreoptGlobalConfigKey = android.NewOnceKey("DexpreoptGlobalConfig")
-var dexpreoptTestGlobalConfigKey = android.NewOnceKey("TestDexpreoptGlobalConfig")
-
// systemServerClasspath returns the on-device locations of the modules in the system server classpath. It is computed
// once the first time it is called for any ctx.Config(), and returns the same slice for all future calls with the same
// ctx.Config().
func systemServerClasspath(ctx android.PathContext) []string {
return ctx.Config().OnceStringSlice(systemServerClasspathKey, func() []string {
- global := dexpreoptGlobalConfig(ctx)
+ global := dexpreopt.GetGlobalConfig(ctx)
var systemServerClasspathLocations []string
for _, m := range global.SystemServerJars {
@@ -133,7 +88,7 @@
func genBootImageConfigs(ctx android.PathContext) map[string]*bootImageConfig {
return ctx.Config().Once(bootImageConfigKey, func() interface{} {
- global := dexpreoptGlobalConfig(ctx)
+ global := dexpreopt.GetGlobalConfig(ctx)
targets := dexpreoptTargets(ctx)
deviceDir := android.PathForOutput(ctx, ctx.Config().DeviceName())
@@ -274,7 +229,7 @@
func defaultBootclasspath(ctx android.PathContext) []string {
return ctx.Config().OnceStringSlice(defaultBootclasspathKey, func() []string {
- global := dexpreoptGlobalConfig(ctx)
+ global := dexpreopt.GetGlobalConfig(ctx)
image := defaultBootImageConfig(ctx)
updatableBootclasspath := make([]string, len(global.UpdatableBootJars))
diff --git a/java/droiddoc.go b/java/droiddoc.go
index f62f5f9..a10ec81 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -1456,6 +1456,8 @@
func metalavaCmd(ctx android.ModuleContext, rule *android.RuleBuilder, javaVersion javaVersion, srcs android.Paths,
srcJarList android.Path, bootclasspath, classpath classpath, sourcepaths android.Paths) *android.RuleBuilderCommand {
+ // Metalava uses lots of memory, restrict the number of metalava jobs that can run in parallel.
+ rule.HighMem()
cmd := rule.Command().BuiltTool(ctx, "metalava").
Flag(config.JavacVmFlags).
FlagWithArg("-encoding ", "UTF-8").
diff --git a/java/java.go b/java/java.go
index 4c6a5a5..320cb7b 100644
--- a/java/java.go
+++ b/java/java.go
@@ -29,6 +29,7 @@
"github.com/google/blueprint/proptools"
"android/soong/android"
+ "android/soong/dexpreopt"
"android/soong/java/config"
"android/soong/tradefed"
)
@@ -79,6 +80,8 @@
ctx.RegisterModuleType("java_host_for_device", HostForDeviceFactory)
ctx.RegisterModuleType("dex_import", DexImportFactory)
+ ctx.FinalDepsMutators(dexpreopt.RegisterToolDepsMutator)
+
ctx.RegisterSingletonType("logtags", LogtagsSingleton)
ctx.RegisterSingletonType("kythe_java_extract", kytheExtractJavaFactory)
}
@@ -335,6 +338,7 @@
android.DefaultableModuleBase
android.ApexModuleBase
android.SdkBase
+ dexpreopt.DexPreoptModule
properties CompilerProperties
protoProperties android.ProtoProperties
@@ -1525,6 +1529,16 @@
}
} else {
outputFile = implementationAndResourcesJar
+
+ // dexpreopt.GetGlobalSoongConfig needs to be called at least once even if
+ // no module actually is dexpreopted, to ensure there's a cached
+ // GlobalSoongConfig for the dexpreopt singletons, which will run
+ // regardless.
+ // TODO(b/147613152): Remove when the singletons no longer rely on the
+ // cached GlobalSoongConfig.
+ if !dexpreopt.GetGlobalConfig(ctx).DisablePreopt {
+ _ = dexpreopt.GetGlobalSoongConfig(ctx)
+ }
}
ctx.CheckbuildFile(outputFile)
@@ -2275,6 +2289,7 @@
android.ApexModuleBase
prebuilt android.Prebuilt
android.SdkBase
+ dexpreopt.DexPreoptModule
properties ImportProperties
@@ -2477,6 +2492,7 @@
android.DefaultableModuleBase
android.ApexModuleBase
prebuilt android.Prebuilt
+ dexpreopt.DexPreoptModule
properties DexImportProperties
diff --git a/java/java_test.go b/java/java_test.go
index a2788cb..b724b4d 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -57,7 +57,15 @@
}
func testConfig(env map[string]string, bp string, fs map[string][]byte) android.Config {
- return TestConfig(buildDir, env, bp, fs)
+ bp += dexpreopt.BpToolModulesForTest()
+
+ config := TestConfig(buildDir, env, bp, fs)
+
+ // Set up the global Once cache used for dexpreopt.GlobalSoongConfig, so that
+ // it doesn't create a real one, which would fail.
+ _ = dexpreopt.GlobalSoongConfigForTests(config)
+
+ return config
}
func testContext() *android.TestContext {
@@ -86,6 +94,8 @@
cc.RegisterRequiredBuildComponentsForTest(ctx)
ctx.RegisterModuleType("ndk_prebuilt_shared_stl", cc.NdkPrebuiltSharedStlFactory)
+ dexpreopt.RegisterToolModulesForTest(ctx)
+
return ctx
}
@@ -93,7 +103,7 @@
t.Helper()
pathCtx := android.PathContextForTesting(config)
- setDexpreoptTestGlobalConfig(config, dexpreopt.GlobalConfigForTests(pathCtx))
+ dexpreopt.SetTestGlobalConfig(config, dexpreopt.GlobalConfigForTests(pathCtx))
ctx.Register(config)
_, errs := ctx.ParseBlueprintsFiles("Android.bp")
@@ -112,7 +122,7 @@
ctx := testContext()
pathCtx := android.PathContextForTesting(config)
- setDexpreoptTestGlobalConfig(config, dexpreopt.GlobalConfigForTests(pathCtx))
+ dexpreopt.SetTestGlobalConfig(config, dexpreopt.GlobalConfigForTests(pathCtx))
ctx.Register(config)
_, errs := ctx.ParseBlueprintsFiles("Android.bp")
diff --git a/java/sdk.go b/java/sdk.go
index 73b7dcf..2dbcf4a 100644
--- a/java/sdk.go
+++ b/java/sdk.go
@@ -15,14 +15,15 @@
package java
import (
- "android/soong/android"
- "android/soong/java/config"
"fmt"
"path/filepath"
"sort"
"strconv"
"strings"
+ "android/soong/android"
+ "android/soong/java/config"
+
"github.com/google/blueprint/pathtools"
)
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 9b30e2c..72c4a7c 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -838,16 +838,8 @@
Sdk_version *string
- Installable *bool
-
// List of shared java libs that this module has dependencies to
Libs []string
-
- // List of files to remove from the jar file(s)
- Exclude_files []string
-
- // List of directories to remove from the jar file(s)
- Exclude_dirs []string
}
type sdkLibraryImport struct {
diff --git a/python/python.go b/python/python.go
index c67c577..8b912be 100644
--- a/python/python.go
+++ b/python/python.go
@@ -340,6 +340,11 @@
// dependencies later.
ctx.AddFarVariationDependencies(ctx.Target().Variations(), launcherSharedLibTag, "libsqlite")
+ if ctx.Device() {
+ ctx.AddFarVariationDependencies(ctx.Target().Variations(), launcherSharedLibTag,
+ "liblog")
+ }
+
if ctx.Target().Os.Bionic() {
ctx.AddFarVariationDependencies(ctx.Target().Variations(), launcherSharedLibTag,
"libc", "libdl", "libm")
diff --git a/rust/rust.go b/rust/rust.go
index 14513fb..e2af6f0 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -85,6 +85,10 @@
return true
}
+func (mod *Module) RamdiskVariantNeeded(android.BaseModuleContext) bool {
+ return mod.InRamdisk()
+}
+
func (mod *Module) RecoveryVariantNeeded(android.BaseModuleContext) bool {
return mod.InRecovery()
}
@@ -152,6 +156,10 @@
panic(fmt.Errorf("Toc() called on non-library module: %q", mod.BaseModuleName()))
}
+func (mod *Module) OnlyInRamdisk() bool {
+ return false
+}
+
func (mod *Module) OnlyInRecovery() bool {
return false
}
diff --git a/sdk/bp.go b/sdk/bp.go
index 19fb70d..ae06a09 100644
--- a/sdk/bp.go
+++ b/sdk/bp.go
@@ -51,13 +51,23 @@
return s.properties[name]
}
-func (s *bpPropertySet) copy() bpPropertySet {
+func (s *bpPropertySet) deepCopy() *bpPropertySet {
propertiesCopy := make(map[string]interface{})
for p, v := range s.properties {
- propertiesCopy[p] = v
+ var valueCopy interface{}
+ if ps, ok := v.(*bpPropertySet); ok {
+ valueCopy = ps.deepCopy()
+ } else if values, ok := v.([]string); ok {
+ valuesCopy := make([]string, len(values))
+ copy(valuesCopy, values)
+ valueCopy = valuesCopy
+ } else {
+ valueCopy = v
+ }
+ propertiesCopy[p] = valueCopy
}
- return bpPropertySet{
+ return &bpPropertySet{
properties: propertiesCopy,
order: append([]string(nil), s.order...),
}
@@ -95,15 +105,15 @@
}
type bpModule struct {
- bpPropertySet
+ *bpPropertySet
moduleType string
}
var _ android.BpModule = (*bpModule)(nil)
-func (m *bpModule) copy() *bpModule {
+func (m *bpModule) deepCopy() *bpModule {
return &bpModule{
- bpPropertySet: m.bpPropertySet.copy(),
+ bpPropertySet: m.bpPropertySet.deepCopy(),
moduleType: m.moduleType,
}
}
@@ -134,8 +144,9 @@
func (f *bpFile) newModule(moduleType string) *bpModule {
module := &bpModule{
- moduleType: moduleType,
+ moduleType: moduleType,
+ bpPropertySet: &bpPropertySet{},
}
- (&module.bpPropertySet).init()
+ module.bpPropertySet.init()
return module
}
diff --git a/sdk/update.go b/sdk/update.go
index 5bc3b83..2731d50 100644
--- a/sdk/update.go
+++ b/sdk/update.go
@@ -196,7 +196,7 @@
for _, unversioned := range builder.prebuiltOrder {
// Copy the unversioned module so it can be modified to make it versioned.
- versioned := unversioned.copy()
+ versioned := unversioned.deepCopy()
name := versioned.properties["name"].(string)
versioned.setProperty("name", builder.versionedSdkMemberName(name))
versioned.insertAfter("name", "sdk_member_name", name)
@@ -286,7 +286,7 @@
for _, bpModule := range bpFile.order {
contents.Printfln("")
contents.Printfln("%s {", bpModule.moduleType)
- outputPropertySet(contents, &bpModule.bpPropertySet)
+ outputPropertySet(contents, bpModule.bpPropertySet)
contents.Printfln("}")
}
}
diff --git a/ui/build/build.go b/ui/build/build.go
index 69ef003..f3feac2 100644
--- a/ui/build/build.go
+++ b/ui/build/build.go
@@ -48,8 +48,11 @@
var combinedBuildNinjaTemplate = template.Must(template.New("combined").Parse(`
builddir = {{.OutDir}}
-pool local_pool
+{{if .UseRemoteBuild }}pool local_pool
depth = {{.Parallel}}
+{{end -}}
+pool highmem_pool
+ depth = {{.HighmemParallel}}
build _kati_always_build_: phony
{{if .HasKatiSuffix}}subninja {{.KatiBuildNinjaFile}}
subninja {{.KatiPackageNinjaFile}}
diff --git a/ui/build/config.go b/ui/build/config.go
index c084171..9b19ede 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -722,6 +722,33 @@
return c.parallel
}
+func (c *configImpl) HighmemParallel() int {
+ if i, ok := c.environ.GetInt("NINJA_HIGHMEM_NUM_JOBS"); ok {
+ return i
+ }
+
+ const minMemPerHighmemProcess = 8 * 1024 * 1024 * 1024
+ parallel := c.Parallel()
+ if c.UseRemoteBuild() {
+ // Ninja doesn't support nested pools, and when remote builds are enabled the total ninja parallelism
+ // is set very high (i.e. 500). Using a large value here would cause the total number of running jobs
+ // to be the sum of the sizes of the local and highmem pools, which will cause extra CPU contention.
+ // Return 1/16th of the size of the local pool, rounding up.
+ return (parallel + 15) / 16
+ } else if c.totalRAM == 0 {
+ // Couldn't detect the total RAM, don't restrict highmem processes.
+ return parallel
+ } else if c.totalRAM <= 32*1024*1024*1024 {
+ // Less than 32GB of ram, restrict to 2 highmem processes
+ return 2
+ } else if p := int(c.totalRAM / minMemPerHighmemProcess); p < parallel {
+ // If less than 8GB total RAM per process, reduce the number of highmem processes
+ return p
+ }
+ // No restriction on highmem processes
+ return parallel
+}
+
func (c *configImpl) TotalRAM() uint64 {
return c.totalRAM
}
@@ -782,10 +809,11 @@
// gomacc) are run in parallel. Note the parallelism of all other jobs is
// still limited by Parallel()
func (c *configImpl) RemoteParallel() int {
- if v, ok := c.environ.Get("NINJA_REMOTE_NUM_JOBS"); ok {
- if i, err := strconv.Atoi(v); err == nil {
- return i
- }
+ if !c.UseRemoteBuild() {
+ return 0
+ }
+ if i, ok := c.environ.GetInt("NINJA_REMOTE_NUM_JOBS"); ok {
+ return i
}
return 500
}
diff --git a/ui/build/config_darwin.go b/ui/build/config_darwin.go
index 480d8d1..fe74e31 100644
--- a/ui/build/config_darwin.go
+++ b/ui/build/config_darwin.go
@@ -22,7 +22,7 @@
func detectTotalRAM(ctx Context) uint64 {
s, err := syscall.Sysctl("hw.memsize")
if err != nil {
- ctx.Printf("Failed to get system memory size: %s")
+ ctx.Printf("Failed to get system memory size: %v", err)
return 0
}
@@ -32,7 +32,7 @@
}
if len(s) != 8 {
- ctx.Printf("Failed to get system memory size, returned %d bytes, 8", len(s))
+ ctx.Printf("Failed to get system memory size, returned %d bytes, expecting 8 bytes", len(s))
return 0
}
diff --git a/ui/build/config_linux.go b/ui/build/config_linux.go
index 9e1bdc7..162d372 100644
--- a/ui/build/config_linux.go
+++ b/ui/build/config_linux.go
@@ -20,9 +20,8 @@
var info syscall.Sysinfo_t
err := syscall.Sysinfo(&info)
if err != nil {
- ctx.Printf("Failed to get system memory size: %s")
+ ctx.Printf("Failed to get system memory size: %v", err)
return 0
}
- memBytes := uint64(info.Totalram) * uint64(info.Unit)
- return memBytes
+ return uint64(info.Totalram) * uint64(info.Unit)
}
diff --git a/ui/build/environment.go b/ui/build/environment.go
index d8ff7f2..9bca7c0 100644
--- a/ui/build/environment.go
+++ b/ui/build/environment.go
@@ -19,6 +19,7 @@
"fmt"
"io"
"os"
+ "strconv"
"strings"
)
@@ -44,6 +45,17 @@
return "", false
}
+// Get returns the int value associated with the key, and whether it exists
+// and is a valid int.
+func (e *Environment) GetInt(key string) (int, bool) {
+ if v, ok := e.Get(key); ok {
+ if i, err := strconv.Atoi(v); err == nil {
+ return i, true
+ }
+ }
+ return 0, false
+}
+
// Set sets the value associated with the key, overwriting the current value
// if it exists.
func (e *Environment) Set(key, value string) {