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