Move LoadHooks from Soong to Blueprint
Move LoadHooks from Blueprint and run them during ParseBlueprintsFiles. This will allow them to add scoped module types that are visible to the rest of the Blueprints file. Requires passing the config object to ParseBlueprintsFiles. Test: all blueprint tests Change-Id: Ia2a2c9a0223d5458bfd48bd22ebed0fdbd0156c6
This commit is contained in:
parent
9999eddc46
commit
da70fd0b84
6 changed files with 141 additions and 23 deletions
|
@ -140,7 +140,7 @@ func Main(ctx *blueprint.Context, config interface{}, extraNinjaFileDeps ...stri
|
||||||
|
|
||||||
ctx.RegisterSingletonType("glob", globSingletonFactory(ctx))
|
ctx.RegisterSingletonType("glob", globSingletonFactory(ctx))
|
||||||
|
|
||||||
deps, errs := ctx.ParseFileList(filepath.Dir(bootstrapConfig.topLevelBlueprintsFile), filesToParse)
|
deps, errs := ctx.ParseFileList(filepath.Dir(bootstrapConfig.topLevelBlueprintsFile), filesToParse, config)
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
fatalErrors(errs)
|
fatalErrors(errs)
|
||||||
}
|
}
|
||||||
|
|
61
context.go
61
context.go
|
@ -631,17 +631,19 @@ type fileParseContext struct {
|
||||||
// which the future output will depend is returned. This list will include both
|
// which the future output will depend is returned. This list will include both
|
||||||
// Blueprints file paths as well as directory paths for cases where wildcard
|
// Blueprints file paths as well as directory paths for cases where wildcard
|
||||||
// subdirs are found.
|
// subdirs are found.
|
||||||
func (c *Context) ParseBlueprintsFiles(rootFile string) (deps []string, errs []error) {
|
func (c *Context) ParseBlueprintsFiles(rootFile string,
|
||||||
|
config interface{}) (deps []string, errs []error) {
|
||||||
|
|
||||||
baseDir := filepath.Dir(rootFile)
|
baseDir := filepath.Dir(rootFile)
|
||||||
pathsToParse, err := c.ListModulePaths(baseDir)
|
pathsToParse, err := c.ListModulePaths(baseDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, []error{err}
|
return nil, []error{err}
|
||||||
}
|
}
|
||||||
return c.ParseFileList(baseDir, pathsToParse)
|
return c.ParseFileList(baseDir, pathsToParse, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Context) ParseFileList(rootDir string, filePaths []string) (deps []string,
|
func (c *Context) ParseFileList(rootDir string, filePaths []string,
|
||||||
errs []error) {
|
config interface{}) (deps []string, errs []error) {
|
||||||
|
|
||||||
if len(filePaths) < 1 {
|
if len(filePaths) < 1 {
|
||||||
return nil, []error{fmt.Errorf("no paths provided to parse")}
|
return nil, []error{fmt.Errorf("no paths provided to parse")}
|
||||||
|
@ -649,7 +651,12 @@ func (c *Context) ParseFileList(rootDir string, filePaths []string) (deps []stri
|
||||||
|
|
||||||
c.dependenciesReady = false
|
c.dependenciesReady = false
|
||||||
|
|
||||||
moduleCh := make(chan *moduleInfo)
|
type newModuleInfo struct {
|
||||||
|
*moduleInfo
|
||||||
|
added chan<- struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleCh := make(chan newModuleInfo)
|
||||||
errsCh := make(chan []error)
|
errsCh := make(chan []error)
|
||||||
doneCh := make(chan struct{})
|
doneCh := make(chan struct{})
|
||||||
var numErrs uint32
|
var numErrs uint32
|
||||||
|
@ -661,24 +668,45 @@ func (c *Context) ParseFileList(rootDir string, filePaths []string) (deps []stri
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addedCh := make(chan struct{})
|
||||||
|
|
||||||
|
var addModule func(module *moduleInfo) []error
|
||||||
|
addModule = func(module *moduleInfo) (errs []error) {
|
||||||
|
moduleCh <- newModuleInfo{module, addedCh}
|
||||||
|
<-addedCh
|
||||||
|
var newModules []*moduleInfo
|
||||||
|
newModules, errs = runAndRemoveLoadHooks(c, config, module)
|
||||||
|
if len(errs) > 0 {
|
||||||
|
return errs
|
||||||
|
}
|
||||||
|
for _, n := range newModules {
|
||||||
|
errs = addModule(n)
|
||||||
|
if len(errs) > 0 {
|
||||||
|
return errs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
for _, def := range file.Defs {
|
for _, def := range file.Defs {
|
||||||
var module *moduleInfo
|
|
||||||
var errs []error
|
|
||||||
switch def := def.(type) {
|
switch def := def.(type) {
|
||||||
case *parser.Module:
|
case *parser.Module:
|
||||||
module, errs = c.processModuleDef(def, file.Name)
|
module, errs := c.processModuleDef(def, file.Name)
|
||||||
|
if len(errs) == 0 && module != nil {
|
||||||
|
errs = addModule(module)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(errs) > 0 {
|
||||||
|
atomic.AddUint32(&numErrs, uint32(len(errs)))
|
||||||
|
errsCh <- errs
|
||||||
|
}
|
||||||
|
|
||||||
case *parser.Assignment:
|
case *parser.Assignment:
|
||||||
// Already handled via Scope object
|
// Already handled via Scope object
|
||||||
default:
|
default:
|
||||||
panic("unknown definition type")
|
panic("unknown definition type")
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(errs) > 0 {
|
|
||||||
atomic.AddUint32(&numErrs, uint32(len(errs)))
|
|
||||||
errsCh <- errs
|
|
||||||
} else if module != nil {
|
|
||||||
moduleCh <- module
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -698,7 +726,10 @@ loop:
|
||||||
case newErrs := <-errsCh:
|
case newErrs := <-errsCh:
|
||||||
errs = append(errs, newErrs...)
|
errs = append(errs, newErrs...)
|
||||||
case module := <-moduleCh:
|
case module := <-moduleCh:
|
||||||
newErrs := c.addModule(module)
|
newErrs := c.addModule(module.moduleInfo)
|
||||||
|
if module.added != nil {
|
||||||
|
module.added <- struct{}{}
|
||||||
|
}
|
||||||
if len(newErrs) > 0 {
|
if len(newErrs) > 0 {
|
||||||
errs = append(errs, newErrs...)
|
errs = append(errs, newErrs...)
|
||||||
}
|
}
|
||||||
|
|
|
@ -168,7 +168,7 @@ func TestWalkDeps(t *testing.T) {
|
||||||
|
|
||||||
ctx.RegisterModuleType("foo_module", newFooModule)
|
ctx.RegisterModuleType("foo_module", newFooModule)
|
||||||
ctx.RegisterModuleType("bar_module", newBarModule)
|
ctx.RegisterModuleType("bar_module", newBarModule)
|
||||||
_, errs := ctx.ParseBlueprintsFiles("Blueprints")
|
_, errs := ctx.ParseBlueprintsFiles("Blueprints", nil)
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
t.Errorf("unexpected parse errors:")
|
t.Errorf("unexpected parse errors:")
|
||||||
for _, err := range errs {
|
for _, err := range errs {
|
||||||
|
@ -260,7 +260,7 @@ func TestWalkDepsDuplicates(t *testing.T) {
|
||||||
|
|
||||||
ctx.RegisterModuleType("foo_module", newFooModule)
|
ctx.RegisterModuleType("foo_module", newFooModule)
|
||||||
ctx.RegisterModuleType("bar_module", newBarModule)
|
ctx.RegisterModuleType("bar_module", newBarModule)
|
||||||
_, errs := ctx.ParseBlueprintsFiles("Blueprints")
|
_, errs := ctx.ParseBlueprintsFiles("Blueprints", nil)
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
t.Errorf("unexpected parse errors:")
|
t.Errorf("unexpected parse errors:")
|
||||||
for _, err := range errs {
|
for _, err := range errs {
|
||||||
|
@ -316,7 +316,7 @@ func TestCreateModule(t *testing.T) {
|
||||||
|
|
||||||
ctx.RegisterModuleType("foo_module", newFooModule)
|
ctx.RegisterModuleType("foo_module", newFooModule)
|
||||||
ctx.RegisterModuleType("bar_module", newBarModule)
|
ctx.RegisterModuleType("bar_module", newBarModule)
|
||||||
_, errs := ctx.ParseBlueprintsFiles("Blueprints")
|
_, errs := ctx.ParseBlueprintsFiles("Blueprints", nil)
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
t.Errorf("unexpected parse errors:")
|
t.Errorf("unexpected parse errors:")
|
||||||
for _, err := range errs {
|
for _, err := range errs {
|
||||||
|
@ -499,7 +499,7 @@ func TestParseFailsForModuleWithoutName(t *testing.T) {
|
||||||
ctx.RegisterModuleType("foo_module", newFooModule)
|
ctx.RegisterModuleType("foo_module", newFooModule)
|
||||||
ctx.RegisterModuleType("bar_module", newBarModule)
|
ctx.RegisterModuleType("bar_module", newBarModule)
|
||||||
|
|
||||||
_, errs := ctx.ParseBlueprintsFiles("Blueprints")
|
_, errs := ctx.ParseBlueprintsFiles("Blueprints", nil)
|
||||||
|
|
||||||
expectedErrs := []error{
|
expectedErrs := []error{
|
||||||
errors.New(`Blueprints:6:4: property 'name' is missing from a module`),
|
errors.New(`Blueprints:6:4: property 'name' is missing from a module`),
|
||||||
|
|
|
@ -17,6 +17,7 @@ package blueprint
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"sync"
|
||||||
"text/scanner"
|
"text/scanner"
|
||||||
|
|
||||||
"github.com/google/blueprint/pathtools"
|
"github.com/google/blueprint/pathtools"
|
||||||
|
@ -120,7 +121,7 @@ type DynamicDependerModule interface {
|
||||||
DynamicDependencies(DynamicDependerModuleContext) []string
|
DynamicDependencies(DynamicDependerModuleContext) []string
|
||||||
}
|
}
|
||||||
|
|
||||||
type BaseModuleContext interface {
|
type EarlyModuleContext interface {
|
||||||
// Module returns the current module as a Module. It should rarely be necessary, as the module already has a
|
// Module returns the current module as a Module. It should rarely be necessary, as the module already has a
|
||||||
// reference to itself.
|
// reference to itself.
|
||||||
Module() Module
|
Module() Module
|
||||||
|
@ -178,6 +179,10 @@ type BaseModuleContext interface {
|
||||||
// Namespace returns the Namespace object provided by the NameInterface set by Context.SetNameInterface, or the
|
// Namespace returns the Namespace object provided by the NameInterface set by Context.SetNameInterface, or the
|
||||||
// default SimpleNameInterface if Context.SetNameInterface was not called.
|
// default SimpleNameInterface if Context.SetNameInterface was not called.
|
||||||
Namespace() Namespace
|
Namespace() Namespace
|
||||||
|
}
|
||||||
|
|
||||||
|
type BaseModuleContext interface {
|
||||||
|
EarlyModuleContext
|
||||||
|
|
||||||
// GetDirectDepWithTag returns the Module the direct dependency with the specified name, or nil if
|
// 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.
|
// none exists. It panics if the dependency does not have the specified tag.
|
||||||
|
@ -995,3 +1000,85 @@ type SimpleName struct {
|
||||||
func (s *SimpleName) Name() string {
|
func (s *SimpleName) Name() string {
|
||||||
return s.Properties.Name
|
return s.Properties.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Load Hooks
|
||||||
|
|
||||||
|
type LoadHookContext interface {
|
||||||
|
EarlyModuleContext
|
||||||
|
|
||||||
|
// CreateModule creates a new module by calling the factory method for the specified moduleType, and applies
|
||||||
|
// the specified property structs to it as if the properties were set in a blueprint file.
|
||||||
|
CreateModule(ModuleFactory, ...interface{}) Module
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *loadHookContext) CreateModule(factory ModuleFactory, props ...interface{}) Module {
|
||||||
|
module := l.context.newModule(factory)
|
||||||
|
|
||||||
|
module.relBlueprintsFile = l.module.relBlueprintsFile
|
||||||
|
module.pos = l.module.pos
|
||||||
|
module.propertyPos = l.module.propertyPos
|
||||||
|
module.createdBy = l.module
|
||||||
|
|
||||||
|
for _, p := range props {
|
||||||
|
err := proptools.AppendMatchingProperties(module.properties, p, nil)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
l.newModules = append(l.newModules, module)
|
||||||
|
|
||||||
|
return module.logicModule
|
||||||
|
}
|
||||||
|
|
||||||
|
type loadHookContext struct {
|
||||||
|
baseModuleContext
|
||||||
|
newModules []*moduleInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
type LoadHook func(ctx LoadHookContext)
|
||||||
|
|
||||||
|
// Load hooks need to be added by module factories, which don't have any parameter to get to the
|
||||||
|
// Context, and only produce a Module interface with no base implementation, so the load hooks
|
||||||
|
// must be stored in a global map. The key is a pointer allocated by the module factory, so there
|
||||||
|
// is no chance of collisions even if tests are running in parallel with multiple contexts. The
|
||||||
|
// contents should be short-lived, they are added during a module factory and removed immediately
|
||||||
|
// after the module factory returns.
|
||||||
|
var pendingHooks sync.Map
|
||||||
|
|
||||||
|
func AddLoadHook(module Module, hook LoadHook) {
|
||||||
|
// Only one goroutine can be processing a given module, so no additional locking is required
|
||||||
|
// for the slice stored in the sync.Map.
|
||||||
|
v, exists := pendingHooks.Load(module)
|
||||||
|
if !exists {
|
||||||
|
v, _ = pendingHooks.LoadOrStore(module, new([]LoadHook))
|
||||||
|
}
|
||||||
|
hooks := v.(*[]LoadHook)
|
||||||
|
*hooks = append(*hooks, hook)
|
||||||
|
}
|
||||||
|
|
||||||
|
func runAndRemoveLoadHooks(ctx *Context, config interface{},
|
||||||
|
module *moduleInfo) (newModules []*moduleInfo, errs []error) {
|
||||||
|
|
||||||
|
if v, exists := pendingHooks.Load(module.logicModule); exists {
|
||||||
|
hooks := v.(*[]LoadHook)
|
||||||
|
mctx := &loadHookContext{
|
||||||
|
baseModuleContext: baseModuleContext{
|
||||||
|
context: ctx,
|
||||||
|
config: config,
|
||||||
|
module: module,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, hook := range *hooks {
|
||||||
|
hook(mctx)
|
||||||
|
newModules = append(newModules, mctx.newModules...)
|
||||||
|
errs = append(errs, mctx.errs...)
|
||||||
|
}
|
||||||
|
pendingHooks.Delete(module.logicModule)
|
||||||
|
|
||||||
|
return newModules, errs
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
|
@ -76,7 +76,7 @@ func TestAliases(t *testing.T) {
|
||||||
|
|
||||||
ctx.MockFileSystem(mockFS)
|
ctx.MockFileSystem(mockFS)
|
||||||
|
|
||||||
_, errs := ctx.ParseFileList(".", []string{"Blueprints"})
|
_, errs := ctx.ParseFileList(".", []string{"Blueprints"}, nil)
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
t.Errorf("unexpected parse errors:")
|
t.Errorf("unexpected parse errors:")
|
||||||
for _, err := range errs {
|
for _, err := range errs {
|
||||||
|
|
|
@ -125,7 +125,7 @@ func setupVisitTest(t *testing.T) *Context {
|
||||||
`),
|
`),
|
||||||
})
|
})
|
||||||
|
|
||||||
_, errs := ctx.ParseBlueprintsFiles("Blueprints")
|
_, errs := ctx.ParseBlueprintsFiles("Blueprints", nil)
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
t.Errorf("unexpected parse errors:")
|
t.Errorf("unexpected parse errors:")
|
||||||
for _, err := range errs {
|
for _, err := range errs {
|
||||||
|
|
Loading…
Reference in a new issue