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