Merge pull request #111 from colincross/visit
Reimplement VisitDirectDeps[If] on Context.walkdeps
This commit is contained in:
commit
1c1ec45ebb
8 changed files with 444 additions and 150 deletions
|
@ -8,6 +8,7 @@ bootstrap_go_package(
|
|||
pkgPath = "github.com/google/blueprint",
|
||||
srcs = [
|
||||
"context.go",
|
||||
"fs.go",
|
||||
"live_tracker.go",
|
||||
"mangle.go",
|
||||
"module_ctx.go",
|
||||
|
@ -25,6 +26,7 @@ bootstrap_go_package(
|
|||
"ninja_writer_test.go",
|
||||
"splice_modules_test.go",
|
||||
"unpack_test.go",
|
||||
"visit_test.go",
|
||||
],
|
||||
)
|
||||
|
||||
|
|
|
@ -60,8 +60,8 @@ rule g.bootstrap.link
|
|||
build $
|
||||
${g.bootstrap.buildDir}/.bootstrap/blueprint/pkg/github.com/google/blueprint.a $
|
||||
: g.bootstrap.compile ${g.bootstrap.srcDir}/context.go $
|
||||
${g.bootstrap.srcDir}/live_tracker.go ${g.bootstrap.srcDir}/mangle.go $
|
||||
${g.bootstrap.srcDir}/module_ctx.go $
|
||||
${g.bootstrap.srcDir}/fs.go ${g.bootstrap.srcDir}/live_tracker.go $
|
||||
${g.bootstrap.srcDir}/mangle.go ${g.bootstrap.srcDir}/module_ctx.go $
|
||||
${g.bootstrap.srcDir}/ninja_defs.go $
|
||||
${g.bootstrap.srcDir}/ninja_strings.go $
|
||||
${g.bootstrap.srcDir}/ninja_writer.go $
|
||||
|
@ -81,7 +81,7 @@ default $
|
|||
# Variant:
|
||||
# Type: bootstrap_go_package
|
||||
# Factory: github.com/google/blueprint/bootstrap.newGoPackageModuleFactory.func1
|
||||
# Defined: Blueprints:81:1
|
||||
# Defined: Blueprints:83:1
|
||||
|
||||
build $
|
||||
${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap/pkg/github.com/google/blueprint/bootstrap.a $
|
||||
|
@ -108,7 +108,7 @@ default $
|
|||
# Variant:
|
||||
# Type: bootstrap_go_package
|
||||
# Factory: github.com/google/blueprint/bootstrap.newGoPackageModuleFactory.func1
|
||||
# Defined: Blueprints:100:1
|
||||
# Defined: Blueprints:102:1
|
||||
|
||||
build $
|
||||
${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap-bpdoc/pkg/github.com/google/blueprint/bootstrap/bpdoc.a $
|
||||
|
@ -128,7 +128,7 @@ default $
|
|||
# Variant:
|
||||
# Type: bootstrap_go_package
|
||||
# Factory: github.com/google/blueprint/bootstrap.newGoPackageModuleFactory.func1
|
||||
# Defined: Blueprints:47:1
|
||||
# Defined: Blueprints:49:1
|
||||
|
||||
build $
|
||||
${g.bootstrap.buildDir}/.bootstrap/blueprint-deptools/pkg/github.com/google/blueprint/deptools.a $
|
||||
|
@ -143,7 +143,7 @@ default $
|
|||
# Variant:
|
||||
# Type: bootstrap_go_package
|
||||
# Factory: github.com/google/blueprint/bootstrap.newGoPackageModuleFactory.func1
|
||||
# Defined: Blueprints:31:1
|
||||
# Defined: Blueprints:33:1
|
||||
|
||||
build $
|
||||
${g.bootstrap.buildDir}/.bootstrap/blueprint-parser/pkg/github.com/google/blueprint/parser.a $
|
||||
|
@ -161,7 +161,7 @@ default $
|
|||
# Variant:
|
||||
# Type: bootstrap_go_package
|
||||
# Factory: github.com/google/blueprint/bootstrap.newGoPackageModuleFactory.func1
|
||||
# Defined: Blueprints:53:1
|
||||
# Defined: Blueprints:55:1
|
||||
|
||||
build $
|
||||
${g.bootstrap.buildDir}/.bootstrap/blueprint-pathtools/pkg/github.com/google/blueprint/pathtools.a $
|
||||
|
@ -176,7 +176,7 @@ default $
|
|||
# Variant:
|
||||
# Type: bootstrap_go_package
|
||||
# Factory: github.com/google/blueprint/bootstrap.newGoPackageModuleFactory.func1
|
||||
# Defined: Blueprints:65:1
|
||||
# Defined: Blueprints:67:1
|
||||
|
||||
build $
|
||||
${g.bootstrap.buildDir}/.bootstrap/blueprint-proptools/pkg/github.com/google/blueprint/proptools.a $
|
||||
|
@ -194,7 +194,7 @@ default $
|
|||
# Variant:
|
||||
# Type: bootstrap_core_go_binary
|
||||
# Factory: github.com/google/blueprint/bootstrap.newGoBinaryModuleFactory.func1
|
||||
# Defined: Blueprints:143:1
|
||||
# Defined: Blueprints:145:1
|
||||
|
||||
build ${g.bootstrap.buildDir}/.bootstrap/choosestage/obj/choosestage.a: $
|
||||
g.bootstrap.compile ${g.bootstrap.srcDir}/choosestage/choosestage.go | $
|
||||
|
@ -217,7 +217,7 @@ default ${g.bootstrap.BinDir}/choosestage
|
|||
# Variant:
|
||||
# Type: bootstrap_core_go_binary
|
||||
# Factory: github.com/google/blueprint/bootstrap.newGoBinaryModuleFactory.func1
|
||||
# Defined: Blueprints:133:1
|
||||
# Defined: Blueprints:135:1
|
||||
|
||||
build ${g.bootstrap.buildDir}/.bootstrap/gotestmain/obj/gotestmain.a: $
|
||||
g.bootstrap.compile ${g.bootstrap.srcDir}/gotestmain/gotestmain.go | $
|
||||
|
@ -240,7 +240,7 @@ default ${g.bootstrap.BinDir}/gotestmain
|
|||
# Variant:
|
||||
# Type: bootstrap_core_go_binary
|
||||
# Factory: github.com/google/blueprint/bootstrap.newGoBinaryModuleFactory.func1
|
||||
# Defined: Blueprints:138:1
|
||||
# Defined: Blueprints:140:1
|
||||
|
||||
build ${g.bootstrap.buildDir}/.bootstrap/gotestrunner/obj/gotestrunner.a: $
|
||||
g.bootstrap.compile ${g.bootstrap.srcDir}/gotestrunner/gotestrunner.go $
|
||||
|
@ -263,7 +263,7 @@ default ${g.bootstrap.BinDir}/gotestrunner
|
|||
# Variant:
|
||||
# Type: bootstrap_core_go_binary
|
||||
# Factory: github.com/google/blueprint/bootstrap.newGoBinaryModuleFactory.func1
|
||||
# Defined: Blueprints:112:1
|
||||
# Defined: Blueprints:114:1
|
||||
|
||||
build ${g.bootstrap.buildDir}/.bootstrap/minibp/obj/minibp.a: $
|
||||
g.bootstrap.compile ${g.bootstrap.srcDir}/bootstrap/minibp/main.go | $
|
||||
|
|
152
context.go
152
context.go
|
@ -19,7 +19,6 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime"
|
||||
|
@ -98,6 +97,8 @@ type Context struct {
|
|||
|
||||
// set lazily by sortedModuleNames
|
||||
cachedSortedModuleNames []string
|
||||
|
||||
fs fileSystem
|
||||
}
|
||||
|
||||
// An Error describes a problem that was encountered that is related to a
|
||||
|
@ -246,6 +247,7 @@ func NewContext() *Context {
|
|||
moduleGroups: make(map[string]*moduleGroup),
|
||||
moduleInfo: make(map[Module]*moduleInfo),
|
||||
moduleNinjaNames: make(map[string]*moduleGroup),
|
||||
fs: fs,
|
||||
}
|
||||
|
||||
ctx.RegisterBottomUpMutator("blueprint_deps", blueprintDepsMutator)
|
||||
|
@ -711,6 +713,14 @@ loop:
|
|||
return
|
||||
}
|
||||
|
||||
// MockFileSystem causes the Context to replace all reads with accesses to the provided map of
|
||||
// filenames to contents stored as a byte slice.
|
||||
func (c *Context) MockFileSystem(files map[string][]byte) {
|
||||
c.fs = &mockFS{
|
||||
files: files,
|
||||
}
|
||||
}
|
||||
|
||||
// parseBlueprintFile parses a single Blueprints file, returning any errors through
|
||||
// errsCh, any defined modules through modulesCh, any sub-Blueprints files through
|
||||
// blueprintsCh, and any dependencies on Blueprints files or directories through
|
||||
|
@ -719,7 +729,7 @@ func (c *Context) parseBlueprintsFile(filename string, scope *parser.Scope, root
|
|||
errsCh chan<- []error, fileCh chan<- *parser.File, blueprintsCh chan<- stringAndScope,
|
||||
depsCh chan<- string) {
|
||||
|
||||
f, err := os.Open(filename)
|
||||
f, err := c.fs.Open(filename)
|
||||
if err != nil {
|
||||
errsCh <- []error{err}
|
||||
return
|
||||
|
@ -772,15 +782,15 @@ func (c *Context) findBuildBlueprints(dir string, build []string,
|
|||
deps = append(deps, matchedDirs...)
|
||||
|
||||
for _, foundBlueprints := range matches {
|
||||
fileInfo, err := os.Stat(foundBlueprints)
|
||||
if os.IsNotExist(err) {
|
||||
exists, dir, err := c.fs.Exists(foundBlueprints)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
} else if !exists {
|
||||
errs = append(errs, &Error{
|
||||
Err: fmt.Errorf("%q not found", foundBlueprints),
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
if fileInfo.IsDir() {
|
||||
} else if dir {
|
||||
errs = append(errs, &Error{
|
||||
Err: fmt.Errorf("%q is a directory", foundBlueprints),
|
||||
})
|
||||
|
@ -819,29 +829,34 @@ func (c *Context) findSubdirBlueprints(dir string, subdirs []string, subdirsPos
|
|||
deps = append(deps, matchedDirs...)
|
||||
|
||||
for _, foundSubdir := range matches {
|
||||
fileInfo, subdirStatErr := os.Stat(foundSubdir)
|
||||
exists, dir, subdirStatErr := c.fs.Exists(foundSubdir)
|
||||
if subdirStatErr != nil {
|
||||
errs = append(errs, subdirStatErr)
|
||||
continue
|
||||
}
|
||||
|
||||
// Skip files
|
||||
if !fileInfo.IsDir() {
|
||||
if !dir {
|
||||
continue
|
||||
}
|
||||
|
||||
var subBlueprints string
|
||||
if subBlueprintsName != "" {
|
||||
subBlueprints = filepath.Join(foundSubdir, subBlueprintsName)
|
||||
_, err = os.Stat(subBlueprints)
|
||||
exists, _, err = c.fs.Exists(subBlueprints)
|
||||
}
|
||||
|
||||
if os.IsNotExist(err) || subBlueprints == "" {
|
||||
if err == nil && (!exists || subBlueprints == "") {
|
||||
subBlueprints = filepath.Join(foundSubdir, "Blueprints")
|
||||
_, err = os.Stat(subBlueprints)
|
||||
exists, _, err = c.fs.Exists(subBlueprints)
|
||||
}
|
||||
|
||||
if os.IsNotExist(err) {
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if !exists {
|
||||
// There is no Blueprints file in this subdirectory. We
|
||||
// need to add the directory to the list of dependencies
|
||||
// so that if someone adds a Blueprints file in the
|
||||
|
@ -1978,15 +1993,15 @@ func (c *Context) processLocalBuildActions(out, in *localBuildActions,
|
|||
}
|
||||
|
||||
func (c *Context) walkDeps(topModule *moduleInfo,
|
||||
visit func(Module, Module) bool) {
|
||||
visitDown func(depInfo, *moduleInfo) bool, visitUp func(depInfo, *moduleInfo)) {
|
||||
|
||||
visited := make(map[*moduleInfo]bool)
|
||||
var visiting *moduleInfo
|
||||
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
panic(newPanicErrorf(r, "WalkDeps(%s, %s) for dependency %s",
|
||||
topModule, funcName(visit), visiting))
|
||||
panic(newPanicErrorf(r, "WalkDeps(%s, %s, %s) for dependency %s",
|
||||
topModule, funcName(visitDown), funcName(visitUp), visiting))
|
||||
}
|
||||
}()
|
||||
|
||||
|
@ -1996,9 +2011,16 @@ func (c *Context) walkDeps(topModule *moduleInfo,
|
|||
if !visited[dep.module] {
|
||||
visited[dep.module] = true
|
||||
visiting = dep.module
|
||||
if visit(dep.module.logicModule, module.logicModule) {
|
||||
recurse := true
|
||||
if visitDown != nil {
|
||||
recurse = visitDown(dep, module)
|
||||
}
|
||||
if recurse {
|
||||
walk(dep.module)
|
||||
}
|
||||
if visitUp != nil {
|
||||
visitUp(dep, module)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2008,68 +2030,6 @@ func (c *Context) walkDeps(topModule *moduleInfo,
|
|||
|
||||
type innerPanicError error
|
||||
|
||||
func (c *Context) visitDepsDepthFirst(topModule *moduleInfo, visit func(Module)) {
|
||||
visited := make(map[*moduleInfo]bool)
|
||||
var visiting *moduleInfo
|
||||
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
panic(newPanicErrorf(r, "VisitDepsDepthFirst(%s, %s) for dependency %s",
|
||||
topModule, funcName(visit), visiting))
|
||||
}
|
||||
}()
|
||||
|
||||
var walk func(module *moduleInfo)
|
||||
walk = func(module *moduleInfo) {
|
||||
visited[module] = true
|
||||
for _, dep := range module.directDeps {
|
||||
if !visited[dep.module] {
|
||||
walk(dep.module)
|
||||
}
|
||||
}
|
||||
|
||||
if module != topModule {
|
||||
visiting = module
|
||||
visit(module.logicModule)
|
||||
}
|
||||
}
|
||||
|
||||
walk(topModule)
|
||||
}
|
||||
|
||||
func (c *Context) visitDepsDepthFirstIf(topModule *moduleInfo, pred func(Module) bool,
|
||||
visit func(Module)) {
|
||||
|
||||
visited := make(map[*moduleInfo]bool)
|
||||
var visiting *moduleInfo
|
||||
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
panic(newPanicErrorf(r, "VisitDepsDepthFirstIf(%s, %s, %s) for dependency %s",
|
||||
topModule, funcName(pred), funcName(visit), visiting))
|
||||
}
|
||||
}()
|
||||
|
||||
var walk func(module *moduleInfo)
|
||||
walk = func(module *moduleInfo) {
|
||||
visited[module] = true
|
||||
for _, dep := range module.directDeps {
|
||||
if !visited[dep.module] {
|
||||
walk(dep.module)
|
||||
}
|
||||
}
|
||||
|
||||
if module != topModule {
|
||||
if pred(module.logicModule) {
|
||||
visiting = module
|
||||
visit(module.logicModule)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
walk(topModule)
|
||||
}
|
||||
|
||||
func (c *Context) sortedModuleNames() []string {
|
||||
if c.cachedSortedModuleNames == nil {
|
||||
c.cachedSortedModuleNames = make([]string, 0, len(c.moduleGroups))
|
||||
|
@ -2399,13 +2359,43 @@ func (c *Context) VisitAllModulesIf(pred func(Module) bool,
|
|||
func (c *Context) VisitDepsDepthFirst(module Module,
|
||||
visit func(Module)) {
|
||||
|
||||
c.visitDepsDepthFirst(c.moduleInfo[module], visit)
|
||||
topModule := c.moduleInfo[module]
|
||||
|
||||
var visiting *moduleInfo
|
||||
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
panic(newPanicErrorf(r, "VisitDepsDepthFirst(%s, %s) for dependency %s",
|
||||
topModule, funcName(visit), visiting))
|
||||
}
|
||||
}()
|
||||
|
||||
c.walkDeps(topModule, nil, func(dep depInfo, parent *moduleInfo) {
|
||||
visiting = dep.module
|
||||
visit(dep.module.logicModule)
|
||||
})
|
||||
}
|
||||
|
||||
func (c *Context) VisitDepsDepthFirstIf(module Module,
|
||||
pred func(Module) bool, visit func(Module)) {
|
||||
|
||||
c.visitDepsDepthFirstIf(c.moduleInfo[module], pred, visit)
|
||||
topModule := c.moduleInfo[module]
|
||||
|
||||
var visiting *moduleInfo
|
||||
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
panic(newPanicErrorf(r, "VisitDepsDepthFirstIf(%s, %s, %s) for dependency %s",
|
||||
topModule, funcName(pred), funcName(visit), visiting))
|
||||
}
|
||||
}()
|
||||
|
||||
c.walkDeps(topModule, nil, func(dep depInfo, parent *moduleInfo) {
|
||||
if pred(dep.module.logicModule) {
|
||||
visiting = dep.module
|
||||
visit(dep.module.logicModule)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (c *Context) PrimaryModule(module Module) Module {
|
||||
|
|
|
@ -109,22 +109,83 @@ func TestContextParse(t *testing.T) {
|
|||
// |===F===| B, D and E should not be walked.
|
||||
func TestWalkDeps(t *testing.T) {
|
||||
ctx := NewContext()
|
||||
ctx.MockFileSystem(map[string][]byte{
|
||||
"Blueprints": []byte(`
|
||||
foo_module {
|
||||
name: "A",
|
||||
deps: ["B", "C"],
|
||||
}
|
||||
|
||||
bar_module {
|
||||
name: "B",
|
||||
deps: ["D"],
|
||||
}
|
||||
|
||||
foo_module {
|
||||
name: "C",
|
||||
deps: ["E", "F"],
|
||||
}
|
||||
|
||||
foo_module {
|
||||
name: "D",
|
||||
}
|
||||
|
||||
bar_module {
|
||||
name: "E",
|
||||
deps: ["G"],
|
||||
}
|
||||
|
||||
foo_module {
|
||||
name: "F",
|
||||
deps: ["G"],
|
||||
}
|
||||
|
||||
foo_module {
|
||||
name: "G",
|
||||
}
|
||||
`),
|
||||
})
|
||||
|
||||
ctx.RegisterModuleType("foo_module", newFooModule)
|
||||
ctx.RegisterModuleType("bar_module", newBarModule)
|
||||
ctx.ParseBlueprintsFiles("context_test_Blueprints")
|
||||
ctx.ResolveDependencies(nil)
|
||||
_, errs := ctx.ParseBlueprintsFiles("Blueprints")
|
||||
if len(errs) > 0 {
|
||||
t.Errorf("unexpected parse errors:")
|
||||
for _, err := range errs {
|
||||
t.Errorf(" %s", err)
|
||||
}
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
var output string
|
||||
errs = ctx.ResolveDependencies(nil)
|
||||
if len(errs) > 0 {
|
||||
t.Errorf("unexpected dep errors:")
|
||||
for _, err := range errs {
|
||||
t.Errorf(" %s", err)
|
||||
}
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
var outputDown string
|
||||
var outputUp string
|
||||
topModule := ctx.moduleGroups["A"].modules[0]
|
||||
ctx.walkDeps(topModule,
|
||||
func(module, parent Module) bool {
|
||||
if module.(Walker).Walk() {
|
||||
output += ctx.ModuleName(module)
|
||||
func(dep depInfo, parent *moduleInfo) bool {
|
||||
if dep.module.logicModule.(Walker).Walk() {
|
||||
outputDown += ctx.ModuleName(dep.module.logicModule)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
func(dep depInfo, parent *moduleInfo) {
|
||||
if dep.module.logicModule.(Walker).Walk() {
|
||||
outputUp += ctx.ModuleName(dep.module.logicModule)
|
||||
}
|
||||
})
|
||||
if output != "CFG" {
|
||||
t.Fatalf("unexpected walkDeps behaviour: %s\nshould be: CFG", output)
|
||||
if outputDown != "CFG" {
|
||||
t.Fatalf("unexpected walkDeps behaviour: %s\ndown should be: CFG", outputDown)
|
||||
}
|
||||
if outputUp != "GFC" {
|
||||
t.Fatalf("unexpected walkDeps behaviour: %s\nup should be: GFC", outputUp)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
foo_module {
|
||||
name: "A",
|
||||
deps: ["B", "C"],
|
||||
}
|
||||
|
||||
bar_module {
|
||||
name: "B",
|
||||
deps: ["D"],
|
||||
}
|
||||
|
||||
foo_module {
|
||||
name: "C",
|
||||
deps: ["E", "F"],
|
||||
}
|
||||
|
||||
foo_module {
|
||||
name: "D",
|
||||
}
|
||||
|
||||
bar_module {
|
||||
name: "E",
|
||||
deps: ["G"],
|
||||
}
|
||||
|
||||
foo_module {
|
||||
name: "F",
|
||||
deps: ["G"],
|
||||
}
|
||||
|
||||
foo_module {
|
||||
name: "G",
|
||||
}
|
73
fs.go
Normal file
73
fs.go
Normal file
|
@ -0,0 +1,73 @@
|
|||
// Copyright 2016 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 (
|
||||
"bytes"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
)
|
||||
|
||||
// Based on Andrew Gerrand's "10 things you (probably) dont' know about Go"
|
||||
|
||||
var fs fileSystem = osFS{}
|
||||
|
||||
type fileSystem interface {
|
||||
Open(name string) (io.ReadCloser, error)
|
||||
Exists(name string) (bool, bool, error)
|
||||
}
|
||||
|
||||
// osFS implements fileSystem using the local disk.
|
||||
type osFS struct{}
|
||||
|
||||
func (osFS) Open(name string) (io.ReadCloser, error) { return os.Open(name) }
|
||||
func (osFS) Exists(name string) (bool, bool, error) {
|
||||
stat, err := os.Stat(name)
|
||||
if err == nil {
|
||||
return true, stat.IsDir(), nil
|
||||
} else if os.IsNotExist(err) {
|
||||
return false, false, nil
|
||||
} else {
|
||||
return false, false, err
|
||||
}
|
||||
}
|
||||
|
||||
type mockFS struct {
|
||||
files map[string][]byte
|
||||
}
|
||||
|
||||
func (m mockFS) Open(name string) (io.ReadCloser, error) {
|
||||
if f, ok := m.files[name]; ok {
|
||||
return struct {
|
||||
io.Closer
|
||||
*bytes.Reader
|
||||
}{
|
||||
ioutil.NopCloser(nil),
|
||||
bytes.NewReader(f),
|
||||
}, nil
|
||||
}
|
||||
|
||||
return nil, &os.PathError{
|
||||
Op: "open",
|
||||
Path: name,
|
||||
Err: os.ErrNotExist,
|
||||
}
|
||||
}
|
||||
|
||||
func (m mockFS) Exists(name string) (bool, bool, error) {
|
||||
_, ok := m.files[name]
|
||||
return ok, false, nil
|
||||
}
|
|
@ -155,11 +155,12 @@ type ModuleContext interface {
|
|||
var _ BaseModuleContext = (*baseModuleContext)(nil)
|
||||
|
||||
type baseModuleContext struct {
|
||||
context *Context
|
||||
config interface{}
|
||||
module *moduleInfo
|
||||
errs []error
|
||||
visiting depInfo
|
||||
context *Context
|
||||
config interface{}
|
||||
module *moduleInfo
|
||||
errs []error
|
||||
visitingParent *moduleInfo
|
||||
visitingDep depInfo
|
||||
}
|
||||
|
||||
func (d *baseModuleContext) moduleInfo() *moduleInfo {
|
||||
|
@ -255,11 +256,11 @@ func (m *baseModuleContext) OtherModuleErrorf(logicModule Module, format string,
|
|||
|
||||
func (m *baseModuleContext) OtherModuleDependencyTag(logicModule Module) DependencyTag {
|
||||
// fast path for calling OtherModuleDependencyTag from inside VisitDirectDeps
|
||||
if logicModule == m.visiting.module.logicModule {
|
||||
return m.visiting.tag
|
||||
if logicModule == m.visitingDep.module.logicModule {
|
||||
return m.visitingDep.tag
|
||||
}
|
||||
|
||||
for _, dep := range m.module.directDeps {
|
||||
for _, dep := range m.visitingParent.directDeps {
|
||||
if dep.module.logicModule == logicModule {
|
||||
return dep.tag
|
||||
}
|
||||
|
@ -272,46 +273,91 @@ func (m *baseModuleContext) VisitDirectDeps(visit func(Module)) {
|
|||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
panic(newPanicErrorf(r, "VisitDirectDeps(%s, %s) for dependency %s",
|
||||
m.module, funcName(visit), m.visiting.module))
|
||||
m.module, funcName(visit), m.visitingDep.module))
|
||||
}
|
||||
}()
|
||||
|
||||
m.visitingParent = m.module
|
||||
|
||||
for _, dep := range m.module.directDeps {
|
||||
m.visiting = dep
|
||||
m.visitingDep = dep
|
||||
visit(dep.module.logicModule)
|
||||
}
|
||||
m.visiting = depInfo{}
|
||||
|
||||
m.visitingParent = nil
|
||||
m.visitingDep = depInfo{}
|
||||
}
|
||||
|
||||
func (m *baseModuleContext) VisitDirectDepsIf(pred func(Module) bool, visit func(Module)) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
panic(newPanicErrorf(r, "VisitDirectDepsIf(%s, %s, %s) for dependency %s",
|
||||
m.module, funcName(pred), funcName(visit), m.visiting.module))
|
||||
m.module, funcName(pred), funcName(visit), m.visitingDep.module))
|
||||
}
|
||||
}()
|
||||
|
||||
m.visitingParent = m.module
|
||||
|
||||
for _, dep := range m.module.directDeps {
|
||||
m.visiting = dep
|
||||
m.visitingDep = dep
|
||||
if pred(dep.module.logicModule) {
|
||||
visit(dep.module.logicModule)
|
||||
}
|
||||
}
|
||||
m.visiting = depInfo{}
|
||||
|
||||
m.visitingParent = nil
|
||||
m.visitingDep = depInfo{}
|
||||
}
|
||||
|
||||
func (m *baseModuleContext) VisitDepsDepthFirst(visit func(Module)) {
|
||||
m.context.visitDepsDepthFirst(m.module, visit)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
panic(newPanicErrorf(r, "VisitDepsDepthFirst(%s, %s) for dependency %s",
|
||||
m.module, funcName(visit), m.visitingDep.module))
|
||||
}
|
||||
}()
|
||||
|
||||
m.context.walkDeps(m.module, nil, func(dep depInfo, parent *moduleInfo) {
|
||||
m.visitingParent = parent
|
||||
m.visitingDep = dep
|
||||
visit(dep.module.logicModule)
|
||||
})
|
||||
|
||||
m.visitingParent = nil
|
||||
m.visitingDep = depInfo{}
|
||||
}
|
||||
|
||||
func (m *baseModuleContext) VisitDepsDepthFirstIf(pred func(Module) bool,
|
||||
visit func(Module)) {
|
||||
|
||||
m.context.visitDepsDepthFirstIf(m.module, pred, visit)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
panic(newPanicErrorf(r, "VisitDepsDepthFirstIf(%s, %s, %s) for dependency %s",
|
||||
m.module, funcName(pred), funcName(visit), m.visitingDep.module))
|
||||
}
|
||||
}()
|
||||
|
||||
m.context.walkDeps(m.module, nil, func(dep depInfo, parent *moduleInfo) {
|
||||
if pred(dep.module.logicModule) {
|
||||
m.visitingParent = parent
|
||||
m.visitingDep = dep
|
||||
visit(dep.module.logicModule)
|
||||
}
|
||||
})
|
||||
|
||||
m.visitingParent = nil
|
||||
m.visitingDep = depInfo{}
|
||||
}
|
||||
|
||||
func (m *baseModuleContext) WalkDeps(visit func(Module, Module) bool) {
|
||||
m.context.walkDeps(m.module, visit)
|
||||
m.context.walkDeps(m.module, func(dep depInfo, parent *moduleInfo) bool {
|
||||
m.visitingParent = parent
|
||||
m.visitingDep = dep
|
||||
return visit(dep.module.logicModule, parent.logicModule)
|
||||
}, nil)
|
||||
|
||||
m.visitingParent = nil
|
||||
m.visitingDep = depInfo{}
|
||||
}
|
||||
|
||||
func (m *moduleContext) ModuleSubDir() string {
|
||||
|
|
154
visit_test.go
Normal file
154
visit_test.go
Normal file
|
@ -0,0 +1,154 @@
|
|||
// Copyright 2016 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"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type visitModule struct {
|
||||
properties struct {
|
||||
Visit []string
|
||||
VisitDepsDepthFirst string `blueprint:"mutated"`
|
||||
VisitDepsDepthFirstIf string `blueprint:"mutated"`
|
||||
VisitDirectDeps string `blueprint:"mutated"`
|
||||
VisitDirectDepsIf string `blueprint:"mutated"`
|
||||
}
|
||||
}
|
||||
|
||||
func newVisitModule() (Module, []interface{}) {
|
||||
m := &visitModule{}
|
||||
return m, []interface{}{&m.properties}
|
||||
}
|
||||
|
||||
func (f *visitModule) GenerateBuildActions(ModuleContext) {
|
||||
}
|
||||
|
||||
type visitTag struct {
|
||||
BaseDependencyTag
|
||||
}
|
||||
|
||||
var visitTagDep visitTag
|
||||
|
||||
func visitDepsMutator(ctx BottomUpMutatorContext) {
|
||||
if m, ok := ctx.Module().(*visitModule); ok {
|
||||
ctx.AddDependency(ctx.Module(), visitTagDep, m.properties.Visit...)
|
||||
}
|
||||
}
|
||||
|
||||
func visitMutator(ctx TopDownMutatorContext) {
|
||||
if m, ok := ctx.Module().(*visitModule); ok {
|
||||
ctx.VisitDepsDepthFirst(func(dep Module) {
|
||||
if ctx.OtherModuleDependencyTag(dep) != visitTagDep {
|
||||
panic(fmt.Errorf("unexpected dependency tag on %q", ctx.OtherModuleName(dep)))
|
||||
}
|
||||
m.properties.VisitDepsDepthFirst = m.properties.VisitDepsDepthFirst + ctx.OtherModuleName(dep)
|
||||
})
|
||||
ctx.VisitDepsDepthFirstIf(func(dep Module) bool {
|
||||
return ctx.OtherModuleName(dep) != "B"
|
||||
}, func(dep Module) {
|
||||
m.properties.VisitDepsDepthFirstIf = m.properties.VisitDepsDepthFirstIf + ctx.OtherModuleName(dep)
|
||||
})
|
||||
ctx.VisitDirectDeps(func(dep Module) {
|
||||
m.properties.VisitDirectDeps = m.properties.VisitDirectDeps + ctx.OtherModuleName(dep)
|
||||
})
|
||||
ctx.VisitDirectDepsIf(func(dep Module) bool {
|
||||
return ctx.OtherModuleName(dep) != "B"
|
||||
}, func(dep Module) {
|
||||
m.properties.VisitDirectDepsIf = m.properties.VisitDirectDepsIf + ctx.OtherModuleName(dep)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// A
|
||||
// |
|
||||
// B
|
||||
// |\
|
||||
// C \
|
||||
// \|
|
||||
// D
|
||||
// |
|
||||
// E
|
||||
func setupVisitTest(t *testing.T) *Context {
|
||||
ctx := NewContext()
|
||||
ctx.RegisterModuleType("visit_module", newVisitModule)
|
||||
ctx.RegisterBottomUpMutator("visit_deps", visitDepsMutator)
|
||||
ctx.RegisterTopDownMutator("visit", visitMutator)
|
||||
|
||||
ctx.MockFileSystem(map[string][]byte{
|
||||
"Blueprints": []byte(`
|
||||
visit_module {
|
||||
name: "A",
|
||||
visit: ["B"],
|
||||
}
|
||||
|
||||
visit_module {
|
||||
name: "B",
|
||||
visit: ["C", "D"],
|
||||
}
|
||||
|
||||
visit_module {
|
||||
name: "C",
|
||||
visit: ["D"],
|
||||
}
|
||||
|
||||
visit_module {
|
||||
name: "D",
|
||||
visit: ["E"],
|
||||
}
|
||||
|
||||
visit_module {
|
||||
name: "E",
|
||||
}
|
||||
`),
|
||||
})
|
||||
|
||||
_, errs := ctx.ParseBlueprintsFiles("Blueprints")
|
||||
if len(errs) > 0 {
|
||||
t.Errorf("unexpected parse errors:")
|
||||
for _, err := range errs {
|
||||
t.Errorf(" %s", err)
|
||||
}
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
errs = ctx.ResolveDependencies(nil)
|
||||
if len(errs) > 0 {
|
||||
t.Errorf("unexpected dep errors:")
|
||||
for _, err := range errs {
|
||||
t.Errorf(" %s", err)
|
||||
}
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
return ctx
|
||||
}
|
||||
|
||||
func TestVisit(t *testing.T) {
|
||||
ctx := setupVisitTest(t)
|
||||
|
||||
topModule := ctx.moduleGroups["A"].modules[0].logicModule.(*visitModule)
|
||||
assertString(t, topModule.properties.VisitDepsDepthFirst, "EDCB")
|
||||
assertString(t, topModule.properties.VisitDepsDepthFirstIf, "EDC")
|
||||
assertString(t, topModule.properties.VisitDirectDeps, "B")
|
||||
assertString(t, topModule.properties.VisitDirectDepsIf, "")
|
||||
}
|
||||
|
||||
func assertString(t *testing.T, got, expected string) {
|
||||
if got != expected {
|
||||
t.Errorf("expected %q got %q", expected, got)
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue