Home | History | Annotate | Download | only in bootstrap
      1 // Copyright 2014 Google Inc. All rights reserved.
      2 //
      3 // Licensed under the Apache License, Version 2.0 (the "License");
      4 // you may not use this file except in compliance with the License.
      5 // You may obtain a copy of the License at
      6 //
      7 //     http://www.apache.org/licenses/LICENSE-2.0
      8 //
      9 // Unless required by applicable law or agreed to in writing, software
     10 // distributed under the License is distributed on an "AS IS" BASIS,
     11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 // See the License for the specific language governing permissions and
     13 // limitations under the License.
     14 
     15 package bootstrap
     16 
     17 import (
     18 	"fmt"
     19 	"path/filepath"
     20 	"strings"
     21 
     22 	"github.com/google/blueprint"
     23 	"github.com/google/blueprint/pathtools"
     24 )
     25 
     26 const bootstrapSubDir = ".bootstrap"
     27 const miniBootstrapSubDir = ".minibootstrap"
     28 
     29 var (
     30 	pctx = blueprint.NewPackageContext("github.com/google/blueprint/bootstrap")
     31 
     32 	goTestMainCmd   = pctx.StaticVariable("goTestMainCmd", filepath.Join(bootstrapDir, "bin", "gotestmain"))
     33 	goTestRunnerCmd = pctx.StaticVariable("goTestRunnerCmd", filepath.Join(bootstrapDir, "bin", "gotestrunner"))
     34 	chooseStageCmd  = pctx.StaticVariable("chooseStageCmd", filepath.Join(bootstrapDir, "bin", "choosestage"))
     35 	pluginGenSrcCmd = pctx.StaticVariable("pluginGenSrcCmd", filepath.Join(bootstrapDir, "bin", "loadplugins"))
     36 
     37 	compile = pctx.StaticRule("compile",
     38 		blueprint.RuleParams{
     39 			Command: "GOROOT='$goRoot' $compileCmd -o $out -p $pkgPath -complete " +
     40 				"$incFlags -pack $in",
     41 			CommandDeps: []string{"$compileCmd"},
     42 			Description: "compile $out",
     43 		},
     44 		"pkgPath", "incFlags")
     45 
     46 	link = pctx.StaticRule("link",
     47 		blueprint.RuleParams{
     48 			Command:     "GOROOT='$goRoot' $linkCmd -o $out $libDirFlags $in",
     49 			CommandDeps: []string{"$linkCmd"},
     50 			Description: "link $out",
     51 		},
     52 		"libDirFlags")
     53 
     54 	goTestMain = pctx.StaticRule("gotestmain",
     55 		blueprint.RuleParams{
     56 			Command:     "$goTestMainCmd -o $out -pkg $pkg $in",
     57 			CommandDeps: []string{"$goTestMainCmd"},
     58 			Description: "gotestmain $out",
     59 		},
     60 		"pkg")
     61 
     62 	pluginGenSrc = pctx.StaticRule("pluginGenSrc",
     63 		blueprint.RuleParams{
     64 			Command:     "$pluginGenSrcCmd -o $out -p $pkg $plugins",
     65 			CommandDeps: []string{"$pluginGenSrcCmd"},
     66 			Description: "create $out",
     67 		},
     68 		"pkg", "plugins")
     69 
     70 	test = pctx.StaticRule("test",
     71 		blueprint.RuleParams{
     72 			Command:     "$goTestRunnerCmd -p $pkgSrcDir -f $out -- $in -test.short",
     73 			CommandDeps: []string{"$goTestRunnerCmd"},
     74 			Description: "test $pkg",
     75 		},
     76 		"pkg", "pkgSrcDir")
     77 
     78 	cp = pctx.StaticRule("cp",
     79 		blueprint.RuleParams{
     80 			Command:     "cp $in $out",
     81 			Description: "cp $out",
     82 		},
     83 		"generator")
     84 
     85 	bootstrap = pctx.StaticRule("bootstrap",
     86 		blueprint.RuleParams{
     87 			Command:     "BUILDDIR=$buildDir $bootstrapCmd -i $in",
     88 			CommandDeps: []string{"$bootstrapCmd"},
     89 			Description: "bootstrap $in",
     90 			Generator:   true,
     91 		})
     92 
     93 	chooseStage = pctx.StaticRule("chooseStage",
     94 		blueprint.RuleParams{
     95 			Command:     "$chooseStageCmd --current $current --bootstrap $bootstrapManifest -o $out $in",
     96 			CommandDeps: []string{"$chooseStageCmd", "$bootstrapManifest"},
     97 			Description: "choosing next stage",
     98 		},
     99 		"current", "generator")
    100 
    101 	touch = pctx.StaticRule("touch",
    102 		blueprint.RuleParams{
    103 			Command:     "touch $out",
    104 			Description: "touch $out",
    105 		},
    106 		"depfile", "generator")
    107 
    108 	// Work around a Ninja issue.  See https://github.com/martine/ninja/pull/634
    109 	phony = pctx.StaticRule("phony",
    110 		blueprint.RuleParams{
    111 			Command:     "# phony $out",
    112 			Description: "phony $out",
    113 			Generator:   true,
    114 		},
    115 		"depfile")
    116 
    117 	binDir     = pctx.StaticVariable("BinDir", filepath.Join(bootstrapDir, "bin"))
    118 	minibpFile = filepath.Join("$BinDir", "minibp")
    119 
    120 	docsDir = filepath.Join(bootstrapDir, "docs")
    121 
    122 	bootstrapDir     = filepath.Join("$buildDir", bootstrapSubDir)
    123 	miniBootstrapDir = filepath.Join("$buildDir", miniBootstrapSubDir)
    124 )
    125 
    126 type bootstrapGoCore interface {
    127 	BuildStage() Stage
    128 	SetBuildStage(Stage)
    129 }
    130 
    131 func propagateStageBootstrap(mctx blueprint.TopDownMutatorContext) {
    132 	if mod, ok := mctx.Module().(bootstrapGoCore); !ok || mod.BuildStage() != StageBootstrap {
    133 		return
    134 	}
    135 
    136 	mctx.VisitDirectDeps(func(mod blueprint.Module) {
    137 		if m, ok := mod.(bootstrapGoCore); ok {
    138 			m.SetBuildStage(StageBootstrap)
    139 		}
    140 	})
    141 }
    142 
    143 func pluginDeps(ctx blueprint.BottomUpMutatorContext) {
    144 	if pkg, ok := ctx.Module().(*goPackage); ok {
    145 		for _, plugin := range pkg.properties.PluginFor {
    146 			ctx.AddReverseDependency(ctx.Module(), plugin)
    147 		}
    148 	}
    149 }
    150 
    151 type goPackageProducer interface {
    152 	GoPkgRoot() string
    153 	GoPackageTarget() string
    154 }
    155 
    156 func isGoPackageProducer(module blueprint.Module) bool {
    157 	_, ok := module.(goPackageProducer)
    158 	return ok
    159 }
    160 
    161 type goTestProducer interface {
    162 	GoTestTarget() string
    163 	BuildStage() Stage
    164 }
    165 
    166 func isGoTestProducer(module blueprint.Module) bool {
    167 	_, ok := module.(goTestProducer)
    168 	return ok
    169 }
    170 
    171 type goPluginProvider interface {
    172 	GoPkgPath() string
    173 	IsPluginFor(string) bool
    174 }
    175 
    176 func isGoPluginFor(name string) func(blueprint.Module) bool {
    177 	return func(module blueprint.Module) bool {
    178 		if plugin, ok := module.(goPluginProvider); ok {
    179 			return plugin.IsPluginFor(name)
    180 		}
    181 		return false
    182 	}
    183 }
    184 
    185 func isBootstrapModule(module blueprint.Module) bool {
    186 	_, isPackage := module.(*goPackage)
    187 	_, isBinary := module.(*goBinary)
    188 	return isPackage || isBinary
    189 }
    190 
    191 func isBootstrapBinaryModule(module blueprint.Module) bool {
    192 	_, isBinary := module.(*goBinary)
    193 	return isBinary
    194 }
    195 
    196 // A goPackage is a module for building Go packages.
    197 type goPackage struct {
    198 	properties struct {
    199 		PkgPath   string
    200 		Srcs      []string
    201 		TestSrcs  []string
    202 		PluginFor []string
    203 	}
    204 
    205 	// The root dir in which the package .a file is located.  The full .a file
    206 	// path will be "packageRoot/PkgPath.a"
    207 	pkgRoot string
    208 
    209 	// The path of the .a file that is to be built.
    210 	archiveFile string
    211 
    212 	// The path of the test .a file that is to be built.
    213 	testArchiveFile string
    214 
    215 	// The bootstrap Config
    216 	config *Config
    217 
    218 	// The stage in which this module should be built
    219 	buildStage Stage
    220 }
    221 
    222 var _ goPackageProducer = (*goPackage)(nil)
    223 
    224 func newGoPackageModuleFactory(config *Config) func() (blueprint.Module, []interface{}) {
    225 	return func() (blueprint.Module, []interface{}) {
    226 		module := &goPackage{
    227 			buildStage: StagePrimary,
    228 			config:     config,
    229 		}
    230 		return module, []interface{}{&module.properties}
    231 	}
    232 }
    233 
    234 func (g *goPackage) GoPkgPath() string {
    235 	return g.properties.PkgPath
    236 }
    237 
    238 func (g *goPackage) GoPkgRoot() string {
    239 	return g.pkgRoot
    240 }
    241 
    242 func (g *goPackage) GoPackageTarget() string {
    243 	return g.archiveFile
    244 }
    245 
    246 func (g *goPackage) GoTestTarget() string {
    247 	return g.testArchiveFile
    248 }
    249 
    250 func (g *goPackage) BuildStage() Stage {
    251 	return g.buildStage
    252 }
    253 
    254 func (g *goPackage) SetBuildStage(buildStage Stage) {
    255 	g.buildStage = buildStage
    256 }
    257 
    258 func (g *goPackage) IsPluginFor(name string) bool {
    259 	for _, plugin := range g.properties.PluginFor {
    260 		if plugin == name {
    261 			return true
    262 		}
    263 	}
    264 	return false
    265 }
    266 
    267 func (g *goPackage) GenerateBuildActions(ctx blueprint.ModuleContext) {
    268 	var (
    269 		name       = ctx.ModuleName()
    270 		hasPlugins = false
    271 		pluginSrc  = ""
    272 		genSrcs    = []string{}
    273 	)
    274 
    275 	if g.properties.PkgPath == "" {
    276 		ctx.ModuleErrorf("module %s did not specify a valid pkgPath", name)
    277 		return
    278 	}
    279 
    280 	g.pkgRoot = packageRoot(ctx)
    281 	g.archiveFile = filepath.Join(g.pkgRoot,
    282 		filepath.FromSlash(g.properties.PkgPath)+".a")
    283 	if len(g.properties.TestSrcs) > 0 && g.config.runGoTests {
    284 		g.testArchiveFile = filepath.Join(testRoot(ctx),
    285 			filepath.FromSlash(g.properties.PkgPath)+".a")
    286 	}
    287 
    288 	ctx.VisitDepsDepthFirstIf(isGoPluginFor(name),
    289 		func(module blueprint.Module) { hasPlugins = true })
    290 	if hasPlugins {
    291 		pluginSrc = filepath.Join(moduleGenSrcDir(ctx), "plugin.go")
    292 		genSrcs = append(genSrcs, pluginSrc)
    293 	}
    294 
    295 	// We only actually want to build the builder modules if we're running as
    296 	// minibp (i.e. we're generating a bootstrap Ninja file).  This is to break
    297 	// the circular dependence that occurs when the builder requires a new Ninja
    298 	// file to be built, but building a new ninja file requires the builder to
    299 	// be built.
    300 	if g.config.stage == g.BuildStage() {
    301 		var deps []string
    302 
    303 		if hasPlugins && !buildGoPluginLoader(ctx, g.properties.PkgPath, pluginSrc, g.config.stage) {
    304 			return
    305 		}
    306 
    307 		if g.config.runGoTests {
    308 			deps = buildGoTest(ctx, testRoot(ctx), g.testArchiveFile,
    309 				g.properties.PkgPath, g.properties.Srcs, genSrcs,
    310 				g.properties.TestSrcs)
    311 		}
    312 
    313 		buildGoPackage(ctx, g.pkgRoot, g.properties.PkgPath, g.archiveFile,
    314 			g.properties.Srcs, genSrcs, deps)
    315 	} else if g.config.stage != StageBootstrap {
    316 		if len(g.properties.TestSrcs) > 0 && g.config.runGoTests {
    317 			phonyGoTarget(ctx, g.testArchiveFile, g.properties.TestSrcs, nil, nil)
    318 		}
    319 		phonyGoTarget(ctx, g.archiveFile, g.properties.Srcs, genSrcs, nil)
    320 	}
    321 }
    322 
    323 // A goBinary is a module for building executable binaries from Go sources.
    324 type goBinary struct {
    325 	properties struct {
    326 		Srcs           []string
    327 		TestSrcs       []string
    328 		PrimaryBuilder bool
    329 	}
    330 
    331 	// The path of the test .a file that is to be built.
    332 	testArchiveFile string
    333 
    334 	// The bootstrap Config
    335 	config *Config
    336 
    337 	// The stage in which this module should be built
    338 	buildStage Stage
    339 }
    340 
    341 func newGoBinaryModuleFactory(config *Config, buildStage Stage) func() (blueprint.Module, []interface{}) {
    342 	return func() (blueprint.Module, []interface{}) {
    343 		module := &goBinary{
    344 			config:     config,
    345 			buildStage: buildStage,
    346 		}
    347 		return module, []interface{}{&module.properties}
    348 	}
    349 }
    350 
    351 func (g *goBinary) GoTestTarget() string {
    352 	return g.testArchiveFile
    353 }
    354 
    355 func (g *goBinary) BuildStage() Stage {
    356 	return g.buildStage
    357 }
    358 
    359 func (g *goBinary) SetBuildStage(buildStage Stage) {
    360 	g.buildStage = buildStage
    361 }
    362 
    363 func (g *goBinary) GenerateBuildActions(ctx blueprint.ModuleContext) {
    364 	var (
    365 		name        = ctx.ModuleName()
    366 		objDir      = moduleObjDir(ctx)
    367 		archiveFile = filepath.Join(objDir, name+".a")
    368 		aoutFile    = filepath.Join(objDir, "a.out")
    369 		binaryFile  = filepath.Join("$BinDir", name)
    370 		hasPlugins  = false
    371 		pluginSrc   = ""
    372 		genSrcs     = []string{}
    373 	)
    374 
    375 	if len(g.properties.TestSrcs) > 0 && g.config.runGoTests {
    376 		g.testArchiveFile = filepath.Join(testRoot(ctx), name+".a")
    377 	}
    378 
    379 	ctx.VisitDepsDepthFirstIf(isGoPluginFor(name),
    380 		func(module blueprint.Module) { hasPlugins = true })
    381 	if hasPlugins {
    382 		pluginSrc = filepath.Join(moduleGenSrcDir(ctx), "plugin.go")
    383 		genSrcs = append(genSrcs, pluginSrc)
    384 	}
    385 
    386 	// We only actually want to build the builder modules if we're running as
    387 	// minibp (i.e. we're generating a bootstrap Ninja file).  This is to break
    388 	// the circular dependence that occurs when the builder requires a new Ninja
    389 	// file to be built, but building a new ninja file requires the builder to
    390 	// be built.
    391 	if g.config.stage == g.BuildStage() {
    392 		var deps []string
    393 
    394 		if hasPlugins && !buildGoPluginLoader(ctx, "main", pluginSrc, g.config.stage) {
    395 			return
    396 		}
    397 
    398 		if g.config.runGoTests {
    399 			deps = buildGoTest(ctx, testRoot(ctx), g.testArchiveFile,
    400 				name, g.properties.Srcs, genSrcs, g.properties.TestSrcs)
    401 		}
    402 
    403 		buildGoPackage(ctx, objDir, name, archiveFile, g.properties.Srcs, genSrcs, deps)
    404 
    405 		var libDirFlags []string
    406 		ctx.VisitDepsDepthFirstIf(isGoPackageProducer,
    407 			func(module blueprint.Module) {
    408 				dep := module.(goPackageProducer)
    409 				libDir := dep.GoPkgRoot()
    410 				libDirFlags = append(libDirFlags, "-L "+libDir)
    411 			})
    412 
    413 		linkArgs := map[string]string{}
    414 		if len(libDirFlags) > 0 {
    415 			linkArgs["libDirFlags"] = strings.Join(libDirFlags, " ")
    416 		}
    417 
    418 		ctx.Build(pctx, blueprint.BuildParams{
    419 			Rule:    link,
    420 			Outputs: []string{aoutFile},
    421 			Inputs:  []string{archiveFile},
    422 			Args:    linkArgs,
    423 		})
    424 
    425 		ctx.Build(pctx, blueprint.BuildParams{
    426 			Rule:    cp,
    427 			Outputs: []string{binaryFile},
    428 			Inputs:  []string{aoutFile},
    429 		})
    430 	} else if g.config.stage != StageBootstrap {
    431 		if len(g.properties.TestSrcs) > 0 && g.config.runGoTests {
    432 			phonyGoTarget(ctx, g.testArchiveFile, g.properties.TestSrcs, nil, nil)
    433 		}
    434 
    435 		intermediates := []string{aoutFile, archiveFile}
    436 		phonyGoTarget(ctx, binaryFile, g.properties.Srcs, genSrcs, intermediates)
    437 	}
    438 }
    439 
    440 func buildGoPluginLoader(ctx blueprint.ModuleContext, pkgPath, pluginSrc string, stage Stage) bool {
    441 	ret := true
    442 	name := ctx.ModuleName()
    443 
    444 	var pluginPaths []string
    445 	ctx.VisitDepsDepthFirstIf(isGoPluginFor(name),
    446 		func(module blueprint.Module) {
    447 			plugin := module.(goPluginProvider)
    448 			pluginPaths = append(pluginPaths, plugin.GoPkgPath())
    449 			if stage == StageBootstrap {
    450 				ctx.OtherModuleErrorf(module, "plugin %q may not be included in core module %q",
    451 					ctx.OtherModuleName(module), name)
    452 				ret = false
    453 			}
    454 		})
    455 
    456 	ctx.Build(pctx, blueprint.BuildParams{
    457 		Rule:    pluginGenSrc,
    458 		Outputs: []string{pluginSrc},
    459 		Args: map[string]string{
    460 			"pkg":     pkgPath,
    461 			"plugins": strings.Join(pluginPaths, " "),
    462 		},
    463 	})
    464 
    465 	return ret
    466 }
    467 
    468 func buildGoPackage(ctx blueprint.ModuleContext, pkgRoot string,
    469 	pkgPath string, archiveFile string, srcs []string, genSrcs []string, orderDeps []string) {
    470 
    471 	srcDir := moduleSrcDir(ctx)
    472 	srcFiles := pathtools.PrefixPaths(srcs, srcDir)
    473 	srcFiles = append(srcFiles, genSrcs...)
    474 
    475 	var incFlags []string
    476 	var deps []string
    477 	ctx.VisitDepsDepthFirstIf(isGoPackageProducer,
    478 		func(module blueprint.Module) {
    479 			dep := module.(goPackageProducer)
    480 			incDir := dep.GoPkgRoot()
    481 			target := dep.GoPackageTarget()
    482 			incFlags = append(incFlags, "-I "+incDir)
    483 			deps = append(deps, target)
    484 		})
    485 
    486 	compileArgs := map[string]string{
    487 		"pkgPath": pkgPath,
    488 	}
    489 
    490 	if len(incFlags) > 0 {
    491 		compileArgs["incFlags"] = strings.Join(incFlags, " ")
    492 	}
    493 
    494 	ctx.Build(pctx, blueprint.BuildParams{
    495 		Rule:      compile,
    496 		Outputs:   []string{archiveFile},
    497 		Inputs:    srcFiles,
    498 		OrderOnly: orderDeps,
    499 		Implicits: deps,
    500 		Args:      compileArgs,
    501 	})
    502 }
    503 
    504 func buildGoTest(ctx blueprint.ModuleContext, testRoot, testPkgArchive,
    505 	pkgPath string, srcs, genSrcs, testSrcs []string) []string {
    506 
    507 	if len(testSrcs) == 0 {
    508 		return nil
    509 	}
    510 
    511 	srcDir := moduleSrcDir(ctx)
    512 	testFiles := pathtools.PrefixPaths(testSrcs, srcDir)
    513 
    514 	mainFile := filepath.Join(testRoot, "test.go")
    515 	testArchive := filepath.Join(testRoot, "test.a")
    516 	testFile := filepath.Join(testRoot, "test")
    517 	testPassed := filepath.Join(testRoot, "test.passed")
    518 
    519 	buildGoPackage(ctx, testRoot, pkgPath, testPkgArchive,
    520 		append(srcs, testSrcs...), genSrcs, nil)
    521 
    522 	ctx.Build(pctx, blueprint.BuildParams{
    523 		Rule:    goTestMain,
    524 		Outputs: []string{mainFile},
    525 		Inputs:  testFiles,
    526 		Args: map[string]string{
    527 			"pkg": pkgPath,
    528 		},
    529 	})
    530 
    531 	libDirFlags := []string{"-L " + testRoot}
    532 	ctx.VisitDepsDepthFirstIf(isGoPackageProducer,
    533 		func(module blueprint.Module) {
    534 			dep := module.(goPackageProducer)
    535 			libDir := dep.GoPkgRoot()
    536 			libDirFlags = append(libDirFlags, "-L "+libDir)
    537 		})
    538 
    539 	ctx.Build(pctx, blueprint.BuildParams{
    540 		Rule:      compile,
    541 		Outputs:   []string{testArchive},
    542 		Inputs:    []string{mainFile},
    543 		Implicits: []string{testPkgArchive},
    544 		Args: map[string]string{
    545 			"pkgPath":  "main",
    546 			"incFlags": "-I " + testRoot,
    547 		},
    548 	})
    549 
    550 	ctx.Build(pctx, blueprint.BuildParams{
    551 		Rule:    link,
    552 		Outputs: []string{testFile},
    553 		Inputs:  []string{testArchive},
    554 		Args: map[string]string{
    555 			"libDirFlags": strings.Join(libDirFlags, " "),
    556 		},
    557 	})
    558 
    559 	ctx.Build(pctx, blueprint.BuildParams{
    560 		Rule:    test,
    561 		Outputs: []string{testPassed},
    562 		Inputs:  []string{testFile},
    563 		Args: map[string]string{
    564 			"pkg":       pkgPath,
    565 			"pkgSrcDir": filepath.Dir(testFiles[0]),
    566 		},
    567 	})
    568 
    569 	return []string{testPassed}
    570 }
    571 
    572 func phonyGoTarget(ctx blueprint.ModuleContext, target string, srcs []string,
    573 	gensrcs []string, intermediates []string) {
    574 
    575 	var depTargets []string
    576 	ctx.VisitDepsDepthFirstIf(isGoPackageProducer,
    577 		func(module blueprint.Module) {
    578 			dep := module.(goPackageProducer)
    579 			target := dep.GoPackageTarget()
    580 			depTargets = append(depTargets, target)
    581 		})
    582 
    583 	moduleDir := ctx.ModuleDir()
    584 	srcs = pathtools.PrefixPaths(srcs, filepath.Join("$srcDir", moduleDir))
    585 	srcs = append(srcs, gensrcs...)
    586 
    587 	ctx.Build(pctx, blueprint.BuildParams{
    588 		Rule:      phony,
    589 		Outputs:   []string{target},
    590 		Inputs:    srcs,
    591 		Implicits: depTargets,
    592 	})
    593 
    594 	// If one of the source files gets deleted or renamed that will prevent the
    595 	// re-bootstrapping happening because it depends on the missing source file.
    596 	// To get around this we add a build statement using the built-in phony rule
    597 	// for each source file, which will cause Ninja to treat it as dirty if its
    598 	// missing.
    599 	for _, src := range srcs {
    600 		ctx.Build(pctx, blueprint.BuildParams{
    601 			Rule:    blueprint.Phony,
    602 			Outputs: []string{src},
    603 		})
    604 	}
    605 
    606 	// If there is no rule to build the intermediate files of a bootstrap go package
    607 	// the cleanup phase of the primary builder will delete the intermediate files,
    608 	// forcing an unnecessary rebuild.  Add phony rules for all of them.
    609 	for _, intermediate := range intermediates {
    610 		ctx.Build(pctx, blueprint.BuildParams{
    611 			Rule:    blueprint.Phony,
    612 			Outputs: []string{intermediate},
    613 		})
    614 	}
    615 
    616 }
    617 
    618 type singleton struct {
    619 	// The bootstrap Config
    620 	config *Config
    621 }
    622 
    623 func newSingletonFactory(config *Config) func() blueprint.Singleton {
    624 	return func() blueprint.Singleton {
    625 		return &singleton{
    626 			config: config,
    627 		}
    628 	}
    629 }
    630 
    631 func (s *singleton) GenerateBuildActions(ctx blueprint.SingletonContext) {
    632 	// Find the module that's marked as the "primary builder", which means it's
    633 	// creating the binary that we'll use to generate the non-bootstrap
    634 	// build.ninja file.
    635 	var primaryBuilders []*goBinary
    636 	// rebootstrapDeps contains modules that will be built in StageBootstrap
    637 	var rebootstrapDeps []string
    638 	// primaryRebootstrapDeps contains modules that will be built in StagePrimary
    639 	var primaryRebootstrapDeps []string
    640 	ctx.VisitAllModulesIf(isBootstrapBinaryModule,
    641 		func(module blueprint.Module) {
    642 			binaryModule := module.(*goBinary)
    643 			binaryModuleName := ctx.ModuleName(binaryModule)
    644 			binaryModulePath := filepath.Join("$BinDir", binaryModuleName)
    645 
    646 			if binaryModule.BuildStage() == StageBootstrap {
    647 				rebootstrapDeps = append(rebootstrapDeps, binaryModulePath)
    648 			} else {
    649 				primaryRebootstrapDeps = append(primaryRebootstrapDeps, binaryModulePath)
    650 			}
    651 			if binaryModule.properties.PrimaryBuilder {
    652 				primaryBuilders = append(primaryBuilders, binaryModule)
    653 			}
    654 		})
    655 
    656 	var primaryBuilderName, primaryBuilderExtraFlags string
    657 	switch len(primaryBuilders) {
    658 	case 0:
    659 		// If there's no primary builder module then that means we'll use minibp
    660 		// as the primary builder.  We can trigger its primary builder mode with
    661 		// the -p flag.
    662 		primaryBuilderName = "minibp"
    663 		primaryBuilderExtraFlags = "-p"
    664 
    665 	case 1:
    666 		primaryBuilderName = ctx.ModuleName(primaryBuilders[0])
    667 
    668 	default:
    669 		ctx.Errorf("multiple primary builder modules present:")
    670 		for _, primaryBuilder := range primaryBuilders {
    671 			ctx.ModuleErrorf(primaryBuilder, "<-- module %s",
    672 				ctx.ModuleName(primaryBuilder))
    673 		}
    674 		return
    675 	}
    676 
    677 	primaryBuilderFile := filepath.Join("$BinDir", primaryBuilderName)
    678 
    679 	if s.config.runGoTests {
    680 		primaryBuilderExtraFlags += " -t"
    681 	}
    682 
    683 	// Get the filename of the top-level Blueprints file to pass to minibp.
    684 	topLevelBlueprints := filepath.Join("$srcDir",
    685 		filepath.Base(s.config.topLevelBlueprintsFile))
    686 
    687 	rebootstrapDeps = append(rebootstrapDeps, topLevelBlueprints)
    688 	primaryRebootstrapDeps = append(primaryRebootstrapDeps, topLevelBlueprints)
    689 
    690 	mainNinjaFile := filepath.Join(bootstrapDir, "main.ninja.in")
    691 	mainNinjaTimestampFile := mainNinjaFile + ".timestamp"
    692 	mainNinjaTimestampDepFile := mainNinjaTimestampFile + ".d"
    693 	primaryBuilderNinjaFile := filepath.Join(bootstrapDir, "primary.ninja.in")
    694 	primaryBuilderNinjaTimestampFile := primaryBuilderNinjaFile + ".timestamp"
    695 	primaryBuilderNinjaTimestampDepFile := primaryBuilderNinjaTimestampFile + ".d"
    696 	bootstrapNinjaFile := filepath.Join(bootstrapDir, "bootstrap.ninja.in")
    697 	docsFile := filepath.Join(docsDir, primaryBuilderName+".html")
    698 
    699 	primaryRebootstrapDeps = append(primaryRebootstrapDeps, docsFile)
    700 
    701 	// If the tests change, be sure to re-run them. These need to be
    702 	// dependencies for the ninja file so that it's updated after these
    703 	// run. Otherwise we'd never leave the bootstrap stage, since the
    704 	// timestamp file would be newer than the ninja file.
    705 	ctx.VisitAllModulesIf(isGoTestProducer,
    706 		func(module blueprint.Module) {
    707 			testModule := module.(goTestProducer)
    708 			target := testModule.GoTestTarget()
    709 			if target != "" {
    710 				if testModule.BuildStage() == StageBootstrap {
    711 					rebootstrapDeps = append(rebootstrapDeps, target)
    712 				} else {
    713 					primaryRebootstrapDeps = append(primaryRebootstrapDeps, target)
    714 				}
    715 			}
    716 		})
    717 
    718 	switch s.config.stage {
    719 	case StageBootstrap:
    720 		// We're generating a bootstrapper Ninja file, so we need to set things
    721 		// up to rebuild the build.ninja file using the primary builder.
    722 
    723 		// BuildDir must be different between the three stages, otherwise the
    724 		// cleanup process will remove files from the other builds.
    725 		ctx.SetNinjaBuildDir(pctx, miniBootstrapDir)
    726 
    727 		// Generate the Ninja file to build the primary builder. Save the
    728 		// timestamps and deps, so that we can come back to this stage if
    729 		// it needs to be regenerated.
    730 		primarybp := ctx.Rule(pctx, "primarybp",
    731 			blueprint.RuleParams{
    732 				Command: fmt.Sprintf("%s --build-primary $runTests -m $bootstrapManifest "+
    733 					"--timestamp $timestamp --timestampdep $timestampdep "+
    734 					"-b $buildDir -d $outfile.d -o $outfile $in", minibpFile),
    735 				Description: "minibp $outfile",
    736 				Depfile:     "$outfile.d",
    737 			},
    738 			"runTests", "timestamp", "timestampdep", "outfile")
    739 
    740 		args := map[string]string{
    741 			"outfile":      primaryBuilderNinjaFile,
    742 			"timestamp":    primaryBuilderNinjaTimestampFile,
    743 			"timestampdep": primaryBuilderNinjaTimestampDepFile,
    744 		}
    745 
    746 		if s.config.runGoTests {
    747 			args["runTests"] = "-t"
    748 		}
    749 
    750 		ctx.Build(pctx, blueprint.BuildParams{
    751 			Rule:      primarybp,
    752 			Outputs:   []string{primaryBuilderNinjaFile, primaryBuilderNinjaTimestampFile},
    753 			Inputs:    []string{topLevelBlueprints},
    754 			Implicits: rebootstrapDeps,
    755 			Args:      args,
    756 		})
    757 
    758 		// Rebuild the bootstrap Ninja file using the minibp that we just built.
    759 		// If this produces a difference, choosestage will retrigger this stage.
    760 		minibp := ctx.Rule(pctx, "minibp",
    761 			blueprint.RuleParams{
    762 				Command: fmt.Sprintf("%s $runTests -m $bootstrapManifest "+
    763 					"-b $buildDir -d $out.d -o $out $in", minibpFile),
    764 				// $bootstrapManifest is here so that when it is updated, we
    765 				// force a rebuild of bootstrap.ninja.in. chooseStage should
    766 				// have already copied the new version over, but kept the old
    767 				// timestamps to force this regeneration.
    768 				CommandDeps: []string{"$bootstrapManifest", minibpFile},
    769 				Description: "minibp $out",
    770 				Generator:   true,
    771 				Depfile:     "$out.d",
    772 			},
    773 			"runTests")
    774 
    775 		args = map[string]string{}
    776 
    777 		if s.config.runGoTests {
    778 			args["runTests"] = "-t"
    779 		}
    780 
    781 		ctx.Build(pctx, blueprint.BuildParams{
    782 			Rule:    minibp,
    783 			Outputs: []string{bootstrapNinjaFile},
    784 			Inputs:  []string{topLevelBlueprints},
    785 			Args:    args,
    786 		})
    787 
    788 		// When the current build.ninja file is a bootstrapper, we always want
    789 		// to have it replace itself with a non-bootstrapper build.ninja.  To
    790 		// accomplish that we depend on a file that should never exist and
    791 		// "build" it using Ninja's built-in phony rule.
    792 		notAFile := filepath.Join(bootstrapDir, "notAFile")
    793 		ctx.Build(pctx, blueprint.BuildParams{
    794 			Rule:    blueprint.Phony,
    795 			Outputs: []string{notAFile},
    796 		})
    797 
    798 		ctx.Build(pctx, blueprint.BuildParams{
    799 			Rule:      chooseStage,
    800 			Outputs:   []string{filepath.Join(bootstrapDir, "build.ninja.in")},
    801 			Inputs:    []string{bootstrapNinjaFile, primaryBuilderNinjaFile},
    802 			Implicits: []string{notAFile},
    803 			Args: map[string]string{
    804 				"current": bootstrapNinjaFile,
    805 			},
    806 		})
    807 
    808 	case StagePrimary:
    809 		// We're generating a bootstrapper Ninja file, so we need to set things
    810 		// up to rebuild the build.ninja file using the primary builder.
    811 
    812 		// BuildDir must be different between the three stages, otherwise the
    813 		// cleanup process will remove files from the other builds.
    814 		ctx.SetNinjaBuildDir(pctx, bootstrapDir)
    815 
    816 		// We generate the depfile here that includes the dependencies for all
    817 		// the Blueprints files that contribute to generating the big build
    818 		// manifest (build.ninja file).  This depfile will be used by the non-
    819 		// bootstrap build manifest to determine whether it should touch the
    820 		// timestamp file to trigger a re-bootstrap.
    821 		bigbp := ctx.Rule(pctx, "bigbp",
    822 			blueprint.RuleParams{
    823 				Command: fmt.Sprintf("%s %s -m $bootstrapManifest "+
    824 					"--timestamp $timestamp --timestampdep $timestampdep "+
    825 					"-b $buildDir -d $outfile.d -o $outfile $in", primaryBuilderFile,
    826 					primaryBuilderExtraFlags),
    827 				Description: fmt.Sprintf("%s $outfile", primaryBuilderName),
    828 				Depfile:     "$outfile.d",
    829 			},
    830 			"timestamp", "timestampdep", "outfile")
    831 
    832 		ctx.Build(pctx, blueprint.BuildParams{
    833 			Rule:      bigbp,
    834 			Outputs:   []string{mainNinjaFile, mainNinjaTimestampFile},
    835 			Inputs:    []string{topLevelBlueprints},
    836 			Implicits: primaryRebootstrapDeps,
    837 			Args: map[string]string{
    838 				"timestamp":    mainNinjaTimestampFile,
    839 				"timestampdep": mainNinjaTimestampDepFile,
    840 				"outfile":      mainNinjaFile,
    841 			},
    842 		})
    843 
    844 		// Generate build system docs for the primary builder.  Generating docs reads the source
    845 		// files used to build the primary builder, but that dependency will be picked up through
    846 		// the dependency on the primary builder itself.  There are no dependencies on the
    847 		// Blueprints files, as any relevant changes to the Blueprints files would have caused
    848 		// a rebuild of the primary builder.
    849 		bigbpDocs := ctx.Rule(pctx, "bigbpDocs",
    850 			blueprint.RuleParams{
    851 				Command: fmt.Sprintf("%s %s -b $buildDir --docs $out %s", primaryBuilderFile,
    852 					primaryBuilderExtraFlags, topLevelBlueprints),
    853 				CommandDeps: []string{primaryBuilderFile},
    854 				Description: fmt.Sprintf("%s docs $out", primaryBuilderName),
    855 			})
    856 
    857 		ctx.Build(pctx, blueprint.BuildParams{
    858 			Rule:    bigbpDocs,
    859 			Outputs: []string{docsFile},
    860 		})
    861 
    862 		// Detect whether we need to rebuild the primary stage by going back to
    863 		// the bootstrapper. If this is newer than the primaryBuilderNinjaFile,
    864 		// then chooseStage will trigger a rebuild of primaryBuilderNinjaFile by
    865 		// returning to the bootstrap stage.
    866 		ctx.Build(pctx, blueprint.BuildParams{
    867 			Rule:      touch,
    868 			Outputs:   []string{primaryBuilderNinjaTimestampFile},
    869 			Implicits: rebootstrapDeps,
    870 			Args: map[string]string{
    871 				"depfile":   primaryBuilderNinjaTimestampDepFile,
    872 				"generator": "true",
    873 			},
    874 		})
    875 
    876 		// When the current build.ninja file is a bootstrapper, we always want
    877 		// to have it replace itself with a non-bootstrapper build.ninja.  To
    878 		// accomplish that we depend on a file that should never exist and
    879 		// "build" it using Ninja's built-in phony rule.
    880 		notAFile := filepath.Join(bootstrapDir, "notAFile")
    881 		ctx.Build(pctx, blueprint.BuildParams{
    882 			Rule:    blueprint.Phony,
    883 			Outputs: []string{notAFile},
    884 		})
    885 
    886 		ctx.Build(pctx, blueprint.BuildParams{
    887 			Rule:      chooseStage,
    888 			Outputs:   []string{filepath.Join(bootstrapDir, "build.ninja.in")},
    889 			Inputs:    []string{bootstrapNinjaFile, primaryBuilderNinjaFile, mainNinjaFile},
    890 			Implicits: []string{notAFile, primaryBuilderNinjaTimestampFile},
    891 			Args: map[string]string{
    892 				"current": primaryBuilderNinjaFile,
    893 			},
    894 		})
    895 
    896 		// Create this phony rule so that upgrades don't delete these during
    897 		// cleanup
    898 		ctx.Build(pctx, blueprint.BuildParams{
    899 			Rule:    blueprint.Phony,
    900 			Outputs: []string{bootstrapNinjaFile},
    901 		})
    902 
    903 	case StageMain:
    904 		ctx.SetNinjaBuildDir(pctx, "${buildDir}")
    905 
    906 		// We're generating a non-bootstrapper Ninja file, so we need to set it
    907 		// up to re-bootstrap if necessary. We do this by making build.ninja.in
    908 		// depend on the various Ninja files, the source build.ninja.in, and
    909 		// on the timestamp files.
    910 		//
    911 		// The timestamp files themselves are set up with the same dependencies
    912 		// as their Ninja files, including their own depfile. If any of the
    913 		// dependencies need to be updated, we'll touch the timestamp file,
    914 		// which will tell choosestage to switch to the stage that rebuilds
    915 		// that Ninja file.
    916 		ctx.Build(pctx, blueprint.BuildParams{
    917 			Rule:      touch,
    918 			Outputs:   []string{primaryBuilderNinjaTimestampFile},
    919 			Implicits: rebootstrapDeps,
    920 			Args: map[string]string{
    921 				"depfile":   primaryBuilderNinjaTimestampDepFile,
    922 				"generator": "true",
    923 			},
    924 		})
    925 
    926 		ctx.Build(pctx, blueprint.BuildParams{
    927 			Rule:      touch,
    928 			Outputs:   []string{mainNinjaTimestampFile},
    929 			Implicits: primaryRebootstrapDeps,
    930 			Args: map[string]string{
    931 				"depfile":   mainNinjaTimestampDepFile,
    932 				"generator": "true",
    933 			},
    934 		})
    935 
    936 		ctx.Build(pctx, blueprint.BuildParams{
    937 			Rule:      chooseStage,
    938 			Outputs:   []string{filepath.Join(bootstrapDir, "build.ninja.in")},
    939 			Inputs:    []string{bootstrapNinjaFile, primaryBuilderNinjaFile, mainNinjaFile},
    940 			Implicits: []string{primaryBuilderNinjaTimestampFile, mainNinjaTimestampFile},
    941 			Args: map[string]string{
    942 				"current":   mainNinjaFile,
    943 				"generator": "true",
    944 			},
    945 		})
    946 
    947 		// Create this phony rule so that upgrades don't delete these during
    948 		// cleanup
    949 		ctx.Build(pctx, blueprint.BuildParams{
    950 			Rule:    blueprint.Phony,
    951 			Outputs: []string{mainNinjaFile, docsFile, "$bootstrapManifest"},
    952 		})
    953 
    954 		if primaryBuilderName == "minibp" {
    955 			// This is a standalone Blueprint build, so we copy the minibp
    956 			// binary to the "bin" directory to make it easier to find.
    957 			finalMinibp := filepath.Join("$buildDir", "bin", primaryBuilderName)
    958 			ctx.Build(pctx, blueprint.BuildParams{
    959 				Rule:    cp,
    960 				Inputs:  []string{primaryBuilderFile},
    961 				Outputs: []string{finalMinibp},
    962 			})
    963 		}
    964 	}
    965 
    966 	ctx.Build(pctx, blueprint.BuildParams{
    967 		Rule:    bootstrap,
    968 		Outputs: []string{"$buildDir/build.ninja"},
    969 		Inputs:  []string{filepath.Join(bootstrapDir, "build.ninja.in")},
    970 	})
    971 }
    972 
    973 // packageRoot returns the module-specific package root directory path.  This
    974 // directory is where the final package .a files are output and where dependant
    975 // modules search for this package via -I arguments.
    976 func packageRoot(ctx blueprint.ModuleContext) string {
    977 	return filepath.Join(bootstrapDir, ctx.ModuleName(), "pkg")
    978 }
    979 
    980 // testRoot returns the module-specific package root directory path used for
    981 // building tests. The .a files generated here will include everything from
    982 // packageRoot, plus the test-only code.
    983 func testRoot(ctx blueprint.ModuleContext) string {
    984 	return filepath.Join(bootstrapDir, ctx.ModuleName(), "test")
    985 }
    986 
    987 // moduleSrcDir returns the path of the directory that all source file paths are
    988 // specified relative to.
    989 func moduleSrcDir(ctx blueprint.ModuleContext) string {
    990 	return filepath.Join("$srcDir", ctx.ModuleDir())
    991 }
    992 
    993 // moduleObjDir returns the module-specific object directory path.
    994 func moduleObjDir(ctx blueprint.ModuleContext) string {
    995 	return filepath.Join(bootstrapDir, ctx.ModuleName(), "obj")
    996 }
    997 
    998 // moduleGenSrcDir returns the module-specific generated sources path.
    999 func moduleGenSrcDir(ctx blueprint.ModuleContext) string {
   1000 	return filepath.Join(bootstrapDir, ctx.ModuleName(), "gen")
   1001 }
   1002