diff options
Diffstat (limited to 'android')
-rw-r--r-- | android/androidmk.go | 36 | ||||
-rw-r--r-- | android/apex.go | 46 | ||||
-rw-r--r-- | android/arch.go | 157 | ||||
-rw-r--r-- | android/arch_test.go | 4 | ||||
-rw-r--r-- | android/config.go | 2 | ||||
-rw-r--r-- | android/hooks.go | 10 | ||||
-rw-r--r-- | android/module.go | 52 | ||||
-rw-r--r-- | android/mutator.go | 7 | ||||
-rw-r--r-- | android/notices.go | 2 | ||||
-rw-r--r-- | android/package_ctx.go | 34 | ||||
-rw-r--r-- | android/paths.go | 91 | ||||
-rw-r--r-- | android/paths_test.go | 44 | ||||
-rw-r--r-- | android/prebuilt_etc.go | 6 | ||||
-rw-r--r-- | android/prebuilt_etc_test.go | 22 | ||||
-rw-r--r-- | android/sdk.go | 146 | ||||
-rw-r--r-- | android/sh_binary.go | 10 | ||||
-rw-r--r-- | android/singleton.go | 5 | ||||
-rw-r--r-- | android/util.go | 33 | ||||
-rw-r--r-- | android/util_test.go | 99 |
19 files changed, 588 insertions, 218 deletions
diff --git a/android/androidmk.go b/android/androidmk.go index 124523f4c..907134776 100644 --- a/android/androidmk.go +++ b/android/androidmk.go @@ -80,12 +80,14 @@ type AndroidMkEntries struct { footer bytes.Buffer ExtraEntries []AndroidMkExtraEntriesFunc + ExtraFooters []AndroidMkExtraFootersFunc EntryMap map[string][]string entryOrder []string } type AndroidMkExtraEntriesFunc func(entries *AndroidMkEntries) +type AndroidMkExtraFootersFunc func(w io.Writer, name, prefix, moduleDir string, entries *AndroidMkEntries) func (a *AndroidMkEntries) SetString(name, value string) { if _, ok := a.EntryMap[name]; !ok { @@ -94,6 +96,13 @@ func (a *AndroidMkEntries) SetString(name, value string) { a.EntryMap[name] = []string{value} } +func (a *AndroidMkEntries) SetPath(name string, path Path) { + if _, ok := a.EntryMap[name]; !ok { + a.entryOrder = append(a.entryOrder, name) + } + a.EntryMap[name] = []string{path.String()} +} + func (a *AndroidMkEntries) SetBoolIfTrue(name string, flag bool) { if flag { if _, ok := a.EntryMap[name]; !ok { @@ -103,6 +112,17 @@ func (a *AndroidMkEntries) SetBoolIfTrue(name string, flag bool) { } } +func (a *AndroidMkEntries) SetBool(name string, flag bool) { + if _, ok := a.EntryMap[name]; !ok { + a.entryOrder = append(a.entryOrder, name) + } + if flag { + a.EntryMap[name] = []string{"true"} + } else { + a.EntryMap[name] = []string{"false"} + } +} + func (a *AndroidMkEntries) AddStrings(name string, value ...string) { if len(value) == 0 { return @@ -254,9 +274,21 @@ func (a *AndroidMkEntries) fillInEntries(config Config, bpPath string, mod bluep // Write to footer. fmt.Fprintln(&a.footer, "include "+a.Include) + blueprintDir := filepath.Dir(bpPath) + for _, footerFunc := range a.ExtraFooters { + footerFunc(&a.footer, name, prefix, blueprintDir, a) + } } func (a *AndroidMkEntries) write(w io.Writer) { + if a.Disabled { + return + } + + if !a.OutputFile.Valid() { + return + } + w.Write(a.header.Bytes()) for _, name := range a.entryOrder { fmt.Fprintln(w, name+" := "+strings.Join(a.EntryMap[name], " ")) @@ -264,6 +296,10 @@ func (a *AndroidMkEntries) write(w io.Writer) { w.Write(a.footer.Bytes()) } +func (a *AndroidMkEntries) FooterLinesForTests() []string { + return strings.Split(string(a.footer.Bytes()), "\n") +} + func AndroidMkSingleton() Singleton { return &androidMkSingleton{} } diff --git a/android/apex.go b/android/apex.go index 99b13ab72..557febfc9 100644 --- a/android/apex.go +++ b/android/apex.go @@ -78,12 +78,23 @@ type ApexModule interface { // Return the no_apex property NoApex() bool + + // Tests if this module is available for the specified APEX or ":platform" + AvailableFor(what string) bool } type ApexProperties struct { // Whether this module should not be part of any APEX. Default is false. + // TODO(b/128708192): remove this as this is equal to apex_available: [":platform"] No_apex *bool + // Availability of this module in APEXes. Only the listed APEXes can include this module. + // "//apex_available:anyapex" is a pseudo APEX name that matches to any APEX. + // "//apex_available:platform" refers to non-APEX partitions like "system.img". + // Default is ["//apex_available:platform", "//apex_available:anyapex"]. + // TODO(b/128708192) change the default to ["//apex_available:platform"] + Apex_available []string + // Name of the apex variant that this module is mutated into ApexName string `blueprint:"mutated"` } @@ -136,15 +147,46 @@ func (m *ApexModuleBase) NoApex() bool { return proptools.Bool(m.ApexProperties.No_apex) } +const ( + availableToPlatform = "//apex_available:platform" + availableToAnyApex = "//apex_available:anyapex" +) + +func (m *ApexModuleBase) AvailableFor(what string) bool { + if len(m.ApexProperties.Apex_available) == 0 { + // apex_available defaults to ["//apex_available:platform", "//apex_available:anyapex"], + // which means 'available to everybody'. + return true + } + return InList(what, m.ApexProperties.Apex_available) || + (what != availableToPlatform && InList(availableToAnyApex, m.ApexProperties.Apex_available)) +} + +func (m *ApexModuleBase) checkApexAvailableProperty(mctx BaseModuleContext) { + for _, n := range m.ApexProperties.Apex_available { + if n == availableToPlatform || n == availableToAnyApex { + continue + } + if !mctx.OtherModuleExists(n) { + mctx.PropertyErrorf("apex_available", "%q is not a valid module name", n) + } + } +} + func (m *ApexModuleBase) CreateApexVariations(mctx BottomUpMutatorContext) []blueprint.Module { if len(m.apexVariations) > 0 { + m.checkApexAvailableProperty(mctx) sort.Strings(m.apexVariations) - variations := []string{""} // Original variation for platform + variations := []string{} + availableForPlatform := m.AvailableFor(availableToPlatform) + if availableForPlatform { + variations = append(variations, "") // Original variation for platform + } variations = append(variations, m.apexVariations...) modules := mctx.CreateVariations(variations...) for i, m := range modules { - if i == 0 { + if availableForPlatform && i == 0 { continue } m.(ApexModule).setApexName(variations[i]) diff --git a/android/arch.go b/android/arch.go index b0389560b..348b06492 100644 --- a/android/arch.go +++ b/android/arch.go @@ -854,149 +854,11 @@ func decodeMultilib(base *ModuleBase, class OsClass) (multilib, extraMultilib st } } -func filterArchStructFields(fields []reflect.StructField) (filteredFields []reflect.StructField, filtered bool) { - for _, field := range fields { - if !proptools.HasTag(field, "android", "arch_variant") { - filtered = true - continue - } - - // The arch_variant field isn't necessary past this point - // Instead of wasting space, just remove it. Go also has a - // 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 = "" - } - - // Recurse into structs - switch field.Type.Kind() { - case reflect.Struct: - var subFiltered bool - field.Type, subFiltered = filterArchStruct(field.Type) - filtered = filtered || subFiltered - if field.Type == nil { - continue - } - case reflect.Ptr: - if field.Type.Elem().Kind() == reflect.Struct { - nestedType, subFiltered := filterArchStruct(field.Type.Elem()) - filtered = filtered || subFiltered - if nestedType == nil { - continue - } - field.Type = reflect.PtrTo(nestedType) - } - case reflect.Interface: - panic("Interfaces are not supported in arch_variant properties") - } - - filteredFields = append(filteredFields, field) - } - - return filteredFields, filtered -} - -// filterArchStruct takes a reflect.Type that is either a sturct or a pointer to a struct, and returns a reflect.Type -// that only contains the fields in the original type that have an `android:"arch_variant"` struct tag, and a bool -// that is true if the new struct type has fewer fields than the original type. If there are no fields in the -// original type with the struct tag it returns nil and true. -func filterArchStruct(prop reflect.Type) (filteredProp reflect.Type, filtered bool) { - var fields []reflect.StructField - - ptr := prop.Kind() == reflect.Ptr - if ptr { - prop = prop.Elem() - } - - for i := 0; i < prop.NumField(); i++ { - fields = append(fields, prop.Field(i)) - } - - filteredFields, filtered := filterArchStructFields(fields) - - if len(filteredFields) == 0 { - return nil, true - } - - if !filtered { - if ptr { - return reflect.PtrTo(prop), false - } - return prop, false - } - - ret := reflect.StructOf(filteredFields) - if ptr { - ret = reflect.PtrTo(ret) - } - - return ret, true -} - -// filterArchStruct takes a reflect.Type that is either a sturct or a pointer to a struct, and returns a list of -// reflect.Type that only contains the fields in the original type that have an `android:"arch_variant"` struct tag, -// and a bool that is true if the new struct type has fewer fields than the original type. If there are no fields in -// the original type with the struct tag it returns nil and true. Each returned struct type will have a maximum of -// 10 top level fields in it to attempt to avoid hitting the reflect.StructOf name length limit, although the limit -// can still be reached with a single struct field with many fields in it. -func filterArchStructSharded(prop reflect.Type) (filteredProp []reflect.Type, filtered bool) { - var fields []reflect.StructField - - ptr := prop.Kind() == reflect.Ptr - if ptr { - prop = prop.Elem() - } - - for i := 0; i < prop.NumField(); i++ { - fields = append(fields, prop.Field(i)) - } - - fields, filtered = filterArchStructFields(fields) - if !filtered { - if ptr { - return []reflect.Type{reflect.PtrTo(prop)}, false - } - return []reflect.Type{prop}, false - } - - if len(fields) == 0 { - return nil, true - } - - shards := shardFields(fields, 10) - - for _, shard := range shards { - s := reflect.StructOf(shard) - if ptr { - s = reflect.PtrTo(s) - } - filteredProp = append(filteredProp, s) - } - - return filteredProp, true -} - -func shardFields(fields []reflect.StructField, shardSize int) [][]reflect.StructField { - ret := make([][]reflect.StructField, 0, (len(fields)+shardSize-1)/shardSize) - for len(fields) > shardSize { - ret = append(ret, fields[0:shardSize]) - fields = fields[shardSize:] - } - if len(fields) > 0 { - ret = append(ret, fields) - } - return ret -} - // 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, _ := filterArchStructSharded(props) + propShards, _ := proptools.FilterPropertyStructSharded(props, filterArchStruct) if len(propShards) == 0 { return nil } @@ -1095,6 +957,23 @@ func createArchType(props reflect.Type) []reflect.Type { return ret } +func filterArchStruct(field reflect.StructField, prefix string) (bool, reflect.StructField) { + if proptools.HasTag(field, "android", "arch_variant") { + // The arch_variant field isn't necessary past this point + // Instead of wasting space, just remove it. Go also has a + // 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 = "" + } + return true, field + } + return false, field +} + var archPropTypeMap OncePer func InitArchModule(m Module) { diff --git a/android/arch_test.go b/android/arch_test.go index 0589e6c3a..11edb4f1c 100644 --- a/android/arch_test.go +++ b/android/arch_test.go @@ -17,6 +17,8 @@ package android import ( "reflect" "testing" + + "github.com/google/blueprint/proptools" ) type Named struct { @@ -219,7 +221,7 @@ func TestFilterArchStruct(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - out, filtered := filterArchStruct(reflect.TypeOf(test.in)) + out, filtered := proptools.FilterPropertyStruct(reflect.TypeOf(test.in), filterArchStruct) if filtered != test.filtered { t.Errorf("expected filtered %v, got %v", test.filtered, filtered) } diff --git a/android/config.go b/android/config.go index 209b4ded9..26c4e6ebd 100644 --- a/android/config.go +++ b/android/config.go @@ -813,7 +813,7 @@ func (c *config) ArtUseReadBarrier() bool { func (c *config) EnforceRROForModule(name string) bool { enforceList := c.productVariables.EnforceRROTargets if enforceList != nil { - if len(enforceList) == 1 && (enforceList)[0] == "*" { + if InList("*", enforceList) { return true } return InList(name, enforceList) diff --git a/android/hooks.go b/android/hooks.go index 58109961b..604cb9c28 100644 --- a/android/hooks.go +++ b/android/hooks.go @@ -30,7 +30,7 @@ type LoadHookContext interface { BaseModuleContext AppendProperties(...interface{}) PrependProperties(...interface{}) - CreateModule(blueprint.ModuleFactory, ...interface{}) + CreateModule(ModuleFactory, ...interface{}) Module } // Arch hooks are run after the module has been split into architecture variants, and can be used @@ -75,7 +75,7 @@ func (x *hooks) runArchHooks(ctx ArchHookContext, m *ModuleBase) { type InstallHookContext interface { ModuleContext - Path() OutputPath + Path() InstallPath Symlink() bool } @@ -89,11 +89,11 @@ func AddInstallHook(m blueprint.Module, hook func(InstallHookContext)) { type installHookContext struct { ModuleContext - path OutputPath + path InstallPath symlink bool } -func (x *installHookContext) Path() OutputPath { +func (x *installHookContext) Path() InstallPath { return x.path } @@ -101,7 +101,7 @@ func (x *installHookContext) Symlink() bool { return x.symlink } -func (x *hooks) runInstallHooks(ctx ModuleContext, path OutputPath, symlink bool) { +func (x *hooks) runInstallHooks(ctx ModuleContext, path InstallPath, symlink bool) { if len(x.install) > 0 { mctx := &installHookContext{ ModuleContext: ctx, diff --git a/android/module.go b/android/module.go index 8076a99ff..6988ac0d4 100644 --- a/android/module.go +++ b/android/module.go @@ -147,15 +147,17 @@ type ModuleContext interface { ExpandSource(srcFile, prop string) Path ExpandOptionalSource(srcFile *string, prop string) OptionalPath - InstallExecutable(installPath OutputPath, name string, srcPath Path, deps ...Path) OutputPath - InstallFile(installPath OutputPath, name string, srcPath Path, deps ...Path) OutputPath - InstallSymlink(installPath OutputPath, name string, srcPath OutputPath) OutputPath - InstallAbsoluteSymlink(installPath OutputPath, name string, absPath string) OutputPath + InstallExecutable(installPath InstallPath, name string, srcPath Path, deps ...Path) InstallPath + InstallFile(installPath InstallPath, name string, srcPath Path, deps ...Path) InstallPath + InstallSymlink(installPath InstallPath, name string, srcPath InstallPath) InstallPath + InstallAbsoluteSymlink(installPath InstallPath, name string, absPath string) InstallPath CheckbuildFile(srcPath Path) InstallInData() bool + InstallInTestcases() bool InstallInSanitizerDir() bool InstallInRecovery() bool + InstallInRoot() bool InstallBypassMake() bool RequiredModuleNames() []string @@ -192,8 +194,10 @@ type Module interface { Enabled() bool Target() Target InstallInData() bool + InstallInTestcases() bool InstallInSanitizerDir() bool InstallInRecovery() bool + InstallInRoot() bool InstallBypassMake() bool SkipInstall() ExportedToMake() bool @@ -832,6 +836,10 @@ func (m *ModuleBase) InstallInData() bool { return false } +func (m *ModuleBase) InstallInTestcases() bool { + return false +} + func (m *ModuleBase) InstallInSanitizerDir() bool { return false } @@ -840,6 +848,10 @@ func (m *ModuleBase) InstallInRecovery() bool { return Bool(m.commonProperties.Recovery) } +func (m *ModuleBase) InstallInRoot() bool { + return false +} + func (m *ModuleBase) InstallBypassMake() bool { return false } @@ -1177,6 +1189,12 @@ func (m *moduleContext) Variable(pctx PackageContext, name, value string) { func (m *moduleContext) Rule(pctx PackageContext, name string, params blueprint.RuleParams, argNames ...string) blueprint.Rule { + if m.config.UseGoma() && params.Pool == nil { + // When USE_GOMA=true is set and the rule is not supported by goma, restrict jobs to the + // local parallelism value + params.Pool = localPool + } + rule := m.bp.Rule(pctx.PackageContext, name, params, argNames...) if m.config.captureBuild { @@ -1504,6 +1522,10 @@ func (m *moduleContext) InstallInData() bool { return m.module.InstallInData() } +func (m *moduleContext) InstallInTestcases() bool { + return m.module.InstallInTestcases() +} + func (m *moduleContext) InstallInSanitizerDir() bool { return m.module.InstallInSanitizerDir() } @@ -1512,11 +1534,15 @@ func (m *moduleContext) InstallInRecovery() bool { return m.module.InstallInRecovery() } +func (m *moduleContext) InstallInRoot() bool { + return m.module.InstallInRoot() +} + func (m *moduleContext) InstallBypassMake() bool { return m.module.InstallBypassMake() } -func (m *moduleContext) skipInstall(fullInstallPath OutputPath) bool { +func (m *moduleContext) skipInstall(fullInstallPath InstallPath) bool { if m.module.base().commonProperties.SkipInstall { return true } @@ -1541,18 +1567,18 @@ func (m *moduleContext) skipInstall(fullInstallPath OutputPath) bool { return false } -func (m *moduleContext) InstallFile(installPath OutputPath, name string, srcPath Path, - deps ...Path) OutputPath { +func (m *moduleContext) InstallFile(installPath InstallPath, name string, srcPath Path, + deps ...Path) InstallPath { return m.installFile(installPath, name, srcPath, Cp, deps) } -func (m *moduleContext) InstallExecutable(installPath OutputPath, name string, srcPath Path, - deps ...Path) OutputPath { +func (m *moduleContext) InstallExecutable(installPath InstallPath, name string, srcPath Path, + deps ...Path) InstallPath { return m.installFile(installPath, name, srcPath, CpExecutable, deps) } -func (m *moduleContext) installFile(installPath OutputPath, name string, srcPath Path, - rule blueprint.Rule, deps []Path) OutputPath { +func (m *moduleContext) installFile(installPath InstallPath, name string, srcPath Path, + rule blueprint.Rule, deps []Path) InstallPath { fullInstallPath := installPath.Join(m, name) m.module.base().hooks.runInstallHooks(m, fullInstallPath, false) @@ -1587,7 +1613,7 @@ func (m *moduleContext) installFile(installPath OutputPath, name string, srcPath return fullInstallPath } -func (m *moduleContext) InstallSymlink(installPath OutputPath, name string, srcPath OutputPath) OutputPath { +func (m *moduleContext) InstallSymlink(installPath InstallPath, name string, srcPath InstallPath) InstallPath { fullInstallPath := installPath.Join(m, name) m.module.base().hooks.runInstallHooks(m, fullInstallPath, true) @@ -1616,7 +1642,7 @@ func (m *moduleContext) InstallSymlink(installPath OutputPath, name string, srcP // installPath/name -> absPath where absPath might be a path that is available only at runtime // (e.g. /apex/...) -func (m *moduleContext) InstallAbsoluteSymlink(installPath OutputPath, name string, absPath string) OutputPath { +func (m *moduleContext) InstallAbsoluteSymlink(installPath InstallPath, name string, absPath string) InstallPath { fullInstallPath := installPath.Join(m, name) m.module.base().hooks.runInstallHooks(m, fullInstallPath, true) diff --git a/android/mutator.go b/android/mutator.go index e76f847c9..510e63c0c 100644 --- a/android/mutator.go +++ b/android/mutator.go @@ -121,7 +121,7 @@ type TopDownMutatorContext interface { Rename(name string) - CreateModule(blueprint.ModuleFactory, ...interface{}) + CreateModule(ModuleFactory, ...interface{}) Module } type topDownMutatorContext struct { @@ -243,9 +243,10 @@ func (t *topDownMutatorContext) Rename(name string) { t.Module().base().commonProperties.DebugName = name } -func (t *topDownMutatorContext) CreateModule(factory blueprint.ModuleFactory, props ...interface{}) { +func (t *topDownMutatorContext) CreateModule(factory ModuleFactory, props ...interface{}) Module { inherited := []interface{}{&t.Module().base().commonProperties, &t.Module().base().variableProperties} - t.bp.CreateModule(factory, append(inherited, props...)...) + module := t.bp.CreateModule(ModuleFactoryAdaptor(factory), append(inherited, props...)...).(Module) + return module } func (b *bottomUpMutatorContext) MutatorName() string { diff --git a/android/notices.go b/android/notices.go index 7b61d65ba..bf273b544 100644 --- a/android/notices.go +++ b/android/notices.go @@ -60,7 +60,7 @@ func MergeNotices(ctx ModuleContext, mergedNotice WritablePath, noticePaths []Pa }) } -func BuildNoticeOutput(ctx ModuleContext, installPath OutputPath, installFilename string, +func BuildNoticeOutput(ctx ModuleContext, installPath InstallPath, installFilename string, noticePaths []Path) NoticeOutputs { // Merge all NOTICE files into one. // TODO(jungjw): We should just produce a well-formatted NOTICE.html file in a single pass. diff --git a/android/package_ctx.go b/android/package_ctx.go index 00b99ff62..548450e44 100644 --- a/android/package_ctx.go +++ b/android/package_ctx.go @@ -104,7 +104,8 @@ func (p PackageContext) PoolFunc(name string, } // RuleFunc wraps blueprint.PackageContext.RuleFunc, converting the interface{} config -// argument to a Context that supports Config(). +// argument to a Context that supports Config(), and provides a default Pool if none is +// specified. func (p PackageContext) RuleFunc(name string, f func(PackageRuleContext) blueprint.RuleParams, argNames ...string) blueprint.Rule { @@ -114,6 +115,11 @@ func (p PackageContext) RuleFunc(name string, if len(ctx.errors) > 0 { return params, ctx.errors[0] } + if ctx.Config().UseGoma() && params.Pool == nil { + // When USE_GOMA=true is set and the rule is not supported by goma, restrict jobs to the + // local parallelism value + params.Pool = localPool + } return params, nil }, argNames...) } @@ -234,10 +240,16 @@ func (p PackageContext) PrefixedExistentPathsForSourcesVariable( }) } -// AndroidStaticRule wraps blueprint.StaticRule and provides a default Pool if none is specified +// AndroidStaticRule is an alias for StaticRule. func (p PackageContext) AndroidStaticRule(name string, params blueprint.RuleParams, argNames ...string) blueprint.Rule { - return p.AndroidRuleFunc(name, func(PackageRuleContext) blueprint.RuleParams { + return p.StaticRule(name, params, argNames...) +} + +// StaticRule wraps blueprint.StaticRule and provides a default Pool if none is specified. +func (p PackageContext) StaticRule(name string, params blueprint.RuleParams, + argNames ...string) blueprint.Rule { + return p.RuleFunc(name, func(PackageRuleContext) blueprint.RuleParams { return params }, argNames...) } @@ -245,18 +257,6 @@ func (p PackageContext) AndroidStaticRule(name string, params blueprint.RulePara // AndroidGomaStaticRule wraps blueprint.StaticRule but uses goma's parallelism if goma is enabled func (p PackageContext) AndroidGomaStaticRule(name string, params blueprint.RuleParams, argNames ...string) blueprint.Rule { - return p.StaticRule(name, params, argNames...) -} - -func (p PackageContext) AndroidRuleFunc(name string, - f func(PackageRuleContext) blueprint.RuleParams, argNames ...string) blueprint.Rule { - return p.RuleFunc(name, func(ctx PackageRuleContext) blueprint.RuleParams { - params := f(ctx) - if ctx.Config().UseGoma() && params.Pool == nil { - // When USE_GOMA=true is set and the rule is not supported by goma, restrict jobs to the - // local parallelism value - params.Pool = localPool - } - return params - }, argNames...) + // bypass android.PackageContext.StaticRule so that Pool does not get set to local_pool. + return p.PackageContext.StaticRule(name, params, argNames...) } diff --git a/android/paths.go b/android/paths.go index 0d64a61db..8dbb08644 100644 --- a/android/paths.go +++ b/android/paths.go @@ -44,8 +44,10 @@ type ModuleInstallPathContext interface { BaseModuleContext InstallInData() bool + InstallInTestcases() bool InstallInSanitizerDir() bool InstallInRecovery() bool + InstallInRoot() bool InstallBypassMake() bool } @@ -791,7 +793,7 @@ func (p SourcePath) OverlayPath(ctx ModuleContext, path Path) OptionalPath { return OptionalPathForPath(PathForSource(ctx, relPath)) } -// OutputPath is a Path representing a file path rooted from the build directory +// OutputPath is a Path representing an intermediates file path rooted from the build directory type OutputPath struct { basePath } @@ -819,17 +821,6 @@ func PathForOutput(ctx PathContext, pathComponents ...string) OutputPath { return OutputPath{basePath{path, ctx.Config(), ""}} } -// pathForInstallInMakeDir is used by PathForModuleInstall when the module returns true -// for InstallBypassMake to produce an OutputPath that installs to $OUT_DIR instead of -// $OUT_DIR/soong. -func pathForInstallInMakeDir(ctx PathContext, pathComponents ...string) OutputPath { - path, err := validatePath(pathComponents...) - if err != nil { - reportPathError(ctx, err) - } - return OutputPath{basePath{"../" + path, ctx.Config(), ""}} -} - // PathsForOutput returns Paths rooted from buildDir func PathsForOutput(ctx PathContext, paths []string) WritablePaths { ret := make(WritablePaths, len(paths)) @@ -845,10 +836,6 @@ func (p OutputPath) String() string { return filepath.Join(p.config.buildDir, p.path) } -func (p OutputPath) RelPathString() string { - return p.path -} - // Join creates a new OutputPath with paths... joined with the current path. The // provided paths... may not use '..' to escape from the current path. func (p OutputPath) Join(ctx PathContext, paths ...string) OutputPath { @@ -1117,9 +1104,44 @@ func PathForModuleRes(ctx ModuleContext, pathComponents ...string) ModuleResPath return ModuleResPath{PathForModuleOut(ctx, "res", p)} } +// InstallPath is a Path representing a installed file path rooted from the build directory +type InstallPath struct { + basePath + + baseDir string // "../" for Make paths to convert "out/soong" to "out", "" for Soong paths +} + +func (p InstallPath) writablePath() {} + +func (p InstallPath) String() string { + return filepath.Join(p.config.buildDir, p.baseDir, p.path) +} + +// Join creates a new InstallPath with paths... joined with the current path. The +// provided paths... may not use '..' to escape from the current path. +func (p InstallPath) Join(ctx PathContext, paths ...string) InstallPath { + path, err := validatePath(paths...) + if err != nil { + reportPathError(ctx, err) + } + return p.withRel(path) +} + +func (p InstallPath) withRel(rel string) InstallPath { + p.basePath = p.basePath.withRel(rel) + return p +} + +// ToMakePath returns a new InstallPath that points to Make's install directory instead of Soong's, +// i.e. out/ instead of out/soong/. +func (p InstallPath) ToMakePath() InstallPath { + p.baseDir = "../" + return p +} + // PathForModuleInstall returns a Path representing the install path for the // module appended with paths... -func PathForModuleInstall(ctx ModuleInstallPathContext, pathComponents ...string) OutputPath { +func PathForModuleInstall(ctx ModuleInstallPathContext, pathComponents ...string) InstallPath { var outPaths []string if ctx.Device() { partition := modulePartition(ctx) @@ -1139,13 +1161,30 @@ func PathForModuleInstall(ctx ModuleInstallPathContext, pathComponents ...string outPaths = append([]string{"debug"}, outPaths...) } outPaths = append(outPaths, pathComponents...) + + path, err := validatePath(outPaths...) + if err != nil { + reportPathError(ctx, err) + } + + ret := InstallPath{basePath{path, ctx.Config(), ""}, ""} if ctx.InstallBypassMake() && ctx.Config().EmbeddedInMake() { - return pathForInstallInMakeDir(ctx, outPaths...) + ret = ret.ToMakePath() } - return PathForOutput(ctx, outPaths...) + + return ret } -func InstallPathToOnDevicePath(ctx PathContext, path OutputPath) string { +func PathForNdkInstall(ctx PathContext, paths ...string) InstallPath { + paths = append([]string{"ndk"}, paths...) + path, err := validatePath(paths...) + if err != nil { + reportPathError(ctx, err) + } + return InstallPath{basePath{path, ctx.Config(), ""}, ""} +} + +func InstallPathToOnDevicePath(ctx PathContext, path InstallPath) string { rel := Rel(ctx, PathForOutput(ctx, "target", "product", ctx.Config().DeviceName()).String(), path.String()) return "/" + rel @@ -1155,9 +1194,15 @@ func modulePartition(ctx ModuleInstallPathContext) string { var partition string if ctx.InstallInData() { partition = "data" + } else if ctx.InstallInTestcases() { + partition = "testcases" } else if ctx.InstallInRecovery() { - // the layout of recovery partion is the same as that of system partition - partition = "recovery/root/system" + if ctx.InstallInRoot() { + partition = "recovery/root" + } else { + // the layout of recovery partion is the same as that of system partition + partition = "recovery/root/system" + } } else if ctx.SocSpecific() { partition = ctx.DeviceConfig().VendorPath() } else if ctx.DeviceSpecific() { @@ -1166,6 +1211,8 @@ func modulePartition(ctx ModuleInstallPathContext) string { partition = ctx.DeviceConfig().ProductPath() } else if ctx.SystemExtSpecific() { partition = ctx.DeviceConfig().SystemExtPath() + } else if ctx.InstallInRoot() { + partition = "root" } else { partition = "system" } diff --git a/android/paths_test.go b/android/paths_test.go index f2996bff8..2e67272e0 100644 --- a/android/paths_test.go +++ b/android/paths_test.go @@ -201,8 +201,10 @@ type moduleInstallPathContextImpl struct { baseModuleContext inData bool + inTestcases bool inSanitizerDir bool inRecovery bool + inRoot bool } func (moduleInstallPathContextImpl) Fs() pathtools.FileSystem { @@ -219,6 +221,10 @@ func (m moduleInstallPathContextImpl) InstallInData() bool { return m.inData } +func (m moduleInstallPathContextImpl) InstallInTestcases() bool { + return m.inTestcases +} + func (m moduleInstallPathContextImpl) InstallInSanitizerDir() bool { return m.inSanitizerDir } @@ -227,6 +233,10 @@ func (m moduleInstallPathContextImpl) InstallInRecovery() bool { return m.inRecovery } +func (m moduleInstallPathContextImpl) InstallInRoot() bool { + return m.inRoot +} + func (m moduleInstallPathContextImpl) InstallBypassMake() bool { return false } @@ -308,6 +318,40 @@ func TestPathForModuleInstall(t *testing.T) { in: []string{"bin", "my_test"}, out: "target/product/test_device/system_ext/bin/my_test", }, + { + name: "root binary", + ctx: &moduleInstallPathContextImpl{ + baseModuleContext: baseModuleContext{ + target: deviceTarget, + }, + inRoot: true, + }, + in: []string{"my_test"}, + out: "target/product/test_device/root/my_test", + }, + { + name: "recovery binary", + ctx: &moduleInstallPathContextImpl{ + baseModuleContext: baseModuleContext{ + target: deviceTarget, + }, + inRecovery: true, + }, + in: []string{"bin/my_test"}, + out: "target/product/test_device/recovery/root/system/bin/my_test", + }, + { + name: "recovery root binary", + ctx: &moduleInstallPathContextImpl{ + baseModuleContext: baseModuleContext{ + target: deviceTarget, + }, + inRecovery: true, + inRoot: true, + }, + in: []string{"my_test"}, + out: "target/product/test_device/recovery/root/my_test", + }, { name: "system native test binary", diff --git a/android/prebuilt_etc.go b/android/prebuilt_etc.go index d29ed16f6..6c4813bd1 100644 --- a/android/prebuilt_etc.go +++ b/android/prebuilt_etc.go @@ -65,7 +65,7 @@ type PrebuiltEtc struct { installDirBase string // The base install location when soc_specific property is set to true, e.g. "firmware" for prebuilt_firmware. socInstallDirBase string - installDirPath OutputPath + installDirPath InstallPath additionalDependencies *Paths } @@ -91,7 +91,7 @@ func (p *PrebuiltEtc) SourceFilePath(ctx ModuleContext) Path { return PathForModuleSrc(ctx, String(p.properties.Src)) } -func (p *PrebuiltEtc) InstallDirPath() OutputPath { +func (p *PrebuiltEtc) InstallDirPath() InstallPath { return p.installDirPath } @@ -158,7 +158,7 @@ func (p *PrebuiltEtc) AndroidMkEntries() AndroidMkEntries { ExtraEntries: []AndroidMkExtraEntriesFunc{ func(entries *AndroidMkEntries) { entries.SetString("LOCAL_MODULE_TAGS", "optional") - entries.SetString("LOCAL_MODULE_PATH", "$(OUT_DIR)/"+p.installDirPath.RelPathString()) + entries.SetString("LOCAL_MODULE_PATH", p.installDirPath.ToMakePath().String()) entries.SetString("LOCAL_INSTALLED_MODULE_STEM", p.outputFilePath.Base()) entries.SetString("LOCAL_UNINSTALLABLE_MODULE", strconv.FormatBool(!p.Installable())) if p.additionalDependencies != nil { diff --git a/android/prebuilt_etc_test.go b/android/prebuilt_etc_test.go index 0a2c7a4f5..f675ea3c3 100644 --- a/android/prebuilt_etc_test.go +++ b/android/prebuilt_etc_test.go @@ -182,9 +182,9 @@ func TestPrebuiltUserShareInstallDirPath(t *testing.T) { `) p := ctx.ModuleForTests("foo.conf", "android_arm64_armv8-a_core").Module().(*PrebuiltEtc) - expected := "target/product/test_device/system/usr/share/bar" - if p.installDirPath.RelPathString() != expected { - t.Errorf("expected %q, got %q", expected, p.installDirPath.RelPathString()) + expected := buildDir + "/target/product/test_device/system/usr/share/bar" + if p.installDirPath.String() != expected { + t.Errorf("expected %q, got %q", expected, p.installDirPath.String()) } } @@ -199,9 +199,9 @@ func TestPrebuiltUserShareHostInstallDirPath(t *testing.T) { buildOS := BuildOs.String() p := ctx.ModuleForTests("foo.conf", buildOS+"_common").Module().(*PrebuiltEtc) - expected := filepath.Join("host", config.PrebuiltOS(), "usr", "share", "bar") - if p.installDirPath.RelPathString() != expected { - t.Errorf("expected %q, got %q", expected, p.installDirPath.RelPathString()) + expected := filepath.Join(buildDir, "host", config.PrebuiltOS(), "usr", "share", "bar") + if p.installDirPath.String() != expected { + t.Errorf("expected %q, got %q", expected, p.installDirPath.String()) } } @@ -214,14 +214,14 @@ func TestPrebuiltFontInstallDirPath(t *testing.T) { `) p := ctx.ModuleForTests("foo.conf", "android_arm64_armv8-a_core").Module().(*PrebuiltEtc) - expected := "target/product/test_device/system/fonts" - if p.installDirPath.RelPathString() != expected { - t.Errorf("expected %q, got %q", expected, p.installDirPath.RelPathString()) + expected := buildDir + "/target/product/test_device/system/fonts" + if p.installDirPath.String() != expected { + t.Errorf("expected %q, got %q", expected, p.installDirPath.String()) } } func TestPrebuiltFirmwareDirPath(t *testing.T) { - targetPath := "target/product/test_device" + targetPath := buildDir + "/target/product/test_device" tests := []struct { description string config string @@ -249,7 +249,7 @@ func TestPrebuiltFirmwareDirPath(t *testing.T) { t.Run(tt.description, func(t *testing.T) { ctx, _ := testPrebuiltEtc(t, tt.config) p := ctx.ModuleForTests("foo.conf", "android_arm64_armv8-a_core").Module().(*PrebuiltEtc) - if p.installDirPath.RelPathString() != tt.expectedPath { + if p.installDirPath.String() != tt.expectedPath { t.Errorf("expected %q, got %q", tt.expectedPath, p.installDirPath) } }) diff --git a/android/sdk.go b/android/sdk.go new file mode 100644 index 000000000..52c392f4d --- /dev/null +++ b/android/sdk.go @@ -0,0 +1,146 @@ +// Copyright (C) 2019 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package android + +import ( + "strings" + + "github.com/google/blueprint/proptools" +) + +// SdkAware is the interface that must be supported by any module to become a member of SDK or to be +// built with SDK +type SdkAware interface { + Module + sdkBase() *SdkBase + MakeMemberOf(sdk SdkRef) + IsInAnySdk() bool + ContainingSdk() SdkRef + MemberName() string + BuildWithSdks(sdks SdkRefs) + RequiredSdks() SdkRefs +} + +// SdkRef refers to a version of an SDK +type SdkRef struct { + Name string + Version string +} + +const ( + // currentVersion refers to the in-development version of an SDK + currentVersion = "current" +) + +// IsCurrentVersion determines if the SdkRef is referencing to an in-development version of an SDK +func (s SdkRef) IsCurrentVersion() bool { + return s.Version == currentVersion +} + +// IsCurrentVersionOf determines if the SdkRef is referencing to an in-development version of the +// specified SDK +func (s SdkRef) IsCurrentVersionOf(name string) bool { + return s.Name == name && s.IsCurrentVersion() +} + +// ParseSdkRef parses a `name#version` style string into a corresponding SdkRef struct +func ParseSdkRef(ctx BaseModuleContext, str string, property string) SdkRef { + tokens := strings.Split(str, "#") + if len(tokens) < 1 || len(tokens) > 2 { + ctx.PropertyErrorf(property, "%q does not follow name#version syntax", str) + return SdkRef{Name: "invalid sdk name", Version: "invalid sdk version"} + } + + name := tokens[0] + + version := currentVersion // If version is omitted, defaults to "current" + if len(tokens) == 2 { + version = tokens[1] + } + + return SdkRef{Name: name, Version: version} +} + +type SdkRefs []SdkRef + +func (refs SdkRefs) Contains(s SdkRef) bool { + for _, r := range refs { + if r == s { + return true + } + } + return false +} + +type sdkProperties struct { + // The SDK that this module is a member of. nil if it is not a member of any SDK + ContainingSdk *SdkRef `blueprint:"mutated"` + + // The list of SDK names and versions that are used to build this module + RequiredSdks SdkRefs `blueprint:"mutated"` + + // Name of the module that this sdk member is representing + Sdk_member_name *string +} + +// SdkBase is a struct that is expected to be included in module types to implement the SdkAware +// interface. InitSdkAwareModule should be called to initialize this struct. +type SdkBase struct { + properties sdkProperties +} + +func (s *SdkBase) sdkBase() *SdkBase { + return s +} + +// MakeMemberof sets this module to be a member of a specific SDK +func (s *SdkBase) MakeMemberOf(sdk SdkRef) { + s.properties.ContainingSdk = &sdk +} + +// IsInAnySdk returns true if this module is a member of any SDK +func (s *SdkBase) IsInAnySdk() bool { + return s.properties.ContainingSdk != nil +} + +// ContainingSdk returns the SDK that this module is a member of +func (s *SdkBase) ContainingSdk() SdkRef { + if s.properties.ContainingSdk != nil { + return *s.properties.ContainingSdk + } + return SdkRef{Name: "", Version: currentVersion} +} + +// Membername returns the name of the module that this SDK member is overriding +func (s *SdkBase) MemberName() string { + return proptools.String(s.properties.Sdk_member_name) +} + +// BuildWithSdks is used to mark that this module has to be built with the given SDK(s). +func (s *SdkBase) BuildWithSdks(sdks SdkRefs) { + s.properties.RequiredSdks = sdks +} + +// RequiredSdks returns the SDK(s) that this module has to be built with +func (s *SdkBase) RequiredSdks() SdkRefs { + return s.properties.RequiredSdks +} + +// InitSdkAwareModule initializes the SdkBase struct. This must be called by all modules including +// SdkBase. +func InitSdkAwareModule(m SdkAware) { + base := m.sdkBase() + m.AddProperties(&base.properties) +} diff --git a/android/sh_binary.go b/android/sh_binary.go index ba0c8be7d..6db9892fa 100644 --- a/android/sh_binary.go +++ b/android/sh_binary.go @@ -48,6 +48,9 @@ type shBinaryProperties struct { // Whether this module is directly installable to one of the partitions. Default: true. Installable *bool + + // install symlinks to the binary + Symlinks []string `android:"arch_variant"` } type TestProperties struct { @@ -103,6 +106,10 @@ func (s *ShBinary) Installable() bool { return s.properties.Installable == nil || Bool(s.properties.Installable) } +func (s *ShBinary) Symlinks() []string { + return s.properties.Symlinks +} + func (s *ShBinary) GenerateAndroidBuildActions(ctx ModuleContext) { s.sourceFilePath = PathForModuleSrc(ctx, String(s.properties.Src)) filename := String(s.properties.Filename) @@ -145,6 +152,9 @@ func (s *ShBinary) customAndroidMkEntries(entries *AndroidMkEntries) { entries.SetString("LOCAL_MODULE_RELATIVE_PATH", String(s.properties.Sub_dir)) entries.SetString("LOCAL_MODULE_SUFFIX", "") entries.SetString("LOCAL_MODULE_STEM", s.outputFilePath.Rel()) + if len(s.properties.Symlinks) > 0 { + entries.SetString("LOCAL_MODULE_SYMLINKS", strings.Join(s.properties.Symlinks, " ")) + } } func (s *ShTest) GenerateAndroidBuildActions(ctx ModuleContext) { diff --git a/android/singleton.go b/android/singleton.go index a59d54aa2..7f9c21606 100644 --- a/android/singleton.go +++ b/android/singleton.go @@ -127,6 +127,11 @@ func (s *singletonContextAdaptor) Variable(pctx PackageContext, name, value stri } func (s *singletonContextAdaptor) Rule(pctx PackageContext, name string, params blueprint.RuleParams, argNames ...string) blueprint.Rule { + if s.Config().UseGoma() && params.Pool == nil { + // When USE_GOMA=true is set and the rule is not supported by goma, restrict jobs to the + // local parallelism value + params.Pool = localPool + } rule := s.SingletonContext.Rule(pctx.PackageContext, name, params, argNames...) if s.Config().captureBuild { s.ruleParams[rule] = params diff --git a/android/util.go b/android/util.go index 010244209..71ded5e07 100644 --- a/android/util.go +++ b/android/util.go @@ -319,3 +319,36 @@ func SplitFileExt(name string) (string, string, string) { return root, suffix, ext } + +// ShardPaths takes a Paths, and returns a slice of Paths where each one has at most shardSize paths. +func ShardPaths(paths Paths, shardSize int) []Paths { + if len(paths) == 0 { + return nil + } + ret := make([]Paths, 0, (len(paths)+shardSize-1)/shardSize) + for len(paths) > shardSize { + ret = append(ret, paths[0:shardSize]) + paths = paths[shardSize:] + } + if len(paths) > 0 { + ret = append(ret, paths) + } + return ret +} + +// ShardStrings takes a slice of strings, and returns a slice of slices of strings where each one has at most shardSize +// elements. +func ShardStrings(s []string, shardSize int) [][]string { + if len(s) == 0 { + return nil + } + ret := make([][]string, 0, (len(s)+shardSize-1)/shardSize) + for len(s) > shardSize { + ret = append(ret, s[0:shardSize]) + s = s[shardSize:] + } + if len(s) > 0 { + ret = append(ret, s) + } + return ret +} diff --git a/android/util_test.go b/android/util_test.go index 1df1c5af5..90fefeede 100644 --- a/android/util_test.go +++ b/android/util_test.go @@ -469,3 +469,102 @@ func TestSplitFileExt(t *testing.T) { } }) } + +func Test_Shard(t *testing.T) { + type args struct { + strings []string + shardSize int + } + tests := []struct { + name string + args args + want [][]string + }{ + { + name: "empty", + args: args{ + strings: nil, + shardSize: 1, + }, + want: [][]string(nil), + }, + { + name: "single shard", + args: args{ + strings: []string{"a", "b"}, + shardSize: 2, + }, + want: [][]string{{"a", "b"}}, + }, + { + name: "single short shard", + args: args{ + strings: []string{"a", "b"}, + shardSize: 3, + }, + want: [][]string{{"a", "b"}}, + }, + { + name: "shard per input", + args: args{ + strings: []string{"a", "b", "c"}, + shardSize: 1, + }, + want: [][]string{{"a"}, {"b"}, {"c"}}, + }, + { + name: "balanced shards", + args: args{ + strings: []string{"a", "b", "c", "d"}, + shardSize: 2, + }, + want: [][]string{{"a", "b"}, {"c", "d"}}, + }, + { + name: "unbalanced shards", + args: args{ + strings: []string{"a", "b", "c"}, + shardSize: 2, + }, + want: [][]string{{"a", "b"}, {"c"}}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Run("strings", func(t *testing.T) { + if got := ShardStrings(tt.args.strings, tt.args.shardSize); !reflect.DeepEqual(got, tt.want) { + t.Errorf("ShardStrings(%v, %v) = %v, want %v", + tt.args.strings, tt.args.shardSize, got, tt.want) + } + }) + + t.Run("paths", func(t *testing.T) { + stringsToPaths := func(strings []string) Paths { + if strings == nil { + return nil + } + paths := make(Paths, len(strings)) + for i, s := range strings { + paths[i] = PathForTesting(s) + } + return paths + } + + paths := stringsToPaths(tt.args.strings) + + var want []Paths + if sWant := tt.want; sWant != nil { + want = make([]Paths, len(sWant)) + for i, w := range sWant { + want[i] = stringsToPaths(w) + } + } + + if got := ShardPaths(paths, tt.args.shardSize); !reflect.DeepEqual(got, want) { + t.Errorf("ShardPaths(%v, %v) = %v, want %v", + paths, tt.args.shardSize, got, want) + } + }) + }) + } +} |