diff --git a/.gitignore b/.gitignore index de99854..3a9ef96 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ out.test +src.test diff --git a/.travis.yml b/.travis.yml index ecb8c5c..2824056 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,3 +23,4 @@ script: - ./blueprint.bash - diff -us ../build.ninja.in .minibootstrap/build.ninja.in - ../tests/test.sh + - ../tests/test_tree_tests.sh diff --git a/Blueprints b/Blueprints index f0a0f87..702d0de 100644 --- a/Blueprints +++ b/Blueprints @@ -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"], diff --git a/bootstrap/bpglob/bpglob.go b/bootstrap/bpglob/bpglob.go new file mode 100644 index 0000000..cb6633b --- /dev/null +++ b/bootstrap/bpglob/bpglob.go @@ -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) + } +} diff --git a/bootstrap/command.go b/bootstrap/command.go index cc55b28..7c220e5 100644 --- a/bootstrap/command.go +++ b/bootstrap/command.go @@ -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) diff --git a/bootstrap/glob.go b/bootstrap/glob.go new file mode 100644 index 0000000..2e209d9 --- /dev/null +++ b/bootstrap/glob.go @@ -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) + } +} diff --git a/build.ninja.in b/build.ninja.in index 8b3fe98..ec32fcf 100644 --- a/build.ninja.in +++ b/build.ninja.in @@ -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 $ diff --git a/context.go b/context.go index 48856cf..7e54150 100644 --- a/context.go +++ b/context.go @@ -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) { diff --git a/context_test.go b/context_test.go index 5d9f4bc..b05c541 100644 --- a/context_test.go +++ b/context_test.go @@ -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 { diff --git a/glob.go b/glob.go new file mode 100644 index 0000000..dad5edf --- /dev/null +++ b/glob.go @@ -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" +} diff --git a/module_ctx.go b/module_ctx.go index cfbb2db..f8a42f0 100644 --- a/module_ctx.go +++ b/module_ctx.go @@ -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 { diff --git a/pathtools/glob.go b/pathtools/glob.go index 5541643..8e0a7e0 100644 --- a/pathtools/glob.go +++ b/pathtools/glob.go @@ -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 +} diff --git a/singleton_ctx.go b/singleton_ctx.go index 007e180..fc4a781 100644 --- a/singleton_ctx.go +++ b/singleton_ctx.go @@ -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) +} diff --git a/tests/test_tree/Blueprints b/tests/test_tree/Blueprints new file mode 100644 index 0000000..d5e1358 --- /dev/null +++ b/tests/test_tree/Blueprints @@ -0,0 +1,3 @@ +// Root Blueprints file for test_tree + +subdirs=["*"] diff --git a/tests/test_tree/a/Blueprints b/tests/test_tree/a/Blueprints new file mode 100644 index 0000000..836d6ba --- /dev/null +++ b/tests/test_tree/a/Blueprints @@ -0,0 +1 @@ +// a/Blueprints diff --git a/tests/test_tree/blueprint b/tests/test_tree/blueprint new file mode 120000 index 0000000..c25bddb --- /dev/null +++ b/tests/test_tree/blueprint @@ -0,0 +1 @@ +../.. \ No newline at end of file diff --git a/tests/test_tree/build.ninja.in b/tests/test_tree/build.ninja.in new file mode 100644 index 0000000..5604b40 --- /dev/null +++ b/tests/test_tree/build.ninja.in @@ -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 + diff --git a/tests/test_tree_tests.sh b/tests/test_tree_tests.sh new file mode 100755 index 0000000..a269fdf --- /dev/null +++ b/tests/test_tree_tests.sh @@ -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