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