Import globbing from Soong

Add globbing with dependency checking to blueprint.  Calling
ModuleContext.GlobWithDeps or SingletonContext.GlobWithDeps will return
a list of files that match the globs, while also adding efficient
dependencies to rerun the primary builder if a file that matches the
glob is added or removed.

Also use the globbing support for optional_subdirs=, subdirs= and build=
lines in blueprints files.  The globbing slightly changes the behavior
of subname= lines, it no longer falls back to looking for a file called
"Blueprints".  Blueprint files that need to include a subdirectory with
a different name can use build= instead of subdir= to directly include
them.  The Blueprints file is updated to reset subname="Blueprints" in
case we want to include subdirectories inside blueprint and the primary
builder has changed the subname.

Also adds a new test directory that contains a simple primary builder
tree to test regeneration for globbing, and runs the tests in travis.

Change-Id: I83ce525fd11e11579cc58ba5308d01ca8eea7bc6
This commit is contained in:
Colin Cross 2016-11-01 11:10:51 -07:00
parent b589835c0d
commit 127d2eae8b
18 changed files with 1012 additions and 109 deletions

1
.gitignore vendored
View file

@ -1 +1,2 @@
out.test
src.test

View file

@ -23,3 +23,4 @@ script:
- ./blueprint.bash
- diff -us ../build.ninja.in .minibootstrap/build.ninja.in
- ../tests/test.sh
- ../tests/test_tree_tests.sh

View file

@ -9,6 +9,7 @@ bootstrap_go_package(
srcs = [
"context.go",
"fs.go",
"glob.go",
"live_tracker.go",
"mangle.go",
"module_ctx.go",
@ -55,6 +56,9 @@ bootstrap_go_package(
bootstrap_go_package(
name = "blueprint-pathtools",
pkgPath = "github.com/google/blueprint/pathtools",
deps = [
"blueprint-deptools",
],
srcs = [
"pathtools/lists.go",
"pathtools/glob.go",
@ -97,6 +101,7 @@ bootstrap_go_package(
"bootstrap/command.go",
"bootstrap/config.go",
"bootstrap/doc.go",
"bootstrap/glob.go",
"bootstrap/writedocs.go",
],
)
@ -122,6 +127,12 @@ bootstrap_core_go_binary(
srcs = ["bootstrap/minibp/main.go"],
)
bootstrap_core_go_binary(
name = "bpglob",
deps = ["blueprint-pathtools"],
srcs = ["bootstrap/bpglob/bpglob.go"],
)
blueprint_go_binary(
name = "bpfmt",
deps = ["blueprint-parser"],

View file

@ -0,0 +1,77 @@
// Copyright 2015 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.
// bpglob is the command line tool that checks if the list of files matching a glob has
// changed, and only updates the output file list if it has changed. It is used to optimize
// out build.ninja regenerations when non-matching files are added. See
// github.com/google/blueprint/bootstrap/glob.go for a longer description.
package main
import (
"flag"
"fmt"
"os"
"github.com/google/blueprint/pathtools"
)
var (
out = flag.String("o", "", "file to write list of files that match glob")
excludes multiArg
)
func init() {
flag.Var(&excludes, "e", "pattern to exclude from results")
}
type multiArg []string
func (m *multiArg) String() string {
return `""`
}
func (m *multiArg) Set(s string) error {
*m = append(*m, s)
return nil
}
func (m *multiArg) Get() interface{} {
return m
}
func usage() {
fmt.Fprintf(os.Stderr, "usage: bpglob -o out glob\n")
flag.PrintDefaults()
os.Exit(2)
}
func main() {
flag.Parse()
if *out == "" {
fmt.Fprintf(os.Stderr, "error: -o is required\n")
usage()
}
if flag.NArg() != 1 {
usage()
}
_, err := pathtools.GlobWithDepFile(flag.Arg(0), *out, *out+".d", excludes)
if err != nil {
fmt.Fprintf(os.Stderr, "error: %s\n", err.Error())
os.Exit(1)
}
}

View file

@ -117,6 +117,8 @@ func Main(ctx *blueprint.Context, config interface{}, extraNinjaFileDeps ...stri
ctx.RegisterTopDownMutator("bootstrap_stage", propagateStageBootstrap)
ctx.RegisterSingletonType("bootstrap", newSingletonFactory(bootstrapConfig))
ctx.RegisterSingletonType("glob", globSingletonFactory(ctx))
deps, errs := ctx.ParseBlueprintsFiles(bootstrapConfig.topLevelBlueprintsFile)
if len(errs) > 0 {
fatalErrors(errs)

144
bootstrap/glob.go Normal file
View file

@ -0,0 +1,144 @@
// 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 bootstrap
import (
"fmt"
"path/filepath"
"strings"
"github.com/google/blueprint"
"github.com/google/blueprint/deptools"
"github.com/google/blueprint/pathtools"
)
// This file supports globbing source files in Blueprints files.
//
// The build.ninja file needs to be regenerated any time a file matching the glob is added
// or removed. The naive solution is to have the build.ninja file depend on all the
// traversed directories, but this will cause the regeneration step to run every time a
// non-matching file is added to a traversed directory, including backup files created by
// editors.
//
// The solution implemented here optimizes out regenerations when the directory modifications
// don't match the glob by having the build.ninja file depend on an intermedate file that
// is only updated when a file matching the glob is added or removed. The intermediate file
// depends on the traversed directories via a depfile. The depfile is used to avoid build
// errors if a directory is deleted - a direct dependency on the deleted directory would result
// in a build failure with a "missing and no known rule to make it" error.
var (
globCmd = filepath.Join("$BinDir", "bpglob")
// globRule rule traverses directories to produce a list of files that match $glob
// and writes it to $out if it has changed, and writes the directories to $out.d
GlobRule = pctx.StaticRule("GlobRule",
blueprint.RuleParams{
Command: fmt.Sprintf(`%s -o $out $excludes "$glob"`, globCmd),
CommandDeps: []string{globCmd},
Description: "glob $glob",
Restat: true,
Deps: blueprint.DepsGCC,
Depfile: "$out.d",
},
"glob", "excludes")
)
// GlobFileContext is the subset of ModuleContext and SingletonContext needed by GlobFile
type GlobFileContext interface {
Build(pctx blueprint.PackageContext, params blueprint.BuildParams)
}
// GlobFile creates a rule to write to fileListFile a list of the files that match the specified
// pattern but do not match any of the patterns specified in excludes. The file will include
// appropriate dependencies written to depFile to regenerate the file if and only if the list of
// matching files has changed.
func GlobFile(ctx GlobFileContext, pattern string, excludes []string,
fileListFile, depFile string) {
ctx.Build(pctx, blueprint.BuildParams{
Rule: GlobRule,
Outputs: []string{fileListFile},
Args: map[string]string{
"glob": pattern,
"excludes": joinWithPrefixAndQuote(excludes, "-e "),
},
})
}
func joinWithPrefixAndQuote(strs []string, prefix string) string {
if len(strs) == 0 {
return ""
}
if len(strs) == 1 {
return prefix + `"` + strs[0] + `"`
}
n := len(" ") * (len(strs) - 1)
for _, s := range strs {
n += len(prefix) + len(s) + len(`""`)
}
ret := make([]byte, 0, n)
for i, s := range strs {
if i != 0 {
ret = append(ret, ' ')
}
ret = append(ret, prefix...)
ret = append(ret, '"')
ret = append(ret, s...)
ret = append(ret, '"')
}
return string(ret)
}
// globSingleton collects any glob patterns that were seen by Context and writes out rules to
// re-evaluate them whenever the contents of the searched directories change, and retrigger the
// primary builder if the results change.
type globSingleton struct {
globLister func() []blueprint.GlobPath
}
func globSingletonFactory(ctx *blueprint.Context) func() blueprint.Singleton {
return func() blueprint.Singleton {
return &globSingleton{
globLister: ctx.Globs,
}
}
}
func (s *globSingleton) GenerateBuildActions(ctx blueprint.SingletonContext) {
if config, ok := ctx.Config().(ConfigInterface); ok && config.GeneratingBootstrapper() {
// Skip singleton for bootstrap.bash -r case to avoid putting unnecessary glob lines into
// the bootstrap manifest
return
}
for _, g := range s.globLister() {
fileListFile := filepath.Join(BuildDir, ".glob", g.Name)
depFile := fileListFile + ".d"
fileList := strings.Join(g.Files, "\n") + "\n"
pathtools.WriteFileIfChanged(fileListFile, []byte(fileList), 0666)
deptools.WriteDepFile(depFile, fileListFile, g.Deps)
GlobFile(ctx, g.Pattern, g.Excludes, fileListFile, depFile)
// Make build.ninja depend on the fileListFile
ctx.AddNinjaFileDeps(fileListFile)
}
}

View file

@ -58,8 +58,9 @@ 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}/fs.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}/glob.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 $
@ -67,9 +68,10 @@ build $
${g.bootstrap.srcDir}/singleton_ctx.go ${g.bootstrap.srcDir}/unpack.go $
| ${g.bootstrap.compileCmd} $
${g.bootstrap.buildDir}/.bootstrap/blueprint-parser/pkg/github.com/google/blueprint/parser.a $
${g.bootstrap.buildDir}/.bootstrap/blueprint-deptools/pkg/github.com/google/blueprint/deptools.a $
${g.bootstrap.buildDir}/.bootstrap/blueprint-pathtools/pkg/github.com/google/blueprint/pathtools.a $
${g.bootstrap.buildDir}/.bootstrap/blueprint-proptools/pkg/github.com/google/blueprint/proptools.a
incFlags = -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-parser/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-pathtools/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-proptools/pkg
incFlags = -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-parser/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-deptools/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-pathtools/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-proptools/pkg
pkgPath = github.com/google/blueprint
default $
${g.bootstrap.buildDir}/.bootstrap/blueprint/pkg/github.com/google/blueprint.a
@ -79,7 +81,7 @@ default $
# Variant:
# Type: bootstrap_go_package
# Factory: github.com/google/blueprint/bootstrap.newGoPackageModuleFactory.func1
# Defined: Blueprints:85:1
# Defined: Blueprints:89:1
build $
${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap/pkg/github.com/google/blueprint/bootstrap.a $
@ -88,15 +90,16 @@ build $
${g.bootstrap.srcDir}/bootstrap/command.go $
${g.bootstrap.srcDir}/bootstrap/config.go $
${g.bootstrap.srcDir}/bootstrap/doc.go $
${g.bootstrap.srcDir}/bootstrap/glob.go $
${g.bootstrap.srcDir}/bootstrap/writedocs.go | $
${g.bootstrap.compileCmd} $
${g.bootstrap.buildDir}/.bootstrap/blueprint-parser/pkg/github.com/google/blueprint/parser.a $
${g.bootstrap.buildDir}/.bootstrap/blueprint-deptools/pkg/github.com/google/blueprint/deptools.a $
${g.bootstrap.buildDir}/.bootstrap/blueprint-pathtools/pkg/github.com/google/blueprint/pathtools.a $
${g.bootstrap.buildDir}/.bootstrap/blueprint-proptools/pkg/github.com/google/blueprint/proptools.a $
${g.bootstrap.buildDir}/.bootstrap/blueprint/pkg/github.com/google/blueprint.a $
${g.bootstrap.buildDir}/.bootstrap/blueprint-deptools/pkg/github.com/google/blueprint/deptools.a $
${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap-bpdoc/pkg/github.com/google/blueprint/bootstrap/bpdoc.a
incFlags = -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-parser/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-pathtools/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-proptools/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-deptools/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap-bpdoc/pkg
incFlags = -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-parser/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-deptools/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-pathtools/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-proptools/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap-bpdoc/pkg
pkgPath = github.com/google/blueprint/bootstrap
default $
${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap/pkg/github.com/google/blueprint/bootstrap.a
@ -106,17 +109,18 @@ default $
# Variant:
# Type: bootstrap_go_package
# Factory: github.com/google/blueprint/bootstrap.newGoPackageModuleFactory.func1
# Defined: Blueprints:104:1
# Defined: Blueprints:109:1
build $
${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap-bpdoc/pkg/github.com/google/blueprint/bootstrap/bpdoc.a $
: g.bootstrap.compile ${g.bootstrap.srcDir}/bootstrap/bpdoc/bpdoc.go | $
${g.bootstrap.compileCmd} $
${g.bootstrap.buildDir}/.bootstrap/blueprint-parser/pkg/github.com/google/blueprint/parser.a $
${g.bootstrap.buildDir}/.bootstrap/blueprint-deptools/pkg/github.com/google/blueprint/deptools.a $
${g.bootstrap.buildDir}/.bootstrap/blueprint-pathtools/pkg/github.com/google/blueprint/pathtools.a $
${g.bootstrap.buildDir}/.bootstrap/blueprint-proptools/pkg/github.com/google/blueprint/proptools.a $
${g.bootstrap.buildDir}/.bootstrap/blueprint/pkg/github.com/google/blueprint.a
incFlags = -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-parser/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-pathtools/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-proptools/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint/pkg
incFlags = -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-parser/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-deptools/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-pathtools/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-proptools/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint/pkg
pkgPath = github.com/google/blueprint/bootstrap/bpdoc
default $
${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap-bpdoc/pkg/github.com/google/blueprint/bootstrap/bpdoc.a
@ -126,7 +130,7 @@ default $
# Variant:
# Type: bootstrap_go_package
# Factory: github.com/google/blueprint/bootstrap.newGoPackageModuleFactory.func1
# Defined: Blueprints:49:1
# Defined: Blueprints:50:1
build $
${g.bootstrap.buildDir}/.bootstrap/blueprint-deptools/pkg/github.com/google/blueprint/deptools.a $
@ -141,7 +145,7 @@ default $
# Variant:
# Type: bootstrap_go_package
# Factory: github.com/google/blueprint/bootstrap.newGoPackageModuleFactory.func1
# Defined: Blueprints:33:1
# Defined: Blueprints:34:1
build $
${g.bootstrap.buildDir}/.bootstrap/blueprint-parser/pkg/github.com/google/blueprint/parser.a $
@ -159,12 +163,14 @@ default $
# Variant:
# Type: bootstrap_go_package
# Factory: github.com/google/blueprint/bootstrap.newGoPackageModuleFactory.func1
# Defined: Blueprints:55:1
# Defined: Blueprints:56:1
build $
${g.bootstrap.buildDir}/.bootstrap/blueprint-pathtools/pkg/github.com/google/blueprint/pathtools.a $
: g.bootstrap.compile ${g.bootstrap.srcDir}/pathtools/lists.go $
${g.bootstrap.srcDir}/pathtools/glob.go | ${g.bootstrap.compileCmd}
${g.bootstrap.srcDir}/pathtools/glob.go | ${g.bootstrap.compileCmd} $
${g.bootstrap.buildDir}/.bootstrap/blueprint-deptools/pkg/github.com/google/blueprint/deptools.a
incFlags = -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-deptools/pkg
pkgPath = github.com/google/blueprint/pathtools
default $
${g.bootstrap.buildDir}/.bootstrap/blueprint-pathtools/pkg/github.com/google/blueprint/pathtools.a
@ -174,7 +180,7 @@ default $
# Variant:
# Type: bootstrap_go_package
# Factory: github.com/google/blueprint/bootstrap.newGoPackageModuleFactory.func1
# Defined: Blueprints:67:1
# Defined: Blueprints:71:1
build $
${g.bootstrap.buildDir}/.bootstrap/blueprint-proptools/pkg/github.com/google/blueprint/proptools.a $
@ -188,12 +194,38 @@ build $
default $
${g.bootstrap.buildDir}/.bootstrap/blueprint-proptools/pkg/github.com/google/blueprint/proptools.a
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Module: bpglob
# Variant:
# Type: bootstrap_core_go_binary
# Factory: github.com/google/blueprint/bootstrap.newGoBinaryModuleFactory.func1
# Defined: Blueprints:130:1
build ${g.bootstrap.buildDir}/.bootstrap/bpglob/obj/bpglob.a: $
g.bootstrap.compile ${g.bootstrap.srcDir}/bootstrap/bpglob/bpglob.go | $
${g.bootstrap.compileCmd} $
${g.bootstrap.buildDir}/.bootstrap/blueprint-deptools/pkg/github.com/google/blueprint/deptools.a $
${g.bootstrap.buildDir}/.bootstrap/blueprint-pathtools/pkg/github.com/google/blueprint/pathtools.a
incFlags = -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-deptools/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-pathtools/pkg
pkgPath = bpglob
default ${g.bootstrap.buildDir}/.bootstrap/bpglob/obj/bpglob.a
build ${g.bootstrap.buildDir}/.bootstrap/bpglob/obj/a.out: g.bootstrap.link $
${g.bootstrap.buildDir}/.bootstrap/bpglob/obj/bpglob.a | $
${g.bootstrap.linkCmd}
libDirFlags = -L ${g.bootstrap.buildDir}/.bootstrap/blueprint-deptools/pkg -L ${g.bootstrap.buildDir}/.bootstrap/blueprint-pathtools/pkg
default ${g.bootstrap.buildDir}/.bootstrap/bpglob/obj/a.out
build ${g.bootstrap.BinDir}/bpglob: g.bootstrap.cp $
${g.bootstrap.buildDir}/.bootstrap/bpglob/obj/a.out
default ${g.bootstrap.BinDir}/bpglob
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Module: gotestmain
# Variant:
# Type: bootstrap_core_go_binary
# Factory: github.com/google/blueprint/bootstrap.newGoBinaryModuleFactory.func1
# Defined: Blueprints:137:1
# Defined: Blueprints:148:1
build ${g.bootstrap.buildDir}/.bootstrap/gotestmain/obj/gotestmain.a: $
g.bootstrap.compile ${g.bootstrap.srcDir}/gotestmain/gotestmain.go | $
@ -216,7 +248,7 @@ default ${g.bootstrap.BinDir}/gotestmain
# Variant:
# Type: bootstrap_core_go_binary
# Factory: github.com/google/blueprint/bootstrap.newGoBinaryModuleFactory.func1
# Defined: Blueprints:142:1
# Defined: Blueprints:153:1
build ${g.bootstrap.buildDir}/.bootstrap/gotestrunner/obj/gotestrunner.a: $
g.bootstrap.compile ${g.bootstrap.srcDir}/gotestrunner/gotestrunner.go $
@ -239,26 +271,26 @@ default ${g.bootstrap.BinDir}/gotestrunner
# Variant:
# Type: bootstrap_core_go_binary
# Factory: github.com/google/blueprint/bootstrap.newGoBinaryModuleFactory.func1
# Defined: Blueprints:116:1
# Defined: Blueprints:121:1
build ${g.bootstrap.buildDir}/.bootstrap/minibp/obj/minibp.a: $
g.bootstrap.compile ${g.bootstrap.srcDir}/bootstrap/minibp/main.go | $
${g.bootstrap.compileCmd} $
${g.bootstrap.buildDir}/.bootstrap/blueprint-parser/pkg/github.com/google/blueprint/parser.a $
${g.bootstrap.buildDir}/.bootstrap/blueprint-deptools/pkg/github.com/google/blueprint/deptools.a $
${g.bootstrap.buildDir}/.bootstrap/blueprint-pathtools/pkg/github.com/google/blueprint/pathtools.a $
${g.bootstrap.buildDir}/.bootstrap/blueprint-proptools/pkg/github.com/google/blueprint/proptools.a $
${g.bootstrap.buildDir}/.bootstrap/blueprint/pkg/github.com/google/blueprint.a $
${g.bootstrap.buildDir}/.bootstrap/blueprint-deptools/pkg/github.com/google/blueprint/deptools.a $
${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap-bpdoc/pkg/github.com/google/blueprint/bootstrap/bpdoc.a $
${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap/pkg/github.com/google/blueprint/bootstrap.a
incFlags = -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-parser/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-pathtools/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-proptools/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-deptools/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap-bpdoc/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap/pkg
incFlags = -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-parser/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-deptools/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-pathtools/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-proptools/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap-bpdoc/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap/pkg
pkgPath = minibp
default ${g.bootstrap.buildDir}/.bootstrap/minibp/obj/minibp.a
build ${g.bootstrap.buildDir}/.bootstrap/minibp/obj/a.out: g.bootstrap.link $
${g.bootstrap.buildDir}/.bootstrap/minibp/obj/minibp.a | $
${g.bootstrap.linkCmd}
libDirFlags = -L ${g.bootstrap.buildDir}/.bootstrap/blueprint-parser/pkg -L ${g.bootstrap.buildDir}/.bootstrap/blueprint-pathtools/pkg -L ${g.bootstrap.buildDir}/.bootstrap/blueprint-proptools/pkg -L ${g.bootstrap.buildDir}/.bootstrap/blueprint/pkg -L ${g.bootstrap.buildDir}/.bootstrap/blueprint-deptools/pkg -L ${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap-bpdoc/pkg -L ${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap/pkg
libDirFlags = -L ${g.bootstrap.buildDir}/.bootstrap/blueprint-parser/pkg -L ${g.bootstrap.buildDir}/.bootstrap/blueprint-deptools/pkg -L ${g.bootstrap.buildDir}/.bootstrap/blueprint-pathtools/pkg -L ${g.bootstrap.buildDir}/.bootstrap/blueprint-proptools/pkg -L ${g.bootstrap.buildDir}/.bootstrap/blueprint/pkg -L ${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap-bpdoc/pkg -L ${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap/pkg
default ${g.bootstrap.buildDir}/.bootstrap/minibp/obj/a.out
build ${g.bootstrap.BinDir}/minibp: g.bootstrap.cp $

View file

@ -25,6 +25,7 @@ import (
"sort"
"strconv"
"strings"
"sync"
"sync/atomic"
"text/scanner"
"text/template"
@ -105,6 +106,9 @@ type Context struct {
renames []rename
replacements []replace
globs map[string]GlobPath
globLock sync.Mutex
fs fileSystem
}
@ -270,6 +274,7 @@ func NewContext() *Context {
moduleNames: make(map[string]*moduleGroup),
moduleInfo: make(map[Module]*moduleInfo),
moduleNinjaNames: make(map[string]*moduleGroup),
globs: make(map[string]GlobPath),
fs: fs,
}
@ -524,12 +529,11 @@ func (c *Context) SetAllowMissingDependencies(allowMissingDependencies bool) {
// filename specifies the path to the Blueprints file. These paths are used for
// error reporting and for determining the module's directory.
func (c *Context) parse(rootDir, filename string, r io.Reader,
scope *parser.Scope) (file *parser.File, subBlueprints []stringAndScope, deps []string,
errs []error) {
scope *parser.Scope) (file *parser.File, subBlueprints []stringAndScope, errs []error) {
relBlueprintsFile, err := filepath.Rel(rootDir, filename)
if err != nil {
return nil, nil, nil, []error{err}
return nil, nil, []error{err}
}
scope = parser.NewScope(scope)
@ -550,7 +554,7 @@ func (c *Context) parse(rootDir, filename string, r io.Reader,
// If there were any parse errors don't bother trying to interpret the
// result.
return nil, nil, nil, errs
return nil, nil, errs
}
file.Name = relBlueprintsFile
@ -570,24 +574,28 @@ func (c *Context) parse(rootDir, filename string, r io.Reader,
}
subBlueprintsName, _, err := getStringFromScope(scope, "subname")
if err != nil {
errs = append(errs, err)
}
if subBlueprintsName == "" {
subBlueprintsName = "Blueprints"
}
var blueprints []string
newBlueprints, newDeps, newErrs := c.findBuildBlueprints(filepath.Dir(filename), build, buildPos)
newBlueprints, newErrs := c.findBuildBlueprints(filepath.Dir(filename), build, buildPos)
blueprints = append(blueprints, newBlueprints...)
deps = append(deps, newDeps...)
errs = append(errs, newErrs...)
newBlueprints, newDeps, newErrs = c.findSubdirBlueprints(filepath.Dir(filename), subdirs, subdirsPos,
newBlueprints, newErrs = c.findSubdirBlueprints(filepath.Dir(filename), subdirs, subdirsPos,
subBlueprintsName, false)
blueprints = append(blueprints, newBlueprints...)
deps = append(deps, newDeps...)
errs = append(errs, newErrs...)
newBlueprints, newDeps, newErrs = c.findSubdirBlueprints(filepath.Dir(filename), optionalSubdirs,
newBlueprints, newErrs = c.findSubdirBlueprints(filepath.Dir(filename), optionalSubdirs,
optionalSubdirsPos, subBlueprintsName, true)
blueprints = append(blueprints, newBlueprints...)
deps = append(deps, newDeps...)
errs = append(errs, newErrs...)
subBlueprintsAndScope := make([]stringAndScope, len(blueprints))
@ -595,7 +603,7 @@ func (c *Context) parse(rootDir, filename string, r io.Reader,
subBlueprintsAndScope[i] = stringAndScope{b, scope}
}
return file, subBlueprintsAndScope, deps, errs
return file, subBlueprintsAndScope, errs
}
type stringAndScope struct {
@ -790,7 +798,7 @@ func (c *Context) parseBlueprintsFile(filename string, scope *parser.Scope, root
}
}()
file, subBlueprints, deps, errs := c.parse(rootDir, filename, f, scope)
file, subBlueprints, errs := c.parse(rootDir, filename, f, scope)
if len(errs) > 0 {
errsCh <- errs
} else {
@ -799,22 +807,31 @@ func (c *Context) parseBlueprintsFile(filename string, scope *parser.Scope, root
for _, b := range subBlueprints {
blueprintsCh <- b
}
for _, d := range deps {
depsCh <- d
depsCh <- b.string
}
}
func (c *Context) findBuildBlueprints(dir string, build []string,
buildPos scanner.Position) (blueprints, deps []string, errs []error) {
buildPos scanner.Position) ([]string, []error) {
var blueprints []string
var errs []error
for _, file := range build {
globPattern := filepath.Join(dir, file)
matches, matchedDirs, err := pathtools.Glob(globPattern)
pattern := filepath.Join(dir, file)
var matches []string
var err error
if pathtools.IsGlob(pattern) {
matches, err = c.glob(pattern, nil)
} else {
// Not a glob, but use filepath.Glob to check if it exists
matches, err = filepath.Glob(pattern)
}
if err != nil {
errs = append(errs, &BlueprintError{
Err: fmt.Errorf("%q: %s", globPattern, err.Error()),
Err: fmt.Errorf("%q: %s", pattern, err.Error()),
Pos: buildPos,
})
continue
@ -822,47 +839,40 @@ func (c *Context) findBuildBlueprints(dir string, build []string,
if len(matches) == 0 {
errs = append(errs, &BlueprintError{
Err: fmt.Errorf("%q: not found", globPattern),
Err: fmt.Errorf("%q: not found", pattern),
Pos: buildPos,
})
}
// Depend on all searched directories so we pick up future changes.
deps = append(deps, matchedDirs...)
for _, foundBlueprints := range matches {
exists, dir, err := c.fs.Exists(foundBlueprints)
if err != nil {
errs = append(errs, err)
} else if !exists {
errs = append(errs, &BlueprintError{
Err: fmt.Errorf("%q not found", foundBlueprints),
})
continue
} else if dir {
errs = append(errs, &BlueprintError{
Err: fmt.Errorf("%q is a directory", foundBlueprints),
})
continue
}
blueprints = append(blueprints, foundBlueprints)
deps = append(deps, foundBlueprints)
}
}
return blueprints, deps, errs
return blueprints, errs
}
func (c *Context) findSubdirBlueprints(dir string, subdirs []string, subdirsPos scanner.Position,
subBlueprintsName string, optional bool) (blueprints, deps []string, errs []error) {
subBlueprintsName string, optional bool) ([]string, []error) {
var blueprints []string
var errs []error
for _, subdir := range subdirs {
globPattern := filepath.Join(dir, subdir)
matches, matchedDirs, err := pathtools.Glob(globPattern)
pattern := filepath.Join(dir, subdir, subBlueprintsName)
var matches []string
var err error
if pathtools.IsGlob(pattern) {
matches, err = c.glob(pattern, nil)
} else {
// Not a glob, but use filepath.Glob to check if it exists
matches, err = filepath.Glob(pattern)
}
if err != nil {
errs = append(errs, &BlueprintError{
Err: fmt.Errorf("%q: %s", globPattern, err.Error()),
Err: fmt.Errorf("%q: %s", pattern, err.Error()),
Pos: subdirsPos,
})
continue
@ -870,56 +880,17 @@ func (c *Context) findSubdirBlueprints(dir string, subdirs []string, subdirsPos
if len(matches) == 0 && !optional {
errs = append(errs, &BlueprintError{
Err: fmt.Errorf("%q: not found", globPattern),
Err: fmt.Errorf("%q: not found", pattern),
Pos: subdirsPos,
})
}
// Depend on all searched directories so we pick up future changes.
deps = append(deps, matchedDirs...)
for _, foundSubdir := range matches {
exists, dir, subdirStatErr := c.fs.Exists(foundSubdir)
if subdirStatErr != nil {
errs = append(errs, subdirStatErr)
continue
}
// Skip files
if !dir {
continue
}
var subBlueprints string
if subBlueprintsName != "" {
subBlueprints = filepath.Join(foundSubdir, subBlueprintsName)
exists, _, err = c.fs.Exists(subBlueprints)
}
if err == nil && (!exists || subBlueprints == "") {
subBlueprints = filepath.Join(foundSubdir, "Blueprints")
exists, _, err = c.fs.Exists(subBlueprints)
}
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
// future we'll pick it up.
deps = append(deps, foundSubdir)
} else {
deps = append(deps, subBlueprints)
blueprints = append(blueprints, subBlueprints)
}
for _, subBlueprints := range matches {
blueprints = append(blueprints, subBlueprints)
}
}
return blueprints, deps, errs
return blueprints, errs
}
func getLocalStringListFromScope(scope *parser.Scope, v string) ([]string, scanner.Position, error) {

View file

@ -95,7 +95,7 @@ func TestContextParse(t *testing.T) {
}
`)
_, _, _, errs := ctx.parse(".", "Blueprint", r, nil)
_, _, errs := ctx.parse(".", "Blueprint", r, nil)
if len(errs) > 0 {
t.Errorf("unexpected parse errors:")
for _, err := range errs {

116
glob.go Normal file
View file

@ -0,0 +1,116 @@
// Copyright 2015 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"
"reflect"
"sort"
"github.com/google/blueprint/pathtools"
)
type GlobPath struct {
Pattern string
Excludes []string
Files []string
Deps []string
Name string
}
func verifyGlob(fileName, pattern string, excludes []string, g GlobPath) {
if pattern != g.Pattern {
panic(fmt.Errorf("Mismatched patterns %q and %q for glob file %q", pattern, g.Pattern, fileName))
}
if !reflect.DeepEqual(g.Excludes, excludes) {
panic(fmt.Errorf("Mismatched excludes %v and %v for glob file %q", excludes, g.Excludes, fileName))
}
}
func (c *Context) glob(pattern string, excludes []string) ([]string, error) {
fileName := globToFileName(pattern, excludes)
// Try to get existing glob from the stored results
c.globLock.Lock()
g, exists := c.globs[fileName]
c.globLock.Unlock()
if exists {
// Glob has already been done, double check it is identical
verifyGlob(fileName, pattern, excludes, g)
return g.Files, nil
}
// Get a globbed file list
files, deps, err := pathtools.GlobWithExcludes(pattern, excludes)
if err != nil {
return nil, err
}
// Store the results
c.globLock.Lock()
if g, exists = c.globs[fileName]; !exists {
c.globs[fileName] = GlobPath{pattern, excludes, files, deps, fileName}
}
c.globLock.Unlock()
// Getting the list raced with another goroutine, throw away the results and use theirs
if exists {
verifyGlob(fileName, pattern, excludes, g)
return g.Files, nil
}
return files, nil
}
func (c *Context) Globs() []GlobPath {
fileNames := make([]string, 0, len(c.globs))
for k := range c.globs {
fileNames = append(fileNames, k)
}
sort.Strings(fileNames)
globs := make([]GlobPath, len(fileNames))
for i, fileName := range fileNames {
globs[i] = c.globs[fileName]
}
return globs
}
func globToString(pattern string) string {
ret := ""
for _, c := range pattern {
switch {
case c >= 'a' && c <= 'z',
c >= 'A' && c <= 'Z',
c >= '0' && c <= '9',
c == '_', c == '-', c == '/':
ret += string(c)
default:
ret += "_"
}
}
return ret
}
func globToFileName(pattern string, excludes []string) string {
ret := globToString(pattern)
for _, e := range excludes {
ret += "__" + globToString(e)
}
return ret + ".glob"
}

View file

@ -128,6 +128,12 @@ type BaseModuleContext interface {
PropertyErrorf(property, fmt string, args ...interface{})
Failed() bool
// GlobWithDeps returns a list of files that match the specified pattern but do not match any
// of the patterns in excludes. It also adds efficient dependencies to rerun the primary
// builder whenever a file matching the pattern as added or removed, without rerunning if a
// file that does not match the pattern is added to a searched directory.
GlobWithDeps(pattern string, excludes []string) ([]string, error)
moduleInfo() *moduleInfo
error(err error)
}
@ -246,6 +252,11 @@ func (d *baseModuleContext) Failed() bool {
return len(d.errs) > 0
}
func (d *baseModuleContext) GlobWithDeps(pattern string,
excludes []string) ([]string, error) {
return d.context.glob(pattern, excludes)
}
var _ ModuleContext = (*moduleContext)(nil)
type moduleContext struct {

View file

@ -16,9 +16,12 @@ package pathtools
import (
"errors"
"io/ioutil"
"os"
"path/filepath"
"strings"
"github.com/google/blueprint/deptools"
)
var GlobMultipleRecursiveErr = errors.New("pattern contains multiple **")
@ -28,7 +31,12 @@ var GlobLastRecursiveErr = errors.New("pattern ** as last path element")
// list of directories that were searched to construct the file list.
// The supported glob patterns are equivalent to filepath.Glob, with an
// extension that recursive glob (** matching zero or more complete path
// entries) is supported.
// entries) is supported. Glob also returns a list of directories that were
// searched.
//
// In general ModuleContext.GlobWithDeps or SingletonContext.GlobWithDeps
// should be used instead, as they will automatically set up dependencies
// to rerun the primary builder when the list of matching files changes.
func Glob(pattern string) (matches, dirs []string, err error) {
return GlobWithExcludes(pattern, nil)
}
@ -38,6 +46,11 @@ func Glob(pattern string) (matches, dirs []string, err error) {
// that were searched to construct the file list. The supported glob and
// exclude patterns are equivalent to filepath.Glob, with an extension that
// recursive glob (** matching zero or more complete path entries) is supported.
// GlobWIthExcludes also returns a list of directories that were searched.
//
// In general ModuleContext.GlobWithDeps or SingletonContext.GlobWithDeps
// should be used instead, as they will automatically set up dependencies
// to rerun the primary builder when the list of matching files changes.
func GlobWithExcludes(pattern string, excludes []string) (matches, dirs []string, err error) {
if filepath.Base(pattern) == "**" {
return nil, nil, GlobLastRecursiveErr
@ -287,3 +300,98 @@ func GlobPatternList(patterns []string, prefix string) (globedList []string, dep
}
return globedList, depDirs, nil
}
// IsGlob returns true if the pattern contains any glob characters (*, ?, or [).
func IsGlob(pattern string) bool {
return strings.IndexAny(pattern, "*?[") >= 0
}
// HasGlob returns true if any string in the list contains any glob characters (*, ?, or [).
func HasGlob(in []string) bool {
for _, s := range in {
if IsGlob(s) {
return true
}
}
return false
}
// GlobWithDepFile finds all files that match glob. It compares the list of files
// against the contents of fileListFile, and rewrites fileListFile if it has changed. It also
// writes all of the the directories it traversed as a depenencies on fileListFile to depFile.
//
// The format of glob is either path/*.ext for a single directory glob, or path/**/*.ext
// for a recursive glob.
//
// Returns a list of file paths, and an error.
//
// In general ModuleContext.GlobWithDeps or SingletonContext.GlobWithDeps
// should be used instead, as they will automatically set up dependencies
// to rerun the primary builder when the list of matching files changes.
func GlobWithDepFile(glob, fileListFile, depFile string, excludes []string) (files []string, err error) {
files, dirs, err := GlobWithExcludes(glob, excludes)
if err != nil {
return nil, err
}
fileList := strings.Join(files, "\n") + "\n"
WriteFileIfChanged(fileListFile, []byte(fileList), 0666)
deptools.WriteDepFile(depFile, fileListFile, dirs)
return
}
// WriteFileIfChanged wraps ioutil.WriteFile, but only writes the file if
// the files does not already exist with identical contents. This can be used
// along with ninja restat rules to skip rebuilding downstream rules if no
// changes were made by a rule.
func WriteFileIfChanged(filename string, data []byte, perm os.FileMode) error {
var isChanged bool
dir := filepath.Dir(filename)
err := os.MkdirAll(dir, 0777)
if err != nil {
return err
}
info, err := os.Stat(filename)
if err != nil {
if os.IsNotExist(err) {
// The file does not exist yet.
isChanged = true
} else {
return err
}
} else {
if info.Size() != int64(len(data)) {
isChanged = true
} else {
oldData, err := ioutil.ReadFile(filename)
if err != nil {
return err
}
if len(oldData) != len(data) {
isChanged = true
} else {
for i := range data {
if oldData[i] != data[i] {
isChanged = true
break
}
}
}
}
}
if isChanged {
err = ioutil.WriteFile(filename, data, perm)
if err != nil {
return err
}
}
return nil
}

View file

@ -62,6 +62,12 @@ type SingletonContext interface {
FinalModule(module Module) Module
AddNinjaFileDeps(deps ...string)
// GlobWithDeps returns a list of files that match the specified pattern but do not match any
// of the patterns in excludes. It also adds efficient dependencies to rerun the primary
// builder whenever a file matching the pattern as added or removed, without rerunning if a
// file that does not match the pattern is added to a searched directory.
GlobWithDeps(pattern string, excludes []string) ([]string, error)
}
var _ SingletonContext = (*singletonContext)(nil)
@ -228,3 +234,8 @@ func (s *singletonContext) VisitAllModuleVariants(module Module, visit func(Modu
func (s *singletonContext) AddNinjaFileDeps(deps ...string) {
s.ninjaFileDeps = append(s.ninjaFileDeps, deps...)
}
func (s *singletonContext) GlobWithDeps(pattern string,
excludes []string) ([]string, error) {
return s.context.glob(pattern, excludes)
}

View file

@ -0,0 +1,3 @@
// Root Blueprints file for test_tree
subdirs=["*"]

View file

@ -0,0 +1 @@
// a/Blueprints

1
tests/test_tree/blueprint Symbolic link
View file

@ -0,0 +1 @@
../..

View file

@ -0,0 +1,335 @@
# ******************************************************************************
# *** This file is generated and should not be edited ***
# ******************************************************************************
#
# This file contains variables, rules, and pools with name prefixes indicating
# they were generated by the following Go packages:
#
# bootstrap [from Go package github.com/google/blueprint/bootstrap]
#
ninja_required_version = 1.7.0
g.bootstrap.buildDir = @@BuildDir@@
g.bootstrap.BinDir = ${g.bootstrap.buildDir}/.bootstrap/bin
g.bootstrap.bootstrapCmd = @@Bootstrap@@
g.bootstrap.compileCmd = @@GoCompile@@
g.bootstrap.goRoot = @@GoRoot@@
g.bootstrap.linkCmd = @@GoLink@@
g.bootstrap.srcDir = @@SrcDir@@
builddir = ${g.bootstrap.buildDir}/.minibootstrap
rule g.bootstrap.bootstrap
command = BUILDDIR=${g.bootstrap.buildDir} ${g.bootstrap.bootstrapCmd} -i ${in}
description = bootstrap ${in}
generator = true
rule g.bootstrap.build.ninja
command = ${builder} ${extra} -b ${g.bootstrap.buildDir} -d ${out}.d -o ${out} ${in}
depfile = ${out}.d
description = ${builder} ${out}
restat = true
rule g.bootstrap.compile
command = GOROOT='${g.bootstrap.goRoot}' ${g.bootstrap.compileCmd} -o ${out} -p ${pkgPath} -complete ${incFlags} -pack ${in}
description = compile ${out}
rule g.bootstrap.cp
command = cp ${in} ${out}
description = cp ${out}
rule g.bootstrap.link
command = GOROOT='${g.bootstrap.goRoot}' ${g.bootstrap.linkCmd} -o ${out} ${libDirFlags} ${in}
description = link ${out}
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Module: blueprint
# Variant:
# Type: bootstrap_go_package
# Factory: github.com/google/blueprint/bootstrap.newGoPackageModuleFactory.func1
# Defined: blueprint/Blueprints:1:1
build $
${g.bootstrap.buildDir}/.bootstrap/blueprint/pkg/github.com/google/blueprint.a $
: g.bootstrap.compile ${g.bootstrap.srcDir}/blueprint/context.go $
${g.bootstrap.srcDir}/blueprint/fs.go $
${g.bootstrap.srcDir}/blueprint/glob.go $
${g.bootstrap.srcDir}/blueprint/live_tracker.go $
${g.bootstrap.srcDir}/blueprint/mangle.go $
${g.bootstrap.srcDir}/blueprint/module_ctx.go $
${g.bootstrap.srcDir}/blueprint/ninja_defs.go $
${g.bootstrap.srcDir}/blueprint/ninja_strings.go $
${g.bootstrap.srcDir}/blueprint/ninja_writer.go $
${g.bootstrap.srcDir}/blueprint/package_ctx.go $
${g.bootstrap.srcDir}/blueprint/scope.go $
${g.bootstrap.srcDir}/blueprint/singleton_ctx.go $
${g.bootstrap.srcDir}/blueprint/unpack.go | ${g.bootstrap.compileCmd} $
${g.bootstrap.buildDir}/.bootstrap/blueprint-parser/pkg/github.com/google/blueprint/parser.a $
${g.bootstrap.buildDir}/.bootstrap/blueprint-deptools/pkg/github.com/google/blueprint/deptools.a $
${g.bootstrap.buildDir}/.bootstrap/blueprint-pathtools/pkg/github.com/google/blueprint/pathtools.a $
${g.bootstrap.buildDir}/.bootstrap/blueprint-proptools/pkg/github.com/google/blueprint/proptools.a
incFlags = -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-parser/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-deptools/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-pathtools/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-proptools/pkg
pkgPath = github.com/google/blueprint
default $
${g.bootstrap.buildDir}/.bootstrap/blueprint/pkg/github.com/google/blueprint.a
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Module: blueprint-bootstrap
# Variant:
# Type: bootstrap_go_package
# Factory: github.com/google/blueprint/bootstrap.newGoPackageModuleFactory.func1
# Defined: blueprint/Blueprints:89:1
build $
${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap/pkg/github.com/google/blueprint/bootstrap.a $
: g.bootstrap.compile $
${g.bootstrap.srcDir}/blueprint/bootstrap/bootstrap.go $
${g.bootstrap.srcDir}/blueprint/bootstrap/cleanup.go $
${g.bootstrap.srcDir}/blueprint/bootstrap/command.go $
${g.bootstrap.srcDir}/blueprint/bootstrap/config.go $
${g.bootstrap.srcDir}/blueprint/bootstrap/doc.go $
${g.bootstrap.srcDir}/blueprint/bootstrap/glob.go $
${g.bootstrap.srcDir}/blueprint/bootstrap/writedocs.go | $
${g.bootstrap.compileCmd} $
${g.bootstrap.buildDir}/.bootstrap/blueprint-parser/pkg/github.com/google/blueprint/parser.a $
${g.bootstrap.buildDir}/.bootstrap/blueprint-deptools/pkg/github.com/google/blueprint/deptools.a $
${g.bootstrap.buildDir}/.bootstrap/blueprint-pathtools/pkg/github.com/google/blueprint/pathtools.a $
${g.bootstrap.buildDir}/.bootstrap/blueprint-proptools/pkg/github.com/google/blueprint/proptools.a $
${g.bootstrap.buildDir}/.bootstrap/blueprint/pkg/github.com/google/blueprint.a $
${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap-bpdoc/pkg/github.com/google/blueprint/bootstrap/bpdoc.a
incFlags = -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-parser/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-deptools/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-pathtools/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-proptools/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap-bpdoc/pkg
pkgPath = github.com/google/blueprint/bootstrap
default $
${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap/pkg/github.com/google/blueprint/bootstrap.a
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Module: blueprint-bootstrap-bpdoc
# Variant:
# Type: bootstrap_go_package
# Factory: github.com/google/blueprint/bootstrap.newGoPackageModuleFactory.func1
# Defined: blueprint/Blueprints:109:1
build $
${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap-bpdoc/pkg/github.com/google/blueprint/bootstrap/bpdoc.a $
: g.bootstrap.compile $
${g.bootstrap.srcDir}/blueprint/bootstrap/bpdoc/bpdoc.go | $
${g.bootstrap.compileCmd} $
${g.bootstrap.buildDir}/.bootstrap/blueprint-parser/pkg/github.com/google/blueprint/parser.a $
${g.bootstrap.buildDir}/.bootstrap/blueprint-deptools/pkg/github.com/google/blueprint/deptools.a $
${g.bootstrap.buildDir}/.bootstrap/blueprint-pathtools/pkg/github.com/google/blueprint/pathtools.a $
${g.bootstrap.buildDir}/.bootstrap/blueprint-proptools/pkg/github.com/google/blueprint/proptools.a $
${g.bootstrap.buildDir}/.bootstrap/blueprint/pkg/github.com/google/blueprint.a
incFlags = -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-parser/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-deptools/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-pathtools/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-proptools/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint/pkg
pkgPath = github.com/google/blueprint/bootstrap/bpdoc
default $
${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap-bpdoc/pkg/github.com/google/blueprint/bootstrap/bpdoc.a
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Module: blueprint-deptools
# Variant:
# Type: bootstrap_go_package
# Factory: github.com/google/blueprint/bootstrap.newGoPackageModuleFactory.func1
# Defined: blueprint/Blueprints:50:1
build $
${g.bootstrap.buildDir}/.bootstrap/blueprint-deptools/pkg/github.com/google/blueprint/deptools.a $
: g.bootstrap.compile $
${g.bootstrap.srcDir}/blueprint/deptools/depfile.go | $
${g.bootstrap.compileCmd}
pkgPath = github.com/google/blueprint/deptools
default $
${g.bootstrap.buildDir}/.bootstrap/blueprint-deptools/pkg/github.com/google/blueprint/deptools.a
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Module: blueprint-parser
# Variant:
# Type: bootstrap_go_package
# Factory: github.com/google/blueprint/bootstrap.newGoPackageModuleFactory.func1
# Defined: blueprint/Blueprints:34:1
build $
${g.bootstrap.buildDir}/.bootstrap/blueprint-parser/pkg/github.com/google/blueprint/parser.a $
: g.bootstrap.compile ${g.bootstrap.srcDir}/blueprint/parser/ast.go $
${g.bootstrap.srcDir}/blueprint/parser/modify.go $
${g.bootstrap.srcDir}/blueprint/parser/parser.go $
${g.bootstrap.srcDir}/blueprint/parser/printer.go $
${g.bootstrap.srcDir}/blueprint/parser/sort.go | $
${g.bootstrap.compileCmd}
pkgPath = github.com/google/blueprint/parser
default $
${g.bootstrap.buildDir}/.bootstrap/blueprint-parser/pkg/github.com/google/blueprint/parser.a
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Module: blueprint-pathtools
# Variant:
# Type: bootstrap_go_package
# Factory: github.com/google/blueprint/bootstrap.newGoPackageModuleFactory.func1
# Defined: blueprint/Blueprints:56:1
build $
${g.bootstrap.buildDir}/.bootstrap/blueprint-pathtools/pkg/github.com/google/blueprint/pathtools.a $
: g.bootstrap.compile $
${g.bootstrap.srcDir}/blueprint/pathtools/lists.go $
${g.bootstrap.srcDir}/blueprint/pathtools/glob.go | $
${g.bootstrap.compileCmd} $
${g.bootstrap.buildDir}/.bootstrap/blueprint-deptools/pkg/github.com/google/blueprint/deptools.a
incFlags = -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-deptools/pkg
pkgPath = github.com/google/blueprint/pathtools
default $
${g.bootstrap.buildDir}/.bootstrap/blueprint-pathtools/pkg/github.com/google/blueprint/pathtools.a
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Module: blueprint-proptools
# Variant:
# Type: bootstrap_go_package
# Factory: github.com/google/blueprint/bootstrap.newGoPackageModuleFactory.func1
# Defined: blueprint/Blueprints:71:1
build $
${g.bootstrap.buildDir}/.bootstrap/blueprint-proptools/pkg/github.com/google/blueprint/proptools.a $
: g.bootstrap.compile $
${g.bootstrap.srcDir}/blueprint/proptools/clone.go $
${g.bootstrap.srcDir}/blueprint/proptools/escape.go $
${g.bootstrap.srcDir}/blueprint/proptools/extend.go $
${g.bootstrap.srcDir}/blueprint/proptools/proptools.go $
${g.bootstrap.srcDir}/blueprint/proptools/typeequal.go | $
${g.bootstrap.compileCmd}
pkgPath = github.com/google/blueprint/proptools
default $
${g.bootstrap.buildDir}/.bootstrap/blueprint-proptools/pkg/github.com/google/blueprint/proptools.a
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Module: bpglob
# Variant:
# Type: bootstrap_core_go_binary
# Factory: github.com/google/blueprint/bootstrap.newGoBinaryModuleFactory.func1
# Defined: blueprint/Blueprints:130:1
build ${g.bootstrap.buildDir}/.bootstrap/bpglob/obj/bpglob.a: $
g.bootstrap.compile $
${g.bootstrap.srcDir}/blueprint/bootstrap/bpglob/bpglob.go | $
${g.bootstrap.compileCmd} $
${g.bootstrap.buildDir}/.bootstrap/blueprint-deptools/pkg/github.com/google/blueprint/deptools.a $
${g.bootstrap.buildDir}/.bootstrap/blueprint-pathtools/pkg/github.com/google/blueprint/pathtools.a
incFlags = -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-deptools/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-pathtools/pkg
pkgPath = bpglob
default ${g.bootstrap.buildDir}/.bootstrap/bpglob/obj/bpglob.a
build ${g.bootstrap.buildDir}/.bootstrap/bpglob/obj/a.out: g.bootstrap.link $
${g.bootstrap.buildDir}/.bootstrap/bpglob/obj/bpglob.a | $
${g.bootstrap.linkCmd}
libDirFlags = -L ${g.bootstrap.buildDir}/.bootstrap/blueprint-deptools/pkg -L ${g.bootstrap.buildDir}/.bootstrap/blueprint-pathtools/pkg
default ${g.bootstrap.buildDir}/.bootstrap/bpglob/obj/a.out
build ${g.bootstrap.BinDir}/bpglob: g.bootstrap.cp $
${g.bootstrap.buildDir}/.bootstrap/bpglob/obj/a.out
default ${g.bootstrap.BinDir}/bpglob
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Module: gotestmain
# Variant:
# Type: bootstrap_core_go_binary
# Factory: github.com/google/blueprint/bootstrap.newGoBinaryModuleFactory.func1
# Defined: blueprint/Blueprints:148:1
build ${g.bootstrap.buildDir}/.bootstrap/gotestmain/obj/gotestmain.a: $
g.bootstrap.compile $
${g.bootstrap.srcDir}/blueprint/gotestmain/gotestmain.go | $
${g.bootstrap.compileCmd}
pkgPath = gotestmain
default ${g.bootstrap.buildDir}/.bootstrap/gotestmain/obj/gotestmain.a
build ${g.bootstrap.buildDir}/.bootstrap/gotestmain/obj/a.out: $
g.bootstrap.link $
${g.bootstrap.buildDir}/.bootstrap/gotestmain/obj/gotestmain.a | $
${g.bootstrap.linkCmd}
default ${g.bootstrap.buildDir}/.bootstrap/gotestmain/obj/a.out
build ${g.bootstrap.BinDir}/gotestmain: g.bootstrap.cp $
${g.bootstrap.buildDir}/.bootstrap/gotestmain/obj/a.out
default ${g.bootstrap.BinDir}/gotestmain
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Module: gotestrunner
# Variant:
# Type: bootstrap_core_go_binary
# Factory: github.com/google/blueprint/bootstrap.newGoBinaryModuleFactory.func1
# Defined: blueprint/Blueprints:153:1
build ${g.bootstrap.buildDir}/.bootstrap/gotestrunner/obj/gotestrunner.a: $
g.bootstrap.compile $
${g.bootstrap.srcDir}/blueprint/gotestrunner/gotestrunner.go | $
${g.bootstrap.compileCmd}
pkgPath = gotestrunner
default ${g.bootstrap.buildDir}/.bootstrap/gotestrunner/obj/gotestrunner.a
build ${g.bootstrap.buildDir}/.bootstrap/gotestrunner/obj/a.out: $
g.bootstrap.link $
${g.bootstrap.buildDir}/.bootstrap/gotestrunner/obj/gotestrunner.a | $
${g.bootstrap.linkCmd}
default ${g.bootstrap.buildDir}/.bootstrap/gotestrunner/obj/a.out
build ${g.bootstrap.BinDir}/gotestrunner: g.bootstrap.cp $
${g.bootstrap.buildDir}/.bootstrap/gotestrunner/obj/a.out
default ${g.bootstrap.BinDir}/gotestrunner
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Module: minibp
# Variant:
# Type: bootstrap_core_go_binary
# Factory: github.com/google/blueprint/bootstrap.newGoBinaryModuleFactory.func1
# Defined: blueprint/Blueprints:121:1
build ${g.bootstrap.buildDir}/.bootstrap/minibp/obj/minibp.a: $
g.bootstrap.compile $
${g.bootstrap.srcDir}/blueprint/bootstrap/minibp/main.go | $
${g.bootstrap.compileCmd} $
${g.bootstrap.buildDir}/.bootstrap/blueprint-parser/pkg/github.com/google/blueprint/parser.a $
${g.bootstrap.buildDir}/.bootstrap/blueprint-deptools/pkg/github.com/google/blueprint/deptools.a $
${g.bootstrap.buildDir}/.bootstrap/blueprint-pathtools/pkg/github.com/google/blueprint/pathtools.a $
${g.bootstrap.buildDir}/.bootstrap/blueprint-proptools/pkg/github.com/google/blueprint/proptools.a $
${g.bootstrap.buildDir}/.bootstrap/blueprint/pkg/github.com/google/blueprint.a $
${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap-bpdoc/pkg/github.com/google/blueprint/bootstrap/bpdoc.a $
${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap/pkg/github.com/google/blueprint/bootstrap.a
incFlags = -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-parser/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-deptools/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-pathtools/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-proptools/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap-bpdoc/pkg -I ${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap/pkg
pkgPath = minibp
default ${g.bootstrap.buildDir}/.bootstrap/minibp/obj/minibp.a
build ${g.bootstrap.buildDir}/.bootstrap/minibp/obj/a.out: g.bootstrap.link $
${g.bootstrap.buildDir}/.bootstrap/minibp/obj/minibp.a | $
${g.bootstrap.linkCmd}
libDirFlags = -L ${g.bootstrap.buildDir}/.bootstrap/blueprint-parser/pkg -L ${g.bootstrap.buildDir}/.bootstrap/blueprint-deptools/pkg -L ${g.bootstrap.buildDir}/.bootstrap/blueprint-pathtools/pkg -L ${g.bootstrap.buildDir}/.bootstrap/blueprint-proptools/pkg -L ${g.bootstrap.buildDir}/.bootstrap/blueprint/pkg -L ${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap-bpdoc/pkg -L ${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap/pkg
default ${g.bootstrap.buildDir}/.bootstrap/minibp/obj/a.out
build ${g.bootstrap.BinDir}/minibp: g.bootstrap.cp $
${g.bootstrap.buildDir}/.bootstrap/minibp/obj/a.out
default ${g.bootstrap.BinDir}/minibp
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Singleton: bootstrap
# Factory: github.com/google/blueprint/bootstrap.newSingletonFactory.func1
build ${g.bootstrap.buildDir}/.bootstrap/build.ninja: g.bootstrap.build.ninja $
${g.bootstrap.srcDir}/Blueprints | ${builder}
builder = ${g.bootstrap.BinDir}/minibp
extra = --build-primary
default ${g.bootstrap.buildDir}/.bootstrap/build.ninja
build ${g.bootstrap.buildDir}/.minibootstrap/build.ninja.in: $
g.bootstrap.build.ninja ${g.bootstrap.srcDir}/Blueprints | ${builder}
builder = ${g.bootstrap.BinDir}/minibp
extra =
default ${g.bootstrap.buildDir}/.minibootstrap/build.ninja.in
build ${g.bootstrap.buildDir}/.minibootstrap/build.ninja: $
g.bootstrap.bootstrap $
${g.bootstrap.buildDir}/.minibootstrap/build.ninja.in | $
${g.bootstrap.bootstrapCmd}
default ${g.bootstrap.buildDir}/.minibootstrap/build.ninja

78
tests/test_tree_tests.sh Executable file
View file

@ -0,0 +1,78 @@
#!/bin/bash -ex
function mtime() {
stat -c %Y $1
}
# Go to top of blueprint tree
TOP=$(dirname ${BASH_SOURCE[0]})/..
cd ${TOP}
rm -rf out.test
mkdir out.test
rm -rf src.test
mkdir src.test
cp -r tests/test_tree src.test/test_tree
cd out.test
export SRCDIR=../src.test/test_tree
${SRCDIR}/blueprint/bootstrap.bash
./blueprint.bash
if ! cmp -s ${SRCDIR}/build.ninja.in .minibootstrap/build.ninja.in; then
echo "tests/test_tree/build.ninja.in and .minibootstrap/build.ninja.in should be the same" >&2
exit 1
fi
OLDTIME=$(mtime build.ninja)
sleep 2
./blueprint.bash
if [ ${OLDTIME} != $(mtime build.ninja) ]; then
echo "unnecessary build.ninja regeneration for null build" >&2
exit 1
fi
mkdir ${SRCDIR}/newglob
sleep 2
./blueprint.bash
if [ ${OLDTIME} != $(mtime build.ninja) ]; then
echo "unnecessary build.ninja regeneration for glob addition" >&2
exit 1
fi
touch ${SRCDIR}/newglob/Blueprints
sleep 2
./blueprint.bash
if [ ${OLDTIME} = $(mtime build.ninja) ]; then
echo "Failed to rebuild for glob addition" >&2
exit 1
fi
OLDTIME=$(mtime build.ninja)
rm ${SRCDIR}/newglob/Blueprints
sleep 2
./blueprint.bash
if [ ${OLDTIME} = $(mtime build.ninja) ]; then
echo "Failed to rebuild for glob removal" >&2
exit 1
fi
OLDTIME=$(mtime build.ninja)
rmdir ${SRCDIR}/newglob
sleep 2
./blueprint.bash
if [ ${OLDTIME} != $(mtime build.ninja) ]; then
echo "unnecessary build.ninja regeneration for glob removal" >&2
exit 1
fi