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