func (s *singleton) GenerateBuildActions(ctx blueprint.SingletonContext) {
	// Find the module that's marked as the "primary builder", which means it's
	// creating the binary that we'll use to generate the non-bootstrap
	// build.ninja file.
	var primaryBuilders []*goBinary
	// rebootstrapDeps contains modules that will be built in StageBootstrap
	var rebootstrapDeps []string
	// primaryRebootstrapDeps contains modules that will be built in StagePrimary
	var primaryRebootstrapDeps []string
	ctx.VisitAllModulesIf(isBootstrapBinaryModule,
		func(module blueprint.Module) {
			binaryModule := module.(*goBinary)
			binaryModuleName := ctx.ModuleName(binaryModule)
			binaryModulePath := filepath.Join("$BinDir", binaryModuleName)

			if binaryModule.BuildStage() == StageBootstrap {
				rebootstrapDeps = append(rebootstrapDeps, binaryModulePath)
			} else {
				primaryRebootstrapDeps = append(primaryRebootstrapDeps, binaryModulePath)
			}
			if binaryModule.properties.PrimaryBuilder {
				primaryBuilders = append(primaryBuilders, binaryModule)
			}
		})

	var primaryBuilderName, primaryBuilderExtraFlags string
	switch len(primaryBuilders) {
	case 0:
		// If there's no primary builder module then that means we'll use minibp
		// as the primary builder.  We can trigger its primary builder mode with
		// the -p flag.
		primaryBuilderName = "minibp"
		primaryBuilderExtraFlags = "-p"

	case 1:
		primaryBuilderName = ctx.ModuleName(primaryBuilders[0])

	default:
		ctx.Errorf("multiple primary builder modules present:")
		for _, primaryBuilder := range primaryBuilders {
			ctx.ModuleErrorf(primaryBuilder, "<-- module %s",
				ctx.ModuleName(primaryBuilder))
		}
		return
	}

	primaryBuilderFile := filepath.Join("$BinDir", primaryBuilderName)

	if s.config.runGoTests {
		primaryBuilderExtraFlags += " -t"
	}

	// Get the filename of the top-level Blueprints file to pass to minibp.
	topLevelBlueprints := filepath.Join("$srcDir",
		filepath.Base(s.config.topLevelBlueprintsFile))

	rebootstrapDeps = append(rebootstrapDeps, topLevelBlueprints)
	primaryRebootstrapDeps = append(primaryRebootstrapDeps, topLevelBlueprints)

	mainNinjaFile := filepath.Join(bootstrapDir, "main.ninja.in")
	mainNinjaTimestampFile := mainNinjaFile + ".timestamp"
	mainNinjaTimestampDepFile := mainNinjaTimestampFile + ".d"
	primaryBuilderNinjaFile := filepath.Join(bootstrapDir, "primary.ninja.in")
	primaryBuilderNinjaTimestampFile := primaryBuilderNinjaFile + ".timestamp"
	primaryBuilderNinjaTimestampDepFile := primaryBuilderNinjaTimestampFile + ".d"
	bootstrapNinjaFile := filepath.Join(bootstrapDir, "bootstrap.ninja.in")
	docsFile := filepath.Join(docsDir, primaryBuilderName+".html")

	primaryRebootstrapDeps = append(primaryRebootstrapDeps, docsFile)

	// 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 != "" {
				if testModule.BuildStage() == StageBootstrap {
					rebootstrapDeps = append(rebootstrapDeps, target)
				} else {
					primaryRebootstrapDeps = append(primaryRebootstrapDeps, 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.

		// BuildDir must be different between the three stages, otherwise the
		// cleanup process will remove files from the other builds.
		ctx.SetBuildDir(pctx, miniBootstrapDir)

		// Generate the Ninja file to build the primary builder. Save the
		// timestamps and deps, so that we can come back to this stage if
		// it needs to be regenerated.
		primarybp := ctx.Rule(pctx, "primarybp",
			blueprint.RuleParams{
				Command: fmt.Sprintf("%s --build-primary $runTests -m $bootstrapManifest "+
					"--timestamp $timestamp --timestampdep $timestampdep "+
					"-b $buildDir -d $outfile.d -o $outfile $in", minibpFile),
				Description: "minibp $outfile",
				Depfile:     "$outfile.d",
			},
			"runTests", "timestamp", "timestampdep", "outfile")

		args := map[string]string{
			"outfile":      primaryBuilderNinjaFile,
			"timestamp":    primaryBuilderNinjaTimestampFile,
			"timestampdep": primaryBuilderNinjaTimestampDepFile,
		}

		if s.config.runGoTests {
			args["runTests"] = "-t"
		}

		ctx.Build(pctx, blueprint.BuildParams{
			Rule:      primarybp,
			Outputs:   []string{primaryBuilderNinjaFile, primaryBuilderNinjaTimestampFile},
			Inputs:    []string{topLevelBlueprints},
			Implicits: rebootstrapDeps,
			Args:      args,
		})

		// 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 "+
					"-b $buildDir -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,
		})

		// 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.
		notAFile := filepath.Join(bootstrapDir, "notAFile")
		ctx.Build(pctx, blueprint.BuildParams{
			Rule:    blueprint.Phony,
			Outputs: []string{notAFile},
		})

		ctx.Build(pctx, blueprint.BuildParams{
			Rule:      chooseStage,
			Outputs:   []string{filepath.Join(bootstrapDir, "build.ninja.in")},
			Inputs:    []string{bootstrapNinjaFile, primaryBuilderNinjaFile},
			Implicits: []string{"$chooseStageCmd", "$bootstrapManifest", notAFile},
			Args: map[string]string{
				"current": bootstrapNinjaFile,
			},
		})

	case StagePrimary:
		// We're generating a bootstrapper Ninja file, so we need to set things
		// up to rebuild the build.ninja file using the primary builder.

		// BuildDir must be different between the three stages, otherwise the
		// cleanup process will remove files from the other builds.
		ctx.SetBuildDir(pctx, bootstrapDir)

		// 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 "+
					"-b $buildDir -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: primaryRebootstrapDeps,
			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
		// Blueprints files, as any relevant changes to the Blueprints files would have caused
		// a rebuild of the primary builder.
		bigbpDocs := ctx.Rule(pctx, "bigbpDocs",
			blueprint.RuleParams{
				Command: fmt.Sprintf("%s %s -b $buildDir --docs $out %s", primaryBuilderFile,
					primaryBuilderExtraFlags, topLevelBlueprints),
				Description: fmt.Sprintf("%s docs $out", primaryBuilderName),
			})

		ctx.Build(pctx, blueprint.BuildParams{
			Rule:      bigbpDocs,
			Outputs:   []string{docsFile},
			Implicits: []string{primaryBuilderFile},
		})

		// Detect whether we need to rebuild the primary stage by going back to
		// the bootstrapper. If this is newer than the primaryBuilderNinjaFile,
		// then chooseStage will trigger a rebuild of primaryBuilderNinjaFile by
		// returning to the bootstrap stage.
		ctx.Build(pctx, blueprint.BuildParams{
			Rule:      touch,
			Outputs:   []string{primaryBuilderNinjaTimestampFile},
			Implicits: rebootstrapDeps,
			Args: map[string]string{
				"depfile":   primaryBuilderNinjaTimestampDepFile,
				"generator": "true",
			},
		})

		// 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.
		notAFile := filepath.Join(bootstrapDir, "notAFile")
		ctx.Build(pctx, blueprint.BuildParams{
			Rule:    blueprint.Phony,
			Outputs: []string{notAFile},
		})

		ctx.Build(pctx, blueprint.BuildParams{
			Rule:      chooseStage,
			Outputs:   []string{filepath.Join(bootstrapDir, "build.ninja.in")},
			Inputs:    []string{bootstrapNinjaFile, primaryBuilderNinjaFile, mainNinjaFile},
			Implicits: []string{"$chooseStageCmd", "$bootstrapManifest", notAFile, primaryBuilderNinjaTimestampFile},
			Args: map[string]string{
				"current": primaryBuilderNinjaFile,
			},
		})

		// Create this phony rule so that upgrades don't delete these during
		// cleanup
		ctx.Build(pctx, blueprint.BuildParams{
			Rule:    blueprint.Phony,
			Outputs: []string{bootstrapNinjaFile},
		})

	case StageMain:
		ctx.SetBuildDir(pctx, "${buildDir}")

		// We're generating a non-bootstrapper Ninja file, so we need to set it
		// 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.
		//
		// 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:      touch,
			Outputs:   []string{primaryBuilderNinjaTimestampFile},
			Implicits: rebootstrapDeps,
			Args: map[string]string{
				"depfile":   primaryBuilderNinjaTimestampDepFile,
				"generator": "true",
			},
		})

		ctx.Build(pctx, blueprint.BuildParams{
			Rule:      touch,
			Outputs:   []string{mainNinjaTimestampFile},
			Implicits: primaryRebootstrapDeps,
			Args: map[string]string{
				"depfile":   mainNinjaTimestampDepFile,
				"generator": "true",
			},
		})

		ctx.Build(pctx, blueprint.BuildParams{
			Rule:      chooseStage,
			Outputs:   []string{filepath.Join(bootstrapDir, "build.ninja.in")},
			Inputs:    []string{bootstrapNinjaFile, primaryBuilderNinjaFile, mainNinjaFile},
			Implicits: []string{"$chooseStageCmd", "$bootstrapManifest", primaryBuilderNinjaTimestampFile, 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, "$bootstrapManifest"},
		})

		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.
			finalMinibp := filepath.Join("$buildDir", "bin", primaryBuilderName)
			ctx.Build(pctx, blueprint.BuildParams{
				Rule:    cp,
				Inputs:  []string{primaryBuilderFile},
				Outputs: []string{finalMinibp},
			})
		}
	}

	ctx.Build(pctx, blueprint.BuildParams{
		Rule:      bootstrap,
		Outputs:   []string{"$buildDir/build.ninja"},
		Inputs:    []string{filepath.Join(bootstrapDir, "build.ninja.in")},
		Implicits: []string{"$bootstrapCmd"},
	})
}
Exemple #2
0
func (s *singleton) GenerateBuildActions(ctx blueprint.SingletonContext) {
	// Find the module that's marked as the "primary builder", which means it's
	// creating the binary that we'll use to generate the non-bootstrap
	// build.ninja file.
	var primaryBuilders []*goBinary
	var rebootstrapDeps []string
	ctx.VisitAllModulesIf(isBootstrapBinaryModule,
		func(module blueprint.Module) {
			binaryModule := module.(*goBinary)
			binaryModuleName := ctx.ModuleName(binaryModule)
			binaryModulePath := filepath.Join(BinDir, binaryModuleName)
			rebootstrapDeps = append(rebootstrapDeps, binaryModulePath)
			if binaryModule.properties.PrimaryBuilder {
				primaryBuilders = append(primaryBuilders, binaryModule)
			}
		})

	var primaryBuilderName, primaryBuilderExtraFlags string
	switch len(primaryBuilders) {
	case 0:
		// If there's no primary builder module then that means we'll use minibp
		// as the primary builder.  We can trigger its primary builder mode with
		// the -p flag.
		primaryBuilderName = "minibp"
		primaryBuilderExtraFlags = "-p"

	case 1:
		primaryBuilderName = ctx.ModuleName(primaryBuilders[0])

	default:
		ctx.Errorf("multiple primary builder modules present:")
		for _, primaryBuilder := range primaryBuilders {
			ctx.ModuleErrorf(primaryBuilder, "<-- module %s",
				ctx.ModuleName(primaryBuilder))
		}
		return
	}

	primaryBuilderFile := filepath.Join(BinDir, primaryBuilderName)

	if s.config.runGoTests {
		primaryBuilderExtraFlags += " -t"
	}

	// 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))

	mainNinjaFile := filepath.Join(bootstrapDir, "main.ninja.in")
	mainNinjaDepFile := mainNinjaFile + ".d"
	bootstrapNinjaFile := filepath.Join(bootstrapDir, "bootstrap.ninja.in")
	docsFile := filepath.Join(docsDir, primaryBuilderName+".html")

	rebootstrapDeps = append(rebootstrapDeps, docsFile)

	if s.config.generatingBootstrapper {
		// We're generating a bootstrapper Ninja file, so we need to set things
		// up to rebuild the build.ninja file using the primary builder.

		// Because the non-bootstrap build.ninja file manually re-invokes Ninja,
		// its builddir must be different than that of the bootstrap build.ninja
		// file.  Otherwise we occasionally get "warning: bad deps log signature
		// or version; starting over" messages from Ninja, presumably because
		// two Ninja processes try to write to the same log concurrently.
		ctx.SetBuildDir(pctx, bootstrapDir)

		// 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
		// Blueprints files, as any relevant changes to the Blueprints files would have caused
		// a rebuild of the primary builder.
		bigbpDocs := ctx.Rule(pctx, "bigbpDocs",
			blueprint.RuleParams{
				Command: fmt.Sprintf("%s %s --docs $out %s", primaryBuilderFile,
					primaryBuilderExtraFlags, topLevelBlueprints),
				Description: fmt.Sprintf("%s docs $out", primaryBuilderName),
			})

		ctx.Build(pctx, blueprint.BuildParams{
			Rule:      bigbpDocs,
			Outputs:   []string{docsFile},
			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,
			Outputs: []string{notAFile},
		})

		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",
			},
			"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)
				}
			})

		// 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.
		//
		// 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...)

		ctx.Build(pctx, blueprint.BuildParams{
			Rule:      rebootstrap,
			Outputs:   []string{"build.ninja"},
			Inputs:    []string{"$bootstrapManifest"},
			Implicits: buildNinjaDeps,
		})

		ctx.Build(pctx, blueprint.BuildParams{
			Rule:    phony,
			Outputs: []string{mainNinjaFile},
			Inputs:  []string{topLevelBlueprints},
			Args: map[string]string{
				"depfile": mainNinjaDepFile,
			},
		})

		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},
			Args: map[string]string{
				"generator": "true",
			},
		})

		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.
			finalMinibp := filepath.Join("bin", primaryBuilderName)
			ctx.Build(pctx, blueprint.BuildParams{
				Rule:    cp,
				Inputs:  []string{primaryBuilderFile},
				Outputs: []string{finalMinibp},
			})
		}
	}
}