diff --git a/android/Android.bp b/android/Android.bp index 7fbba4362..62f534c53 100644 --- a/android/Android.bp +++ b/android/Android.bp @@ -37,6 +37,7 @@ bootstrap_go_package { "api_levels.go", "arch.go", "arch_list.go", + "base_module_context.go", "bazel.go", "bazel_handler.go", "bazel_paths.go", @@ -51,6 +52,7 @@ bootstrap_go_package { "defs.go", "depset_generic.go", "deptag.go", + "early_module_context.go", "expand.go", "filegroup.go", "fixture.go", @@ -65,6 +67,7 @@ bootstrap_go_package { "makevars.go", "metrics.go", "module.go", + "module_context.go", "mutator.go", "namespace.go", "neverallow.go", diff --git a/android/base_module_context.go b/android/base_module_context.go new file mode 100644 index 000000000..ec9c888be --- /dev/null +++ b/android/base_module_context.go @@ -0,0 +1,656 @@ +// Copyright 2015 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package android + +import ( + "fmt" + "github.com/google/blueprint" + "regexp" + "strings" +) + +// BaseModuleContext is the same as blueprint.BaseModuleContext except that Config() returns +// a Config instead of an interface{}, and some methods have been wrapped to use an android.Module +// instead of a blueprint.Module, plus some extra methods that return Android-specific information +// about the current module. +type BaseModuleContext interface { + EarlyModuleContext + + blueprintBaseModuleContext() blueprint.BaseModuleContext + + // OtherModuleName returns the name of another Module. See BaseModuleContext.ModuleName for more information. + // It is intended for use inside the visit functions of Visit* and WalkDeps. + OtherModuleName(m blueprint.Module) string + + // OtherModuleDir returns the directory of another Module. See BaseModuleContext.ModuleDir for more information. + // It is intended for use inside the visit functions of Visit* and WalkDeps. + OtherModuleDir(m blueprint.Module) string + + // OtherModuleErrorf reports an error on another Module. See BaseModuleContext.ModuleErrorf for more information. + // It is intended for use inside the visit functions of Visit* and WalkDeps. + OtherModuleErrorf(m blueprint.Module, fmt string, args ...interface{}) + + // OtherModuleDependencyTag returns the dependency tag used to depend on a module, or nil if there is no dependency + // on the module. When called inside a Visit* method with current module being visited, and there are multiple + // dependencies on the module being visited, it returns the dependency tag used for the current dependency. + OtherModuleDependencyTag(m blueprint.Module) blueprint.DependencyTag + + // OtherModuleExists returns true if a module with the specified name exists, as determined by the NameInterface + // passed to Context.SetNameInterface, or SimpleNameInterface if it was not called. + OtherModuleExists(name string) bool + + // OtherModuleDependencyVariantExists returns true if a module with the + // specified name and variant exists. The variant must match the given + // variations. It must also match all the non-local variations of the current + // module. In other words, it checks for the module that AddVariationDependencies + // would add a dependency on with the same arguments. + OtherModuleDependencyVariantExists(variations []blueprint.Variation, name string) bool + + // OtherModuleFarDependencyVariantExists returns true if a module with the + // specified name and variant exists. The variant must match the given + // variations, but not the non-local variations of the current module. In + // other words, it checks for the module that AddFarVariationDependencies + // would add a dependency on with the same arguments. + OtherModuleFarDependencyVariantExists(variations []blueprint.Variation, name string) bool + + // OtherModuleReverseDependencyVariantExists returns true if a module with the + // specified name exists with the same variations as the current module. In + // other words, it checks for the module that AddReverseDependency would add a + // dependency on with the same argument. + OtherModuleReverseDependencyVariantExists(name string) bool + + // OtherModuleType returns the type of another Module. See BaseModuleContext.ModuleType for more information. + // It is intended for use inside the visit functions of Visit* and WalkDeps. + OtherModuleType(m blueprint.Module) string + + // OtherModuleProvider returns the value for a provider for the given module. If the value is + // not set it returns the zero value of the type of the provider, so the return value can always + // be type asserted to the type of the provider. The value returned may be a deep copy of the + // value originally passed to SetProvider. + OtherModuleProvider(m blueprint.Module, provider blueprint.ProviderKey) interface{} + + // OtherModuleHasProvider returns true if the provider for the given module has been set. + OtherModuleHasProvider(m blueprint.Module, provider blueprint.ProviderKey) bool + + // Provider returns the value for a provider for the current module. If the value is + // not set it returns the zero value of the type of the provider, so the return value can always + // be type asserted to the type of the provider. It panics if called before the appropriate + // mutator or GenerateBuildActions pass for the provider. The value returned may be a deep + // copy of the value originally passed to SetProvider. + Provider(provider blueprint.ProviderKey) interface{} + + // HasProvider returns true if the provider for the current module has been set. + HasProvider(provider blueprint.ProviderKey) bool + + // SetProvider sets the value for a provider for the current module. It panics if not called + // during the appropriate mutator or GenerateBuildActions pass for the provider, if the value + // is not of the appropriate type, or if the value has already been set. The value should not + // be modified after being passed to SetProvider. + SetProvider(provider blueprint.ProviderKey, value interface{}) + + GetDirectDepsWithTag(tag blueprint.DependencyTag) []Module + + // GetDirectDepWithTag returns the Module the direct dependency with the specified name, or nil if + // none exists. It panics if the dependency does not have the specified tag. It skips any + // dependencies that are not an android.Module. + GetDirectDepWithTag(name string, tag blueprint.DependencyTag) blueprint.Module + + // GetDirectDep returns the Module and DependencyTag for the direct dependency with the specified + // name, or nil if none exists. If there are multiple dependencies on the same module it returns + // the first DependencyTag. + GetDirectDep(name string) (blueprint.Module, blueprint.DependencyTag) + + ModuleFromName(name string) (blueprint.Module, bool) + + // VisitDirectDepsBlueprint calls visit for each direct dependency. If there are multiple + // direct dependencies on the same module visit will be called multiple times on that module + // and OtherModuleDependencyTag will return a different tag for each. + // + // The Module passed to the visit function should not be retained outside of the visit + // function, it may be invalidated by future mutators. + VisitDirectDepsBlueprint(visit func(blueprint.Module)) + + // VisitDirectDeps calls visit for each direct dependency. If there are multiple + // direct dependencies on the same module visit will be called multiple times on that module + // and OtherModuleDependencyTag will return a different tag for each. It raises an error if any of the + // dependencies are not an android.Module. + // + // The Module passed to the visit function should not be retained outside of the visit + // function, it may be invalidated by future mutators. + VisitDirectDeps(visit func(Module)) + + VisitDirectDepsWithTag(tag blueprint.DependencyTag, visit func(Module)) + + // VisitDirectDepsIf calls pred for each direct dependency, and if pred returns true calls visit. If there are + // multiple direct dependencies on the same module pred and visit will be called multiple times on that module and + // OtherModuleDependencyTag will return a different tag for each. It skips any + // dependencies that are not an android.Module. + // + // The Module passed to the visit function should not be retained outside of the visit function, it may be + // invalidated by future mutators. + VisitDirectDepsIf(pred func(Module) bool, visit func(Module)) + // Deprecated: use WalkDeps instead to support multiple dependency tags on the same module + VisitDepsDepthFirst(visit func(Module)) + // Deprecated: use WalkDeps instead to support multiple dependency tags on the same module + VisitDepsDepthFirstIf(pred func(Module) bool, visit func(Module)) + + // WalkDeps calls visit for each transitive dependency, traversing the dependency tree in top down order. visit may + // be called multiple times for the same (child, parent) pair if there are multiple direct dependencies between the + // child and parent with different tags. OtherModuleDependencyTag will return the tag for the currently visited + // (child, parent) pair. If visit returns false WalkDeps will not continue recursing down to child. It skips + // any dependencies that are not an android.Module. + // + // The Modules passed to the visit function should not be retained outside of the visit function, they may be + // invalidated by future mutators. + WalkDeps(visit func(child, parent Module) bool) + + // WalkDepsBlueprint calls visit for each transitive dependency, traversing the dependency + // tree in top down order. visit may be called multiple times for the same (child, parent) + // pair if there are multiple direct dependencies between the child and parent with different + // tags. OtherModuleDependencyTag will return the tag for the currently visited + // (child, parent) pair. If visit returns false WalkDeps will not continue recursing down + // to child. + // + // The Modules passed to the visit function should not be retained outside of the visit function, they may be + // invalidated by future mutators. + WalkDepsBlueprint(visit func(blueprint.Module, blueprint.Module) bool) + + // GetWalkPath is supposed to be called in visit function passed in WalkDeps() + // and returns a top-down dependency path from a start module to current child module. + GetWalkPath() []Module + + // PrimaryModule returns the first variant of the current module. Variants of a module are always visited in + // order by mutators and GenerateBuildActions, so the data created by the current mutator can be read from the + // Module returned by PrimaryModule without data races. This can be used to perform singleton actions that are + // only done once for all variants of a module. + PrimaryModule() Module + + // FinalModule returns the last variant of the current module. Variants of a module are always visited in + // order by mutators and GenerateBuildActions, so the data created by the current mutator can be read from all + // variants using VisitAllModuleVariants if the current module == FinalModule(). This can be used to perform + // singleton actions that are only done once for all variants of a module. + FinalModule() Module + + // VisitAllModuleVariants calls visit for each variant of the current module. Variants of a module are always + // visited in order by mutators and GenerateBuildActions, so the data created by the current mutator can be read + // from all variants if the current module == FinalModule(). Otherwise, care must be taken to not access any + // data modified by the current mutator. + VisitAllModuleVariants(visit func(Module)) + + // GetTagPath is supposed to be called in visit function passed in WalkDeps() + // and returns a top-down dependency tags path from a start module to current child module. + // It has one less entry than GetWalkPath() as it contains the dependency tags that + // exist between each adjacent pair of modules in the GetWalkPath(). + // GetTagPath()[i] is the tag between GetWalkPath()[i] and GetWalkPath()[i+1] + GetTagPath() []blueprint.DependencyTag + + // GetPathString is supposed to be called in visit function passed in WalkDeps() + // and returns a multi-line string showing the modules and dependency tags + // among them along the top-down dependency path from a start module to current child module. + // skipFirst when set to true, the output doesn't include the start module, + // which is already printed when this function is used along with ModuleErrorf(). + GetPathString(skipFirst bool) string + + AddMissingDependencies(missingDeps []string) + + // getMissingDependencies returns the list of missing dependencies. + // Calling this function prevents adding new dependencies. + getMissingDependencies() []string + + // AddUnconvertedBp2buildDep stores module name of a direct dependency that was not converted via bp2build + AddUnconvertedBp2buildDep(dep string) + + // AddMissingBp2buildDep stores the module name of a direct dependency that was not found. + AddMissingBp2buildDep(dep string) + + Target() Target + TargetPrimary() bool + + // The additional arch specific targets (e.g. 32/64 bit) that this module variant is + // responsible for creating. + MultiTargets() []Target + Arch() Arch + Os() OsType + Host() bool + Device() bool + Darwin() bool + Windows() bool + PrimaryArch() bool +} + +type baseModuleContext struct { + bp blueprint.BaseModuleContext + earlyModuleContext + os OsType + target Target + multiTargets []Target + targetPrimary bool + + walkPath []Module + tagPath []blueprint.DependencyTag + + strictVisitDeps bool // If true, enforce that all dependencies are enabled + + bazelConversionMode bool +} + +func (b *baseModuleContext) isBazelConversionMode() bool { + return b.bazelConversionMode +} +func (b *baseModuleContext) OtherModuleName(m blueprint.Module) string { + return b.bp.OtherModuleName(m) +} +func (b *baseModuleContext) OtherModuleDir(m blueprint.Module) string { return b.bp.OtherModuleDir(m) } +func (b *baseModuleContext) OtherModuleErrorf(m blueprint.Module, fmt string, args ...interface{}) { + b.bp.OtherModuleErrorf(m, fmt, args...) +} +func (b *baseModuleContext) OtherModuleDependencyTag(m blueprint.Module) blueprint.DependencyTag { + return b.bp.OtherModuleDependencyTag(m) +} +func (b *baseModuleContext) OtherModuleExists(name string) bool { return b.bp.OtherModuleExists(name) } +func (b *baseModuleContext) OtherModuleDependencyVariantExists(variations []blueprint.Variation, name string) bool { + return b.bp.OtherModuleDependencyVariantExists(variations, name) +} +func (b *baseModuleContext) OtherModuleFarDependencyVariantExists(variations []blueprint.Variation, name string) bool { + return b.bp.OtherModuleFarDependencyVariantExists(variations, name) +} +func (b *baseModuleContext) OtherModuleReverseDependencyVariantExists(name string) bool { + return b.bp.OtherModuleReverseDependencyVariantExists(name) +} +func (b *baseModuleContext) OtherModuleType(m blueprint.Module) string { + return b.bp.OtherModuleType(m) +} +func (b *baseModuleContext) OtherModuleProvider(m blueprint.Module, provider blueprint.ProviderKey) interface{} { + return b.bp.OtherModuleProvider(m, provider) +} +func (b *baseModuleContext) OtherModuleHasProvider(m blueprint.Module, provider blueprint.ProviderKey) bool { + return b.bp.OtherModuleHasProvider(m, provider) +} +func (b *baseModuleContext) Provider(provider blueprint.ProviderKey) interface{} { + return b.bp.Provider(provider) +} +func (b *baseModuleContext) HasProvider(provider blueprint.ProviderKey) bool { + return b.bp.HasProvider(provider) +} +func (b *baseModuleContext) SetProvider(provider blueprint.ProviderKey, value interface{}) { + b.bp.SetProvider(provider, value) +} + +func (b *baseModuleContext) GetDirectDepWithTag(name string, tag blueprint.DependencyTag) blueprint.Module { + return b.bp.GetDirectDepWithTag(name, tag) +} + +func (b *baseModuleContext) blueprintBaseModuleContext() blueprint.BaseModuleContext { + return b.bp +} + +// AddUnconvertedBp2buildDep stores module name of a dependency that was not converted to Bazel. +func (b *baseModuleContext) AddUnconvertedBp2buildDep(dep string) { + unconvertedDeps := &b.Module().base().commonProperties.BazelConversionStatus.UnconvertedDeps + *unconvertedDeps = append(*unconvertedDeps, dep) +} + +// AddMissingBp2buildDep stores module name of a dependency that was not found in a Android.bp file. +func (b *baseModuleContext) AddMissingBp2buildDep(dep string) { + missingDeps := &b.Module().base().commonProperties.BazelConversionStatus.MissingDeps + *missingDeps = append(*missingDeps, dep) +} + +func (b *baseModuleContext) AddMissingDependencies(deps []string) { + if deps != nil { + missingDeps := &b.Module().base().commonProperties.MissingDeps + *missingDeps = append(*missingDeps, deps...) + *missingDeps = FirstUniqueStrings(*missingDeps) + } +} + +func (b *baseModuleContext) checkedMissingDeps() bool { + return b.Module().base().commonProperties.CheckedMissingDeps +} + +func (b *baseModuleContext) getMissingDependencies() []string { + checked := &b.Module().base().commonProperties.CheckedMissingDeps + *checked = true + var missingDeps []string + missingDeps = append(missingDeps, b.Module().base().commonProperties.MissingDeps...) + missingDeps = append(missingDeps, b.bp.EarlyGetMissingDependencies()...) + missingDeps = FirstUniqueStrings(missingDeps) + return missingDeps +} + +type AllowDisabledModuleDependency interface { + blueprint.DependencyTag + AllowDisabledModuleDependency(target Module) bool +} + +func (b *baseModuleContext) validateAndroidModule(module blueprint.Module, tag blueprint.DependencyTag, strict bool) Module { + aModule, _ := module.(Module) + + if !strict { + return aModule + } + + if aModule == nil { + b.ModuleErrorf("module %q (%#v) not an android module", b.OtherModuleName(module), tag) + return nil + } + + if !aModule.Enabled() { + if t, ok := tag.(AllowDisabledModuleDependency); !ok || !t.AllowDisabledModuleDependency(aModule) { + if b.Config().AllowMissingDependencies() { + b.AddMissingDependencies([]string{b.OtherModuleName(aModule)}) + } else { + b.ModuleErrorf("depends on disabled module %q", b.OtherModuleName(aModule)) + } + } + return nil + } + return aModule +} + +type dep struct { + mod blueprint.Module + tag blueprint.DependencyTag +} + +func (b *baseModuleContext) getDirectDepsInternal(name string, tag blueprint.DependencyTag) []dep { + var deps []dep + b.VisitDirectDepsBlueprint(func(module blueprint.Module) { + if aModule, _ := module.(Module); aModule != nil { + if aModule.base().BaseModuleName() == name { + returnedTag := b.bp.OtherModuleDependencyTag(aModule) + if tag == nil || returnedTag == tag { + deps = append(deps, dep{aModule, returnedTag}) + } + } + } else if b.bp.OtherModuleName(module) == name { + returnedTag := b.bp.OtherModuleDependencyTag(module) + if tag == nil || returnedTag == tag { + deps = append(deps, dep{module, returnedTag}) + } + } + }) + return deps +} + +func (b *baseModuleContext) getDirectDepInternal(name string, tag blueprint.DependencyTag) (blueprint.Module, blueprint.DependencyTag) { + deps := b.getDirectDepsInternal(name, tag) + if len(deps) == 1 { + return deps[0].mod, deps[0].tag + } else if len(deps) >= 2 { + panic(fmt.Errorf("Multiple dependencies having same BaseModuleName() %q found from %q", + name, b.ModuleName())) + } else { + return nil, nil + } +} + +func (b *baseModuleContext) getDirectDepFirstTag(name string) (blueprint.Module, blueprint.DependencyTag) { + foundDeps := b.getDirectDepsInternal(name, nil) + deps := map[blueprint.Module]bool{} + for _, dep := range foundDeps { + deps[dep.mod] = true + } + if len(deps) == 1 { + return foundDeps[0].mod, foundDeps[0].tag + } else if len(deps) >= 2 { + // this could happen if two dependencies have the same name in different namespaces + // TODO(b/186554727): this should not occur if namespaces are handled within + // getDirectDepsInternal. + panic(fmt.Errorf("Multiple dependencies having same BaseModuleName() %q found from %q", + name, b.ModuleName())) + } else { + return nil, nil + } +} + +func (b *baseModuleContext) GetDirectDepsWithTag(tag blueprint.DependencyTag) []Module { + var deps []Module + b.VisitDirectDepsBlueprint(func(module blueprint.Module) { + if aModule, _ := module.(Module); aModule != nil { + if b.bp.OtherModuleDependencyTag(aModule) == tag { + deps = append(deps, aModule) + } + } + }) + return deps +} + +// GetDirectDep returns the Module and DependencyTag for the direct dependency with the specified +// name, or nil if none exists. If there are multiple dependencies on the same module it returns the +// first DependencyTag. +func (b *baseModuleContext) GetDirectDep(name string) (blueprint.Module, blueprint.DependencyTag) { + return b.getDirectDepFirstTag(name) +} + +func (b *baseModuleContext) ModuleFromName(name string) (blueprint.Module, bool) { + if !b.isBazelConversionMode() { + panic("cannot call ModuleFromName if not in bazel conversion mode") + } + var m blueprint.Module + var ok bool + if moduleName, _ := SrcIsModuleWithTag(name); moduleName != "" { + m, ok = b.bp.ModuleFromName(moduleName) + } else { + m, ok = b.bp.ModuleFromName(name) + } + if !ok { + return m, ok + } + // If this module is not preferred, tried to get the prebuilt version instead + if a, aOk := m.(Module); aOk && !IsModulePrebuilt(a) && !IsModulePreferred(a) { + return b.ModuleFromName("prebuilt_" + name) + } + return m, ok +} + +func (b *baseModuleContext) VisitDirectDepsBlueprint(visit func(blueprint.Module)) { + b.bp.VisitDirectDeps(visit) +} + +func (b *baseModuleContext) VisitDirectDeps(visit func(Module)) { + b.bp.VisitDirectDeps(func(module blueprint.Module) { + if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps); aModule != nil { + visit(aModule) + } + }) +} + +func (b *baseModuleContext) VisitDirectDepsWithTag(tag blueprint.DependencyTag, visit func(Module)) { + b.bp.VisitDirectDeps(func(module blueprint.Module) { + if b.bp.OtherModuleDependencyTag(module) == tag { + if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps); aModule != nil { + visit(aModule) + } + } + }) +} + +func (b *baseModuleContext) VisitDirectDepsIf(pred func(Module) bool, visit func(Module)) { + b.bp.VisitDirectDepsIf( + // pred + func(module blueprint.Module) bool { + if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps); aModule != nil { + return pred(aModule) + } else { + return false + } + }, + // visit + func(module blueprint.Module) { + visit(module.(Module)) + }) +} + +func (b *baseModuleContext) VisitDepsDepthFirst(visit func(Module)) { + b.bp.VisitDepsDepthFirst(func(module blueprint.Module) { + if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps); aModule != nil { + visit(aModule) + } + }) +} + +func (b *baseModuleContext) VisitDepsDepthFirstIf(pred func(Module) bool, visit func(Module)) { + b.bp.VisitDepsDepthFirstIf( + // pred + func(module blueprint.Module) bool { + if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps); aModule != nil { + return pred(aModule) + } else { + return false + } + }, + // visit + func(module blueprint.Module) { + visit(module.(Module)) + }) +} + +func (b *baseModuleContext) WalkDepsBlueprint(visit func(blueprint.Module, blueprint.Module) bool) { + b.bp.WalkDeps(visit) +} + +func (b *baseModuleContext) WalkDeps(visit func(Module, Module) bool) { + b.walkPath = []Module{b.Module()} + b.tagPath = []blueprint.DependencyTag{} + b.bp.WalkDeps(func(child, parent blueprint.Module) bool { + childAndroidModule, _ := child.(Module) + parentAndroidModule, _ := parent.(Module) + if childAndroidModule != nil && parentAndroidModule != nil { + // record walkPath before visit + for b.walkPath[len(b.walkPath)-1] != parentAndroidModule { + b.walkPath = b.walkPath[0 : len(b.walkPath)-1] + b.tagPath = b.tagPath[0 : len(b.tagPath)-1] + } + b.walkPath = append(b.walkPath, childAndroidModule) + b.tagPath = append(b.tagPath, b.OtherModuleDependencyTag(childAndroidModule)) + return visit(childAndroidModule, parentAndroidModule) + } else { + return false + } + }) +} + +func (b *baseModuleContext) GetWalkPath() []Module { + return b.walkPath +} + +func (b *baseModuleContext) GetTagPath() []blueprint.DependencyTag { + return b.tagPath +} + +func (b *baseModuleContext) VisitAllModuleVariants(visit func(Module)) { + b.bp.VisitAllModuleVariants(func(module blueprint.Module) { + visit(module.(Module)) + }) +} + +func (b *baseModuleContext) PrimaryModule() Module { + return b.bp.PrimaryModule().(Module) +} + +func (b *baseModuleContext) FinalModule() Module { + return b.bp.FinalModule().(Module) +} + +// IsMetaDependencyTag returns true for cross-cutting metadata dependencies. +func IsMetaDependencyTag(tag blueprint.DependencyTag) bool { + if tag == licenseKindTag { + return true + } else if tag == licensesTag { + return true + } else if tag == acDepTag { + return true + } + return false +} + +// A regexp for removing boilerplate from BaseDependencyTag from the string representation of +// a dependency tag. +var tagCleaner = regexp.MustCompile(`\QBaseDependencyTag:{}\E(, )?`) + +// PrettyPrintTag returns string representation of the tag, but prefers +// custom String() method if available. +func PrettyPrintTag(tag blueprint.DependencyTag) string { + // Use tag's custom String() method if available. + if stringer, ok := tag.(fmt.Stringer); ok { + return stringer.String() + } + + // Otherwise, get a default string representation of the tag's struct. + tagString := fmt.Sprintf("%T: %+v", tag, tag) + + // Remove the boilerplate from BaseDependencyTag as it adds no value. + tagString = tagCleaner.ReplaceAllString(tagString, "") + return tagString +} + +func (b *baseModuleContext) GetPathString(skipFirst bool) string { + sb := strings.Builder{} + tagPath := b.GetTagPath() + walkPath := b.GetWalkPath() + if !skipFirst { + sb.WriteString(walkPath[0].String()) + } + for i, m := range walkPath[1:] { + sb.WriteString("\n") + sb.WriteString(fmt.Sprintf(" via tag %s\n", PrettyPrintTag(tagPath[i]))) + sb.WriteString(fmt.Sprintf(" -> %s", m.String())) + } + return sb.String() +} + +func (b *baseModuleContext) Target() Target { + return b.target +} + +func (b *baseModuleContext) TargetPrimary() bool { + return b.targetPrimary +} + +func (b *baseModuleContext) MultiTargets() []Target { + return b.multiTargets +} + +func (b *baseModuleContext) Arch() Arch { + return b.target.Arch +} + +func (b *baseModuleContext) Os() OsType { + return b.os +} + +func (b *baseModuleContext) Host() bool { + return b.os.Class == Host +} + +func (b *baseModuleContext) Device() bool { + return b.os.Class == Device +} + +func (b *baseModuleContext) Darwin() bool { + return b.os == Darwin +} + +func (b *baseModuleContext) Windows() bool { + return b.os == Windows +} + +func (b *baseModuleContext) PrimaryArch() bool { + if len(b.config.Targets[b.target.Os]) <= 1 { + return true + } + return b.target.Arch.ArchType == b.config.Targets[b.target.Os][0].Arch.ArchType +} diff --git a/android/early_module_context.go b/android/early_module_context.go new file mode 100644 index 000000000..8f7577358 --- /dev/null +++ b/android/early_module_context.go @@ -0,0 +1,169 @@ +// Copyright 2015 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package android + +import ( + "github.com/google/blueprint" + "os" + "text/scanner" +) + +// EarlyModuleContext provides methods that can be called early, as soon as the properties have +// been parsed into the module and before any mutators have run. +type EarlyModuleContext interface { + // Module returns the current module as a Module. It should rarely be necessary, as the module already has a + // reference to itself. + Module() Module + + // ModuleName returns the name of the module. This is generally the value that was returned by Module.Name() when + // the module was created, but may have been modified by calls to BaseMutatorContext.Rename. + ModuleName() string + + // ModuleDir returns the path to the directory that contains the definition of the module. + ModuleDir() string + + // ModuleType returns the name of the module type that was used to create the module, as specified in + // RegisterModuleType. + ModuleType() string + + // BlueprintFile returns the name of the blueprint file that contains the definition of this + // module. + BlueprintsFile() string + + // ContainsProperty returns true if the specified property name was set in the module definition. + ContainsProperty(name string) bool + + // Errorf reports an error at the specified position of the module definition file. + Errorf(pos scanner.Position, fmt string, args ...interface{}) + + // ModuleErrorf reports an error at the line number of the module type in the module definition. + ModuleErrorf(fmt string, args ...interface{}) + + // PropertyErrorf reports an error at the line number of a property in the module definition. + PropertyErrorf(property, fmt string, args ...interface{}) + + // Failed returns true if any errors have been reported. In most cases the module can continue with generating + // build rules after an error, allowing it to report additional errors in a single run, but in cases where the error + // has prevented the module from creating necessary data it can return early when Failed returns true. + Failed() bool + + // AddNinjaFileDeps adds dependencies on the specified files to the rule that creates the ninja manifest. The + // primary builder will be rerun whenever the specified files are modified. + AddNinjaFileDeps(deps ...string) + + DeviceSpecific() bool + SocSpecific() bool + ProductSpecific() bool + SystemExtSpecific() bool + Platform() bool + + Config() Config + DeviceConfig() DeviceConfig + + // Deprecated: use Config() + AConfig() Config + + // GlobWithDeps returns a list of files that match the specified pattern but do not match any + // of the patterns in excludes. It also adds efficient dependencies to rerun the primary + // builder whenever a file matching the pattern as added or removed, without rerunning if a + // file that does not match the pattern is added to a searched directory. + GlobWithDeps(pattern string, excludes []string) ([]string, error) + + Glob(globPattern string, excludes []string) Paths + GlobFiles(globPattern string, excludes []string) Paths + IsSymlink(path Path) bool + Readlink(path Path) string + + // Namespace returns the Namespace object provided by the NameInterface set by Context.SetNameInterface, or the + // default SimpleNameInterface if Context.SetNameInterface was not called. + Namespace() *Namespace +} + +// Deprecated: use EarlyModuleContext instead +type BaseContext interface { + EarlyModuleContext +} + +type earlyModuleContext struct { + blueprint.EarlyModuleContext + + kind moduleKind + config Config +} + +func (e *earlyModuleContext) Glob(globPattern string, excludes []string) Paths { + return Glob(e, globPattern, excludes) +} + +func (e *earlyModuleContext) GlobFiles(globPattern string, excludes []string) Paths { + return GlobFiles(e, globPattern, excludes) +} + +func (e *earlyModuleContext) IsSymlink(path Path) bool { + fileInfo, err := e.config.fs.Lstat(path.String()) + if err != nil { + e.ModuleErrorf("os.Lstat(%q) failed: %s", path.String(), err) + } + return fileInfo.Mode()&os.ModeSymlink == os.ModeSymlink +} + +func (e *earlyModuleContext) Readlink(path Path) string { + dest, err := e.config.fs.Readlink(path.String()) + if err != nil { + e.ModuleErrorf("os.Readlink(%q) failed: %s", path.String(), err) + } + return dest +} + +func (e *earlyModuleContext) Module() Module { + module, _ := e.EarlyModuleContext.Module().(Module) + return module +} + +func (e *earlyModuleContext) Config() Config { + return e.EarlyModuleContext.Config().(Config) +} + +func (e *earlyModuleContext) AConfig() Config { + return e.config +} + +func (e *earlyModuleContext) DeviceConfig() DeviceConfig { + return DeviceConfig{e.config.deviceConfig} +} + +func (e *earlyModuleContext) Platform() bool { + return e.kind == platformModule +} + +func (e *earlyModuleContext) DeviceSpecific() bool { + return e.kind == deviceSpecificModule +} + +func (e *earlyModuleContext) SocSpecific() bool { + return e.kind == socSpecificModule +} + +func (e *earlyModuleContext) ProductSpecific() bool { + return e.kind == productSpecificModule +} + +func (e *earlyModuleContext) SystemExtSpecific() bool { + return e.kind == systemExtSpecificModule +} + +func (e *earlyModuleContext) Namespace() *Namespace { + return e.EarlyModuleContext.Namespace().(*Namespace) +} diff --git a/android/module.go b/android/module.go index 0d405ebd3..af69a1b1c 100644 --- a/android/module.go +++ b/android/module.go @@ -15,22 +15,17 @@ package android import ( + "android/soong/bazel" + "android/soong/ui/metrics/bp2build_metrics_proto" "crypto/md5" "encoding/hex" "encoding/json" "fmt" "net/url" - "os" - "path" "path/filepath" "reflect" - "regexp" "sort" "strings" - "text/scanner" - - "android/soong/bazel" - "android/soong/ui/metrics/bp2build_metrics_proto" "github.com/google/blueprint" "github.com/google/blueprint/proptools" @@ -41,469 +36,6 @@ var ( DeviceStaticLibrary = "static_library" ) -// BuildParameters describes the set of potential parameters to build a Ninja rule. -// In general, these correspond to a Ninja concept. -type BuildParams struct { - // A Ninja Rule that will be written to the Ninja file. This allows factoring out common code - // among multiple modules to reduce repetition in the Ninja file of action requirements. A rule - // can contain variables that should be provided in Args. - Rule blueprint.Rule - // Deps represents the depfile format. When using RuleBuilder, this defaults to GCC when depfiles - // are used. - Deps blueprint.Deps - // Depfile is a writeable path that allows correct incremental builds when the inputs have not - // been fully specified by the Ninja rule. Ninja supports a subset of the Makefile depfile syntax. - Depfile WritablePath - // A description of the build action. - Description string - // Output is an output file of the action. When using this field, references to $out in the Ninja - // command will refer to this file. - Output WritablePath - // Outputs is a slice of output file of the action. When using this field, references to $out in - // the Ninja command will refer to these files. - Outputs WritablePaths - // SymlinkOutput is an output file specifically that is a symlink. - SymlinkOutput WritablePath - // SymlinkOutputs is a slice of output files specifically that is a symlink. - SymlinkOutputs WritablePaths - // ImplicitOutput is an output file generated by the action. Note: references to `$out` in the - // Ninja command will NOT include references to this file. - ImplicitOutput WritablePath - // ImplicitOutputs is a slice of output files generated by the action. Note: references to `$out` - // in the Ninja command will NOT include references to these files. - ImplicitOutputs WritablePaths - // Input is an input file to the Ninja action. When using this field, references to $in in the - // Ninja command will refer to this file. - Input Path - // Inputs is a slice of input files to the Ninja action. When using this field, references to $in - // in the Ninja command will refer to these files. - Inputs Paths - // Implicit is an input file to the Ninja action. Note: references to `$in` in the Ninja command - // will NOT include references to this file. - Implicit Path - // Implicits is a slice of input files to the Ninja action. Note: references to `$in` in the Ninja - // command will NOT include references to these files. - Implicits Paths - // OrderOnly are Ninja order-only inputs to the action. When these are out of date, the output is - // not rebuilt until they are built, but changes in order-only dependencies alone do not cause the - // output to be rebuilt. - OrderOnly Paths - // Validation is an output path for a validation action. Validation outputs imply lower - // non-blocking priority to building non-validation outputs. - Validation Path - // Validations is a slice of output path for a validation action. Validation outputs imply lower - // non-blocking priority to building non-validation outputs. - Validations Paths - // Whether to skip outputting a default target statement which will be built by Ninja when no - // targets are specified on Ninja's command line. - Default bool - // Args is a key value mapping for replacements of variables within the Rule - Args map[string]string -} - -type ModuleBuildParams BuildParams - -// EarlyModuleContext provides methods that can be called early, as soon as the properties have -// been parsed into the module and before any mutators have run. -type EarlyModuleContext interface { - // Module returns the current module as a Module. It should rarely be necessary, as the module already has a - // reference to itself. - Module() Module - - // ModuleName returns the name of the module. This is generally the value that was returned by Module.Name() when - // the module was created, but may have been modified by calls to BaseMutatorContext.Rename. - ModuleName() string - - // ModuleDir returns the path to the directory that contains the definition of the module. - ModuleDir() string - - // ModuleType returns the name of the module type that was used to create the module, as specified in - // RegisterModuleType. - ModuleType() string - - // BlueprintFile returns the name of the blueprint file that contains the definition of this - // module. - BlueprintsFile() string - - // ContainsProperty returns true if the specified property name was set in the module definition. - ContainsProperty(name string) bool - - // Errorf reports an error at the specified position of the module definition file. - Errorf(pos scanner.Position, fmt string, args ...interface{}) - - // ModuleErrorf reports an error at the line number of the module type in the module definition. - ModuleErrorf(fmt string, args ...interface{}) - - // PropertyErrorf reports an error at the line number of a property in the module definition. - PropertyErrorf(property, fmt string, args ...interface{}) - - // Failed returns true if any errors have been reported. In most cases the module can continue with generating - // build rules after an error, allowing it to report additional errors in a single run, but in cases where the error - // has prevented the module from creating necessary data it can return early when Failed returns true. - Failed() bool - - // AddNinjaFileDeps adds dependencies on the specified files to the rule that creates the ninja manifest. The - // primary builder will be rerun whenever the specified files are modified. - AddNinjaFileDeps(deps ...string) - - DeviceSpecific() bool - SocSpecific() bool - ProductSpecific() bool - SystemExtSpecific() bool - Platform() bool - - Config() Config - DeviceConfig() DeviceConfig - - // Deprecated: use Config() - AConfig() Config - - // GlobWithDeps returns a list of files that match the specified pattern but do not match any - // of the patterns in excludes. It also adds efficient dependencies to rerun the primary - // builder whenever a file matching the pattern as added or removed, without rerunning if a - // file that does not match the pattern is added to a searched directory. - GlobWithDeps(pattern string, excludes []string) ([]string, error) - - Glob(globPattern string, excludes []string) Paths - GlobFiles(globPattern string, excludes []string) Paths - IsSymlink(path Path) bool - Readlink(path Path) string - - // Namespace returns the Namespace object provided by the NameInterface set by Context.SetNameInterface, or the - // default SimpleNameInterface if Context.SetNameInterface was not called. - Namespace() *Namespace -} - -// BaseModuleContext is the same as blueprint.BaseModuleContext except that Config() returns -// a Config instead of an interface{}, and some methods have been wrapped to use an android.Module -// instead of a blueprint.Module, plus some extra methods that return Android-specific information -// about the current module. -type BaseModuleContext interface { - EarlyModuleContext - - blueprintBaseModuleContext() blueprint.BaseModuleContext - - // OtherModuleName returns the name of another Module. See BaseModuleContext.ModuleName for more information. - // It is intended for use inside the visit functions of Visit* and WalkDeps. - OtherModuleName(m blueprint.Module) string - - // OtherModuleDir returns the directory of another Module. See BaseModuleContext.ModuleDir for more information. - // It is intended for use inside the visit functions of Visit* and WalkDeps. - OtherModuleDir(m blueprint.Module) string - - // OtherModuleErrorf reports an error on another Module. See BaseModuleContext.ModuleErrorf for more information. - // It is intended for use inside the visit functions of Visit* and WalkDeps. - OtherModuleErrorf(m blueprint.Module, fmt string, args ...interface{}) - - // OtherModuleDependencyTag returns the dependency tag used to depend on a module, or nil if there is no dependency - // on the module. When called inside a Visit* method with current module being visited, and there are multiple - // dependencies on the module being visited, it returns the dependency tag used for the current dependency. - OtherModuleDependencyTag(m blueprint.Module) blueprint.DependencyTag - - // OtherModuleExists returns true if a module with the specified name exists, as determined by the NameInterface - // passed to Context.SetNameInterface, or SimpleNameInterface if it was not called. - OtherModuleExists(name string) bool - - // OtherModuleDependencyVariantExists returns true if a module with the - // specified name and variant exists. The variant must match the given - // variations. It must also match all the non-local variations of the current - // module. In other words, it checks for the module that AddVariationDependencies - // would add a dependency on with the same arguments. - OtherModuleDependencyVariantExists(variations []blueprint.Variation, name string) bool - - // OtherModuleFarDependencyVariantExists returns true if a module with the - // specified name and variant exists. The variant must match the given - // variations, but not the non-local variations of the current module. In - // other words, it checks for the module that AddFarVariationDependencies - // would add a dependency on with the same arguments. - OtherModuleFarDependencyVariantExists(variations []blueprint.Variation, name string) bool - - // OtherModuleReverseDependencyVariantExists returns true if a module with the - // specified name exists with the same variations as the current module. In - // other words, it checks for the module that AddReverseDependency would add a - // dependency on with the same argument. - OtherModuleReverseDependencyVariantExists(name string) bool - - // OtherModuleType returns the type of another Module. See BaseModuleContext.ModuleType for more information. - // It is intended for use inside the visit functions of Visit* and WalkDeps. - OtherModuleType(m blueprint.Module) string - - // OtherModuleProvider returns the value for a provider for the given module. If the value is - // not set it returns the zero value of the type of the provider, so the return value can always - // be type asserted to the type of the provider. The value returned may be a deep copy of the - // value originally passed to SetProvider. - OtherModuleProvider(m blueprint.Module, provider blueprint.ProviderKey) interface{} - - // OtherModuleHasProvider returns true if the provider for the given module has been set. - OtherModuleHasProvider(m blueprint.Module, provider blueprint.ProviderKey) bool - - // Provider returns the value for a provider for the current module. If the value is - // not set it returns the zero value of the type of the provider, so the return value can always - // be type asserted to the type of the provider. It panics if called before the appropriate - // mutator or GenerateBuildActions pass for the provider. The value returned may be a deep - // copy of the value originally passed to SetProvider. - Provider(provider blueprint.ProviderKey) interface{} - - // HasProvider returns true if the provider for the current module has been set. - HasProvider(provider blueprint.ProviderKey) bool - - // SetProvider sets the value for a provider for the current module. It panics if not called - // during the appropriate mutator or GenerateBuildActions pass for the provider, if the value - // is not of the appropriate type, or if the value has already been set. The value should not - // be modified after being passed to SetProvider. - SetProvider(provider blueprint.ProviderKey, value interface{}) - - GetDirectDepsWithTag(tag blueprint.DependencyTag) []Module - - // GetDirectDepWithTag returns the Module the direct dependency with the specified name, or nil if - // none exists. It panics if the dependency does not have the specified tag. It skips any - // dependencies that are not an android.Module. - GetDirectDepWithTag(name string, tag blueprint.DependencyTag) blueprint.Module - - // GetDirectDep returns the Module and DependencyTag for the direct dependency with the specified - // name, or nil if none exists. If there are multiple dependencies on the same module it returns - // the first DependencyTag. - GetDirectDep(name string) (blueprint.Module, blueprint.DependencyTag) - - ModuleFromName(name string) (blueprint.Module, bool) - - // VisitDirectDepsBlueprint calls visit for each direct dependency. If there are multiple - // direct dependencies on the same module visit will be called multiple times on that module - // and OtherModuleDependencyTag will return a different tag for each. - // - // The Module passed to the visit function should not be retained outside of the visit - // function, it may be invalidated by future mutators. - VisitDirectDepsBlueprint(visit func(blueprint.Module)) - - // VisitDirectDeps calls visit for each direct dependency. If there are multiple - // direct dependencies on the same module visit will be called multiple times on that module - // and OtherModuleDependencyTag will return a different tag for each. It raises an error if any of the - // dependencies are not an android.Module. - // - // The Module passed to the visit function should not be retained outside of the visit - // function, it may be invalidated by future mutators. - VisitDirectDeps(visit func(Module)) - - VisitDirectDepsWithTag(tag blueprint.DependencyTag, visit func(Module)) - - // VisitDirectDepsIf calls pred for each direct dependency, and if pred returns true calls visit. If there are - // multiple direct dependencies on the same module pred and visit will be called multiple times on that module and - // OtherModuleDependencyTag will return a different tag for each. It skips any - // dependencies that are not an android.Module. - // - // The Module passed to the visit function should not be retained outside of the visit function, it may be - // invalidated by future mutators. - VisitDirectDepsIf(pred func(Module) bool, visit func(Module)) - // Deprecated: use WalkDeps instead to support multiple dependency tags on the same module - VisitDepsDepthFirst(visit func(Module)) - // Deprecated: use WalkDeps instead to support multiple dependency tags on the same module - VisitDepsDepthFirstIf(pred func(Module) bool, visit func(Module)) - - // WalkDeps calls visit for each transitive dependency, traversing the dependency tree in top down order. visit may - // be called multiple times for the same (child, parent) pair if there are multiple direct dependencies between the - // child and parent with different tags. OtherModuleDependencyTag will return the tag for the currently visited - // (child, parent) pair. If visit returns false WalkDeps will not continue recursing down to child. It skips - // any dependencies that are not an android.Module. - // - // The Modules passed to the visit function should not be retained outside of the visit function, they may be - // invalidated by future mutators. - WalkDeps(visit func(child, parent Module) bool) - - // WalkDepsBlueprint calls visit for each transitive dependency, traversing the dependency - // tree in top down order. visit may be called multiple times for the same (child, parent) - // pair if there are multiple direct dependencies between the child and parent with different - // tags. OtherModuleDependencyTag will return the tag for the currently visited - // (child, parent) pair. If visit returns false WalkDeps will not continue recursing down - // to child. - // - // The Modules passed to the visit function should not be retained outside of the visit function, they may be - // invalidated by future mutators. - WalkDepsBlueprint(visit func(blueprint.Module, blueprint.Module) bool) - - // GetWalkPath is supposed to be called in visit function passed in WalkDeps() - // and returns a top-down dependency path from a start module to current child module. - GetWalkPath() []Module - - // PrimaryModule returns the first variant of the current module. Variants of a module are always visited in - // order by mutators and GenerateBuildActions, so the data created by the current mutator can be read from the - // Module returned by PrimaryModule without data races. This can be used to perform singleton actions that are - // only done once for all variants of a module. - PrimaryModule() Module - - // FinalModule returns the last variant of the current module. Variants of a module are always visited in - // order by mutators and GenerateBuildActions, so the data created by the current mutator can be read from all - // variants using VisitAllModuleVariants if the current module == FinalModule(). This can be used to perform - // singleton actions that are only done once for all variants of a module. - FinalModule() Module - - // VisitAllModuleVariants calls visit for each variant of the current module. Variants of a module are always - // visited in order by mutators and GenerateBuildActions, so the data created by the current mutator can be read - // from all variants if the current module == FinalModule(). Otherwise, care must be taken to not access any - // data modified by the current mutator. - VisitAllModuleVariants(visit func(Module)) - - // GetTagPath is supposed to be called in visit function passed in WalkDeps() - // and returns a top-down dependency tags path from a start module to current child module. - // It has one less entry than GetWalkPath() as it contains the dependency tags that - // exist between each adjacent pair of modules in the GetWalkPath(). - // GetTagPath()[i] is the tag between GetWalkPath()[i] and GetWalkPath()[i+1] - GetTagPath() []blueprint.DependencyTag - - // GetPathString is supposed to be called in visit function passed in WalkDeps() - // and returns a multi-line string showing the modules and dependency tags - // among them along the top-down dependency path from a start module to current child module. - // skipFirst when set to true, the output doesn't include the start module, - // which is already printed when this function is used along with ModuleErrorf(). - GetPathString(skipFirst bool) string - - AddMissingDependencies(missingDeps []string) - - // getMissingDependencies returns the list of missing dependencies. - // Calling this function prevents adding new dependencies. - getMissingDependencies() []string - - // AddUnconvertedBp2buildDep stores module name of a direct dependency that was not converted via bp2build - AddUnconvertedBp2buildDep(dep string) - - // AddMissingBp2buildDep stores the module name of a direct dependency that was not found. - AddMissingBp2buildDep(dep string) - - Target() Target - TargetPrimary() bool - - // The additional arch specific targets (e.g. 32/64 bit) that this module variant is - // responsible for creating. - MultiTargets() []Target - Arch() Arch - Os() OsType - Host() bool - Device() bool - Darwin() bool - Windows() bool - PrimaryArch() bool -} - -// Deprecated: use EarlyModuleContext instead -type BaseContext interface { - EarlyModuleContext -} - -type ModuleContext interface { - BaseModuleContext - - blueprintModuleContext() blueprint.ModuleContext - - // Deprecated: use ModuleContext.Build instead. - ModuleBuild(pctx PackageContext, params ModuleBuildParams) - - // Returns a list of paths expanded from globs and modules referenced using ":module" syntax. The property must - // be tagged with `android:"path" to support automatic source module dependency resolution. - // - // Deprecated: use PathsForModuleSrc or PathsForModuleSrcExcludes instead. - ExpandSources(srcFiles, excludes []string) Paths - - // Returns a single path expanded from globs and modules referenced using ":module" syntax. The property must - // be tagged with `android:"path" to support automatic source module dependency resolution. - // - // Deprecated: use PathForModuleSrc instead. - ExpandSource(srcFile, prop string) Path - - ExpandOptionalSource(srcFile *string, prop string) OptionalPath - - // InstallExecutable creates a rule to copy srcPath to name in the installPath directory, - // with the given additional dependencies. The file is marked executable after copying. - // - // The installed file will be returned by FilesToInstall(), and the PackagingSpec for the - // installed file will be returned by PackagingSpecs() on this module or by - // TransitivePackagingSpecs() on modules that depend on this module through dependency tags - // for which IsInstallDepNeeded returns true. - InstallExecutable(installPath InstallPath, name string, srcPath Path, deps ...Path) InstallPath - - // InstallFile creates a rule to copy srcPath to name in the installPath directory, - // with the given additional dependencies. - // - // The installed file will be returned by FilesToInstall(), and the PackagingSpec for the - // installed file will be returned by PackagingSpecs() on this module or by - // TransitivePackagingSpecs() on modules that depend on this module through dependency tags - // for which IsInstallDepNeeded returns true. - InstallFile(installPath InstallPath, name string, srcPath Path, deps ...Path) InstallPath - - // InstallFileWithExtraFilesZip creates a rule to copy srcPath to name in the installPath - // directory, and also unzip a zip file containing extra files to install into the same - // directory. - // - // The installed file will be returned by FilesToInstall(), and the PackagingSpec for the - // installed file will be returned by PackagingSpecs() on this module or by - // TransitivePackagingSpecs() on modules that depend on this module through dependency tags - // for which IsInstallDepNeeded returns true. - InstallFileWithExtraFilesZip(installPath InstallPath, name string, srcPath Path, extraZip Path, deps ...Path) InstallPath - - // InstallSymlink creates a rule to create a symlink from src srcPath to name in the installPath - // directory. - // - // The installed symlink will be returned by FilesToInstall(), and the PackagingSpec for the - // installed file will be returned by PackagingSpecs() on this module or by - // TransitivePackagingSpecs() on modules that depend on this module through dependency tags - // for which IsInstallDepNeeded returns true. - InstallSymlink(installPath InstallPath, name string, srcPath InstallPath) InstallPath - - // InstallAbsoluteSymlink creates a rule to create an absolute symlink from src srcPath to name - // in the installPath directory. - // - // The installed symlink will be returned by FilesToInstall(), and the PackagingSpec for the - // installed file will be returned by PackagingSpecs() on this module or by - // TransitivePackagingSpecs() on modules that depend on this module through dependency tags - // for which IsInstallDepNeeded returns true. - InstallAbsoluteSymlink(installPath InstallPath, name string, absPath string) InstallPath - - // PackageFile creates a PackagingSpec as if InstallFile was called, but without creating - // the rule to copy the file. This is useful to define how a module would be packaged - // without installing it into the global installation directories. - // - // The created PackagingSpec for the will be returned by PackagingSpecs() on this module or by - // TransitivePackagingSpecs() on modules that depend on this module through dependency tags - // for which IsInstallDepNeeded returns true. - PackageFile(installPath InstallPath, name string, srcPath Path) PackagingSpec - - CheckbuildFile(srcPath Path) - - InstallInData() bool - InstallInTestcases() bool - InstallInSanitizerDir() bool - InstallInRamdisk() bool - InstallInVendorRamdisk() bool - InstallInDebugRamdisk() bool - InstallInRecovery() bool - InstallInRoot() bool - InstallInVendor() bool - InstallForceOS() (*OsType, *ArchType) - - RequiredModuleNames() []string - HostRequiredModuleNames() []string - TargetRequiredModuleNames() []string - - ModuleSubDir() string - SoongConfigTraceHash() string - - Variable(pctx PackageContext, name, value string) - Rule(pctx PackageContext, name string, params blueprint.RuleParams, argNames ...string) blueprint.Rule - // Similar to blueprint.ModuleContext.Build, but takes Paths instead of []string, - // and performs more verification. - Build(pctx PackageContext, params BuildParams) - // Phony creates a Make-style phony rule, a rule with no commands that can depend on other - // phony rules or real files. Phony can be called on the same name multiple times to add - // additional dependencies. - Phony(phony string, deps ...Path) - - // GetMissingDependencies returns the list of dependencies that were passed to AddDependencies or related methods, - // but do not exist. - GetMissingDependencies() []string - - // LicenseMetadataFile returns the path where the license metadata for this module will be - // generated. - LicenseMetadataFile() Path -} - type Module interface { blueprint.Module @@ -1677,18 +1209,6 @@ func (m *ModuleBase) GetPartitionForBp2build() string { return m.commonProperties.BazelConversionStatus.Partition } -// AddUnconvertedBp2buildDep stores module name of a dependency that was not converted to Bazel. -func (b *baseModuleContext) AddUnconvertedBp2buildDep(dep string) { - unconvertedDeps := &b.Module().base().commonProperties.BazelConversionStatus.UnconvertedDeps - *unconvertedDeps = append(*unconvertedDeps, dep) -} - -// AddMissingBp2buildDep stores module name of a dependency that was not found in a Android.bp file. -func (b *baseModuleContext) AddMissingBp2buildDep(dep string) { - missingDeps := &b.Module().base().commonProperties.BazelConversionStatus.MissingDeps - *missingDeps = append(*missingDeps, dep) -} - // GetUnconvertedBp2buildDeps returns the list of module names of this module's direct dependencies that // were not converted to Bazel. func (m *ModuleBase) GetUnconvertedBp2buildDeps() []string { @@ -2605,162 +2125,6 @@ func checkDistProperties(ctx *moduleContext, property string, dist *Dist) { } -type earlyModuleContext struct { - blueprint.EarlyModuleContext - - kind moduleKind - config Config -} - -func (e *earlyModuleContext) Glob(globPattern string, excludes []string) Paths { - return Glob(e, globPattern, excludes) -} - -func (e *earlyModuleContext) GlobFiles(globPattern string, excludes []string) Paths { - return GlobFiles(e, globPattern, excludes) -} - -func (e *earlyModuleContext) IsSymlink(path Path) bool { - fileInfo, err := e.config.fs.Lstat(path.String()) - if err != nil { - e.ModuleErrorf("os.Lstat(%q) failed: %s", path.String(), err) - } - return fileInfo.Mode()&os.ModeSymlink == os.ModeSymlink -} - -func (e *earlyModuleContext) Readlink(path Path) string { - dest, err := e.config.fs.Readlink(path.String()) - if err != nil { - e.ModuleErrorf("os.Readlink(%q) failed: %s", path.String(), err) - } - return dest -} - -func (e *earlyModuleContext) Module() Module { - module, _ := e.EarlyModuleContext.Module().(Module) - return module -} - -func (e *earlyModuleContext) Config() Config { - return e.EarlyModuleContext.Config().(Config) -} - -func (e *earlyModuleContext) AConfig() Config { - return e.config -} - -func (e *earlyModuleContext) DeviceConfig() DeviceConfig { - return DeviceConfig{e.config.deviceConfig} -} - -func (e *earlyModuleContext) Platform() bool { - return e.kind == platformModule -} - -func (e *earlyModuleContext) DeviceSpecific() bool { - return e.kind == deviceSpecificModule -} - -func (e *earlyModuleContext) SocSpecific() bool { - return e.kind == socSpecificModule -} - -func (e *earlyModuleContext) ProductSpecific() bool { - return e.kind == productSpecificModule -} - -func (e *earlyModuleContext) SystemExtSpecific() bool { - return e.kind == systemExtSpecificModule -} - -func (e *earlyModuleContext) Namespace() *Namespace { - return e.EarlyModuleContext.Namespace().(*Namespace) -} - -type baseModuleContext struct { - bp blueprint.BaseModuleContext - earlyModuleContext - os OsType - target Target - multiTargets []Target - targetPrimary bool - - walkPath []Module - tagPath []blueprint.DependencyTag - - strictVisitDeps bool // If true, enforce that all dependencies are enabled - - bazelConversionMode bool -} - -func (b *baseModuleContext) isBazelConversionMode() bool { - return b.bazelConversionMode -} -func (b *baseModuleContext) OtherModuleName(m blueprint.Module) string { - return b.bp.OtherModuleName(m) -} -func (b *baseModuleContext) OtherModuleDir(m blueprint.Module) string { return b.bp.OtherModuleDir(m) } -func (b *baseModuleContext) OtherModuleErrorf(m blueprint.Module, fmt string, args ...interface{}) { - b.bp.OtherModuleErrorf(m, fmt, args...) -} -func (b *baseModuleContext) OtherModuleDependencyTag(m blueprint.Module) blueprint.DependencyTag { - return b.bp.OtherModuleDependencyTag(m) -} -func (b *baseModuleContext) OtherModuleExists(name string) bool { return b.bp.OtherModuleExists(name) } -func (b *baseModuleContext) OtherModuleDependencyVariantExists(variations []blueprint.Variation, name string) bool { - return b.bp.OtherModuleDependencyVariantExists(variations, name) -} -func (b *baseModuleContext) OtherModuleFarDependencyVariantExists(variations []blueprint.Variation, name string) bool { - return b.bp.OtherModuleFarDependencyVariantExists(variations, name) -} -func (b *baseModuleContext) OtherModuleReverseDependencyVariantExists(name string) bool { - return b.bp.OtherModuleReverseDependencyVariantExists(name) -} -func (b *baseModuleContext) OtherModuleType(m blueprint.Module) string { - return b.bp.OtherModuleType(m) -} -func (b *baseModuleContext) OtherModuleProvider(m blueprint.Module, provider blueprint.ProviderKey) interface{} { - return b.bp.OtherModuleProvider(m, provider) -} -func (b *baseModuleContext) OtherModuleHasProvider(m blueprint.Module, provider blueprint.ProviderKey) bool { - return b.bp.OtherModuleHasProvider(m, provider) -} -func (b *baseModuleContext) Provider(provider blueprint.ProviderKey) interface{} { - return b.bp.Provider(provider) -} -func (b *baseModuleContext) HasProvider(provider blueprint.ProviderKey) bool { - return b.bp.HasProvider(provider) -} -func (b *baseModuleContext) SetProvider(provider blueprint.ProviderKey, value interface{}) { - b.bp.SetProvider(provider, value) -} - -func (b *baseModuleContext) GetDirectDepWithTag(name string, tag blueprint.DependencyTag) blueprint.Module { - return b.bp.GetDirectDepWithTag(name, tag) -} - -func (b *baseModuleContext) blueprintBaseModuleContext() blueprint.BaseModuleContext { - return b.bp -} - -type moduleContext struct { - bp blueprint.ModuleContext - baseModuleContext - packagingSpecs []PackagingSpec - installFiles InstallPaths - checkbuildFiles Paths - module Module - phonies map[string]Paths - - katiInstalls []katiInstall - katiSymlinks []katiInstall - - // For tests - buildParams []BuildParams - ruleParams map[blueprint.Rule]blueprint.RuleParams - variables map[string]string -} - // katiInstall stores a request from Soong to Make to create an install rule. type katiInstall struct { from Path @@ -2804,525 +2168,6 @@ func (installs katiInstalls) InstallPaths() InstallPaths { return paths } -func (m *moduleContext) ninjaError(params BuildParams, err error) (PackageContext, BuildParams) { - return pctx, BuildParams{ - Rule: ErrorRule, - Description: params.Description, - Output: params.Output, - Outputs: params.Outputs, - ImplicitOutput: params.ImplicitOutput, - ImplicitOutputs: params.ImplicitOutputs, - Args: map[string]string{ - "error": err.Error(), - }, - } -} - -func (m *moduleContext) ModuleBuild(pctx PackageContext, params ModuleBuildParams) { - m.Build(pctx, BuildParams(params)) -} - -func validateBuildParams(params blueprint.BuildParams) error { - // Validate that the symlink outputs are declared outputs or implicit outputs - allOutputs := map[string]bool{} - for _, output := range params.Outputs { - allOutputs[output] = true - } - for _, output := range params.ImplicitOutputs { - allOutputs[output] = true - } - for _, symlinkOutput := range params.SymlinkOutputs { - if !allOutputs[symlinkOutput] { - return fmt.Errorf( - "Symlink output %s is not a declared output or implicit output", - symlinkOutput) - } - } - return nil -} - -// Convert build parameters from their concrete Android types into their string representations, -// and combine the singular and plural fields of the same type (e.g. Output and Outputs). -func convertBuildParams(params BuildParams) blueprint.BuildParams { - bparams := blueprint.BuildParams{ - Rule: params.Rule, - Description: params.Description, - Deps: params.Deps, - Outputs: params.Outputs.Strings(), - ImplicitOutputs: params.ImplicitOutputs.Strings(), - SymlinkOutputs: params.SymlinkOutputs.Strings(), - Inputs: params.Inputs.Strings(), - Implicits: params.Implicits.Strings(), - OrderOnly: params.OrderOnly.Strings(), - Validations: params.Validations.Strings(), - Args: params.Args, - Optional: !params.Default, - } - - if params.Depfile != nil { - bparams.Depfile = params.Depfile.String() - } - if params.Output != nil { - bparams.Outputs = append(bparams.Outputs, params.Output.String()) - } - if params.SymlinkOutput != nil { - bparams.SymlinkOutputs = append(bparams.SymlinkOutputs, params.SymlinkOutput.String()) - } - if params.ImplicitOutput != nil { - bparams.ImplicitOutputs = append(bparams.ImplicitOutputs, params.ImplicitOutput.String()) - } - if params.Input != nil { - bparams.Inputs = append(bparams.Inputs, params.Input.String()) - } - if params.Implicit != nil { - bparams.Implicits = append(bparams.Implicits, params.Implicit.String()) - } - if params.Validation != nil { - bparams.Validations = append(bparams.Validations, params.Validation.String()) - } - - bparams.Outputs = proptools.NinjaEscapeList(bparams.Outputs) - bparams.ImplicitOutputs = proptools.NinjaEscapeList(bparams.ImplicitOutputs) - bparams.SymlinkOutputs = proptools.NinjaEscapeList(bparams.SymlinkOutputs) - bparams.Inputs = proptools.NinjaEscapeList(bparams.Inputs) - bparams.Implicits = proptools.NinjaEscapeList(bparams.Implicits) - bparams.OrderOnly = proptools.NinjaEscapeList(bparams.OrderOnly) - bparams.Validations = proptools.NinjaEscapeList(bparams.Validations) - bparams.Depfile = proptools.NinjaEscape(bparams.Depfile) - - return bparams -} - -func (m *moduleContext) Variable(pctx PackageContext, name, value string) { - if m.config.captureBuild { - m.variables[name] = value - } - - m.bp.Variable(pctx.PackageContext, name, value) -} - -func (m *moduleContext) Rule(pctx PackageContext, name string, params blueprint.RuleParams, - argNames ...string) blueprint.Rule { - - if m.config.UseRemoteBuild() { - if 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 - } else if params.Pool == remotePool { - // remotePool is a fake pool used to identify rule that are supported for remoting. If the rule's - // pool is the remotePool, replace with nil so that ninja runs it at NINJA_REMOTE_NUM_JOBS - // parallelism. - params.Pool = nil - } - } - - rule := m.bp.Rule(pctx.PackageContext, name, params, argNames...) - - if m.config.captureBuild { - m.ruleParams[rule] = params - } - - return rule -} - -func (m *moduleContext) Build(pctx PackageContext, params BuildParams) { - if params.Description != "" { - params.Description = "${moduleDesc}" + params.Description + "${moduleDescSuffix}" - } - - if missingDeps := m.GetMissingDependencies(); len(missingDeps) > 0 { - pctx, params = m.ninjaError(params, fmt.Errorf("module %s missing dependencies: %s\n", - m.ModuleName(), strings.Join(missingDeps, ", "))) - } - - if m.config.captureBuild { - m.buildParams = append(m.buildParams, params) - } - - bparams := convertBuildParams(params) - err := validateBuildParams(bparams) - if err != nil { - m.ModuleErrorf( - "%s: build parameter validation failed: %s", - m.ModuleName(), - err.Error()) - } - m.bp.Build(pctx.PackageContext, bparams) -} - -func (m *moduleContext) Phony(name string, deps ...Path) { - addPhony(m.config, name, deps...) -} - -func (m *moduleContext) GetMissingDependencies() []string { - var missingDeps []string - missingDeps = append(missingDeps, m.Module().base().commonProperties.MissingDeps...) - missingDeps = append(missingDeps, m.bp.GetMissingDependencies()...) - missingDeps = FirstUniqueStrings(missingDeps) - return missingDeps -} - -func (b *baseModuleContext) AddMissingDependencies(deps []string) { - if deps != nil { - missingDeps := &b.Module().base().commonProperties.MissingDeps - *missingDeps = append(*missingDeps, deps...) - *missingDeps = FirstUniqueStrings(*missingDeps) - } -} - -func (b *baseModuleContext) checkedMissingDeps() bool { - return b.Module().base().commonProperties.CheckedMissingDeps -} - -func (b *baseModuleContext) getMissingDependencies() []string { - checked := &b.Module().base().commonProperties.CheckedMissingDeps - *checked = true - var missingDeps []string - missingDeps = append(missingDeps, b.Module().base().commonProperties.MissingDeps...) - missingDeps = append(missingDeps, b.bp.EarlyGetMissingDependencies()...) - missingDeps = FirstUniqueStrings(missingDeps) - return missingDeps -} - -type AllowDisabledModuleDependency interface { - blueprint.DependencyTag - AllowDisabledModuleDependency(target Module) bool -} - -func (b *baseModuleContext) validateAndroidModule(module blueprint.Module, tag blueprint.DependencyTag, strict bool) Module { - aModule, _ := module.(Module) - - if !strict { - return aModule - } - - if aModule == nil { - b.ModuleErrorf("module %q (%#v) not an android module", b.OtherModuleName(module), tag) - return nil - } - - if !aModule.Enabled() { - if t, ok := tag.(AllowDisabledModuleDependency); !ok || !t.AllowDisabledModuleDependency(aModule) { - if b.Config().AllowMissingDependencies() { - b.AddMissingDependencies([]string{b.OtherModuleName(aModule)}) - } else { - b.ModuleErrorf("depends on disabled module %q", b.OtherModuleName(aModule)) - } - } - return nil - } - return aModule -} - -type dep struct { - mod blueprint.Module - tag blueprint.DependencyTag -} - -func (b *baseModuleContext) getDirectDepsInternal(name string, tag blueprint.DependencyTag) []dep { - var deps []dep - b.VisitDirectDepsBlueprint(func(module blueprint.Module) { - if aModule, _ := module.(Module); aModule != nil { - if aModule.base().BaseModuleName() == name { - returnedTag := b.bp.OtherModuleDependencyTag(aModule) - if tag == nil || returnedTag == tag { - deps = append(deps, dep{aModule, returnedTag}) - } - } - } else if b.bp.OtherModuleName(module) == name { - returnedTag := b.bp.OtherModuleDependencyTag(module) - if tag == nil || returnedTag == tag { - deps = append(deps, dep{module, returnedTag}) - } - } - }) - return deps -} - -func (b *baseModuleContext) getDirectDepInternal(name string, tag blueprint.DependencyTag) (blueprint.Module, blueprint.DependencyTag) { - deps := b.getDirectDepsInternal(name, tag) - if len(deps) == 1 { - return deps[0].mod, deps[0].tag - } else if len(deps) >= 2 { - panic(fmt.Errorf("Multiple dependencies having same BaseModuleName() %q found from %q", - name, b.ModuleName())) - } else { - return nil, nil - } -} - -func (b *baseModuleContext) getDirectDepFirstTag(name string) (blueprint.Module, blueprint.DependencyTag) { - foundDeps := b.getDirectDepsInternal(name, nil) - deps := map[blueprint.Module]bool{} - for _, dep := range foundDeps { - deps[dep.mod] = true - } - if len(deps) == 1 { - return foundDeps[0].mod, foundDeps[0].tag - } else if len(deps) >= 2 { - // this could happen if two dependencies have the same name in different namespaces - // TODO(b/186554727): this should not occur if namespaces are handled within - // getDirectDepsInternal. - panic(fmt.Errorf("Multiple dependencies having same BaseModuleName() %q found from %q", - name, b.ModuleName())) - } else { - return nil, nil - } -} - -func (b *baseModuleContext) GetDirectDepsWithTag(tag blueprint.DependencyTag) []Module { - var deps []Module - b.VisitDirectDepsBlueprint(func(module blueprint.Module) { - if aModule, _ := module.(Module); aModule != nil { - if b.bp.OtherModuleDependencyTag(aModule) == tag { - deps = append(deps, aModule) - } - } - }) - return deps -} - -func (m *moduleContext) GetDirectDepWithTag(name string, tag blueprint.DependencyTag) blueprint.Module { - module, _ := m.getDirectDepInternal(name, tag) - return module -} - -// GetDirectDep returns the Module and DependencyTag for the direct dependency with the specified -// name, or nil if none exists. If there are multiple dependencies on the same module it returns the -// first DependencyTag. -func (b *baseModuleContext) GetDirectDep(name string) (blueprint.Module, blueprint.DependencyTag) { - return b.getDirectDepFirstTag(name) -} - -func (b *baseModuleContext) ModuleFromName(name string) (blueprint.Module, bool) { - if !b.isBazelConversionMode() { - panic("cannot call ModuleFromName if not in bazel conversion mode") - } - var m blueprint.Module - var ok bool - if moduleName, _ := SrcIsModuleWithTag(name); moduleName != "" { - m, ok = b.bp.ModuleFromName(moduleName) - } else { - m, ok = b.bp.ModuleFromName(name) - } - if !ok { - return m, ok - } - // If this module is not preferred, tried to get the prebuilt version instead - if a, aOk := m.(Module); aOk && !IsModulePrebuilt(a) && !IsModulePreferred(a) { - return b.ModuleFromName("prebuilt_" + name) - } - return m, ok -} - -func (b *baseModuleContext) VisitDirectDepsBlueprint(visit func(blueprint.Module)) { - b.bp.VisitDirectDeps(visit) -} - -func (b *baseModuleContext) VisitDirectDeps(visit func(Module)) { - b.bp.VisitDirectDeps(func(module blueprint.Module) { - if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps); aModule != nil { - visit(aModule) - } - }) -} - -func (b *baseModuleContext) VisitDirectDepsWithTag(tag blueprint.DependencyTag, visit func(Module)) { - b.bp.VisitDirectDeps(func(module blueprint.Module) { - if b.bp.OtherModuleDependencyTag(module) == tag { - if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps); aModule != nil { - visit(aModule) - } - } - }) -} - -func (b *baseModuleContext) VisitDirectDepsIf(pred func(Module) bool, visit func(Module)) { - b.bp.VisitDirectDepsIf( - // pred - func(module blueprint.Module) bool { - if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps); aModule != nil { - return pred(aModule) - } else { - return false - } - }, - // visit - func(module blueprint.Module) { - visit(module.(Module)) - }) -} - -func (b *baseModuleContext) VisitDepsDepthFirst(visit func(Module)) { - b.bp.VisitDepsDepthFirst(func(module blueprint.Module) { - if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps); aModule != nil { - visit(aModule) - } - }) -} - -func (b *baseModuleContext) VisitDepsDepthFirstIf(pred func(Module) bool, visit func(Module)) { - b.bp.VisitDepsDepthFirstIf( - // pred - func(module blueprint.Module) bool { - if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps); aModule != nil { - return pred(aModule) - } else { - return false - } - }, - // visit - func(module blueprint.Module) { - visit(module.(Module)) - }) -} - -func (b *baseModuleContext) WalkDepsBlueprint(visit func(blueprint.Module, blueprint.Module) bool) { - b.bp.WalkDeps(visit) -} - -func (b *baseModuleContext) WalkDeps(visit func(Module, Module) bool) { - b.walkPath = []Module{b.Module()} - b.tagPath = []blueprint.DependencyTag{} - b.bp.WalkDeps(func(child, parent blueprint.Module) bool { - childAndroidModule, _ := child.(Module) - parentAndroidModule, _ := parent.(Module) - if childAndroidModule != nil && parentAndroidModule != nil { - // record walkPath before visit - for b.walkPath[len(b.walkPath)-1] != parentAndroidModule { - b.walkPath = b.walkPath[0 : len(b.walkPath)-1] - b.tagPath = b.tagPath[0 : len(b.tagPath)-1] - } - b.walkPath = append(b.walkPath, childAndroidModule) - b.tagPath = append(b.tagPath, b.OtherModuleDependencyTag(childAndroidModule)) - return visit(childAndroidModule, parentAndroidModule) - } else { - return false - } - }) -} - -func (b *baseModuleContext) GetWalkPath() []Module { - return b.walkPath -} - -func (b *baseModuleContext) GetTagPath() []blueprint.DependencyTag { - return b.tagPath -} - -func (b *baseModuleContext) VisitAllModuleVariants(visit func(Module)) { - b.bp.VisitAllModuleVariants(func(module blueprint.Module) { - visit(module.(Module)) - }) -} - -func (b *baseModuleContext) PrimaryModule() Module { - return b.bp.PrimaryModule().(Module) -} - -func (b *baseModuleContext) FinalModule() Module { - return b.bp.FinalModule().(Module) -} - -// IsMetaDependencyTag returns true for cross-cutting metadata dependencies. -func IsMetaDependencyTag(tag blueprint.DependencyTag) bool { - if tag == licenseKindTag { - return true - } else if tag == licensesTag { - return true - } else if tag == acDepTag { - return true - } - return false -} - -// A regexp for removing boilerplate from BaseDependencyTag from the string representation of -// a dependency tag. -var tagCleaner = regexp.MustCompile(`\QBaseDependencyTag:{}\E(, )?`) - -// PrettyPrintTag returns string representation of the tag, but prefers -// custom String() method if available. -func PrettyPrintTag(tag blueprint.DependencyTag) string { - // Use tag's custom String() method if available. - if stringer, ok := tag.(fmt.Stringer); ok { - return stringer.String() - } - - // Otherwise, get a default string representation of the tag's struct. - tagString := fmt.Sprintf("%T: %+v", tag, tag) - - // Remove the boilerplate from BaseDependencyTag as it adds no value. - tagString = tagCleaner.ReplaceAllString(tagString, "") - return tagString -} - -func (b *baseModuleContext) GetPathString(skipFirst bool) string { - sb := strings.Builder{} - tagPath := b.GetTagPath() - walkPath := b.GetWalkPath() - if !skipFirst { - sb.WriteString(walkPath[0].String()) - } - for i, m := range walkPath[1:] { - sb.WriteString("\n") - sb.WriteString(fmt.Sprintf(" via tag %s\n", PrettyPrintTag(tagPath[i]))) - sb.WriteString(fmt.Sprintf(" -> %s", m.String())) - } - return sb.String() -} - -func (m *moduleContext) ModuleSubDir() string { - return m.bp.ModuleSubDir() -} - -func (m *moduleContext) SoongConfigTraceHash() string { - return m.module.base().commonProperties.SoongConfigTraceHash -} - -func (b *baseModuleContext) Target() Target { - return b.target -} - -func (b *baseModuleContext) TargetPrimary() bool { - return b.targetPrimary -} - -func (b *baseModuleContext) MultiTargets() []Target { - return b.multiTargets -} - -func (b *baseModuleContext) Arch() Arch { - return b.target.Arch -} - -func (b *baseModuleContext) Os() OsType { - return b.os -} - -func (b *baseModuleContext) Host() bool { - return b.os.Class == Host -} - -func (b *baseModuleContext) Device() bool { - return b.os.Class == Device -} - -func (b *baseModuleContext) Darwin() bool { - return b.os == Darwin -} - -func (b *baseModuleContext) Windows() bool { - return b.os == Windows -} - -func (b *baseModuleContext) PrimaryArch() bool { - if len(b.config.Targets[b.target.Os]) <= 1 { - return true - } - return b.target.Arch.ArchType == b.config.Targets[b.target.Os][0].Arch.ArchType -} - // Makes this module a platform module, i.e. not specific to soc, device, // product, or system_ext. func (m *ModuleBase) MakeAsPlatform() { @@ -3346,274 +2191,6 @@ func (m *ModuleBase) IsNativeBridgeSupported() bool { return proptools.Bool(m.commonProperties.Native_bridge_supported) } -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() -} - -func (m *moduleContext) InstallInRamdisk() bool { - return m.module.InstallInRamdisk() -} - -func (m *moduleContext) InstallInVendorRamdisk() bool { - return m.module.InstallInVendorRamdisk() -} - -func (m *moduleContext) InstallInDebugRamdisk() bool { - return m.module.InstallInDebugRamdisk() -} - -func (m *moduleContext) InstallInRecovery() bool { - return m.module.InstallInRecovery() -} - -func (m *moduleContext) InstallInRoot() bool { - return m.module.InstallInRoot() -} - -func (m *moduleContext) InstallForceOS() (*OsType, *ArchType) { - return m.module.InstallForceOS() -} - -func (m *moduleContext) InstallInVendor() bool { - return m.module.InstallInVendor() -} - -func (m *moduleContext) skipInstall() bool { - if m.module.base().commonProperties.SkipInstall { - return true - } - - if m.module.base().commonProperties.HideFromMake { - return true - } - - // We'll need a solution for choosing which of modules with the same name in different - // namespaces to install. For now, reuse the list of namespaces exported to Make as the - // list of namespaces to install in a Soong-only build. - if !m.module.base().commonProperties.NamespaceExportedToMake { - return true - } - - return false -} - -func (m *moduleContext) InstallFile(installPath InstallPath, name string, srcPath Path, - deps ...Path) InstallPath { - return m.installFile(installPath, name, srcPath, deps, false, nil) -} - -func (m *moduleContext) InstallExecutable(installPath InstallPath, name string, srcPath Path, - deps ...Path) InstallPath { - return m.installFile(installPath, name, srcPath, deps, true, nil) -} - -func (m *moduleContext) InstallFileWithExtraFilesZip(installPath InstallPath, name string, srcPath Path, - extraZip Path, deps ...Path) InstallPath { - return m.installFile(installPath, name, srcPath, deps, false, &extraFilesZip{ - zip: extraZip, - dir: installPath, - }) -} - -func (m *moduleContext) PackageFile(installPath InstallPath, name string, srcPath Path) PackagingSpec { - fullInstallPath := installPath.Join(m, name) - return m.packageFile(fullInstallPath, srcPath, false) -} - -func (m *moduleContext) packageFile(fullInstallPath InstallPath, srcPath Path, executable bool) PackagingSpec { - licenseFiles := m.Module().EffectiveLicenseFiles() - spec := PackagingSpec{ - relPathInPackage: Rel(m, fullInstallPath.PartitionDir(), fullInstallPath.String()), - srcPath: srcPath, - symlinkTarget: "", - executable: executable, - effectiveLicenseFiles: &licenseFiles, - partition: fullInstallPath.partition, - } - m.packagingSpecs = append(m.packagingSpecs, spec) - return spec -} - -func (m *moduleContext) installFile(installPath InstallPath, name string, srcPath Path, deps []Path, - executable bool, extraZip *extraFilesZip) InstallPath { - - fullInstallPath := installPath.Join(m, name) - m.module.base().hooks.runInstallHooks(m, srcPath, fullInstallPath, false) - - if !m.skipInstall() { - deps = append(deps, InstallPaths(m.module.base().installFilesDepSet.ToList()).Paths()...) - - var implicitDeps, orderOnlyDeps Paths - - if m.Host() { - // Installed host modules might be used during the build, depend directly on their - // dependencies so their timestamp is updated whenever their dependency is updated - implicitDeps = deps - } else { - orderOnlyDeps = deps - } - - if m.Config().KatiEnabled() { - // When creating the install rule in Soong but embedding in Make, write the rule to a - // makefile instead of directly to the ninja file so that main.mk can add the - // dependencies from the `required` property that are hard to resolve in Soong. - m.katiInstalls = append(m.katiInstalls, katiInstall{ - from: srcPath, - to: fullInstallPath, - implicitDeps: implicitDeps, - orderOnlyDeps: orderOnlyDeps, - executable: executable, - extraFiles: extraZip, - }) - } else { - rule := Cp - if executable { - rule = CpExecutable - } - - extraCmds := "" - if extraZip != nil { - extraCmds += fmt.Sprintf(" && ( unzip -qDD -d '%s' '%s' 2>&1 | grep -v \"zipfile is empty\"; exit $${PIPESTATUS[0]} )", - extraZip.dir.String(), extraZip.zip.String()) - extraCmds += " || ( code=$$?; if [ $$code -ne 0 -a $$code -ne 1 ]; then exit $$code; fi )" - implicitDeps = append(implicitDeps, extraZip.zip) - } - - m.Build(pctx, BuildParams{ - Rule: rule, - Description: "install " + fullInstallPath.Base(), - Output: fullInstallPath, - Input: srcPath, - Implicits: implicitDeps, - OrderOnly: orderOnlyDeps, - Default: !m.Config().KatiEnabled(), - Args: map[string]string{ - "extraCmds": extraCmds, - }, - }) - } - - m.installFiles = append(m.installFiles, fullInstallPath) - } - - m.packageFile(fullInstallPath, srcPath, executable) - - m.checkbuildFiles = append(m.checkbuildFiles, srcPath) - - return fullInstallPath -} - -func (m *moduleContext) InstallSymlink(installPath InstallPath, name string, srcPath InstallPath) InstallPath { - fullInstallPath := installPath.Join(m, name) - m.module.base().hooks.runInstallHooks(m, srcPath, fullInstallPath, true) - - relPath, err := filepath.Rel(path.Dir(fullInstallPath.String()), srcPath.String()) - if err != nil { - panic(fmt.Sprintf("Unable to generate symlink between %q and %q: %s", fullInstallPath.Base(), srcPath.Base(), err)) - } - if !m.skipInstall() { - - if m.Config().KatiEnabled() { - // When creating the symlink rule in Soong but embedding in Make, write the rule to a - // makefile instead of directly to the ninja file so that main.mk can add the - // dependencies from the `required` property that are hard to resolve in Soong. - m.katiSymlinks = append(m.katiSymlinks, katiInstall{ - from: srcPath, - to: fullInstallPath, - }) - } else { - // The symlink doesn't need updating when the target is modified, but we sometimes - // have a dependency on a symlink to a binary instead of to the binary directly, and - // the mtime of the symlink must be updated when the binary is modified, so use a - // normal dependency here instead of an order-only dependency. - m.Build(pctx, BuildParams{ - Rule: Symlink, - Description: "install symlink " + fullInstallPath.Base(), - Output: fullInstallPath, - Input: srcPath, - Default: !m.Config().KatiEnabled(), - Args: map[string]string{ - "fromPath": relPath, - }, - }) - } - - m.installFiles = append(m.installFiles, fullInstallPath) - m.checkbuildFiles = append(m.checkbuildFiles, srcPath) - } - - m.packagingSpecs = append(m.packagingSpecs, PackagingSpec{ - relPathInPackage: Rel(m, fullInstallPath.PartitionDir(), fullInstallPath.String()), - srcPath: nil, - symlinkTarget: relPath, - executable: false, - partition: fullInstallPath.partition, - }) - - return fullInstallPath -} - -// installPath/name -> absPath where absPath might be a path that is available only at runtime -// (e.g. /apex/...) -func (m *moduleContext) InstallAbsoluteSymlink(installPath InstallPath, name string, absPath string) InstallPath { - fullInstallPath := installPath.Join(m, name) - m.module.base().hooks.runInstallHooks(m, nil, fullInstallPath, true) - - if !m.skipInstall() { - if m.Config().KatiEnabled() { - // When creating the symlink rule in Soong but embedding in Make, write the rule to a - // makefile instead of directly to the ninja file so that main.mk can add the - // dependencies from the `required` property that are hard to resolve in Soong. - m.katiSymlinks = append(m.katiSymlinks, katiInstall{ - absFrom: absPath, - to: fullInstallPath, - }) - } else { - m.Build(pctx, BuildParams{ - Rule: Symlink, - Description: "install symlink " + fullInstallPath.Base() + " -> " + absPath, - Output: fullInstallPath, - Default: !m.Config().KatiEnabled(), - Args: map[string]string{ - "fromPath": absPath, - }, - }) - } - - m.installFiles = append(m.installFiles, fullInstallPath) - } - - m.packagingSpecs = append(m.packagingSpecs, PackagingSpec{ - relPathInPackage: Rel(m, fullInstallPath.PartitionDir(), fullInstallPath.String()), - srcPath: nil, - symlinkTarget: absPath, - executable: false, - partition: fullInstallPath.partition, - }) - - return fullInstallPath -} - -func (m *moduleContext) CheckbuildFile(srcPath Path) { - m.checkbuildFiles = append(m.checkbuildFiles, srcPath) -} - -func (m *moduleContext) blueprintModuleContext() blueprint.ModuleContext { - return m.bp -} - -func (m *moduleContext) LicenseMetadataFile() Path { - return m.module.base().licenseMetadataFile -} - // SrcIsModule decodes module references in the format ":unqualified-name" or "//namespace:name" // into the module name, or empty string if the input was not a module reference. func SrcIsModule(s string) (module string) { @@ -3820,44 +2397,6 @@ type HostToolProvider interface { HostToolPath() OptionalPath } -// Returns a list of paths expanded from globs and modules referenced using ":module" syntax. The property must -// be tagged with `android:"path" to support automatic source module dependency resolution. -// -// Deprecated: use PathsForModuleSrc or PathsForModuleSrcExcludes instead. -func (m *moduleContext) ExpandSources(srcFiles, excludes []string) Paths { - return PathsForModuleSrcExcludes(m, srcFiles, excludes) -} - -// Returns a single path expanded from globs and modules referenced using ":module" syntax. The property must -// be tagged with `android:"path" to support automatic source module dependency resolution. -// -// Deprecated: use PathForModuleSrc instead. -func (m *moduleContext) ExpandSource(srcFile, _ string) Path { - return PathForModuleSrc(m, srcFile) -} - -// Returns an optional single path expanded from globs and modules referenced using ":module" syntax if -// the srcFile is non-nil. The property must be tagged with `android:"path" to support automatic source module -// dependency resolution. -func (m *moduleContext) ExpandOptionalSource(srcFile *string, _ string) OptionalPath { - if srcFile != nil { - return OptionalPathForPath(PathForModuleSrc(m, *srcFile)) - } - return OptionalPath{} -} - -func (m *moduleContext) RequiredModuleNames() []string { - return m.module.RequiredModuleNames() -} - -func (m *moduleContext) HostRequiredModuleNames() []string { - return m.module.HostRequiredModuleNames() -} - -func (m *moduleContext) TargetRequiredModuleNames() []string { - return m.module.TargetRequiredModuleNames() -} - func init() { RegisterParallelSingletonType("buildtarget", BuildTargetSingleton) RegisterParallelSingletonType("soongconfigtrace", soongConfigTraceSingletonFunc) diff --git a/android/module_context.go b/android/module_context.go new file mode 100644 index 000000000..0b4d28c54 --- /dev/null +++ b/android/module_context.go @@ -0,0 +1,698 @@ +// Copyright 2015 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package android + +import ( + "fmt" + "github.com/google/blueprint" + "github.com/google/blueprint/proptools" + "path" + "path/filepath" + "strings" +) + +// BuildParameters describes the set of potential parameters to build a Ninja rule. +// In general, these correspond to a Ninja concept. +type BuildParams struct { + // A Ninja Rule that will be written to the Ninja file. This allows factoring out common code + // among multiple modules to reduce repetition in the Ninja file of action requirements. A rule + // can contain variables that should be provided in Args. + Rule blueprint.Rule + // Deps represents the depfile format. When using RuleBuilder, this defaults to GCC when depfiles + // are used. + Deps blueprint.Deps + // Depfile is a writeable path that allows correct incremental builds when the inputs have not + // been fully specified by the Ninja rule. Ninja supports a subset of the Makefile depfile syntax. + Depfile WritablePath + // A description of the build action. + Description string + // Output is an output file of the action. When using this field, references to $out in the Ninja + // command will refer to this file. + Output WritablePath + // Outputs is a slice of output file of the action. When using this field, references to $out in + // the Ninja command will refer to these files. + Outputs WritablePaths + // SymlinkOutput is an output file specifically that is a symlink. + SymlinkOutput WritablePath + // SymlinkOutputs is a slice of output files specifically that is a symlink. + SymlinkOutputs WritablePaths + // ImplicitOutput is an output file generated by the action. Note: references to `$out` in the + // Ninja command will NOT include references to this file. + ImplicitOutput WritablePath + // ImplicitOutputs is a slice of output files generated by the action. Note: references to `$out` + // in the Ninja command will NOT include references to these files. + ImplicitOutputs WritablePaths + // Input is an input file to the Ninja action. When using this field, references to $in in the + // Ninja command will refer to this file. + Input Path + // Inputs is a slice of input files to the Ninja action. When using this field, references to $in + // in the Ninja command will refer to these files. + Inputs Paths + // Implicit is an input file to the Ninja action. Note: references to `$in` in the Ninja command + // will NOT include references to this file. + Implicit Path + // Implicits is a slice of input files to the Ninja action. Note: references to `$in` in the Ninja + // command will NOT include references to these files. + Implicits Paths + // OrderOnly are Ninja order-only inputs to the action. When these are out of date, the output is + // not rebuilt until they are built, but changes in order-only dependencies alone do not cause the + // output to be rebuilt. + OrderOnly Paths + // Validation is an output path for a validation action. Validation outputs imply lower + // non-blocking priority to building non-validation outputs. + Validation Path + // Validations is a slice of output path for a validation action. Validation outputs imply lower + // non-blocking priority to building non-validation outputs. + Validations Paths + // Whether to skip outputting a default target statement which will be built by Ninja when no + // targets are specified on Ninja's command line. + Default bool + // Args is a key value mapping for replacements of variables within the Rule + Args map[string]string +} + +type ModuleBuildParams BuildParams + +type ModuleContext interface { + BaseModuleContext + + blueprintModuleContext() blueprint.ModuleContext + + // Deprecated: use ModuleContext.Build instead. + ModuleBuild(pctx PackageContext, params ModuleBuildParams) + + // Returns a list of paths expanded from globs and modules referenced using ":module" syntax. The property must + // be tagged with `android:"path" to support automatic source module dependency resolution. + // + // Deprecated: use PathsForModuleSrc or PathsForModuleSrcExcludes instead. + ExpandSources(srcFiles, excludes []string) Paths + + // Returns a single path expanded from globs and modules referenced using ":module" syntax. The property must + // be tagged with `android:"path" to support automatic source module dependency resolution. + // + // Deprecated: use PathForModuleSrc instead. + ExpandSource(srcFile, prop string) Path + + ExpandOptionalSource(srcFile *string, prop string) OptionalPath + + // InstallExecutable creates a rule to copy srcPath to name in the installPath directory, + // with the given additional dependencies. The file is marked executable after copying. + // + // The installed file will be returned by FilesToInstall(), and the PackagingSpec for the + // installed file will be returned by PackagingSpecs() on this module or by + // TransitivePackagingSpecs() on modules that depend on this module through dependency tags + // for which IsInstallDepNeeded returns true. + InstallExecutable(installPath InstallPath, name string, srcPath Path, deps ...Path) InstallPath + + // InstallFile creates a rule to copy srcPath to name in the installPath directory, + // with the given additional dependencies. + // + // The installed file will be returned by FilesToInstall(), and the PackagingSpec for the + // installed file will be returned by PackagingSpecs() on this module or by + // TransitivePackagingSpecs() on modules that depend on this module through dependency tags + // for which IsInstallDepNeeded returns true. + InstallFile(installPath InstallPath, name string, srcPath Path, deps ...Path) InstallPath + + // InstallFileWithExtraFilesZip creates a rule to copy srcPath to name in the installPath + // directory, and also unzip a zip file containing extra files to install into the same + // directory. + // + // The installed file will be returned by FilesToInstall(), and the PackagingSpec for the + // installed file will be returned by PackagingSpecs() on this module or by + // TransitivePackagingSpecs() on modules that depend on this module through dependency tags + // for which IsInstallDepNeeded returns true. + InstallFileWithExtraFilesZip(installPath InstallPath, name string, srcPath Path, extraZip Path, deps ...Path) InstallPath + + // InstallSymlink creates a rule to create a symlink from src srcPath to name in the installPath + // directory. + // + // The installed symlink will be returned by FilesToInstall(), and the PackagingSpec for the + // installed file will be returned by PackagingSpecs() on this module or by + // TransitivePackagingSpecs() on modules that depend on this module through dependency tags + // for which IsInstallDepNeeded returns true. + InstallSymlink(installPath InstallPath, name string, srcPath InstallPath) InstallPath + + // InstallAbsoluteSymlink creates a rule to create an absolute symlink from src srcPath to name + // in the installPath directory. + // + // The installed symlink will be returned by FilesToInstall(), and the PackagingSpec for the + // installed file will be returned by PackagingSpecs() on this module or by + // TransitivePackagingSpecs() on modules that depend on this module through dependency tags + // for which IsInstallDepNeeded returns true. + InstallAbsoluteSymlink(installPath InstallPath, name string, absPath string) InstallPath + + // PackageFile creates a PackagingSpec as if InstallFile was called, but without creating + // the rule to copy the file. This is useful to define how a module would be packaged + // without installing it into the global installation directories. + // + // The created PackagingSpec for the will be returned by PackagingSpecs() on this module or by + // TransitivePackagingSpecs() on modules that depend on this module through dependency tags + // for which IsInstallDepNeeded returns true. + PackageFile(installPath InstallPath, name string, srcPath Path) PackagingSpec + + CheckbuildFile(srcPath Path) + + InstallInData() bool + InstallInTestcases() bool + InstallInSanitizerDir() bool + InstallInRamdisk() bool + InstallInVendorRamdisk() bool + InstallInDebugRamdisk() bool + InstallInRecovery() bool + InstallInRoot() bool + InstallInVendor() bool + InstallForceOS() (*OsType, *ArchType) + + RequiredModuleNames() []string + HostRequiredModuleNames() []string + TargetRequiredModuleNames() []string + + ModuleSubDir() string + SoongConfigTraceHash() string + + Variable(pctx PackageContext, name, value string) + Rule(pctx PackageContext, name string, params blueprint.RuleParams, argNames ...string) blueprint.Rule + // Similar to blueprint.ModuleContext.Build, but takes Paths instead of []string, + // and performs more verification. + Build(pctx PackageContext, params BuildParams) + // Phony creates a Make-style phony rule, a rule with no commands that can depend on other + // phony rules or real files. Phony can be called on the same name multiple times to add + // additional dependencies. + Phony(phony string, deps ...Path) + + // GetMissingDependencies returns the list of dependencies that were passed to AddDependencies or related methods, + // but do not exist. + GetMissingDependencies() []string + + // LicenseMetadataFile returns the path where the license metadata for this module will be + // generated. + LicenseMetadataFile() Path +} + +type moduleContext struct { + bp blueprint.ModuleContext + baseModuleContext + packagingSpecs []PackagingSpec + installFiles InstallPaths + checkbuildFiles Paths + module Module + phonies map[string]Paths + + katiInstalls []katiInstall + katiSymlinks []katiInstall + + // For tests + buildParams []BuildParams + ruleParams map[blueprint.Rule]blueprint.RuleParams + variables map[string]string +} + +func (m *moduleContext) ninjaError(params BuildParams, err error) (PackageContext, BuildParams) { + return pctx, BuildParams{ + Rule: ErrorRule, + Description: params.Description, + Output: params.Output, + Outputs: params.Outputs, + ImplicitOutput: params.ImplicitOutput, + ImplicitOutputs: params.ImplicitOutputs, + Args: map[string]string{ + "error": err.Error(), + }, + } +} + +func (m *moduleContext) ModuleBuild(pctx PackageContext, params ModuleBuildParams) { + m.Build(pctx, BuildParams(params)) +} + +func validateBuildParams(params blueprint.BuildParams) error { + // Validate that the symlink outputs are declared outputs or implicit outputs + allOutputs := map[string]bool{} + for _, output := range params.Outputs { + allOutputs[output] = true + } + for _, output := range params.ImplicitOutputs { + allOutputs[output] = true + } + for _, symlinkOutput := range params.SymlinkOutputs { + if !allOutputs[symlinkOutput] { + return fmt.Errorf( + "Symlink output %s is not a declared output or implicit output", + symlinkOutput) + } + } + return nil +} + +// Convert build parameters from their concrete Android types into their string representations, +// and combine the singular and plural fields of the same type (e.g. Output and Outputs). +func convertBuildParams(params BuildParams) blueprint.BuildParams { + bparams := blueprint.BuildParams{ + Rule: params.Rule, + Description: params.Description, + Deps: params.Deps, + Outputs: params.Outputs.Strings(), + ImplicitOutputs: params.ImplicitOutputs.Strings(), + SymlinkOutputs: params.SymlinkOutputs.Strings(), + Inputs: params.Inputs.Strings(), + Implicits: params.Implicits.Strings(), + OrderOnly: params.OrderOnly.Strings(), + Validations: params.Validations.Strings(), + Args: params.Args, + Optional: !params.Default, + } + + if params.Depfile != nil { + bparams.Depfile = params.Depfile.String() + } + if params.Output != nil { + bparams.Outputs = append(bparams.Outputs, params.Output.String()) + } + if params.SymlinkOutput != nil { + bparams.SymlinkOutputs = append(bparams.SymlinkOutputs, params.SymlinkOutput.String()) + } + if params.ImplicitOutput != nil { + bparams.ImplicitOutputs = append(bparams.ImplicitOutputs, params.ImplicitOutput.String()) + } + if params.Input != nil { + bparams.Inputs = append(bparams.Inputs, params.Input.String()) + } + if params.Implicit != nil { + bparams.Implicits = append(bparams.Implicits, params.Implicit.String()) + } + if params.Validation != nil { + bparams.Validations = append(bparams.Validations, params.Validation.String()) + } + + bparams.Outputs = proptools.NinjaEscapeList(bparams.Outputs) + bparams.ImplicitOutputs = proptools.NinjaEscapeList(bparams.ImplicitOutputs) + bparams.SymlinkOutputs = proptools.NinjaEscapeList(bparams.SymlinkOutputs) + bparams.Inputs = proptools.NinjaEscapeList(bparams.Inputs) + bparams.Implicits = proptools.NinjaEscapeList(bparams.Implicits) + bparams.OrderOnly = proptools.NinjaEscapeList(bparams.OrderOnly) + bparams.Validations = proptools.NinjaEscapeList(bparams.Validations) + bparams.Depfile = proptools.NinjaEscape(bparams.Depfile) + + return bparams +} + +func (m *moduleContext) Variable(pctx PackageContext, name, value string) { + if m.config.captureBuild { + m.variables[name] = value + } + + m.bp.Variable(pctx.PackageContext, name, value) +} + +func (m *moduleContext) Rule(pctx PackageContext, name string, params blueprint.RuleParams, + argNames ...string) blueprint.Rule { + + if m.config.UseRemoteBuild() { + if 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 + } else if params.Pool == remotePool { + // remotePool is a fake pool used to identify rule that are supported for remoting. If the rule's + // pool is the remotePool, replace with nil so that ninja runs it at NINJA_REMOTE_NUM_JOBS + // parallelism. + params.Pool = nil + } + } + + rule := m.bp.Rule(pctx.PackageContext, name, params, argNames...) + + if m.config.captureBuild { + m.ruleParams[rule] = params + } + + return rule +} + +func (m *moduleContext) Build(pctx PackageContext, params BuildParams) { + if params.Description != "" { + params.Description = "${moduleDesc}" + params.Description + "${moduleDescSuffix}" + } + + if missingDeps := m.GetMissingDependencies(); len(missingDeps) > 0 { + pctx, params = m.ninjaError(params, fmt.Errorf("module %s missing dependencies: %s\n", + m.ModuleName(), strings.Join(missingDeps, ", "))) + } + + if m.config.captureBuild { + m.buildParams = append(m.buildParams, params) + } + + bparams := convertBuildParams(params) + err := validateBuildParams(bparams) + if err != nil { + m.ModuleErrorf( + "%s: build parameter validation failed: %s", + m.ModuleName(), + err.Error()) + } + m.bp.Build(pctx.PackageContext, bparams) +} + +func (m *moduleContext) Phony(name string, deps ...Path) { + addPhony(m.config, name, deps...) +} + +func (m *moduleContext) GetMissingDependencies() []string { + var missingDeps []string + missingDeps = append(missingDeps, m.Module().base().commonProperties.MissingDeps...) + missingDeps = append(missingDeps, m.bp.GetMissingDependencies()...) + missingDeps = FirstUniqueStrings(missingDeps) + return missingDeps +} + +func (m *moduleContext) GetDirectDepWithTag(name string, tag blueprint.DependencyTag) blueprint.Module { + module, _ := m.getDirectDepInternal(name, tag) + return module +} + +func (m *moduleContext) ModuleSubDir() string { + return m.bp.ModuleSubDir() +} + +func (m *moduleContext) SoongConfigTraceHash() string { + return m.module.base().commonProperties.SoongConfigTraceHash +} + +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() +} + +func (m *moduleContext) InstallInRamdisk() bool { + return m.module.InstallInRamdisk() +} + +func (m *moduleContext) InstallInVendorRamdisk() bool { + return m.module.InstallInVendorRamdisk() +} + +func (m *moduleContext) InstallInDebugRamdisk() bool { + return m.module.InstallInDebugRamdisk() +} + +func (m *moduleContext) InstallInRecovery() bool { + return m.module.InstallInRecovery() +} + +func (m *moduleContext) InstallInRoot() bool { + return m.module.InstallInRoot() +} + +func (m *moduleContext) InstallForceOS() (*OsType, *ArchType) { + return m.module.InstallForceOS() +} + +func (m *moduleContext) InstallInVendor() bool { + return m.module.InstallInVendor() +} + +func (m *moduleContext) skipInstall() bool { + if m.module.base().commonProperties.SkipInstall { + return true + } + + if m.module.base().commonProperties.HideFromMake { + return true + } + + // We'll need a solution for choosing which of modules with the same name in different + // namespaces to install. For now, reuse the list of namespaces exported to Make as the + // list of namespaces to install in a Soong-only build. + if !m.module.base().commonProperties.NamespaceExportedToMake { + return true + } + + return false +} + +func (m *moduleContext) InstallFile(installPath InstallPath, name string, srcPath Path, + deps ...Path) InstallPath { + return m.installFile(installPath, name, srcPath, deps, false, nil) +} + +func (m *moduleContext) InstallExecutable(installPath InstallPath, name string, srcPath Path, + deps ...Path) InstallPath { + return m.installFile(installPath, name, srcPath, deps, true, nil) +} + +func (m *moduleContext) InstallFileWithExtraFilesZip(installPath InstallPath, name string, srcPath Path, + extraZip Path, deps ...Path) InstallPath { + return m.installFile(installPath, name, srcPath, deps, false, &extraFilesZip{ + zip: extraZip, + dir: installPath, + }) +} + +func (m *moduleContext) PackageFile(installPath InstallPath, name string, srcPath Path) PackagingSpec { + fullInstallPath := installPath.Join(m, name) + return m.packageFile(fullInstallPath, srcPath, false) +} + +func (m *moduleContext) packageFile(fullInstallPath InstallPath, srcPath Path, executable bool) PackagingSpec { + licenseFiles := m.Module().EffectiveLicenseFiles() + spec := PackagingSpec{ + relPathInPackage: Rel(m, fullInstallPath.PartitionDir(), fullInstallPath.String()), + srcPath: srcPath, + symlinkTarget: "", + executable: executable, + effectiveLicenseFiles: &licenseFiles, + partition: fullInstallPath.partition, + } + m.packagingSpecs = append(m.packagingSpecs, spec) + return spec +} + +func (m *moduleContext) installFile(installPath InstallPath, name string, srcPath Path, deps []Path, + executable bool, extraZip *extraFilesZip) InstallPath { + + fullInstallPath := installPath.Join(m, name) + m.module.base().hooks.runInstallHooks(m, srcPath, fullInstallPath, false) + + if !m.skipInstall() { + deps = append(deps, InstallPaths(m.module.base().installFilesDepSet.ToList()).Paths()...) + + var implicitDeps, orderOnlyDeps Paths + + if m.Host() { + // Installed host modules might be used during the build, depend directly on their + // dependencies so their timestamp is updated whenever their dependency is updated + implicitDeps = deps + } else { + orderOnlyDeps = deps + } + + if m.Config().KatiEnabled() { + // When creating the install rule in Soong but embedding in Make, write the rule to a + // makefile instead of directly to the ninja file so that main.mk can add the + // dependencies from the `required` property that are hard to resolve in Soong. + m.katiInstalls = append(m.katiInstalls, katiInstall{ + from: srcPath, + to: fullInstallPath, + implicitDeps: implicitDeps, + orderOnlyDeps: orderOnlyDeps, + executable: executable, + extraFiles: extraZip, + }) + } else { + rule := Cp + if executable { + rule = CpExecutable + } + + extraCmds := "" + if extraZip != nil { + extraCmds += fmt.Sprintf(" && ( unzip -qDD -d '%s' '%s' 2>&1 | grep -v \"zipfile is empty\"; exit $${PIPESTATUS[0]} )", + extraZip.dir.String(), extraZip.zip.String()) + extraCmds += " || ( code=$$?; if [ $$code -ne 0 -a $$code -ne 1 ]; then exit $$code; fi )" + implicitDeps = append(implicitDeps, extraZip.zip) + } + + m.Build(pctx, BuildParams{ + Rule: rule, + Description: "install " + fullInstallPath.Base(), + Output: fullInstallPath, + Input: srcPath, + Implicits: implicitDeps, + OrderOnly: orderOnlyDeps, + Default: !m.Config().KatiEnabled(), + Args: map[string]string{ + "extraCmds": extraCmds, + }, + }) + } + + m.installFiles = append(m.installFiles, fullInstallPath) + } + + m.packageFile(fullInstallPath, srcPath, executable) + + m.checkbuildFiles = append(m.checkbuildFiles, srcPath) + + return fullInstallPath +} + +func (m *moduleContext) InstallSymlink(installPath InstallPath, name string, srcPath InstallPath) InstallPath { + fullInstallPath := installPath.Join(m, name) + m.module.base().hooks.runInstallHooks(m, srcPath, fullInstallPath, true) + + relPath, err := filepath.Rel(path.Dir(fullInstallPath.String()), srcPath.String()) + if err != nil { + panic(fmt.Sprintf("Unable to generate symlink between %q and %q: %s", fullInstallPath.Base(), srcPath.Base(), err)) + } + if !m.skipInstall() { + + if m.Config().KatiEnabled() { + // When creating the symlink rule in Soong but embedding in Make, write the rule to a + // makefile instead of directly to the ninja file so that main.mk can add the + // dependencies from the `required` property that are hard to resolve in Soong. + m.katiSymlinks = append(m.katiSymlinks, katiInstall{ + from: srcPath, + to: fullInstallPath, + }) + } else { + // The symlink doesn't need updating when the target is modified, but we sometimes + // have a dependency on a symlink to a binary instead of to the binary directly, and + // the mtime of the symlink must be updated when the binary is modified, so use a + // normal dependency here instead of an order-only dependency. + m.Build(pctx, BuildParams{ + Rule: Symlink, + Description: "install symlink " + fullInstallPath.Base(), + Output: fullInstallPath, + Input: srcPath, + Default: !m.Config().KatiEnabled(), + Args: map[string]string{ + "fromPath": relPath, + }, + }) + } + + m.installFiles = append(m.installFiles, fullInstallPath) + m.checkbuildFiles = append(m.checkbuildFiles, srcPath) + } + + m.packagingSpecs = append(m.packagingSpecs, PackagingSpec{ + relPathInPackage: Rel(m, fullInstallPath.PartitionDir(), fullInstallPath.String()), + srcPath: nil, + symlinkTarget: relPath, + executable: false, + partition: fullInstallPath.partition, + }) + + return fullInstallPath +} + +// installPath/name -> absPath where absPath might be a path that is available only at runtime +// (e.g. /apex/...) +func (m *moduleContext) InstallAbsoluteSymlink(installPath InstallPath, name string, absPath string) InstallPath { + fullInstallPath := installPath.Join(m, name) + m.module.base().hooks.runInstallHooks(m, nil, fullInstallPath, true) + + if !m.skipInstall() { + if m.Config().KatiEnabled() { + // When creating the symlink rule in Soong but embedding in Make, write the rule to a + // makefile instead of directly to the ninja file so that main.mk can add the + // dependencies from the `required` property that are hard to resolve in Soong. + m.katiSymlinks = append(m.katiSymlinks, katiInstall{ + absFrom: absPath, + to: fullInstallPath, + }) + } else { + m.Build(pctx, BuildParams{ + Rule: Symlink, + Description: "install symlink " + fullInstallPath.Base() + " -> " + absPath, + Output: fullInstallPath, + Default: !m.Config().KatiEnabled(), + Args: map[string]string{ + "fromPath": absPath, + }, + }) + } + + m.installFiles = append(m.installFiles, fullInstallPath) + } + + m.packagingSpecs = append(m.packagingSpecs, PackagingSpec{ + relPathInPackage: Rel(m, fullInstallPath.PartitionDir(), fullInstallPath.String()), + srcPath: nil, + symlinkTarget: absPath, + executable: false, + partition: fullInstallPath.partition, + }) + + return fullInstallPath +} + +func (m *moduleContext) CheckbuildFile(srcPath Path) { + m.checkbuildFiles = append(m.checkbuildFiles, srcPath) +} + +func (m *moduleContext) blueprintModuleContext() blueprint.ModuleContext { + return m.bp +} + +func (m *moduleContext) LicenseMetadataFile() Path { + return m.module.base().licenseMetadataFile +} + +// Returns a list of paths expanded from globs and modules referenced using ":module" syntax. The property must +// be tagged with `android:"path" to support automatic source module dependency resolution. +// +// Deprecated: use PathsForModuleSrc or PathsForModuleSrcExcludes instead. +func (m *moduleContext) ExpandSources(srcFiles, excludes []string) Paths { + return PathsForModuleSrcExcludes(m, srcFiles, excludes) +} + +// Returns a single path expanded from globs and modules referenced using ":module" syntax. The property must +// be tagged with `android:"path" to support automatic source module dependency resolution. +// +// Deprecated: use PathForModuleSrc instead. +func (m *moduleContext) ExpandSource(srcFile, _ string) Path { + return PathForModuleSrc(m, srcFile) +} + +// Returns an optional single path expanded from globs and modules referenced using ":module" syntax if +// the srcFile is non-nil. The property must be tagged with `android:"path" to support automatic source module +// dependency resolution. +func (m *moduleContext) ExpandOptionalSource(srcFile *string, _ string) OptionalPath { + if srcFile != nil { + return OptionalPathForPath(PathForModuleSrc(m, *srcFile)) + } + return OptionalPath{} +} + +func (m *moduleContext) RequiredModuleNames() []string { + return m.module.RequiredModuleNames() +} + +func (m *moduleContext) HostRequiredModuleNames() []string { + return m.module.HostRequiredModuleNames() +} + +func (m *moduleContext) TargetRequiredModuleNames() []string { + return m.module.TargetRequiredModuleNames() +}