Merge "ignore bp files from PRODUCT_SOURCE_ROOT_DIRS"
This commit is contained in:
commit
82aa0ffb51
4 changed files with 460 additions and 10 deletions
113
context.go
113
context.go
|
@ -142,6 +142,45 @@ type Context struct {
|
||||||
|
|
||||||
// String values that can be used to gate build graph traversal
|
// String values that can be used to gate build graph traversal
|
||||||
includeTags *IncludeTags
|
includeTags *IncludeTags
|
||||||
|
|
||||||
|
sourceRootDirs *SourceRootDirs
|
||||||
|
}
|
||||||
|
|
||||||
|
// A container for String keys. The keys can be used to gate build graph traversal
|
||||||
|
type SourceRootDirs struct {
|
||||||
|
dirs []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dirs *SourceRootDirs) Add(names ...string) {
|
||||||
|
dirs.dirs = append(dirs.dirs, names...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dirs *SourceRootDirs) SourceRootDirAllowed(path string) (bool, string) {
|
||||||
|
sort.Slice(dirs.dirs, func(i, j int) bool {
|
||||||
|
return len(dirs.dirs[i]) < len(dirs.dirs[j])
|
||||||
|
})
|
||||||
|
last := len(dirs.dirs)
|
||||||
|
for i := range dirs.dirs {
|
||||||
|
// iterate from longest paths (most specific)
|
||||||
|
prefix := dirs.dirs[last-i-1]
|
||||||
|
disallowedPrefix := false
|
||||||
|
if len(prefix) >= 1 && prefix[0] == '-' {
|
||||||
|
prefix = prefix[1:]
|
||||||
|
disallowedPrefix = true
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(path, prefix) {
|
||||||
|
if disallowedPrefix {
|
||||||
|
return false, prefix
|
||||||
|
} else {
|
||||||
|
return true, prefix
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true, ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) AddSourceRootDirs(dirs ...string) {
|
||||||
|
c.sourceRootDirs.Add(dirs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// A container for String keys. The keys can be used to gate build graph traversal
|
// A container for String keys. The keys can be used to gate build graph traversal
|
||||||
|
@ -427,6 +466,7 @@ func newContext() *Context {
|
||||||
fs: pathtools.OsFs,
|
fs: pathtools.OsFs,
|
||||||
finishedMutators: make(map[*mutatorInfo]bool),
|
finishedMutators: make(map[*mutatorInfo]bool),
|
||||||
includeTags: &IncludeTags{},
|
includeTags: &IncludeTags{},
|
||||||
|
sourceRootDirs: &SourceRootDirs{},
|
||||||
outDir: nil,
|
outDir: nil,
|
||||||
requiredNinjaMajor: 1,
|
requiredNinjaMajor: 1,
|
||||||
requiredNinjaMinor: 7,
|
requiredNinjaMinor: 7,
|
||||||
|
@ -968,15 +1008,25 @@ func (c *Context) ParseBlueprintsFiles(rootFile string,
|
||||||
return c.ParseFileList(baseDir, pathsToParse, config)
|
return c.ParseFileList(baseDir, pathsToParse, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type shouldVisitFileInfo struct {
|
||||||
|
shouldVisitFile bool
|
||||||
|
skippedModules []string
|
||||||
|
reasonForSkip string
|
||||||
|
errs []error
|
||||||
|
}
|
||||||
|
|
||||||
// Returns a boolean for whether this file should be analyzed
|
// Returns a boolean for whether this file should be analyzed
|
||||||
// Evaluates to true if the file either
|
// Evaluates to true if the file either
|
||||||
// 1. does not contain a blueprint_package_includes
|
// 1. does not contain a blueprint_package_includes
|
||||||
// 2. contains a blueprint_package_includes and all requested tags are set
|
// 2. contains a blueprint_package_includes and all requested tags are set
|
||||||
// This should be processed before adding any modules to the build graph
|
// This should be processed before adding any modules to the build graph
|
||||||
func shouldVisitFile(c *Context, file *parser.File) (bool, []error) {
|
func shouldVisitFile(c *Context, file *parser.File) shouldVisitFileInfo {
|
||||||
|
skippedModules := []string{}
|
||||||
|
var blueprintPackageIncludes *PackageIncludes
|
||||||
for _, def := range file.Defs {
|
for _, def := range file.Defs {
|
||||||
switch def := def.(type) {
|
switch def := def.(type) {
|
||||||
case *parser.Module:
|
case *parser.Module:
|
||||||
|
skippedModules = append(skippedModules, def.Name())
|
||||||
if def.Type != "blueprint_package_includes" {
|
if def.Type != "blueprint_package_includes" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -984,14 +1034,43 @@ func shouldVisitFile(c *Context, file *parser.File) (bool, []error) {
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
// This file contains errors in blueprint_package_includes
|
// This file contains errors in blueprint_package_includes
|
||||||
// Visit anyways so that we can report errors on other modules in the file
|
// Visit anyways so that we can report errors on other modules in the file
|
||||||
return true, errs
|
return shouldVisitFileInfo{
|
||||||
|
shouldVisitFile: true,
|
||||||
|
errs: errs,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
logicModule, _ := c.cloneLogicModule(module)
|
logicModule, _ := c.cloneLogicModule(module)
|
||||||
pi := logicModule.(*PackageIncludes)
|
blueprintPackageIncludes = logicModule.(*PackageIncludes)
|
||||||
return pi.MatchesIncludeTags(c), []error{}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true, []error{}
|
|
||||||
|
if blueprintPackageIncludes != nil {
|
||||||
|
packageMatches := blueprintPackageIncludes.MatchesIncludeTags(c)
|
||||||
|
if !packageMatches {
|
||||||
|
return shouldVisitFileInfo{
|
||||||
|
shouldVisitFile: false,
|
||||||
|
skippedModules: skippedModules,
|
||||||
|
reasonForSkip: fmt.Sprintf(
|
||||||
|
"module is defined in %q which contains a blueprint_package_includes module with unsatisfied tags",
|
||||||
|
file.Name,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
shouldVisit, invalidatingPrefix := c.sourceRootDirs.SourceRootDirAllowed(file.Name)
|
||||||
|
if !shouldVisit {
|
||||||
|
return shouldVisitFileInfo{
|
||||||
|
shouldVisitFile: shouldVisit,
|
||||||
|
skippedModules: skippedModules,
|
||||||
|
reasonForSkip: fmt.Sprintf(
|
||||||
|
"%q is a descendant of %q, and that path prefix was not included in PRODUCT_SOURCE_ROOT_DIRS",
|
||||||
|
file.Name,
|
||||||
|
invalidatingPrefix,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return shouldVisitFileInfo{shouldVisitFile: true}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Context) ParseFileList(rootDir string, filePaths []string,
|
func (c *Context) ParseFileList(rootDir string, filePaths []string,
|
||||||
|
@ -1009,9 +1088,15 @@ func (c *Context) ParseFileList(rootDir string, filePaths []string,
|
||||||
added chan<- struct{}
|
added chan<- struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type newSkipInfo struct {
|
||||||
|
shouldVisitFileInfo
|
||||||
|
file string
|
||||||
|
}
|
||||||
|
|
||||||
moduleCh := make(chan newModuleInfo)
|
moduleCh := make(chan newModuleInfo)
|
||||||
errsCh := make(chan []error)
|
errsCh := make(chan []error)
|
||||||
doneCh := make(chan struct{})
|
doneCh := make(chan struct{})
|
||||||
|
skipCh := make(chan newSkipInfo)
|
||||||
var numErrs uint32
|
var numErrs uint32
|
||||||
var numGoroutines int32
|
var numGoroutines int32
|
||||||
|
|
||||||
|
@ -1046,12 +1131,17 @@ func (c *Context) ParseFileList(rootDir string, filePaths []string,
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
shouldVisit, errs := shouldVisitFile(c, file)
|
shouldVisitInfo := shouldVisitFile(c, file)
|
||||||
|
errs := shouldVisitInfo.errs
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
atomic.AddUint32(&numErrs, uint32(len(errs)))
|
atomic.AddUint32(&numErrs, uint32(len(errs)))
|
||||||
errsCh <- errs
|
errsCh <- errs
|
||||||
}
|
}
|
||||||
if !shouldVisit {
|
if !shouldVisitInfo.shouldVisitFile {
|
||||||
|
skipCh <- newSkipInfo{
|
||||||
|
file: file.Name,
|
||||||
|
shouldVisitFileInfo: shouldVisitInfo,
|
||||||
|
}
|
||||||
// TODO: Write a file that lists the skipped bp files
|
// TODO: Write a file that lists the skipped bp files
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -1108,6 +1198,14 @@ loop:
|
||||||
if n == 0 {
|
if n == 0 {
|
||||||
break loop
|
break loop
|
||||||
}
|
}
|
||||||
|
case skipped := <-skipCh:
|
||||||
|
nctx := newNamespaceContextFromFilename(skipped.file)
|
||||||
|
for _, name := range skipped.skippedModules {
|
||||||
|
c.nameInterface.NewSkippedModule(nctx, name, SkippedModuleInfo{
|
||||||
|
filename: skipped.file,
|
||||||
|
reason: skipped.reasonForSkip,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3515,7 +3613,6 @@ func (c *Context) discoveredMissingDependencies(module *moduleInfo, depName stri
|
||||||
|
|
||||||
func (c *Context) missingDependencyError(module *moduleInfo, depName string) (errs error) {
|
func (c *Context) missingDependencyError(module *moduleInfo, depName string) (errs error) {
|
||||||
err := c.nameInterface.MissingDependencyError(module.Name(), module.namespace(), depName)
|
err := c.nameInterface.MissingDependencyError(module.Name(), module.namespace(), depName)
|
||||||
|
|
||||||
return &BlueprintError{
|
return &BlueprintError{
|
||||||
Err: err,
|
Err: err,
|
||||||
Pos: module.pos,
|
Pos: module.pos,
|
||||||
|
|
290
context_test.go
290
context_test.go
|
@ -1272,3 +1272,293 @@ func TestDeduplicateOrderOnlyDeps(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSourceRootDirAllowed(t *testing.T) {
|
||||||
|
type pathCase struct {
|
||||||
|
path string
|
||||||
|
decidingPrefix string
|
||||||
|
allowed bool
|
||||||
|
}
|
||||||
|
testcases := []struct {
|
||||||
|
desc string
|
||||||
|
rootDirs []string
|
||||||
|
pathCases []pathCase
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "simple case",
|
||||||
|
rootDirs: []string{
|
||||||
|
"a",
|
||||||
|
"b/c/d",
|
||||||
|
"-c",
|
||||||
|
"-d/c/a",
|
||||||
|
"c/some_single_file",
|
||||||
|
},
|
||||||
|
pathCases: []pathCase{
|
||||||
|
{
|
||||||
|
path: "a",
|
||||||
|
decidingPrefix: "a",
|
||||||
|
allowed: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "a/b/c",
|
||||||
|
decidingPrefix: "a",
|
||||||
|
allowed: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "b",
|
||||||
|
decidingPrefix: "",
|
||||||
|
allowed: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "b/c/d/a",
|
||||||
|
decidingPrefix: "b/c/d",
|
||||||
|
allowed: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "c",
|
||||||
|
decidingPrefix: "c",
|
||||||
|
allowed: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "c/a/b",
|
||||||
|
decidingPrefix: "c",
|
||||||
|
allowed: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "c/some_single_file",
|
||||||
|
decidingPrefix: "c/some_single_file",
|
||||||
|
allowed: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "d/c/a/abc",
|
||||||
|
decidingPrefix: "d/c/a",
|
||||||
|
allowed: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "root directory order matters",
|
||||||
|
rootDirs: []string{
|
||||||
|
"-a",
|
||||||
|
"a/c/some_allowed_file",
|
||||||
|
"a/b/d/some_allowed_file",
|
||||||
|
"a/b",
|
||||||
|
"a/c",
|
||||||
|
"-a/b/d",
|
||||||
|
},
|
||||||
|
pathCases: []pathCase{
|
||||||
|
{
|
||||||
|
path: "a",
|
||||||
|
decidingPrefix: "a",
|
||||||
|
allowed: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "a/some_disallowed_file",
|
||||||
|
decidingPrefix: "a",
|
||||||
|
allowed: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "a/c/some_allowed_file",
|
||||||
|
decidingPrefix: "a/c/some_allowed_file",
|
||||||
|
allowed: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "a/b/d/some_allowed_file",
|
||||||
|
decidingPrefix: "a/b/d/some_allowed_file",
|
||||||
|
allowed: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "a/b/c",
|
||||||
|
decidingPrefix: "a/b",
|
||||||
|
allowed: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "a/b/c/some_allowed_file",
|
||||||
|
decidingPrefix: "a/b",
|
||||||
|
allowed: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "a/b/d",
|
||||||
|
decidingPrefix: "a/b/d",
|
||||||
|
allowed: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tc := range testcases {
|
||||||
|
dirs := SourceRootDirs{}
|
||||||
|
dirs.Add(tc.rootDirs...)
|
||||||
|
for _, pc := range tc.pathCases {
|
||||||
|
t.Run(fmt.Sprintf("%s: %s", tc.desc, pc.path), func(t *testing.T) {
|
||||||
|
allowed, decidingPrefix := dirs.SourceRootDirAllowed(pc.path)
|
||||||
|
if allowed != pc.allowed {
|
||||||
|
if pc.allowed {
|
||||||
|
t.Errorf("expected path %q to be allowed, but was not; root allowlist: %q", pc.path, tc.rootDirs)
|
||||||
|
} else {
|
||||||
|
t.Errorf("path %q was allowed unexpectedly; root allowlist: %q", pc.path, tc.rootDirs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if decidingPrefix != pc.decidingPrefix {
|
||||||
|
t.Errorf("expected decidingPrefix to be %q, but got %q", pc.decidingPrefix, decidingPrefix)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSourceRootDirs(t *testing.T) {
|
||||||
|
root_foo_bp := `
|
||||||
|
foo_module {
|
||||||
|
name: "foo",
|
||||||
|
deps: ["foo_dir1", "foo_dir_ignored_special_case"],
|
||||||
|
}
|
||||||
|
`
|
||||||
|
dir1_foo_bp := `
|
||||||
|
foo_module {
|
||||||
|
name: "foo_dir1",
|
||||||
|
deps: ["foo_dir_ignored"],
|
||||||
|
}
|
||||||
|
`
|
||||||
|
dir_ignored_foo_bp := `
|
||||||
|
foo_module {
|
||||||
|
name: "foo_dir_ignored",
|
||||||
|
}
|
||||||
|
`
|
||||||
|
dir_ignored_special_case_foo_bp := `
|
||||||
|
foo_module {
|
||||||
|
name: "foo_dir_ignored_special_case",
|
||||||
|
}
|
||||||
|
`
|
||||||
|
mockFs := map[string][]byte{
|
||||||
|
"Android.bp": []byte(root_foo_bp),
|
||||||
|
"dir1/Android.bp": []byte(dir1_foo_bp),
|
||||||
|
"dir_ignored/Android.bp": []byte(dir_ignored_foo_bp),
|
||||||
|
"dir_ignored/special_case/Android.bp": []byte(dir_ignored_special_case_foo_bp),
|
||||||
|
}
|
||||||
|
fileList := []string{}
|
||||||
|
for f := range mockFs {
|
||||||
|
fileList = append(fileList, f)
|
||||||
|
}
|
||||||
|
testCases := []struct {
|
||||||
|
sourceRootDirs []string
|
||||||
|
expectedModuleDefs []string
|
||||||
|
unexpectedModuleDefs []string
|
||||||
|
expectedErrs []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
sourceRootDirs: []string{},
|
||||||
|
expectedModuleDefs: []string{
|
||||||
|
"foo",
|
||||||
|
"foo_dir1",
|
||||||
|
"foo_dir_ignored",
|
||||||
|
"foo_dir_ignored_special_case",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sourceRootDirs: []string{"-", ""},
|
||||||
|
unexpectedModuleDefs: []string{
|
||||||
|
"foo",
|
||||||
|
"foo_dir1",
|
||||||
|
"foo_dir_ignored",
|
||||||
|
"foo_dir_ignored_special_case",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sourceRootDirs: []string{"-"},
|
||||||
|
unexpectedModuleDefs: []string{
|
||||||
|
"foo",
|
||||||
|
"foo_dir1",
|
||||||
|
"foo_dir_ignored",
|
||||||
|
"foo_dir_ignored_special_case",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sourceRootDirs: []string{"dir1"},
|
||||||
|
expectedModuleDefs: []string{
|
||||||
|
"foo",
|
||||||
|
"foo_dir1",
|
||||||
|
"foo_dir_ignored",
|
||||||
|
"foo_dir_ignored_special_case",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sourceRootDirs: []string{"-dir1"},
|
||||||
|
expectedModuleDefs: []string{
|
||||||
|
"foo",
|
||||||
|
"foo_dir_ignored",
|
||||||
|
"foo_dir_ignored_special_case",
|
||||||
|
},
|
||||||
|
unexpectedModuleDefs: []string{
|
||||||
|
"foo_dir1",
|
||||||
|
},
|
||||||
|
expectedErrs: []string{
|
||||||
|
"Android.bp:2:2: \"foo\" depends on undefined module \"foo_dir1\"",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sourceRootDirs: []string{"-", "dir1"},
|
||||||
|
expectedModuleDefs: []string{
|
||||||
|
"foo_dir1",
|
||||||
|
},
|
||||||
|
unexpectedModuleDefs: []string{
|
||||||
|
"foo",
|
||||||
|
"foo_dir_ignored",
|
||||||
|
"foo_dir_ignored_special_case",
|
||||||
|
},
|
||||||
|
expectedErrs: []string{
|
||||||
|
"dir1/Android.bp:2:2: \"foo_dir1\" depends on undefined module \"foo_dir_ignored\"",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sourceRootDirs: []string{"-", "dir1", "dir_ignored/special_case/Android.bp"},
|
||||||
|
expectedModuleDefs: []string{
|
||||||
|
"foo_dir1",
|
||||||
|
"foo_dir_ignored_special_case",
|
||||||
|
},
|
||||||
|
unexpectedModuleDefs: []string{
|
||||||
|
"foo",
|
||||||
|
"foo_dir_ignored",
|
||||||
|
},
|
||||||
|
expectedErrs: []string{
|
||||||
|
"dir1/Android.bp:2:2: \"foo_dir1\" depends on undefined module \"foo_dir_ignored\"",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(fmt.Sprintf(`source root dirs are %q`, tc.sourceRootDirs), func(t *testing.T) {
|
||||||
|
ctx := NewContext()
|
||||||
|
ctx.MockFileSystem(mockFs)
|
||||||
|
ctx.RegisterModuleType("foo_module", newFooModule)
|
||||||
|
ctx.RegisterBottomUpMutator("deps", depsMutator)
|
||||||
|
ctx.AddSourceRootDirs(tc.sourceRootDirs...)
|
||||||
|
RegisterPackageIncludesModuleType(ctx)
|
||||||
|
ctx.ParseFileList(".", fileList, nil)
|
||||||
|
_, actualErrs := ctx.ResolveDependencies(nil)
|
||||||
|
|
||||||
|
stringErrs := []string(nil)
|
||||||
|
for i := range actualErrs {
|
||||||
|
stringErrs = append(stringErrs, fmt.Sprint(actualErrs[i]))
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(tc.expectedErrs, stringErrs) {
|
||||||
|
t.Errorf("expected to find errors %v; got %v", tc.expectedErrs, stringErrs)
|
||||||
|
}
|
||||||
|
for _, modName := range tc.expectedModuleDefs {
|
||||||
|
allMods := ctx.moduleGroupFromName(modName, nil)
|
||||||
|
if allMods == nil || len(allMods.modules) != 1 {
|
||||||
|
mods := modulesOrAliases{}
|
||||||
|
if allMods != nil {
|
||||||
|
mods = allMods.modules
|
||||||
|
}
|
||||||
|
t.Errorf("expected to find one definition for module %q, but got %v", modName, mods)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, modName := range tc.unexpectedModuleDefs {
|
||||||
|
allMods := ctx.moduleGroupFromName(modName, nil)
|
||||||
|
if allMods != nil {
|
||||||
|
t.Errorf("expected to find no definitions for module %q, but got %v", modName, allMods.modules)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -16,7 +16,9 @@ package blueprint
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"sort"
|
"sort"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// This file exposes the logic of locating a module via a query string, to enable
|
// This file exposes the logic of locating a module via a query string, to enable
|
||||||
|
@ -54,6 +56,9 @@ type NameInterface interface {
|
||||||
// Gets called when a new module is created
|
// Gets called when a new module is created
|
||||||
NewModule(ctx NamespaceContext, group ModuleGroup, module Module) (namespace Namespace, err []error)
|
NewModule(ctx NamespaceContext, group ModuleGroup, module Module) (namespace Namespace, err []error)
|
||||||
|
|
||||||
|
// Gets called when a module was pruned from the build tree by SourceRootDirs
|
||||||
|
NewSkippedModule(ctx NamespaceContext, name string, skipInfo SkippedModuleInfo)
|
||||||
|
|
||||||
// Finds the module with the given name
|
// Finds the module with the given name
|
||||||
ModuleFromName(moduleName string, namespace Namespace) (group ModuleGroup, found bool)
|
ModuleFromName(moduleName string, namespace Namespace) (group ModuleGroup, found bool)
|
||||||
|
|
||||||
|
@ -88,18 +93,29 @@ func newNamespaceContext(moduleInfo *moduleInfo) (ctx NamespaceContext) {
|
||||||
return &namespaceContextImpl{moduleInfo.pos.Filename}
|
return &namespaceContextImpl{moduleInfo.pos.Filename}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newNamespaceContextFromFilename(filename string) NamespaceContext {
|
||||||
|
return &namespaceContextImpl{filename}
|
||||||
|
}
|
||||||
|
|
||||||
func (ctx *namespaceContextImpl) ModulePath() string {
|
func (ctx *namespaceContextImpl) ModulePath() string {
|
||||||
return ctx.modulePath
|
return ctx.modulePath
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SkippedModuleInfo struct {
|
||||||
|
filename string
|
||||||
|
reason string
|
||||||
|
}
|
||||||
|
|
||||||
// a SimpleNameInterface just stores all modules in a map based on name
|
// a SimpleNameInterface just stores all modules in a map based on name
|
||||||
type SimpleNameInterface struct {
|
type SimpleNameInterface struct {
|
||||||
modules map[string]ModuleGroup
|
modules map[string]ModuleGroup
|
||||||
|
skippedModules map[string][]SkippedModuleInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSimpleNameInterface() *SimpleNameInterface {
|
func NewSimpleNameInterface() *SimpleNameInterface {
|
||||||
return &SimpleNameInterface{
|
return &SimpleNameInterface{
|
||||||
modules: make(map[string]ModuleGroup),
|
modules: make(map[string]ModuleGroup),
|
||||||
|
skippedModules: make(map[string][]SkippedModuleInfo),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,8 +134,31 @@ func (s *SimpleNameInterface) NewModule(ctx NamespaceContext, group ModuleGroup,
|
||||||
return nil, []error{}
|
return nil, []error{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *SimpleNameInterface) NewSkippedModule(ctx NamespaceContext, name string, info SkippedModuleInfo) {
|
||||||
|
if name == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.skippedModules[name] = append(s.skippedModules[name], info)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *SimpleNameInterface) ModuleFromName(moduleName string, namespace Namespace) (group ModuleGroup, found bool) {
|
func (s *SimpleNameInterface) ModuleFromName(moduleName string, namespace Namespace) (group ModuleGroup, found bool) {
|
||||||
group, found = s.modules[moduleName]
|
group, found = s.modules[moduleName]
|
||||||
|
skipInfos, skipped := s.skippedModules[moduleName]
|
||||||
|
if skipped {
|
||||||
|
filesFound := make([]string, 0, len(skipInfos))
|
||||||
|
reasons := make([]string, 0, len(skipInfos))
|
||||||
|
for _, info := range skipInfos {
|
||||||
|
filesFound = append(filesFound, info.filename)
|
||||||
|
reasons = append(reasons, info.reason)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(
|
||||||
|
os.Stderr,
|
||||||
|
"module %q was defined in files(s) [%v], but was skipped for reason(s) [%v]\n",
|
||||||
|
moduleName,
|
||||||
|
strings.Join(filesFound, ", "),
|
||||||
|
strings.Join(reasons, "; "),
|
||||||
|
)
|
||||||
|
}
|
||||||
return group, found
|
return group, found
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -60,6 +60,8 @@ type Module struct {
|
||||||
Type string
|
Type string
|
||||||
TypePos scanner.Position
|
TypePos scanner.Position
|
||||||
Map
|
Map
|
||||||
|
//TODO(delmerico) make this a private field once ag/21588220 lands
|
||||||
|
Name__internal_only *string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Module) Copy() *Module {
|
func (m *Module) Copy() *Module {
|
||||||
|
@ -86,6 +88,28 @@ func (m *Module) definitionTag() {}
|
||||||
func (m *Module) Pos() scanner.Position { return m.TypePos }
|
func (m *Module) Pos() scanner.Position { return m.TypePos }
|
||||||
func (m *Module) End() scanner.Position { return m.Map.End() }
|
func (m *Module) End() scanner.Position { return m.Map.End() }
|
||||||
|
|
||||||
|
func (m *Module) Name() string {
|
||||||
|
if m.Name__internal_only != nil {
|
||||||
|
return *m.Name__internal_only
|
||||||
|
}
|
||||||
|
for _, prop := range m.Properties {
|
||||||
|
if prop.Name == "name" {
|
||||||
|
if stringProp, ok := prop.Value.(*String); ok {
|
||||||
|
name := stringProp.Value
|
||||||
|
m.Name__internal_only = &name
|
||||||
|
} else {
|
||||||
|
name := prop.Value.String()
|
||||||
|
m.Name__internal_only = &name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if m.Name__internal_only == nil {
|
||||||
|
name := ""
|
||||||
|
m.Name__internal_only = &name
|
||||||
|
}
|
||||||
|
return *m.Name__internal_only
|
||||||
|
}
|
||||||
|
|
||||||
// A Property is a name: value pair within a Map, which may be a top level Module.
|
// A Property is a name: value pair within a Map, which may be a top level Module.
|
||||||
type Property struct {
|
type Property struct {
|
||||||
Name string
|
Name string
|
||||||
|
|
Loading…
Reference in a new issue