Files
build_soong/android/base_module_context.go
Jiyong Park 8bcf3c64f1 Add required, host_required, and target_required as dependencies
So far, the installation of required modules were handled by Make. This
prevents us from implementing the module installation and packaging
entirely in Soong.

This CL is the first step towards that goal. Soong now correctly tracks
the dependencies and they are correctly returned by
TransitivePackagingSpecs(), which is used by packaging modules like
android_system_image.

Bug: 321626681
Test: build
Change-Id: I9192b5333ceaa0b7d1c5c4abeec2af62febcd976
2024-03-20 17:05:17 +09:00

608 lines
25 KiB
Go

// 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"
"regexp"
"strings"
"github.com/google/blueprint"
"github.com/google/blueprint/parser"
)
// 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 {
ArchModuleContext
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 nil and false. The value returned may be a deep copy of the value originally
// passed to SetProvider.
//
// This method shouldn't be used directly, prefer the type-safe android.OtherModuleProvider instead.
otherModuleProvider(m blueprint.Module, provider blueprint.AnyProviderKey) (any, bool)
// Provider returns the value for a provider for the current module. If the value is
// not set it returns nil and false. 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.
//
// This method shouldn't be used directly, prefer the type-safe android.ModuleProvider instead.
provider(provider blueprint.AnyProviderKey) (any, 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.
//
// This method shouldn't be used directly, prefer the type-safe android.SetProvider instead.
setProvider(provider blueprint.AnyProviderKey, value any)
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)
// 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))
// VisitDirectDepsIgnoreBlueprint 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 silently ignores 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.
VisitDirectDepsIgnoreBlueprint(visit func(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
// EvaluateConfiguration makes ModuleContext a valid proptools.ConfigurableEvaluator, so this context
// can be used to evaluate the final value of Configurable properties.
EvaluateConfiguration(parser.SelectType, string) (string, bool)
}
type baseModuleContext struct {
bp blueprint.BaseModuleContext
earlyModuleContext
archModuleContext
walkPath []Module
tagPath []blueprint.DependencyTag
strictVisitDeps bool // If true, enforce that all dependencies are enabled
}
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.AnyProviderKey) (any, bool) {
return b.bp.OtherModuleProvider(m, provider)
}
func (b *baseModuleContext) provider(provider blueprint.AnyProviderKey) (any, bool) {
return b.bp.Provider(provider)
}
func (b *baseModuleContext) setProvider(provider blueprint.AnyProviderKey, value any) {
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
}
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
}
type AlwaysAllowDisabledModuleDependencyTag struct{}
func (t AlwaysAllowDisabledModuleDependencyTag) AllowDisabledModuleDependency(Module) bool {
return true
}
func (b *baseModuleContext) validateAndroidModule(module blueprint.Module, tag blueprint.DependencyTag, strict bool, ignoreBlueprint bool) Module {
aModule, _ := module.(Module)
if !strict {
return aModule
}
if aModule == nil {
if !ignoreBlueprint {
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) VisitDirectDepsBlueprint(visit func(blueprint.Module)) {
b.bp.VisitDirectDeps(visit)
}
func (b *baseModuleContext) VisitDirectDeps(visit func(Module)) {
b.visitDirectDeps(visit, false)
}
func (b *baseModuleContext) VisitDirectDepsIgnoreBlueprint(visit func(Module)) {
b.visitDirectDeps(visit, true)
}
func (b *baseModuleContext) visitDirectDeps(visit func(Module), ignoreBlueprint bool) {
b.bp.VisitDirectDeps(func(module blueprint.Module) {
if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps, ignoreBlueprint); 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, false); 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, false); 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, false); 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, false); 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 *baseModuleContext) EvaluateConfiguration(ty parser.SelectType, condition string) (string, bool) {
switch ty {
case parser.SelectTypeReleaseVariable:
if v, ok := m.Config().productVariables.BuildFlags[condition]; ok {
return v, true
}
return "", false
case parser.SelectTypeProductVariable:
// TODO(b/323382414): Might add these on a case-by-case basis
m.ModuleErrorf("TODO(b/323382414): Product variables are not yet supported in selects")
return "", false
case parser.SelectTypeSoongConfigVariable:
parts := strings.Split(condition, ":")
namespace := parts[0]
variable := parts[1]
if n, ok := m.Config().productVariables.VendorVars[namespace]; ok {
if v, ok := n[variable]; ok {
return v, true
}
}
return "", false
case parser.SelectTypeVariant:
m.ModuleErrorf("TODO(b/323382414): Variants are not yet supported in selects")
return "", false
default:
panic("Should be unreachable")
}
}