Merge "Conditional inclusion of blueprint modules" am: c3887c2bbb
Original change: https://android-review.googlesource.com/c/platform/build/blueprint/+/2296765 Change-Id: I013db529bab6f374bc1ba5831947464336a571a0 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
commit
9bc3a984d7
3 changed files with 180 additions and 0 deletions
|
@ -97,6 +97,7 @@ func RunBlueprint(args Args, stopBefore StopBefore, ctx *blueprint.Context, conf
|
||||||
ctx.RegisterModuleType("bootstrap_go_package", newGoPackageModuleFactory())
|
ctx.RegisterModuleType("bootstrap_go_package", newGoPackageModuleFactory())
|
||||||
ctx.RegisterModuleType("blueprint_go_binary", newGoBinaryModuleFactory())
|
ctx.RegisterModuleType("blueprint_go_binary", newGoBinaryModuleFactory())
|
||||||
ctx.RegisterSingletonType("bootstrap", newSingletonFactory())
|
ctx.RegisterSingletonType("bootstrap", newSingletonFactory())
|
||||||
|
blueprint.RegisterPackageIncludesModuleType(ctx)
|
||||||
|
|
||||||
ctx.BeginEvent("parse_bp")
|
ctx.BeginEvent("parse_bp")
|
||||||
if blueprintFiles, errs := ctx.ParseFileList(".", filesToParse, config); len(errs) > 0 {
|
if blueprintFiles, errs := ctx.ParseFileList(".", filesToParse, config); len(errs) > 0 {
|
||||||
|
|
108
context.go
108
context.go
|
@ -137,6 +137,31 @@ type Context struct {
|
||||||
|
|
||||||
// Can be set by tests to avoid invalidating Module values after mutators.
|
// Can be set by tests to avoid invalidating Module values after mutators.
|
||||||
skipCloneModulesAfterMutators bool
|
skipCloneModulesAfterMutators bool
|
||||||
|
|
||||||
|
// String values that can be used to gate build graph traversal
|
||||||
|
includeTags *IncludeTags
|
||||||
|
}
|
||||||
|
|
||||||
|
// A container for String keys. The keys can be used to gate build graph traversal
|
||||||
|
type IncludeTags map[string]bool
|
||||||
|
|
||||||
|
func (tags *IncludeTags) Add(names ...string) {
|
||||||
|
for _, name := range names {
|
||||||
|
(*tags)[name] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tags *IncludeTags) Contains(tag string) bool {
|
||||||
|
_, exists := (*tags)[tag]
|
||||||
|
return exists
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) AddIncludeTags(names ...string) {
|
||||||
|
c.includeTags.Add(names...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) ContainsIncludeTag(name string) bool {
|
||||||
|
return c.includeTags.Contains(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// An Error describes a problem that was encountered that is related to a
|
// An Error describes a problem that was encountered that is related to a
|
||||||
|
@ -399,6 +424,7 @@ func newContext() *Context {
|
||||||
globs: make(map[globKey]pathtools.GlobResult),
|
globs: make(map[globKey]pathtools.GlobResult),
|
||||||
fs: pathtools.OsFs,
|
fs: pathtools.OsFs,
|
||||||
finishedMutators: make(map[*mutatorInfo]bool),
|
finishedMutators: make(map[*mutatorInfo]bool),
|
||||||
|
includeTags: &IncludeTags{},
|
||||||
outDir: nil,
|
outDir: nil,
|
||||||
requiredNinjaMajor: 1,
|
requiredNinjaMajor: 1,
|
||||||
requiredNinjaMinor: 7,
|
requiredNinjaMinor: 7,
|
||||||
|
@ -940,6 +966,33 @@ func (c *Context) ParseBlueprintsFiles(rootFile string,
|
||||||
return c.ParseFileList(baseDir, pathsToParse, config)
|
return c.ParseFileList(baseDir, pathsToParse, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns a boolean for whether this file should be analyzed
|
||||||
|
// Evaluates to true if the file either
|
||||||
|
// 1. does not contain a blueprint_package_includes
|
||||||
|
// 2. contains a blueprint_package_includes and all requested tags are set
|
||||||
|
// This should be processed before adding any modules to the build graph
|
||||||
|
func shouldVisitFile(c *Context, file *parser.File) (bool, []error) {
|
||||||
|
for _, def := range file.Defs {
|
||||||
|
switch def := def.(type) {
|
||||||
|
case *parser.Module:
|
||||||
|
if def.Type != "blueprint_package_includes" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
module, errs := processModuleDef(def, file.Name, c.moduleFactories, nil, c.ignoreUnknownModuleTypes)
|
||||||
|
if len(errs) > 0 {
|
||||||
|
// This file contains errors in blueprint_package_includes
|
||||||
|
// Visit anyways so that we can report errors on other modules in the file
|
||||||
|
return true, errs
|
||||||
|
}
|
||||||
|
logicModule, _ := c.cloneLogicModule(module)
|
||||||
|
pi := logicModule.(*PackageIncludes)
|
||||||
|
return pi.MatchesIncludeTags(c), []error{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true, []error{}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
func (c *Context) ParseFileList(rootDir string, filePaths []string,
|
func (c *Context) ParseFileList(rootDir string, filePaths []string,
|
||||||
config interface{}) (deps []string, errs []error) {
|
config interface{}) (deps []string, errs []error) {
|
||||||
|
|
||||||
|
@ -992,6 +1045,15 @@ func (c *Context) ParseFileList(rootDir string, filePaths []string,
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
shouldVisit, errs := shouldVisitFile(c, file)
|
||||||
|
if len(errs) > 0 {
|
||||||
|
atomic.AddUint32(&numErrs, uint32(len(errs)))
|
||||||
|
errsCh <- errs
|
||||||
|
}
|
||||||
|
if !shouldVisit {
|
||||||
|
// TODO: Write a file that lists the skipped bp files
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
for _, def := range file.Defs {
|
for _, def := range file.Defs {
|
||||||
switch def := def.(type) {
|
switch def := def.(type) {
|
||||||
|
@ -4540,3 +4602,49 @@ var singletonHeaderTemplate = `# # # # # # # # # # # # # # # # # # # # # # # # #
|
||||||
Singleton: {{.name}}
|
Singleton: {{.name}}
|
||||||
Factory: {{.goFactory}}
|
Factory: {{.goFactory}}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
// Blueprint module type that can be used to gate blueprint files beneath this directory
|
||||||
|
type PackageIncludes struct {
|
||||||
|
properties struct {
|
||||||
|
// Package will be included if all include tags in this list are set
|
||||||
|
Match_all []string
|
||||||
|
}
|
||||||
|
name *string `blueprint:"mutated"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pi *PackageIncludes) Name() string {
|
||||||
|
return proptools.String(pi.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This module type does not have any build actions
|
||||||
|
func (pi *PackageIncludes) GenerateBuildActions(ctx ModuleContext) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func newPackageIncludesFactory() (Module, []interface{}) {
|
||||||
|
module := &PackageIncludes{}
|
||||||
|
AddLoadHook(module, func(ctx LoadHookContext) {
|
||||||
|
module.name = proptools.StringPtr(ctx.ModuleDir() + "_includes") // Generate a synthetic name
|
||||||
|
})
|
||||||
|
return module, []interface{}{&module.properties}
|
||||||
|
}
|
||||||
|
|
||||||
|
func RegisterPackageIncludesModuleType(ctx *Context) {
|
||||||
|
ctx.RegisterModuleType("blueprint_package_includes", newPackageIncludesFactory)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pi *PackageIncludes) MatchAll() []string {
|
||||||
|
return pi.properties.Match_all
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if all requested include tags are set in the Context object
|
||||||
|
func (pi *PackageIncludes) MatchesIncludeTags(ctx *Context) bool {
|
||||||
|
if len(pi.MatchAll()) == 0 {
|
||||||
|
ctx.ModuleErrorf(pi, "Match_all must be a non-empty list")
|
||||||
|
}
|
||||||
|
for _, includeTag := range pi.MatchAll() {
|
||||||
|
if !ctx.ContainsIncludeTag(includeTag) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
@ -1084,3 +1085,73 @@ func Test_parallelVisit(t *testing.T) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPackageIncludes(t *testing.T) {
|
||||||
|
dir1_foo_bp := `
|
||||||
|
blueprint_package_includes {
|
||||||
|
match_all: ["use_dir1"],
|
||||||
|
}
|
||||||
|
foo_module {
|
||||||
|
name: "foo",
|
||||||
|
}
|
||||||
|
`
|
||||||
|
dir2_foo_bp := `
|
||||||
|
blueprint_package_includes {
|
||||||
|
match_all: ["use_dir2"],
|
||||||
|
}
|
||||||
|
foo_module {
|
||||||
|
name: "foo",
|
||||||
|
}
|
||||||
|
`
|
||||||
|
mockFs := map[string][]byte{
|
||||||
|
"dir1/Android.bp": []byte(dir1_foo_bp),
|
||||||
|
"dir2/Android.bp": []byte(dir2_foo_bp),
|
||||||
|
}
|
||||||
|
testCases := []struct{
|
||||||
|
desc string
|
||||||
|
includeTags []string
|
||||||
|
expectedDir string
|
||||||
|
expectedErr string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "use_dir1 is set, use dir1 foo",
|
||||||
|
includeTags: []string{"use_dir1"},
|
||||||
|
expectedDir: "dir1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "use_dir2 is set, use dir2 foo",
|
||||||
|
includeTags: []string{"use_dir2"},
|
||||||
|
expectedDir: "dir2",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "duplicate module error if both use_dir1 and use_dir2 are set",
|
||||||
|
includeTags: []string{"use_dir1", "use_dir2"},
|
||||||
|
expectedDir: "",
|
||||||
|
expectedErr: `module "foo" already defined`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tc := range testCases {
|
||||||
|
ctx := NewContext()
|
||||||
|
// Register mock FS
|
||||||
|
ctx.MockFileSystem(mockFs)
|
||||||
|
// Register module types
|
||||||
|
ctx.RegisterModuleType("foo_module", newFooModule)
|
||||||
|
RegisterPackageIncludesModuleType(ctx)
|
||||||
|
// Add include tags for test case
|
||||||
|
ctx.AddIncludeTags(tc.includeTags...)
|
||||||
|
// Run test
|
||||||
|
_, actualErrs := ctx.ParseFileList(".", []string{"dir1/Android.bp", "dir2/Android.bp"}, nil)
|
||||||
|
// Evaluate
|
||||||
|
if !strings.Contains(fmt.Sprintf("%s", actualErrs), fmt.Sprintf("%s", tc.expectedErr)) {
|
||||||
|
t.Errorf("Expected errors: %s, got errors: %s\n", tc.expectedErr, actualErrs)
|
||||||
|
}
|
||||||
|
if tc.expectedErr != "" {
|
||||||
|
continue // expectedDir check not necessary
|
||||||
|
}
|
||||||
|
actualBpFile := ctx.moduleGroupFromName("foo", nil).modules.firstModule().relBlueprintsFile
|
||||||
|
if tc.expectedDir != filepath.Dir(actualBpFile) {
|
||||||
|
t.Errorf("Expected foo from %s, got %s\n", tc.expectedDir, filepath.Dir(actualBpFile))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue