diff options
Diffstat (limited to 'bootstrap/bootstrap.go')
| -rw-r--r-- | bootstrap/bootstrap.go | 259 |
1 files changed, 141 insertions, 118 deletions
diff --git a/bootstrap/bootstrap.go b/bootstrap/bootstrap.go index 66a4036..3023b08 100644 --- a/bootstrap/bootstrap.go +++ b/bootstrap/bootstrap.go @@ -28,9 +28,10 @@ const bootstrapDir = ".bootstrap" var ( pctx = blueprint.NewPackageContext("github.com/google/blueprint/bootstrap") - gcCmd = pctx.StaticVariable("gcCmd", "$goToolDir/${goChar}g") - linkCmd = pctx.StaticVariable("linkCmd", "$goToolDir/${goChar}l") - goTestMainCmd = pctx.StaticVariable("goTestMainCmd", filepath.Join(bootstrapDir, "bin", "gotestmain")) + gcCmd = pctx.StaticVariable("gcCmd", "$goToolDir/${goChar}g") + linkCmd = pctx.StaticVariable("linkCmd", "$goToolDir/${goChar}l") + goTestMainCmd = pctx.StaticVariable("goTestMainCmd", filepath.Join(bootstrapDir, "bin", "gotestmain")) + chooseStageCmd = pctx.StaticVariable("chooseStageCmd", filepath.Join(bootstrapDir, "bin", "choosestage")) gc = pctx.StaticRule("gc", blueprint.RuleParams{ @@ -70,11 +71,25 @@ var ( bootstrap = pctx.StaticRule("bootstrap", blueprint.RuleParams{ - Command: "echo \"Choosing $$(basename $in) for next stage\" && $bootstrapCmd -i $in", + Command: "$bootstrapCmd -i $in", Description: "bootstrap $in", Generator: true, }) + chooseStage = pctx.StaticRule("chooseStage", + blueprint.RuleParams{ + Command: "$chooseStageCmd --current $current --bootstrap $bootstrapManifest -o $out $in", + Description: "choosing next stage", + }, + "current", "generator") + + touch = pctx.StaticRule("touch", + blueprint.RuleParams{ + Command: "touch $out", + Description: "touch $out", + }, + "depfile", "generator") + // Work around a Ninja issue. See https://github.com/martine/ninja/pull/634 phony = pctx.StaticRule("phony", blueprint.RuleParams{ @@ -186,7 +201,8 @@ func (g *goPackage) GenerateBuildActions(ctx blueprint.ModuleContext) { // the circular dependence that occurs when the builder requires a new Ninja // file to be built, but building a new ninja file requires the builder to // be built. - if g.config.generatingBootstrapper { + switch g.config.stage { + case StageBootstrap: var deps []string if g.config.runGoTests { @@ -197,7 +213,7 @@ func (g *goPackage) GenerateBuildActions(ctx blueprint.ModuleContext) { buildGoPackage(ctx, g.pkgRoot, g.properties.PkgPath, g.archiveFile, g.properties.Srcs, deps) - } else { + case StageMain: if len(g.properties.TestSrcs) > 0 && g.config.runGoTests { phonyGoTarget(ctx, g.testArchiveFile, g.properties.TestSrcs, nil) } @@ -251,7 +267,8 @@ func (g *goBinary) GenerateBuildActions(ctx blueprint.ModuleContext) { // the circular dependence that occurs when the builder requires a new Ninja // file to be built, but building a new ninja file requires the builder to // be built. - if g.config.generatingBootstrapper { + switch g.config.stage { + case StageBootstrap: var deps []string if g.config.runGoTests { @@ -287,7 +304,7 @@ func (g *goBinary) GenerateBuildActions(ctx blueprint.ModuleContext) { Outputs: []string{binaryFile}, Inputs: []string{aoutFile}, }) - } else { + case StageMain: if len(g.properties.TestSrcs) > 0 && g.config.runGoTests { phonyGoTarget(ctx, g.testArchiveFile, g.properties.TestSrcs, nil) } @@ -506,18 +523,34 @@ func (s *singleton) GenerateBuildActions(ctx blueprint.SingletonContext) { } // Get the filename of the top-level Blueprints file to pass to minibp. - // This comes stored in a global variable that's set by Main. topLevelBlueprints := filepath.Join("$srcDir", filepath.Base(s.config.topLevelBlueprintsFile)) + rebootstrapDeps = append(rebootstrapDeps, topLevelBlueprints) + mainNinjaFile := filepath.Join(bootstrapDir, "main.ninja.in") - mainNinjaDepFile := mainNinjaFile + ".d" + mainNinjaTimestampFile := mainNinjaFile + ".timestamp" + mainNinjaTimestampDepFile := mainNinjaTimestampFile + ".d" bootstrapNinjaFile := filepath.Join(bootstrapDir, "bootstrap.ninja.in") docsFile := filepath.Join(docsDir, primaryBuilderName+".html") rebootstrapDeps = append(rebootstrapDeps, docsFile) - if s.config.generatingBootstrapper { + // If the tests change, be sure to re-run them. These need to be + // dependencies for the ninja file so that it's updated after these + // run. Otherwise we'd never leave the bootstrap stage, since the + // timestamp file would be newer than the ninja file. + ctx.VisitAllModulesIf(isGoTestProducer, + func(module blueprint.Module) { + testModule := module.(goTestProducer) + target := testModule.GoTestTarget() + if target != "" { + rebootstrapDeps = append(rebootstrapDeps, target) + } + }) + + switch s.config.stage { + case StageBootstrap: // We're generating a bootstrapper Ninja file, so we need to set things // up to rebuild the build.ninja file using the primary builder. @@ -525,6 +558,64 @@ func (s *singleton) GenerateBuildActions(ctx blueprint.SingletonContext) { // otherwise the cleanup process will remove files from the other build. ctx.SetBuildDir(pctx, bootstrapDir) + // Rebuild the bootstrap Ninja file using the minibp that we just built. + // If this produces a difference, choosestage will retrigger this stage. + minibp := ctx.Rule(pctx, "minibp", + blueprint.RuleParams{ + Command: fmt.Sprintf("%s $runTests -m $bootstrapManifest "+ + "-d $out.d -o $out $in", minibpFile), + Description: "minibp $out", + Generator: true, + Depfile: "$out.d", + }, + "runTests") + + args := map[string]string{} + + if s.config.runGoTests { + args["runTests"] = "-t" + } + + ctx.Build(pctx, blueprint.BuildParams{ + Rule: minibp, + Outputs: []string{bootstrapNinjaFile}, + Inputs: []string{topLevelBlueprints}, + // $bootstrapManifest is here so that when it is updated, we + // force a rebuild of bootstrap.ninja.in. chooseStage should + // have already copied the new version over, but kept the old + // timestamps to force this regeneration. + Implicits: []string{"$bootstrapManifest", minibpFile}, + Args: args, + }) + + // We generate the depfile here that includes the dependencies for all + // the Blueprints files that contribute to generating the big build + // manifest (build.ninja file). This depfile will be used by the non- + // bootstrap build manifest to determine whether it should touch the + // timestamp file to trigger a re-bootstrap. + bigbp := ctx.Rule(pctx, "bigbp", + blueprint.RuleParams{ + Command: fmt.Sprintf("%s %s -m $bootstrapManifest "+ + "--timestamp $timestamp --timestampdep $timestampdep "+ + "-d $outfile.d -o $outfile $in", primaryBuilderFile, + primaryBuilderExtraFlags), + Description: fmt.Sprintf("%s $outfile", primaryBuilderName), + Depfile: "$outfile.d", + }, + "timestamp", "timestampdep", "outfile") + + ctx.Build(pctx, blueprint.BuildParams{ + Rule: bigbp, + Outputs: []string{mainNinjaFile, mainNinjaTimestampFile}, + Inputs: []string{topLevelBlueprints}, + Implicits: rebootstrapDeps, + Args: map[string]string{ + "timestamp": mainNinjaTimestampFile, + "timestampdep": mainNinjaTimestampDepFile, + "outfile": mainNinjaFile, + }, + }) + // Generate build system docs for the primary builder. Generating docs reads the source // files used to build the primary builder, but that dependency will be picked up through // the dependency on the primary builder itself. There are no dependencies on the @@ -543,36 +634,10 @@ func (s *singleton) GenerateBuildActions(ctx blueprint.SingletonContext) { Implicits: []string{primaryBuilderFile}, }) - // We generate the depfile here that includes the dependencies for all - // the Blueprints files that contribute to generating the big build - // manifest (build.ninja file). This depfile will be used by the non- - // bootstrap build manifest to determine whether it should trigger a re- - // bootstrap. Because the re-bootstrap rule's output is "build.ninja" - // we need to force the depfile to have that as its "make target" - // (recall that depfiles use a subset of the Makefile syntax). - bigbp := ctx.Rule(pctx, "bigbp", - blueprint.RuleParams{ - Command: fmt.Sprintf("%s %s -d %s -m $bootstrapManifest "+ - "-o $out $in", primaryBuilderFile, - primaryBuilderExtraFlags, mainNinjaDepFile), - Description: fmt.Sprintf("%s $out", primaryBuilderName), - Depfile: mainNinjaDepFile, - }) - - ctx.Build(pctx, blueprint.BuildParams{ - Rule: bigbp, - Outputs: []string{mainNinjaFile}, - Inputs: []string{topLevelBlueprints}, - Implicits: rebootstrapDeps, - }) - // When the current build.ninja file is a bootstrapper, we always want // to have it replace itself with a non-bootstrapper build.ninja. To // accomplish that we depend on a file that should never exist and // "build" it using Ninja's built-in phony rule. - // - // We also need to add an implicit dependency on bootstrapNinjaFile so - // that it gets generated as part of the bootstrap process. notAFile := filepath.Join(bootstrapDir, "notAFile") ctx.Build(pctx, blueprint.BuildParams{ Rule: blueprint.Phony, @@ -580,103 +645,54 @@ func (s *singleton) GenerateBuildActions(ctx blueprint.SingletonContext) { }) ctx.Build(pctx, blueprint.BuildParams{ - Rule: bootstrap, - Outputs: []string{"build.ninja"}, - Inputs: []string{mainNinjaFile}, - Implicits: []string{"$bootstrapCmd", notAFile, bootstrapNinjaFile}, - }) - - // Rebuild the bootstrap Ninja file using the minibp that we just built. - // The checkFile tells minibp to compare the new bootstrap file to the - // current one. If the files are the same then minibp sets the new - // file's mtime to match that of the current one. If they're different - // then the new file will have a newer timestamp than the current one - // and it will trigger a reboostrap by the non-boostrap build manifest. - minibp := ctx.Rule(pctx, "minibp", - blueprint.RuleParams{ - Command: fmt.Sprintf("%s $runTests -c $checkFile -m $bootstrapManifest "+ - "-d $out.d -o $out $in", minibpFile), - Description: "minibp $out", - Generator: true, - Depfile: "$out.d", + Rule: chooseStage, + Outputs: []string{filepath.Join(bootstrapDir, "build.ninja.in")}, + Inputs: []string{bootstrapNinjaFile, mainNinjaFile}, + Implicits: []string{"$chooseStageCmd", "$bootstrapManifest", notAFile}, + Args: map[string]string{ + "current": bootstrapNinjaFile, }, - "checkFile", "runTests") - - args := map[string]string{ - "checkFile": "$bootstrapManifest", - } - - if s.config.runGoTests { - args["runTests"] = "-t" - } - - ctx.Build(pctx, blueprint.BuildParams{ - Rule: minibp, - Outputs: []string{bootstrapNinjaFile}, - Inputs: []string{topLevelBlueprints}, - Implicits: []string{minibpFile}, - Args: args, }) - } else { - ctx.VisitAllModulesIf(isGoTestProducer, - func(module blueprint.Module) { - testModule := module.(goTestProducer) - target := testModule.GoTestTarget() - if target != "" { - rebootstrapDeps = append(rebootstrapDeps, target) - } - }) + case StageMain: // We're generating a non-bootstrapper Ninja file, so we need to set it - // up to depend on the bootstrapper Ninja file. The build.ninja target - // also has an implicit dependency on the primary builder and all other - // bootstrap go binaries, which will have phony dependencies on all of - // their sources. This will cause any changes to a bootstrap binary's - // sources to trigger a re-bootstrap operation, which will rebuild the - // binary. + // up to re-bootstrap if necessary. We do this by making build.ninja.in + // depend on the various Ninja files, the source build.ninja.in, and + // on the timestamp files. // - // On top of that we need to use the depfile generated by the bigbp - // rule. We do this by depending on that file and then setting up a - // phony rule to generate it that uses the depfile. - buildNinjaDeps := []string{"$bootstrapCmd", mainNinjaFile} - buildNinjaDeps = append(buildNinjaDeps, rebootstrapDeps...) - + // The timestamp files themselves are set up with the same dependencies + // as their Ninja files, including their own depfile. If any of the + // dependencies need to be updated, we'll touch the timestamp file, + // which will tell choosestage to switch to the stage that rebuilds + // that Ninja file. ctx.Build(pctx, blueprint.BuildParams{ - Rule: bootstrap, - Outputs: []string{"build.ninja"}, - Inputs: []string{"$bootstrapManifest"}, - Implicits: buildNinjaDeps, - }) - - ctx.Build(pctx, blueprint.BuildParams{ - Rule: phony, - Outputs: []string{mainNinjaFile}, - Inputs: []string{topLevelBlueprints}, + Rule: touch, + Outputs: []string{mainNinjaTimestampFile}, + Implicits: rebootstrapDeps, Args: map[string]string{ - "depfile": mainNinjaDepFile, + "depfile": mainNinjaTimestampDepFile, + "generator": "true", }, }) ctx.Build(pctx, blueprint.BuildParams{ - Rule: phony, - Outputs: []string{docsFile}, - Implicits: []string{primaryBuilderFile}, - }) - - // If the bootstrap Ninja invocation caused a new bootstrapNinjaFile to be - // generated then that means we need to rebootstrap using it instead of - // the current bootstrap manifest. We enable the Ninja "generator" - // behavior so that Ninja doesn't invoke this build just because it's - // missing a command line log entry for the bootstrap manifest. - ctx.Build(pctx, blueprint.BuildParams{ - Rule: cp, - Outputs: []string{"$bootstrapManifest"}, - Inputs: []string{bootstrapNinjaFile}, + Rule: chooseStage, + Outputs: []string{filepath.Join(bootstrapDir, "build.ninja.in")}, + Inputs: []string{bootstrapNinjaFile, mainNinjaFile}, + Implicits: []string{"$chooseStageCmd", "$bootstrapManifest", mainNinjaTimestampFile}, Args: map[string]string{ + "current": mainNinjaFile, "generator": "true", }, }) + // Create this phony rule so that upgrades don't delete these during + // cleanup + ctx.Build(pctx, blueprint.BuildParams{ + Rule: blueprint.Phony, + Outputs: []string{mainNinjaFile, docsFile}, + }) + if primaryBuilderName == "minibp" { // This is a standalone Blueprint build, so we copy the minibp // binary to the "bin" directory to make it easier to find. @@ -688,6 +704,13 @@ func (s *singleton) GenerateBuildActions(ctx blueprint.SingletonContext) { }) } } + + ctx.Build(pctx, blueprint.BuildParams{ + Rule: bootstrap, + Outputs: []string{"build.ninja"}, + Inputs: []string{filepath.Join(bootstrapDir, "build.ninja.in")}, + Implicits: []string{"$bootstrapCmd"}, + }) } // packageRoot returns the module-specific package root directory path. This |
