7c2ebdef75
This avoids a concurrent map read and write error in parallel singletons. Bug: 290795374 Test: manual, treehugger Change-Id: I7f89909a98c4f530da92a3d2cc01ca8eaeddbfa0
401 lines
14 KiB
Go
401 lines
14 KiB
Go
// Copyright 2014 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 blueprint
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/google/blueprint/pathtools"
|
|
)
|
|
|
|
type Singleton interface {
|
|
GenerateBuildActions(SingletonContext)
|
|
}
|
|
|
|
type SingletonContext interface {
|
|
// Config returns the config object that was passed to Context.PrepareBuildActions.
|
|
Config() interface{}
|
|
|
|
// Name returns the name of the current singleton passed to Context.RegisterSingletonType
|
|
Name() string
|
|
|
|
// ModuleName returns the name of the given Module. See BaseModuleContext.ModuleName for more information.
|
|
ModuleName(module Module) string
|
|
|
|
// ModuleDir returns the directory of the given Module. See BaseModuleContext.ModuleDir for more information.
|
|
ModuleDir(module Module) string
|
|
|
|
// ModuleSubDir returns the unique subdirectory name of the given Module. See ModuleContext.ModuleSubDir for
|
|
// more information.
|
|
ModuleSubDir(module Module) string
|
|
|
|
// ModuleType returns the type of the given Module. See BaseModuleContext.ModuleType for more information.
|
|
ModuleType(module Module) string
|
|
|
|
// BlueprintFile returns the path of the Blueprint file that defined the given module.
|
|
BlueprintFile(module Module) string
|
|
|
|
// ModuleProvider returns the value, if any, for the provider for a module. If the value for the
|
|
// provider was not set it returns the zero value of the type of the provider, which means the
|
|
// return value can always be type-asserted to the type of the provider. The return value should
|
|
// always be considered read-only. It panics if called before the appropriate mutator or
|
|
// GenerateBuildActions pass for the provider on the module.
|
|
ModuleProvider(module Module, provider ProviderKey) interface{}
|
|
|
|
// ModuleHasProvider returns true if the provider for the given module has been set.
|
|
ModuleHasProvider(m Module, provider ProviderKey) bool
|
|
|
|
// ModuleErrorf reports an error at the line number of the module type in the module definition.
|
|
ModuleErrorf(module Module, format string, args ...interface{})
|
|
|
|
// Errorf reports an error at the specified position of the module definition file.
|
|
Errorf(format string, args ...interface{})
|
|
|
|
// Failed returns true if any errors have been reported. In most cases the singleton 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 singleton from creating necessary data it can return early when Failed returns true.
|
|
Failed() bool
|
|
|
|
// Variable creates a new ninja variable scoped to the singleton. It can be referenced by calls to Rule and Build
|
|
// in the same singleton.
|
|
Variable(pctx PackageContext, name, value string)
|
|
|
|
// Rule creates a new ninja rule scoped to the singleton. It can be referenced by calls to Build in the same
|
|
// singleton.
|
|
Rule(pctx PackageContext, name string, params RuleParams, argNames ...string) Rule
|
|
|
|
// Build creates a new ninja build statement.
|
|
Build(pctx PackageContext, params BuildParams)
|
|
|
|
// RequireNinjaVersion sets the generated ninja manifest to require at least the specified version of ninja.
|
|
RequireNinjaVersion(major, minor, micro int)
|
|
|
|
// SetOutDir sets the value of the top-level "builddir" Ninja variable
|
|
// that controls where Ninja stores its build log files. This value can be
|
|
// set at most one time for a single build, later calls are ignored.
|
|
SetOutDir(pctx PackageContext, value string)
|
|
|
|
// AddSubninja adds a ninja file to include with subninja. This should likely
|
|
// only ever be used inside bootstrap to handle glob rules.
|
|
AddSubninja(file string)
|
|
|
|
// Eval takes a string with embedded ninja variables, and returns a string
|
|
// with all of the variables recursively expanded. Any variables references
|
|
// are expanded in the scope of the PackageContext.
|
|
Eval(pctx PackageContext, ninjaStr string) (string, error)
|
|
|
|
// VisitAllModules calls visit for each defined variant of each module in an unspecified order.
|
|
VisitAllModules(visit func(Module))
|
|
|
|
// VisitAllModules calls pred for each defined variant of each module in an unspecified order, and if pred returns
|
|
// true calls visit.
|
|
VisitAllModulesIf(pred func(Module) bool, visit func(Module))
|
|
|
|
// VisitDirectDeps calls visit for each direct dependency of the Module. 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.
|
|
VisitDirectDeps(module Module, visit func(Module))
|
|
|
|
// VisitDirectDepsIf calls pred for each direct dependency of the Module, 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.
|
|
//
|
|
// The Module passed to the visit function should not be retained outside of the visit
|
|
// function, it may be invalidated by future mutators.
|
|
VisitDirectDepsIf(module Module, pred func(Module) bool, visit func(Module))
|
|
|
|
// VisitDepsDepthFirst calls visit for each transitive dependency, traversing the dependency tree in depth first
|
|
// order. visit will only be called once for any given module, even if there are multiple paths through the
|
|
// dependency tree to the module or multiple direct dependencies with different tags.
|
|
VisitDepsDepthFirst(module Module, visit func(Module))
|
|
|
|
// VisitDepsDepthFirst calls pred for each transitive dependency, and if pred returns true calls visit, traversing
|
|
// the dependency tree in depth first order. visit will only be called once for any given module, even if there are
|
|
// multiple paths through the dependency tree to the module or multiple direct dependencies with different tags.
|
|
VisitDepsDepthFirstIf(module Module, pred func(Module) bool,
|
|
visit func(Module))
|
|
|
|
// VisitAllModuleVariants calls visit for each variant of the given module.
|
|
VisitAllModuleVariants(module Module, visit func(Module))
|
|
|
|
// PrimaryModule returns the first variant of the given module. This can be used to perform
|
|
// // singleton actions that are only done once for all variants of a module.
|
|
PrimaryModule(module Module) Module
|
|
|
|
// FinalModule returns the last variant of the given module. This can be used to perform
|
|
// singleton actions that are only done once for all variants of a module.
|
|
FinalModule(module Module) Module
|
|
|
|
// 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)
|
|
|
|
// GlobWithDeps returns a list of files and directories that match the
|
|
// specified pattern but do not match any of the patterns in excludes.
|
|
// Any directories will have a '/' suffix. 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)
|
|
|
|
// Fs returns a pathtools.Filesystem that can be used to interact with files. Using the Filesystem interface allows
|
|
// the singleton to be used in build system tests that run against a mock filesystem.
|
|
Fs() pathtools.FileSystem
|
|
|
|
// ModuleVariantsFromName returns the list of module variants named `name` in the same namespace as `referer`.
|
|
// Allows generating build actions for `referer` based on the metadata for `name` deferred until the singleton context.
|
|
ModuleVariantsFromName(referer Module, name string) []Module
|
|
}
|
|
|
|
var _ SingletonContext = (*singletonContext)(nil)
|
|
|
|
type singletonContext struct {
|
|
name string
|
|
context *Context
|
|
config interface{}
|
|
scope *localScope
|
|
globals *liveTracker
|
|
|
|
ninjaFileDeps []string
|
|
errs []error
|
|
|
|
actionDefs localBuildActions
|
|
}
|
|
|
|
func (s *singletonContext) Config() interface{} {
|
|
return s.config
|
|
}
|
|
|
|
func (s *singletonContext) Name() string {
|
|
return s.name
|
|
}
|
|
|
|
func (s *singletonContext) ModuleName(logicModule Module) string {
|
|
return s.context.ModuleName(logicModule)
|
|
}
|
|
|
|
func (s *singletonContext) ModuleDir(logicModule Module) string {
|
|
return s.context.ModuleDir(logicModule)
|
|
}
|
|
|
|
func (s *singletonContext) ModuleSubDir(logicModule Module) string {
|
|
return s.context.ModuleSubDir(logicModule)
|
|
}
|
|
|
|
func (s *singletonContext) ModuleType(logicModule Module) string {
|
|
return s.context.ModuleType(logicModule)
|
|
}
|
|
|
|
func (s *singletonContext) ModuleProvider(logicModule Module, provider ProviderKey) interface{} {
|
|
return s.context.ModuleProvider(logicModule, provider)
|
|
}
|
|
|
|
// ModuleHasProvider returns true if the provider for the given module has been set.
|
|
func (s *singletonContext) ModuleHasProvider(logicModule Module, provider ProviderKey) bool {
|
|
return s.context.ModuleHasProvider(logicModule, provider)
|
|
}
|
|
|
|
func (s *singletonContext) BlueprintFile(logicModule Module) string {
|
|
return s.context.BlueprintFile(logicModule)
|
|
}
|
|
|
|
func (s *singletonContext) error(err error) {
|
|
if err != nil {
|
|
s.errs = append(s.errs, err)
|
|
}
|
|
}
|
|
|
|
func (s *singletonContext) ModuleErrorf(logicModule Module, format string,
|
|
args ...interface{}) {
|
|
|
|
s.error(s.context.ModuleErrorf(logicModule, format, args...))
|
|
}
|
|
|
|
func (s *singletonContext) Errorf(format string, args ...interface{}) {
|
|
// TODO: Make this not result in the error being printed as "internal error"
|
|
s.error(fmt.Errorf(format, args...))
|
|
}
|
|
|
|
func (s *singletonContext) Failed() bool {
|
|
return len(s.errs) > 0
|
|
}
|
|
|
|
func (s *singletonContext) Variable(pctx PackageContext, name, value string) {
|
|
s.scope.ReparentTo(pctx)
|
|
|
|
v, err := s.scope.AddLocalVariable(name, value)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
s.actionDefs.variables = append(s.actionDefs.variables, v)
|
|
}
|
|
|
|
func (s *singletonContext) Rule(pctx PackageContext, name string,
|
|
params RuleParams, argNames ...string) Rule {
|
|
|
|
s.scope.ReparentTo(pctx)
|
|
|
|
r, err := s.scope.AddLocalRule(name, ¶ms, argNames...)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
s.actionDefs.rules = append(s.actionDefs.rules, r)
|
|
|
|
return r
|
|
}
|
|
|
|
func (s *singletonContext) Build(pctx PackageContext, params BuildParams) {
|
|
s.scope.ReparentTo(pctx)
|
|
|
|
def, err := parseBuildParams(s.scope, ¶ms, map[string]string{
|
|
"module_name": s.name,
|
|
"module_type": "singleton",
|
|
})
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
s.actionDefs.buildDefs = append(s.actionDefs.buildDefs, def)
|
|
}
|
|
|
|
func (s *singletonContext) Eval(pctx PackageContext, str string) (string, error) {
|
|
s.scope.ReparentTo(pctx)
|
|
|
|
ninjaStr, err := parseNinjaString(s.scope, str)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
err = s.globals.addNinjaStringDeps(ninjaStr)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return s.globals.Eval(ninjaStr)
|
|
}
|
|
|
|
func (s *singletonContext) RequireNinjaVersion(major, minor, micro int) {
|
|
s.context.requireNinjaVersion(major, minor, micro)
|
|
}
|
|
|
|
func (s *singletonContext) SetOutDir(pctx PackageContext, value string) {
|
|
s.scope.ReparentTo(pctx)
|
|
|
|
ninjaValue, err := parseNinjaString(s.scope, value)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
s.context.setOutDir(ninjaValue)
|
|
}
|
|
|
|
func (s *singletonContext) AddSubninja(file string) {
|
|
s.context.subninjas = append(s.context.subninjas, file)
|
|
}
|
|
|
|
func (s *singletonContext) VisitAllModules(visit func(Module)) {
|
|
var visitingModule Module
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
panic(newPanicErrorf(r, "VisitAllModules(%s) for module %s",
|
|
funcName(visit), s.context.moduleInfo[visitingModule]))
|
|
}
|
|
}()
|
|
|
|
s.context.VisitAllModules(func(m Module) {
|
|
visitingModule = m
|
|
visit(m)
|
|
})
|
|
}
|
|
|
|
func (s *singletonContext) VisitAllModulesIf(pred func(Module) bool,
|
|
visit func(Module)) {
|
|
|
|
s.context.VisitAllModulesIf(pred, visit)
|
|
}
|
|
|
|
func (s *singletonContext) VisitDirectDeps(module Module, visit func(Module)) {
|
|
s.context.VisitDirectDeps(module, visit)
|
|
}
|
|
|
|
func (s *singletonContext) VisitDirectDepsIf(module Module, pred func(Module) bool, visit func(Module)) {
|
|
s.context.VisitDirectDepsIf(module, pred, visit)
|
|
}
|
|
|
|
func (s *singletonContext) VisitDepsDepthFirst(module Module,
|
|
visit func(Module)) {
|
|
|
|
s.context.VisitDepsDepthFirst(module, visit)
|
|
}
|
|
|
|
func (s *singletonContext) VisitDepsDepthFirstIf(module Module,
|
|
pred func(Module) bool, visit func(Module)) {
|
|
|
|
s.context.VisitDepsDepthFirstIf(module, pred, visit)
|
|
}
|
|
|
|
func (s *singletonContext) PrimaryModule(module Module) Module {
|
|
return s.context.PrimaryModule(module)
|
|
}
|
|
|
|
func (s *singletonContext) FinalModule(module Module) Module {
|
|
return s.context.FinalModule(module)
|
|
}
|
|
|
|
func (s *singletonContext) VisitAllModuleVariants(module Module, visit func(Module)) {
|
|
s.context.VisitAllModuleVariants(module, visit)
|
|
}
|
|
|
|
func (s *singletonContext) AddNinjaFileDeps(deps ...string) {
|
|
s.ninjaFileDeps = append(s.ninjaFileDeps, deps...)
|
|
}
|
|
|
|
func (s *singletonContext) GlobWithDeps(pattern string,
|
|
excludes []string) ([]string, error) {
|
|
return s.context.glob(pattern, excludes)
|
|
}
|
|
|
|
func (s *singletonContext) Fs() pathtools.FileSystem {
|
|
return s.context.fs
|
|
}
|
|
|
|
func (s *singletonContext) ModuleVariantsFromName(referer Module, name string) []Module {
|
|
c := s.context
|
|
|
|
refererInfo := c.moduleInfo[referer]
|
|
if refererInfo == nil {
|
|
s.ModuleErrorf(referer, "could not find module %q", referer.Name())
|
|
return nil
|
|
}
|
|
|
|
moduleGroup, exists := c.nameInterface.ModuleFromName(name, refererInfo.namespace())
|
|
if !exists {
|
|
return nil
|
|
}
|
|
result := make([]Module, 0, len(moduleGroup.modules))
|
|
for _, module := range moduleGroup.modules {
|
|
moduleInfo := module.module()
|
|
if moduleInfo != nil {
|
|
result = append(result, moduleInfo.logicModule)
|
|
}
|
|
}
|
|
return result
|
|
}
|