aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSpandan Das <spandandas@google.com>2022-11-08 19:44:01 +0000
committerSpandan Das <spandandas@google.com>2022-12-09 19:30:43 +0000
commitfd1f976100fb78d50902bd6feb550e92f70fa962 (patch)
tree62791ccd0971c96ebf4a8ce0b295181777cdbef7
parentc25454a0d5ee92d6d882ae9774063d4c7123cb5e (diff)
downloadblueprint-android13-dev.tar.gz
Conditional inclusion of blueprint modulesandroid13-dev
This introduces a new `blueprint_package_includes` module type. If present, other blueprint modules in that file will be analyzed if and only if the requested include tags are met example syntax: ``` Android.bp blueprint_packgage_includes { match_all: ["tag1", "tag2", ...], } other_module1 {...} other_module2 {...} ``` other_module1 and other_module2 will not be analyzed unless tag1,tag2, ... are set This also adds a new object of type `IncludeTags` to the Context object, which is a container for these string keys. Test: In build/blueprint, go test ./ Test: TH Change-Id: I79de0d7da3224a5b2025c27a5137f39d00c7382e
-rw-r--r--bootstrap/command.go1
-rw-r--r--context.go108
-rw-r--r--context_test.go71
3 files changed, 180 insertions, 0 deletions
diff --git a/bootstrap/command.go b/bootstrap/command.go
index 8c045b4..64bcce0 100644
--- a/bootstrap/command.go
+++ b/bootstrap/command.go
@@ -92,6 +92,7 @@ func RunBlueprint(args Args, stopBefore StopBefore, ctx *blueprint.Context, conf
ctx.RegisterModuleType("bootstrap_go_package", newGoPackageModuleFactory())
ctx.RegisterModuleType("blueprint_go_binary", newGoBinaryModuleFactory())
ctx.RegisterSingletonType("bootstrap", newSingletonFactory())
+ blueprint.RegisterPackageIncludesModuleType(ctx)
ctx.BeginEvent("parse_bp")
blueprintFiles, errs := ctx.ParseFileList(".", filesToParse, config)
diff --git a/context.go b/context.go
index 6496948..1296eed 100644
--- a/context.go
+++ b/context.go
@@ -135,6 +135,31 @@ type Context struct {
// Can be set by tests to avoid invalidating Module values after mutators.
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
@@ -392,6 +417,7 @@ func newContext() *Context {
globs: make(map[globKey]pathtools.GlobResult),
fs: pathtools.OsFs,
finishedMutators: make(map[*mutatorInfo]bool),
+ includeTags: &IncludeTags{},
outDir: nil,
requiredNinjaMajor: 1,
requiredNinjaMinor: 7,
@@ -709,6 +735,33 @@ func (c *Context) ParseBlueprintsFiles(rootFile string,
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,
config interface{}) (deps []string, errs []error) {
@@ -761,6 +814,15 @@ func (c *Context) ParseFileList(rootDir string, filePaths []string,
}
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 {
switch def := def.(type) {
@@ -4230,3 +4292,49 @@ var singletonHeaderTemplate = `# # # # # # # # # # # # # # # # # # # # # # # # #
Singleton: {{.name}}
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
+}
diff --git a/context_test.go b/context_test.go
index 6308ba9..5b045bd 100644
--- a/context_test.go
+++ b/context_test.go
@@ -18,6 +18,7 @@ import (
"bytes"
"errors"
"fmt"
+ "path/filepath"
"reflect"
"strings"
"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))
+ }
+ }
+
+}