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 "runtime" 21 "strings" 22 23 "github.com/google/blueprint" 24 "github.com/google/blueprint/pathtools" 25) 26 27const bootstrapSubDir = ".bootstrap" 28const miniBootstrapSubDir = ".minibootstrap" 29 30var ( 31 pctx = blueprint.NewPackageContext("github.com/google/blueprint/bootstrap") 32 33 goTestMainCmd = pctx.StaticVariable("goTestMainCmd", filepath.Join(bootstrapDir, "bin", "gotestmain")) 34 goTestRunnerCmd = pctx.StaticVariable("goTestRunnerCmd", filepath.Join(bootstrapDir, "bin", "gotestrunner")) 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 touch = pctx.StaticRule("touch", 94 blueprint.RuleParams{ 95 Command: "touch $out", 96 Description: "touch $out", 97 }, 98 "depfile", "generator") 99 100 generateBuildNinja = pctx.StaticRule("build.ninja", 101 blueprint.RuleParams{ 102 Command: "$builder $extra -b $buildDir -d $out.d -o $out $in", 103 CommandDeps: []string{"$builder"}, 104 Description: "$builder $out", 105 Depfile: "$out.d", 106 Restat: true, 107 }, 108 "builder", "extra", "generator") 109 110 // Work around a Ninja issue. See https://github.com/martine/ninja/pull/634 111 phony = pctx.StaticRule("phony", 112 blueprint.RuleParams{ 113 Command: "# phony $out", 114 Description: "phony $out", 115 Generator: true, 116 }, 117 "depfile") 118 119 binDir = pctx.StaticVariable("BinDir", filepath.Join(bootstrapDir, "bin")) 120 minibpFile = filepath.Join("$BinDir", "minibp") 121 122 docsDir = filepath.Join(bootstrapDir, "docs") 123 toolDir = pctx.VariableFunc("ToolDir", func(config interface{}) (string, error) { 124 if c, ok := config.(ConfigBlueprintToolLocation); ok { 125 return c.BlueprintToolLocation(), nil 126 } 127 return filepath.Join("$buildDir", "bin"), nil 128 }) 129 130 bootstrapDir = filepath.Join("$buildDir", bootstrapSubDir) 131 miniBootstrapDir = filepath.Join("$buildDir", miniBootstrapSubDir) 132) 133 134type bootstrapGoCore interface { 135 BuildStage() Stage 136 SetBuildStage(Stage) 137} 138 139func propagateStageBootstrap(mctx blueprint.TopDownMutatorContext) { 140 if mod, ok := mctx.Module().(bootstrapGoCore); ok { 141 stage := mod.BuildStage() 142 143 mctx.VisitDirectDeps(func(mod blueprint.Module) { 144 if m, ok := mod.(bootstrapGoCore); ok && m.BuildStage() > stage { 145 m.SetBuildStage(stage) 146 } 147 }) 148 } 149} 150 151func pluginDeps(ctx blueprint.BottomUpMutatorContext) { 152 if pkg, ok := ctx.Module().(*goPackage); ok { 153 for _, plugin := range pkg.properties.PluginFor { 154 ctx.AddReverseDependency(ctx.Module(), nil, plugin) 155 } 156 } 157} 158 159type goPackageProducer interface { 160 GoPkgRoot() string 161 GoPackageTarget() string 162 GoTestTargets() []string 163} 164 165func isGoPackageProducer(module blueprint.Module) bool { 166 _, ok := module.(goPackageProducer) 167 return ok 168} 169 170type goPluginProvider interface { 171 GoPkgPath() string 172 IsPluginFor(string) bool 173} 174 175func isGoPluginFor(name string) func(blueprint.Module) bool { 176 return func(module blueprint.Module) bool { 177 if plugin, ok := module.(goPluginProvider); ok { 178 return plugin.IsPluginFor(name) 179 } 180 return false 181 } 182} 183 184func isBootstrapModule(module blueprint.Module) bool { 185 _, isPackage := module.(*goPackage) 186 _, isBinary := module.(*goBinary) 187 return isPackage || isBinary 188} 189 190func isBootstrapBinaryModule(module blueprint.Module) bool { 191 _, isBinary := module.(*goBinary) 192 return isBinary 193} 194 195// A goPackage is a module for building Go packages. 196type goPackage struct { 197 blueprint.SimpleName 198 properties struct { 199 Deps []string 200 PkgPath string 201 Srcs []string 202 TestSrcs []string 203 PluginFor []string 204 205 Darwin struct { 206 Srcs []string 207 TestSrcs []string 208 } 209 Linux struct { 210 Srcs []string 211 TestSrcs []string 212 } 213 214 // The stage in which this module should be built 215 BuildStage Stage `blueprint:"mutated"` 216 } 217 218 // The root dir in which the package .a file is located. The full .a file 219 // path will be "packageRoot/PkgPath.a" 220 pkgRoot string 221 222 // The path of the .a file that is to be built. 223 archiveFile string 224 225 // The path of the test result file. 226 testResultFile []string 227 228 // The bootstrap Config 229 config *Config 230} 231 232var _ goPackageProducer = (*goPackage)(nil) 233 234func newGoPackageModuleFactory(config *Config) func() (blueprint.Module, []interface{}) { 235 return func() (blueprint.Module, []interface{}) { 236 module := &goPackage{ 237 config: config, 238 } 239 module.properties.BuildStage = StageMain 240 return module, []interface{}{&module.properties, &module.SimpleName.Properties} 241 } 242} 243 244func (g *goPackage) DynamicDependencies(ctx blueprint.DynamicDependerModuleContext) []string { 245 return g.properties.Deps 246} 247 248func (g *goPackage) GoPkgPath() string { 249 return g.properties.PkgPath 250} 251 252func (g *goPackage) GoPkgRoot() string { 253 return g.pkgRoot 254} 255 256func (g *goPackage) GoPackageTarget() string { 257 return g.archiveFile 258} 259 260func (g *goPackage) GoTestTargets() []string { 261 return g.testResultFile 262} 263 264func (g *goPackage) BuildStage() Stage { 265 return g.properties.BuildStage 266} 267 268func (g *goPackage) SetBuildStage(buildStage Stage) { 269 g.properties.BuildStage = buildStage 270} 271 272func (g *goPackage) IsPluginFor(name string) bool { 273 for _, plugin := range g.properties.PluginFor { 274 if plugin == name { 275 return true 276 } 277 } 278 return false 279} 280 281func (g *goPackage) GenerateBuildActions(ctx blueprint.ModuleContext) { 282 var ( 283 name = ctx.ModuleName() 284 hasPlugins = false 285 pluginSrc = "" 286 genSrcs = []string{} 287 ) 288 289 if g.properties.PkgPath == "" { 290 ctx.ModuleErrorf("module %s did not specify a valid pkgPath", name) 291 return 292 } 293 294 g.pkgRoot = packageRoot(ctx) 295 g.archiveFile = filepath.Join(g.pkgRoot, 296 filepath.FromSlash(g.properties.PkgPath)+".a") 297 298 ctx.VisitDepsDepthFirstIf(isGoPluginFor(name), 299 func(module blueprint.Module) { hasPlugins = true }) 300 if hasPlugins { 301 pluginSrc = filepath.Join(moduleGenSrcDir(ctx), "plugin.go") 302 genSrcs = append(genSrcs, pluginSrc) 303 } 304 305 // We only actually want to build the builder modules if we're running as 306 // minibp (i.e. we're generating a bootstrap Ninja file). This is to break 307 // the circular dependence that occurs when the builder requires a new Ninja 308 // file to be built, but building a new ninja file requires the builder to 309 // be built. 310 if g.config.stage == g.BuildStage() { 311 if hasPlugins && !buildGoPluginLoader(ctx, g.properties.PkgPath, pluginSrc, g.config.stage) { 312 return 313 } 314 315 var srcs, testSrcs []string 316 if runtime.GOOS == "darwin" { 317 srcs = append(g.properties.Srcs, g.properties.Darwin.Srcs...) 318 testSrcs = append(g.properties.TestSrcs, g.properties.Darwin.TestSrcs...) 319 } else if runtime.GOOS == "linux" { 320 srcs = append(g.properties.Srcs, g.properties.Linux.Srcs...) 321 testSrcs = append(g.properties.TestSrcs, g.properties.Linux.TestSrcs...) 322 } 323 324 if g.config.runGoTests { 325 testArchiveFile := filepath.Join(testRoot(ctx), 326 filepath.FromSlash(g.properties.PkgPath)+".a") 327 g.testResultFile = buildGoTest(ctx, testRoot(ctx), testArchiveFile, 328 g.properties.PkgPath, srcs, genSrcs, 329 testSrcs) 330 } 331 332 buildGoPackage(ctx, g.pkgRoot, g.properties.PkgPath, g.archiveFile, 333 srcs, genSrcs) 334 } 335} 336 337// A goBinary is a module for building executable binaries from Go sources. 338type goBinary struct { 339 blueprint.SimpleName 340 properties struct { 341 Deps []string 342 Srcs []string 343 TestSrcs []string 344 PrimaryBuilder bool 345 346 Darwin struct { 347 Srcs []string 348 TestSrcs []string 349 } 350 Linux struct { 351 Srcs []string 352 TestSrcs []string 353 } 354 355 // The stage in which this module should be built 356 BuildStage Stage `blueprint:"mutated"` 357 } 358 359 // The bootstrap Config 360 config *Config 361} 362 363func newGoBinaryModuleFactory(config *Config, buildStage Stage) func() (blueprint.Module, []interface{}) { 364 return func() (blueprint.Module, []interface{}) { 365 module := &goBinary{ 366 config: config, 367 } 368 module.properties.BuildStage = buildStage 369 return module, []interface{}{&module.properties, &module.SimpleName.Properties} 370 } 371} 372 373func (g *goBinary) DynamicDependencies(ctx blueprint.DynamicDependerModuleContext) []string { 374 return g.properties.Deps 375} 376 377func (g *goBinary) BuildStage() Stage { 378 return g.properties.BuildStage 379} 380 381func (g *goBinary) SetBuildStage(buildStage Stage) { 382 g.properties.BuildStage = buildStage 383} 384 385func (g *goBinary) InstallPath() string { 386 if g.BuildStage() == StageMain { 387 return "$ToolDir" 388 } 389 return "$BinDir" 390} 391 392func (g *goBinary) GenerateBuildActions(ctx blueprint.ModuleContext) { 393 var ( 394 name = ctx.ModuleName() 395 objDir = moduleObjDir(ctx) 396 archiveFile = filepath.Join(objDir, name+".a") 397 testArchiveFile = filepath.Join(testRoot(ctx), name+".a") 398 aoutFile = filepath.Join(objDir, "a.out") 399 binaryFile = filepath.Join(g.InstallPath(), name) 400 hasPlugins = false 401 pluginSrc = "" 402 genSrcs = []string{} 403 ) 404 405 ctx.VisitDepsDepthFirstIf(isGoPluginFor(name), 406 func(module blueprint.Module) { hasPlugins = true }) 407 if hasPlugins { 408 pluginSrc = filepath.Join(moduleGenSrcDir(ctx), "plugin.go") 409 genSrcs = append(genSrcs, pluginSrc) 410 } 411 412 if g.config.stage == g.BuildStage() { 413 var deps []string 414 415 if hasPlugins && !buildGoPluginLoader(ctx, "main", pluginSrc, g.config.stage) { 416 return 417 } 418 419 var srcs, testSrcs []string 420 if runtime.GOOS == "darwin" { 421 srcs = append(g.properties.Srcs, g.properties.Darwin.Srcs...) 422 testSrcs = append(g.properties.TestSrcs, g.properties.Darwin.TestSrcs...) 423 } else if runtime.GOOS == "linux" { 424 srcs = append(g.properties.Srcs, g.properties.Linux.Srcs...) 425 testSrcs = append(g.properties.TestSrcs, g.properties.Linux.TestSrcs...) 426 } 427 428 if g.config.runGoTests { 429 deps = buildGoTest(ctx, testRoot(ctx), testArchiveFile, 430 name, srcs, genSrcs, testSrcs) 431 } 432 433 buildGoPackage(ctx, objDir, name, archiveFile, srcs, genSrcs) 434 435 var libDirFlags []string 436 ctx.VisitDepsDepthFirstIf(isGoPackageProducer, 437 func(module blueprint.Module) { 438 dep := module.(goPackageProducer) 439 libDir := dep.GoPkgRoot() 440 libDirFlags = append(libDirFlags, "-L "+libDir) 441 deps = append(deps, dep.GoTestTargets()...) 442 }) 443 444 linkArgs := map[string]string{} 445 if len(libDirFlags) > 0 { 446 linkArgs["libDirFlags"] = strings.Join(libDirFlags, " ") 447 } 448 449 ctx.Build(pctx, blueprint.BuildParams{ 450 Rule: link, 451 Outputs: []string{aoutFile}, 452 Inputs: []string{archiveFile}, 453 Args: linkArgs, 454 }) 455 456 ctx.Build(pctx, blueprint.BuildParams{ 457 Rule: cp, 458 Outputs: []string{binaryFile}, 459 Inputs: []string{aoutFile}, 460 OrderOnly: deps, 461 }) 462 } 463} 464 465func buildGoPluginLoader(ctx blueprint.ModuleContext, pkgPath, pluginSrc string, stage Stage) bool { 466 ret := true 467 name := ctx.ModuleName() 468 469 var pluginPaths []string 470 ctx.VisitDepsDepthFirstIf(isGoPluginFor(name), 471 func(module blueprint.Module) { 472 plugin := module.(goPluginProvider) 473 pluginPaths = append(pluginPaths, plugin.GoPkgPath()) 474 if stage == StageBootstrap { 475 ctx.OtherModuleErrorf(module, "plugin %q may not be included in core module %q", 476 ctx.OtherModuleName(module), name) 477 ret = false 478 } 479 }) 480 481 ctx.Build(pctx, blueprint.BuildParams{ 482 Rule: pluginGenSrc, 483 Outputs: []string{pluginSrc}, 484 Args: map[string]string{ 485 "pkg": pkgPath, 486 "plugins": strings.Join(pluginPaths, " "), 487 }, 488 }) 489 490 return ret 491} 492 493func buildGoPackage(ctx blueprint.ModuleContext, pkgRoot string, 494 pkgPath string, archiveFile string, srcs []string, genSrcs []string) { 495 496 srcDir := moduleSrcDir(ctx) 497 srcFiles := pathtools.PrefixPaths(srcs, srcDir) 498 srcFiles = append(srcFiles, genSrcs...) 499 500 var incFlags []string 501 var deps []string 502 ctx.VisitDepsDepthFirstIf(isGoPackageProducer, 503 func(module blueprint.Module) { 504 dep := module.(goPackageProducer) 505 incDir := dep.GoPkgRoot() 506 target := dep.GoPackageTarget() 507 incFlags = append(incFlags, "-I "+incDir) 508 deps = append(deps, target) 509 }) 510 511 compileArgs := map[string]string{ 512 "pkgPath": pkgPath, 513 } 514 515 if len(incFlags) > 0 { 516 compileArgs["incFlags"] = strings.Join(incFlags, " ") 517 } 518 519 ctx.Build(pctx, blueprint.BuildParams{ 520 Rule: compile, 521 Outputs: []string{archiveFile}, 522 Inputs: srcFiles, 523 Implicits: deps, 524 Args: compileArgs, 525 }) 526} 527 528func buildGoTest(ctx blueprint.ModuleContext, testRoot, testPkgArchive, 529 pkgPath string, srcs, genSrcs, testSrcs []string) []string { 530 531 if len(testSrcs) == 0 { 532 return nil 533 } 534 535 srcDir := moduleSrcDir(ctx) 536 testFiles := pathtools.PrefixPaths(testSrcs, srcDir) 537 538 mainFile := filepath.Join(testRoot, "test.go") 539 testArchive := filepath.Join(testRoot, "test.a") 540 testFile := filepath.Join(testRoot, "test") 541 testPassed := filepath.Join(testRoot, "test.passed") 542 543 buildGoPackage(ctx, testRoot, pkgPath, testPkgArchive, 544 append(srcs, testSrcs...), genSrcs) 545 546 ctx.Build(pctx, blueprint.BuildParams{ 547 Rule: goTestMain, 548 Outputs: []string{mainFile}, 549 Inputs: testFiles, 550 Args: map[string]string{ 551 "pkg": pkgPath, 552 }, 553 }) 554 555 libDirFlags := []string{"-L " + testRoot} 556 testDeps := []string{} 557 ctx.VisitDepsDepthFirstIf(isGoPackageProducer, 558 func(module blueprint.Module) { 559 dep := module.(goPackageProducer) 560 libDir := dep.GoPkgRoot() 561 libDirFlags = append(libDirFlags, "-L "+libDir) 562 testDeps = append(testDeps, dep.GoTestTargets()...) 563 }) 564 565 ctx.Build(pctx, blueprint.BuildParams{ 566 Rule: compile, 567 Outputs: []string{testArchive}, 568 Inputs: []string{mainFile}, 569 Implicits: []string{testPkgArchive}, 570 Args: map[string]string{ 571 "pkgPath": "main", 572 "incFlags": "-I " + testRoot, 573 }, 574 }) 575 576 ctx.Build(pctx, blueprint.BuildParams{ 577 Rule: link, 578 Outputs: []string{testFile}, 579 Inputs: []string{testArchive}, 580 Args: map[string]string{ 581 "libDirFlags": strings.Join(libDirFlags, " "), 582 }, 583 }) 584 585 ctx.Build(pctx, blueprint.BuildParams{ 586 Rule: test, 587 Outputs: []string{testPassed}, 588 Inputs: []string{testFile}, 589 OrderOnly: testDeps, 590 Args: map[string]string{ 591 "pkg": pkgPath, 592 "pkgSrcDir": filepath.Dir(testFiles[0]), 593 }, 594 }) 595 596 return []string{testPassed} 597} 598 599type singleton struct { 600 // The bootstrap Config 601 config *Config 602} 603 604func newSingletonFactory(config *Config) func() blueprint.Singleton { 605 return func() blueprint.Singleton { 606 return &singleton{ 607 config: config, 608 } 609 } 610} 611 612func (s *singleton) GenerateBuildActions(ctx blueprint.SingletonContext) { 613 // Find the module that's marked as the "primary builder", which means it's 614 // creating the binary that we'll use to generate the non-bootstrap 615 // build.ninja file. 616 var primaryBuilders []*goBinary 617 // blueprintTools contains blueprint go binaries that will be built in StageMain 618 var blueprintTools []string 619 ctx.VisitAllModulesIf(isBootstrapBinaryModule, 620 func(module blueprint.Module) { 621 binaryModule := module.(*goBinary) 622 binaryModuleName := ctx.ModuleName(binaryModule) 623 installPath := filepath.Join(binaryModule.InstallPath(), binaryModuleName) 624 625 if binaryModule.BuildStage() == StageMain { 626 blueprintTools = append(blueprintTools, installPath) 627 } 628 if binaryModule.properties.PrimaryBuilder { 629 primaryBuilders = append(primaryBuilders, binaryModule) 630 } 631 }) 632 633 var extraTestFlags string 634 if s.config.runGoTests { 635 extraTestFlags = " -t" 636 } 637 638 var primaryBuilderName, primaryBuilderExtraFlags string 639 switch len(primaryBuilders) { 640 case 0: 641 // If there's no primary builder module then that means we'll use minibp 642 // as the primary builder. We can trigger its primary builder mode with 643 // the -p flag. 644 primaryBuilderName = "minibp" 645 primaryBuilderExtraFlags = "-p" + extraTestFlags 646 647 case 1: 648 primaryBuilderName = ctx.ModuleName(primaryBuilders[0]) 649 primaryBuilderExtraFlags = extraTestFlags 650 651 default: 652 ctx.Errorf("multiple primary builder modules present:") 653 for _, primaryBuilder := range primaryBuilders { 654 ctx.ModuleErrorf(primaryBuilder, "<-- module %s", 655 ctx.ModuleName(primaryBuilder)) 656 } 657 return 658 } 659 660 primaryBuilderFile := filepath.Join("$BinDir", primaryBuilderName) 661 662 // Get the filename of the top-level Blueprints file to pass to minibp. 663 topLevelBlueprints := filepath.Join("$srcDir", 664 filepath.Base(s.config.topLevelBlueprintsFile)) 665 666 mainNinjaFile := filepath.Join("$buildDir", "build.ninja") 667 primaryBuilderNinjaFile := filepath.Join(bootstrapDir, "build.ninja") 668 bootstrapNinjaFileTemplate := filepath.Join(miniBootstrapDir, "build.ninja.in") 669 bootstrapNinjaFile := filepath.Join(miniBootstrapDir, "build.ninja") 670 docsFile := filepath.Join(docsDir, primaryBuilderName+".html") 671 672 switch s.config.stage { 673 case StageBootstrap: 674 // We're generating a bootstrapper Ninja file, so we need to set things 675 // up to rebuild the build.ninja file using the primary builder. 676 677 // BuildDir must be different between the three stages, otherwise the 678 // cleanup process will remove files from the other builds. 679 ctx.SetNinjaBuildDir(pctx, miniBootstrapDir) 680 681 // Generate the Ninja file to build the primary builder. 682 ctx.Build(pctx, blueprint.BuildParams{ 683 Rule: generateBuildNinja, 684 Outputs: []string{primaryBuilderNinjaFile}, 685 Inputs: []string{topLevelBlueprints}, 686 Args: map[string]string{ 687 "builder": minibpFile, 688 "extra": "--build-primary" + extraTestFlags, 689 }, 690 }) 691 692 // Rebuild the bootstrap Ninja file using the minibp that we just built. 693 ctx.Build(pctx, blueprint.BuildParams{ 694 Rule: generateBuildNinja, 695 Outputs: []string{bootstrapNinjaFileTemplate}, 696 Inputs: []string{topLevelBlueprints}, 697 Args: map[string]string{ 698 "builder": minibpFile, 699 "extra": extraTestFlags, 700 }, 701 }) 702 703 ctx.Build(pctx, blueprint.BuildParams{ 704 Rule: bootstrap, 705 Outputs: []string{bootstrapNinjaFile}, 706 Inputs: []string{bootstrapNinjaFileTemplate}, 707 }) 708 709 case StagePrimary: 710 // We're generating a bootstrapper Ninja file, so we need to set things 711 // up to rebuild the build.ninja file using the primary builder. 712 713 // BuildDir must be different between the three stages, otherwise the 714 // cleanup process will remove files from the other builds. 715 ctx.SetNinjaBuildDir(pctx, bootstrapDir) 716 717 // Add a way to rebuild the primary build.ninja so that globs works 718 ctx.Build(pctx, blueprint.BuildParams{ 719 Rule: generateBuildNinja, 720 Outputs: []string{primaryBuilderNinjaFile}, 721 Inputs: []string{topLevelBlueprints}, 722 Args: map[string]string{ 723 "builder": minibpFile, 724 "extra": "--build-primary" + extraTestFlags, 725 "generator": "true", 726 }, 727 }) 728 729 // Build the main build.ninja 730 ctx.Build(pctx, blueprint.BuildParams{ 731 Rule: generateBuildNinja, 732 Outputs: []string{mainNinjaFile}, 733 Inputs: []string{topLevelBlueprints}, 734 Args: map[string]string{ 735 "builder": primaryBuilderFile, 736 "extra": primaryBuilderExtraFlags, 737 }, 738 }) 739 740 // Generate build system docs for the primary builder. Generating docs reads the source 741 // files used to build the primary builder, but that dependency will be picked up through 742 // the dependency on the primary builder itself. There are no dependencies on the 743 // Blueprints files, as any relevant changes to the Blueprints files would have caused 744 // a rebuild of the primary builder. 745 bigbpDocs := ctx.Rule(pctx, "bigbpDocs", 746 blueprint.RuleParams{ 747 Command: fmt.Sprintf("%s %s -b $buildDir --docs $out %s", primaryBuilderFile, 748 primaryBuilderExtraFlags, topLevelBlueprints), 749 CommandDeps: []string{primaryBuilderFile}, 750 Description: fmt.Sprintf("%s docs $out", primaryBuilderName), 751 }) 752 753 ctx.Build(pctx, blueprint.BuildParams{ 754 Rule: bigbpDocs, 755 Outputs: []string{docsFile}, 756 }) 757 758 case StageMain: 759 ctx.SetNinjaBuildDir(pctx, "${buildDir}") 760 761 // Add a way to rebuild the main build.ninja in case it creates rules that 762 // it will depend on itself. (In Android, globs with soong_glob) 763 ctx.Build(pctx, blueprint.BuildParams{ 764 Rule: generateBuildNinja, 765 Outputs: []string{mainNinjaFile}, 766 Inputs: []string{topLevelBlueprints}, 767 Args: map[string]string{ 768 "builder": primaryBuilderFile, 769 "extra": primaryBuilderExtraFlags, 770 "generator": "true", 771 }, 772 }) 773 774 if primaryBuilderName == "minibp" { 775 // This is a standalone Blueprint build, so we copy the minibp 776 // binary to the "bin" directory to make it easier to find. 777 finalMinibp := filepath.Join("$buildDir", "bin", primaryBuilderName) 778 ctx.Build(pctx, blueprint.BuildParams{ 779 Rule: cp, 780 Inputs: []string{primaryBuilderFile}, 781 Outputs: []string{finalMinibp}, 782 }) 783 } 784 785 ctx.Build(pctx, blueprint.BuildParams{ 786 Rule: blueprint.Phony, 787 Outputs: []string{"blueprint_tools"}, 788 Inputs: blueprintTools, 789 }) 790 } 791} 792 793// packageRoot returns the module-specific package root directory path. This 794// directory is where the final package .a files are output and where dependant 795// modules search for this package via -I arguments. 796func packageRoot(ctx blueprint.ModuleContext) string { 797 return filepath.Join(bootstrapDir, ctx.ModuleName(), "pkg") 798} 799 800// testRoot returns the module-specific package root directory path used for 801// building tests. The .a files generated here will include everything from 802// packageRoot, plus the test-only code. 803func testRoot(ctx blueprint.ModuleContext) string { 804 return filepath.Join(bootstrapDir, ctx.ModuleName(), "test") 805} 806 807// moduleSrcDir returns the path of the directory that all source file paths are 808// specified relative to. 809func moduleSrcDir(ctx blueprint.ModuleContext) string { 810 return filepath.Join("$srcDir", ctx.ModuleDir()) 811} 812 813// moduleObjDir returns the module-specific object directory path. 814func moduleObjDir(ctx blueprint.ModuleContext) string { 815 return filepath.Join(bootstrapDir, ctx.ModuleName(), "obj") 816} 817 818// moduleGenSrcDir returns the module-specific generated sources path. 819func moduleGenSrcDir(ctx blueprint.ModuleContext) string { 820 return filepath.Join(bootstrapDir, ctx.ModuleName(), "gen") 821} 822