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 15package bootstrap 16 17import ( 18 "fmt" 19 "path/filepath" 20 "strings" 21 22 "github.com/google/blueprint" 23 "github.com/google/blueprint/pathtools" 24) 25 26const bootstrapSubDir = ".bootstrap" 27const miniBootstrapSubDir = ".minibootstrap" 28 29var ( 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 126type bootstrapGoCore interface { 127 BuildStage() Stage 128 SetBuildStage(Stage) 129} 130 131func 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 143func 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 151type goPackageProducer interface { 152 GoPkgRoot() string 153 GoPackageTarget() string 154} 155 156func isGoPackageProducer(module blueprint.Module) bool { 157 _, ok := module.(goPackageProducer) 158 return ok 159} 160 161type goTestProducer interface { 162 GoTestTarget() string 163 BuildStage() Stage 164} 165 166func isGoTestProducer(module blueprint.Module) bool { 167 _, ok := module.(goTestProducer) 168 return ok 169} 170 171type goPluginProvider interface { 172 GoPkgPath() string 173 IsPluginFor(string) bool 174} 175 176func 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 185func isBootstrapModule(module blueprint.Module) bool { 186 _, isPackage := module.(*goPackage) 187 _, isBinary := module.(*goBinary) 188 return isPackage || isBinary 189} 190 191func isBootstrapBinaryModule(module blueprint.Module) bool { 192 _, isBinary := module.(*goBinary) 193 return isBinary 194} 195 196// A goPackage is a module for building Go packages. 197type 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 222var _ goPackageProducer = (*goPackage)(nil) 223 224func 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 234func (g *goPackage) GoPkgPath() string { 235 return g.properties.PkgPath 236} 237 238func (g *goPackage) GoPkgRoot() string { 239 return g.pkgRoot 240} 241 242func (g *goPackage) GoPackageTarget() string { 243 return g.archiveFile 244} 245 246func (g *goPackage) GoTestTarget() string { 247 return g.testArchiveFile 248} 249 250func (g *goPackage) BuildStage() Stage { 251 return g.buildStage 252} 253 254func (g *goPackage) SetBuildStage(buildStage Stage) { 255 g.buildStage = buildStage 256} 257 258func (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 267func (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. 324type 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 341func 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 351func (g *goBinary) GoTestTarget() string { 352 return g.testArchiveFile 353} 354 355func (g *goBinary) BuildStage() Stage { 356 return g.buildStage 357} 358 359func (g *goBinary) SetBuildStage(buildStage Stage) { 360 g.buildStage = buildStage 361} 362 363func (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 440func 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 468func 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 504func 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 572func 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 618type singleton struct { 619 // The bootstrap Config 620 config *Config 621} 622 623func newSingletonFactory(config *Config) func() blueprint.Singleton { 624 return func() blueprint.Singleton { 625 return &singleton{ 626 config: config, 627 } 628 } 629} 630 631func (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. 976func 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. 983func 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. 989func moduleSrcDir(ctx blueprint.ModuleContext) string { 990 return filepath.Join("$srcDir", ctx.ModuleDir()) 991} 992 993// moduleObjDir returns the module-specific object directory path. 994func moduleObjDir(ctx blueprint.ModuleContext) string { 995 return filepath.Join(bootstrapDir, ctx.ModuleName(), "obj") 996} 997 998// moduleGenSrcDir returns the module-specific generated sources path. 999func moduleGenSrcDir(ctx blueprint.ModuleContext) string { 1000 return filepath.Join(bootstrapDir, ctx.ModuleName(), "gen") 1001} 1002