1// Copyright 2017 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 build 16 17import ( 18 "fmt" 19 "os" 20 "path/filepath" 21 "strconv" 22 "strings" 23 24 "android/soong/bazel" 25 "android/soong/ui/metrics" 26 "android/soong/ui/status" 27 28 "android/soong/shared" 29 30 "github.com/google/blueprint" 31 "github.com/google/blueprint/bootstrap" 32 "github.com/google/blueprint/microfactory" 33) 34 35const ( 36 availableEnvFile = "soong.environment.available" 37 usedEnvFile = "soong.environment.used" 38 39 soongBuildTag = "build" 40 bp2buildFilesTag = "bp2build_files" 41 bp2buildWorkspaceTag = "bp2build_workspace" 42 jsonModuleGraphTag = "modulegraph" 43 queryviewTag = "queryview" 44 apiBp2buildTag = "api_bp2build" 45 soongDocsTag = "soong_docs" 46 47 // bootstrapEpoch is used to determine if an incremental build is incompatible with the current 48 // version of bootstrap and needs cleaning before continuing the build. Increment this for 49 // incompatible changes, for example when moving the location of the bpglob binary that is 50 // executed during bootstrap before the primary builder has had a chance to update the path. 51 bootstrapEpoch = 1 52) 53 54func writeEnvironmentFile(_ Context, envFile string, envDeps map[string]string) error { 55 data, err := shared.EnvFileContents(envDeps) 56 if err != nil { 57 return err 58 } 59 60 return os.WriteFile(envFile, data, 0644) 61} 62 63// This uses Android.bp files and various tools to generate <builddir>/build.ninja. 64// 65// However, the execution of <builddir>/build.ninja happens later in 66// build/soong/ui/build/build.go#Build() 67// 68// We want to rely on as few prebuilts as possible, so we need to bootstrap 69// Soong. The process is as follows: 70// 71// 1. We use "Microfactory", a simple tool to compile Go code, to build 72// first itself, then soong_ui from soong_ui.bash. This binary contains 73// parts of soong_build that are needed to build itself. 74// 2. This simplified version of soong_build then reads the Blueprint files 75// that describe itself and emits .bootstrap/build.ninja that describes 76// how to build its full version and use that to produce the final Ninja 77// file Soong emits. 78// 3. soong_ui executes .bootstrap/build.ninja 79// 80// (After this, Kati is executed to parse the Makefiles, but that's not part of 81// bootstrapping Soong) 82 83// A tiny struct used to tell Blueprint that it's in bootstrap mode. It would 84// probably be nicer to use a flag in bootstrap.Args instead. 85type BlueprintConfig struct { 86 toolDir string 87 soongOutDir string 88 outDir string 89 runGoTests bool 90 debugCompilation bool 91 subninjas []string 92 primaryBuilderInvocations []bootstrap.PrimaryBuilderInvocation 93} 94 95func (c BlueprintConfig) HostToolDir() string { 96 return c.toolDir 97} 98 99func (c BlueprintConfig) SoongOutDir() string { 100 return c.soongOutDir 101} 102 103func (c BlueprintConfig) OutDir() string { 104 return c.outDir 105} 106 107func (c BlueprintConfig) RunGoTests() bool { 108 return c.runGoTests 109} 110 111func (c BlueprintConfig) DebugCompilation() bool { 112 return c.debugCompilation 113} 114 115func (c BlueprintConfig) Subninjas() []string { 116 return c.subninjas 117} 118 119func (c BlueprintConfig) PrimaryBuilderInvocations() []bootstrap.PrimaryBuilderInvocation { 120 return c.primaryBuilderInvocations 121} 122 123func environmentArgs(config Config, tag string) []string { 124 return []string{ 125 "--available_env", shared.JoinPath(config.SoongOutDir(), availableEnvFile), 126 "--used_env", config.UsedEnvFile(tag), 127 } 128} 129 130func writeEmptyFile(ctx Context, path string) { 131 err := os.MkdirAll(filepath.Dir(path), 0777) 132 if err != nil { 133 ctx.Fatalf("Failed to create parent directories of empty file '%s': %s", path, err) 134 } 135 136 if exists, err := fileExists(path); err != nil { 137 ctx.Fatalf("Failed to check if file '%s' exists: %s", path, err) 138 } else if !exists { 139 err = os.WriteFile(path, nil, 0666) 140 if err != nil { 141 ctx.Fatalf("Failed to create empty file '%s': %s", path, err) 142 } 143 } 144} 145 146func fileExists(path string) (bool, error) { 147 if _, err := os.Stat(path); os.IsNotExist(err) { 148 return false, nil 149 } else if err != nil { 150 return false, err 151 } 152 return true, nil 153} 154 155type PrimaryBuilderFactory struct { 156 name string 157 description string 158 config Config 159 output string 160 specificArgs []string 161 debugPort string 162} 163 164func (pb PrimaryBuilderFactory) primaryBuilderInvocation() bootstrap.PrimaryBuilderInvocation { 165 commonArgs := make([]string, 0, 0) 166 167 if !pb.config.skipSoongTests { 168 commonArgs = append(commonArgs, "-t") 169 } 170 171 if pb.config.multitreeBuild { 172 commonArgs = append(commonArgs, "--multitree-build") 173 } 174 if pb.config.buildFromTextStub { 175 commonArgs = append(commonArgs, "--build-from-text-stub") 176 } 177 178 commonArgs = append(commonArgs, "-l", filepath.Join(pb.config.FileListDir(), "Android.bp.list")) 179 invocationEnv := make(map[string]string) 180 if pb.debugPort != "" { 181 //debug mode 182 commonArgs = append(commonArgs, "--delve_listen", pb.debugPort, 183 "--delve_path", shared.ResolveDelveBinary()) 184 // GODEBUG=asyncpreemptoff=1 disables the preemption of goroutines. This 185 // is useful because the preemption happens by sending SIGURG to the OS 186 // thread hosting the goroutine in question and each signal results in 187 // work that needs to be done by Delve; it uses ptrace to debug the Go 188 // process and the tracer process must deal with every signal (it is not 189 // possible to selectively ignore SIGURG). This makes debugging slower, 190 // sometimes by an order of magnitude depending on luck. 191 // The original reason for adding async preemption to Go is here: 192 // https://github.com/golang/proposal/blob/master/design/24543-non-cooperative-preemption.md 193 invocationEnv["GODEBUG"] = "asyncpreemptoff=1" 194 } 195 196 var allArgs []string 197 allArgs = append(allArgs, pb.specificArgs...) 198 allArgs = append(allArgs, 199 "--globListDir", pb.name, 200 "--globFile", pb.config.NamedGlobFile(pb.name)) 201 202 allArgs = append(allArgs, commonArgs...) 203 allArgs = append(allArgs, environmentArgs(pb.config, pb.name)...) 204 if profileCpu := os.Getenv("SOONG_PROFILE_CPU"); profileCpu != "" { 205 allArgs = append(allArgs, "--cpuprofile", profileCpu+"."+pb.name) 206 } 207 if profileMem := os.Getenv("SOONG_PROFILE_MEM"); profileMem != "" { 208 allArgs = append(allArgs, "--memprofile", profileMem+"."+pb.name) 209 } 210 allArgs = append(allArgs, "Android.bp") 211 212 return bootstrap.PrimaryBuilderInvocation{ 213 Inputs: []string{"Android.bp"}, 214 Outputs: []string{pb.output}, 215 Args: allArgs, 216 Description: pb.description, 217 // NB: Changing the value of this environment variable will not result in a 218 // rebuild. The bootstrap Ninja file will change, but apparently Ninja does 219 // not consider changing the pool specified in a statement a change that's 220 // worth rebuilding for. 221 Console: os.Getenv("SOONG_UNBUFFERED_OUTPUT") == "1", 222 Env: invocationEnv, 223 } 224} 225 226// bootstrapEpochCleanup deletes files used by bootstrap during incremental builds across 227// incompatible changes. Incompatible changes are marked by incrementing the bootstrapEpoch 228// constant. A tree is considered out of date for the current epoch of the 229// .soong.bootstrap.epoch.<epoch> file doesn't exist. 230func bootstrapEpochCleanup(ctx Context, config Config) { 231 epochFile := fmt.Sprintf(".soong.bootstrap.epoch.%d", bootstrapEpoch) 232 epochPath := filepath.Join(config.SoongOutDir(), epochFile) 233 if exists, err := fileExists(epochPath); err != nil { 234 ctx.Fatalf("failed to check if bootstrap epoch file %q exists: %q", epochPath, err) 235 } else if !exists { 236 // The tree is out of date for the current epoch, delete files used by bootstrap 237 // and force the primary builder to rerun. 238 os.Remove(filepath.Join(config.SoongOutDir(), "build.ninja")) 239 for _, globFile := range bootstrapGlobFileList(config) { 240 os.Remove(globFile) 241 } 242 243 // Mark the tree as up to date with the current epoch by writing the epoch marker file. 244 writeEmptyFile(ctx, epochPath) 245 } 246} 247 248func bootstrapGlobFileList(config Config) []string { 249 return []string{ 250 config.NamedGlobFile(soongBuildTag), 251 config.NamedGlobFile(bp2buildFilesTag), 252 config.NamedGlobFile(jsonModuleGraphTag), 253 config.NamedGlobFile(queryviewTag), 254 config.NamedGlobFile(apiBp2buildTag), 255 config.NamedGlobFile(soongDocsTag), 256 } 257} 258 259func bootstrapBlueprint(ctx Context, config Config) { 260 ctx.BeginTrace(metrics.RunSoong, "blueprint bootstrap") 261 defer ctx.EndTrace() 262 263 // Clean up some files for incremental builds across incompatible changes. 264 bootstrapEpochCleanup(ctx, config) 265 266 mainSoongBuildExtraArgs := []string{"-o", config.SoongNinjaFile()} 267 if config.EmptyNinjaFile() { 268 mainSoongBuildExtraArgs = append(mainSoongBuildExtraArgs, "--empty-ninja-file") 269 } 270 if config.bazelProdMode { 271 mainSoongBuildExtraArgs = append(mainSoongBuildExtraArgs, "--bazel-mode") 272 } 273 if config.bazelDevMode { 274 mainSoongBuildExtraArgs = append(mainSoongBuildExtraArgs, "--bazel-mode-dev") 275 } 276 if config.bazelStagingMode { 277 mainSoongBuildExtraArgs = append(mainSoongBuildExtraArgs, "--bazel-mode-staging") 278 } 279 if config.IsPersistentBazelEnabled() { 280 mainSoongBuildExtraArgs = append(mainSoongBuildExtraArgs, "--use-bazel-proxy") 281 } 282 if len(config.bazelForceEnabledModules) > 0 { 283 mainSoongBuildExtraArgs = append(mainSoongBuildExtraArgs, "--bazel-force-enabled-modules="+config.bazelForceEnabledModules) 284 } 285 if config.MultitreeBuild() { 286 mainSoongBuildExtraArgs = append(mainSoongBuildExtraArgs, "--multitree-build") 287 } 288 if config.buildFromTextStub { 289 mainSoongBuildExtraArgs = append(mainSoongBuildExtraArgs, "--build-from-text-stub") 290 } 291 292 queryviewDir := filepath.Join(config.SoongOutDir(), "queryview") 293 // The BUILD files will be generated in out/soong/.api_bp2build (no symlinks to src files) 294 // The final workspace will be generated in out/soong/api_bp2build 295 apiBp2buildDir := filepath.Join(config.SoongOutDir(), ".api_bp2build") 296 297 pbfs := []PrimaryBuilderFactory{ 298 { 299 name: soongBuildTag, 300 description: fmt.Sprintf("analyzing Android.bp files and generating ninja file at %s", config.SoongNinjaFile()), 301 config: config, 302 output: config.SoongNinjaFile(), 303 specificArgs: mainSoongBuildExtraArgs, 304 }, 305 { 306 name: bp2buildFilesTag, 307 description: fmt.Sprintf("converting Android.bp files to BUILD files at %s/bp2build", config.SoongOutDir()), 308 config: config, 309 output: config.Bp2BuildFilesMarkerFile(), 310 specificArgs: []string{"--bp2build_marker", config.Bp2BuildFilesMarkerFile()}, 311 }, 312 { 313 name: bp2buildWorkspaceTag, 314 description: "Creating Bazel symlink forest", 315 config: config, 316 output: config.Bp2BuildWorkspaceMarkerFile(), 317 specificArgs: []string{"--symlink_forest_marker", config.Bp2BuildWorkspaceMarkerFile()}, 318 }, 319 { 320 name: jsonModuleGraphTag, 321 description: fmt.Sprintf("generating the Soong module graph at %s", config.ModuleGraphFile()), 322 config: config, 323 output: config.ModuleGraphFile(), 324 specificArgs: []string{ 325 "--module_graph_file", config.ModuleGraphFile(), 326 "--module_actions_file", config.ModuleActionsFile(), 327 }, 328 }, 329 { 330 name: queryviewTag, 331 description: fmt.Sprintf("generating the Soong module graph as a Bazel workspace at %s", queryviewDir), 332 config: config, 333 output: config.QueryviewMarkerFile(), 334 specificArgs: []string{"--bazel_queryview_dir", queryviewDir}, 335 }, 336 { 337 name: apiBp2buildTag, 338 description: fmt.Sprintf("generating BUILD files for API contributions at %s", apiBp2buildDir), 339 config: config, 340 output: config.ApiBp2buildMarkerFile(), 341 specificArgs: []string{"--bazel_api_bp2build_dir", apiBp2buildDir}, 342 }, 343 { 344 name: soongDocsTag, 345 description: fmt.Sprintf("generating Soong docs at %s", config.SoongDocsHtml()), 346 config: config, 347 output: config.SoongDocsHtml(), 348 specificArgs: []string{"--soong_docs", config.SoongDocsHtml()}, 349 }, 350 } 351 352 // Figure out which invocations will be run under the debugger: 353 // * SOONG_DELVE if set specifies listening port 354 // * SOONG_DELVE_STEPS if set specifies specific invocations to be debugged, otherwise all are 355 debuggedInvocations := make(map[string]bool) 356 delvePort := os.Getenv("SOONG_DELVE") 357 if delvePort != "" { 358 if steps := os.Getenv("SOONG_DELVE_STEPS"); steps != "" { 359 var validSteps []string 360 for _, pbf := range pbfs { 361 debuggedInvocations[pbf.name] = false 362 validSteps = append(validSteps, pbf.name) 363 364 } 365 for _, step := range strings.Split(steps, ",") { 366 if _, ok := debuggedInvocations[step]; ok { 367 debuggedInvocations[step] = true 368 } else { 369 ctx.Fatalf("SOONG_DELVE_STEPS contains unknown soong_build step %s\n"+ 370 "Valid steps are %v", step, validSteps) 371 } 372 } 373 } else { 374 // SOONG_DELVE_STEPS is not set, run all steps in the debugger 375 for _, pbf := range pbfs { 376 debuggedInvocations[pbf.name] = true 377 } 378 } 379 } 380 381 var invocations []bootstrap.PrimaryBuilderInvocation 382 for _, pbf := range pbfs { 383 if debuggedInvocations[pbf.name] { 384 pbf.debugPort = delvePort 385 } 386 pbi := pbf.primaryBuilderInvocation() 387 // Some invocations require adjustment: 388 switch pbf.name { 389 case soongBuildTag: 390 if config.BazelBuildEnabled() { 391 // Mixed builds call Bazel from soong_build and they therefore need the 392 // Bazel workspace to be available. Make that so by adding a dependency on 393 // the bp2build marker file to the action that invokes soong_build . 394 pbi.OrderOnlyInputs = append(pbi.OrderOnlyInputs, config.Bp2BuildWorkspaceMarkerFile()) 395 } 396 case bp2buildWorkspaceTag: 397 pbi.Inputs = append(pbi.Inputs, 398 config.Bp2BuildFilesMarkerFile(), 399 filepath.Join(config.FileListDir(), "bazel.list")) 400 case bp2buildFilesTag: 401 pbi.Inputs = append(pbi.Inputs, filepath.Join(config.FileListDir(), "METADATA.list")) 402 } 403 invocations = append(invocations, pbi) 404 } 405 406 // The glob .ninja files are subninja'd. However, they are generated during 407 // the build itself so we write an empty file if the file does not exist yet 408 // so that the subninja doesn't fail on clean builds 409 for _, globFile := range bootstrapGlobFileList(config) { 410 writeEmptyFile(ctx, globFile) 411 } 412 413 blueprintArgs := bootstrap.Args{ 414 ModuleListFile: filepath.Join(config.FileListDir(), "Android.bp.list"), 415 OutFile: shared.JoinPath(config.SoongOutDir(), "bootstrap.ninja"), 416 EmptyNinjaFile: false, 417 } 418 419 blueprintCtx := blueprint.NewContext() 420 blueprintCtx.AddIncludeTags(config.GetIncludeTags()...) 421 blueprintCtx.AddSourceRootDirs(config.GetSourceRootDirs()...) 422 blueprintCtx.SetIgnoreUnknownModuleTypes(true) 423 blueprintConfig := BlueprintConfig{ 424 soongOutDir: config.SoongOutDir(), 425 toolDir: config.HostToolDir(), 426 outDir: config.OutDir(), 427 runGoTests: !config.skipSoongTests, 428 // If we want to debug soong_build, we need to compile it for debugging 429 debugCompilation: delvePort != "", 430 subninjas: bootstrapGlobFileList(config), 431 primaryBuilderInvocations: invocations, 432 } 433 434 // since `bootstrap.ninja` is regenerated unconditionally, we ignore the deps, i.e. little 435 // reason to write a `bootstrap.ninja.d` file 436 _ = bootstrap.RunBlueprint(blueprintArgs, bootstrap.DoEverything, blueprintCtx, blueprintConfig) 437} 438 439func checkEnvironmentFile(currentEnv *Environment, envFile string) { 440 getenv := func(k string) string { 441 v, _ := currentEnv.Get(k) 442 return v 443 } 444 445 if stale, _ := shared.StaleEnvFile(envFile, getenv); stale { 446 os.Remove(envFile) 447 } 448} 449 450func runSoong(ctx Context, config Config) { 451 ctx.BeginTrace(metrics.RunSoong, "soong") 452 defer ctx.EndTrace() 453 454 // We have two environment files: .available is the one with every variable, 455 // .used with the ones that were actually used. The latter is used to 456 // determine whether Soong needs to be re-run since why re-run it if only 457 // unused variables were changed? 458 envFile := filepath.Join(config.SoongOutDir(), availableEnvFile) 459 460 // This is done unconditionally, but does not take a measurable amount of time 461 bootstrapBlueprint(ctx, config) 462 463 soongBuildEnv := config.Environment().Copy() 464 soongBuildEnv.Set("TOP", os.Getenv("TOP")) 465 // For Bazel mixed builds. 466 soongBuildEnv.Set("BAZEL_PATH", "./build/bazel/bin/bazel") 467 // Bazel's HOME var is set to an output subdirectory which doesn't exist. This 468 // prevents Bazel from file I/O in the actual user HOME directory. 469 soongBuildEnv.Set("BAZEL_HOME", absPath(ctx, filepath.Join(config.BazelOutDir(), "bazelhome"))) 470 soongBuildEnv.Set("BAZEL_OUTPUT_BASE", config.bazelOutputBase()) 471 soongBuildEnv.Set("BAZEL_WORKSPACE", absPath(ctx, ".")) 472 soongBuildEnv.Set("BAZEL_METRICS_DIR", config.BazelMetricsDir()) 473 soongBuildEnv.Set("LOG_DIR", config.LogsDir()) 474 soongBuildEnv.Set("BAZEL_DEPS_FILE", absPath(ctx, filepath.Join(config.BazelOutDir(), "bazel.list"))) 475 476 // For Soong bootstrapping tests 477 if os.Getenv("ALLOW_MISSING_DEPENDENCIES") == "true" { 478 soongBuildEnv.Set("ALLOW_MISSING_DEPENDENCIES", "true") 479 } 480 481 err := writeEnvironmentFile(ctx, envFile, soongBuildEnv.AsMap()) 482 if err != nil { 483 ctx.Fatalf("failed to write environment file %s: %s", envFile, err) 484 } 485 486 func() { 487 ctx.BeginTrace(metrics.RunSoong, "environment check") 488 defer ctx.EndTrace() 489 490 checkEnvironmentFile(soongBuildEnv, config.UsedEnvFile(soongBuildTag)) 491 492 if config.BazelBuildEnabled() || config.Bp2Build() { 493 checkEnvironmentFile(soongBuildEnv, config.UsedEnvFile(bp2buildFilesTag)) 494 } 495 496 if config.JsonModuleGraph() { 497 checkEnvironmentFile(soongBuildEnv, config.UsedEnvFile(jsonModuleGraphTag)) 498 } 499 500 if config.Queryview() { 501 checkEnvironmentFile(soongBuildEnv, config.UsedEnvFile(queryviewTag)) 502 } 503 504 if config.ApiBp2build() { 505 checkEnvironmentFile(soongBuildEnv, config.UsedEnvFile(apiBp2buildTag)) 506 } 507 508 if config.SoongDocs() { 509 checkEnvironmentFile(soongBuildEnv, config.UsedEnvFile(soongDocsTag)) 510 } 511 }() 512 513 runMicrofactory(ctx, config, "bpglob", "github.com/google/blueprint/bootstrap/bpglob", 514 map[string]string{"github.com/google/blueprint": "build/blueprint"}) 515 516 ninja := func(name, ninjaFile string, targets ...string) { 517 ctx.BeginTrace(metrics.RunSoong, name) 518 defer ctx.EndTrace() 519 520 if config.IsPersistentBazelEnabled() { 521 bazelProxy := bazel.NewProxyServer(ctx.Logger, config.OutDir(), filepath.Join(config.SoongOutDir(), "workspace")) 522 bazelProxy.Start() 523 defer bazelProxy.Close() 524 } 525 526 fifo := filepath.Join(config.OutDir(), ".ninja_fifo") 527 nr := status.NewNinjaReader(ctx, ctx.Status.StartTool(), fifo) 528 defer nr.Close() 529 530 ninjaArgs := []string{ 531 "-d", "keepdepfile", 532 "-d", "stats", 533 "-o", "usesphonyoutputs=yes", 534 "-o", "preremoveoutputs=yes", 535 "-w", "dupbuild=err", 536 "-w", "outputdir=err", 537 "-w", "missingoutfile=err", 538 "-j", strconv.Itoa(config.Parallel()), 539 "--frontend_file", fifo, 540 "-f", filepath.Join(config.SoongOutDir(), ninjaFile), 541 } 542 543 if extra, ok := config.Environment().Get("SOONG_UI_NINJA_ARGS"); ok { 544 ctx.Printf(`CAUTION: arguments in $SOONG_UI_NINJA_ARGS=%q, e.g. "-n", can make soong_build FAIL or INCORRECT`, extra) 545 ninjaArgs = append(ninjaArgs, strings.Fields(extra)...) 546 } 547 548 ninjaArgs = append(ninjaArgs, targets...) 549 cmd := Command(ctx, config, "soong "+name, 550 config.PrebuiltBuildTool("ninja"), ninjaArgs...) 551 552 var ninjaEnv Environment 553 554 // This is currently how the command line to invoke soong_build finds the 555 // root of the source tree and the output root 556 ninjaEnv.Set("TOP", os.Getenv("TOP")) 557 558 cmd.Environment = &ninjaEnv 559 cmd.Sandbox = soongSandbox 560 cmd.RunAndStreamOrFatal() 561 } 562 563 targets := make([]string, 0, 0) 564 565 if config.JsonModuleGraph() { 566 targets = append(targets, config.ModuleGraphFile()) 567 } 568 569 if config.Bp2Build() { 570 targets = append(targets, config.Bp2BuildWorkspaceMarkerFile()) 571 } 572 573 if config.Queryview() { 574 targets = append(targets, config.QueryviewMarkerFile()) 575 } 576 577 if config.ApiBp2build() { 578 targets = append(targets, config.ApiBp2buildMarkerFile()) 579 } 580 581 if config.SoongDocs() { 582 targets = append(targets, config.SoongDocsHtml()) 583 } 584 585 if config.SoongBuildInvocationNeeded() { 586 // This build generates <builddir>/build.ninja, which is used later by build/soong/ui/build/build.go#Build(). 587 targets = append(targets, config.SoongNinjaFile()) 588 } 589 590 ninja("bootstrap", "bootstrap.ninja", targets...) 591 592 distGzipFile(ctx, config, config.SoongNinjaFile(), "soong") 593 distFile(ctx, config, config.SoongVarsFile(), "soong") 594 595 if !config.SkipKati() { 596 distGzipFile(ctx, config, config.SoongAndroidMk(), "soong") 597 distGzipFile(ctx, config, config.SoongMakeVarsMk(), "soong") 598 } 599 600 if config.JsonModuleGraph() { 601 distGzipFile(ctx, config, config.ModuleGraphFile(), "soong") 602 } 603} 604 605func runMicrofactory(ctx Context, config Config, name string, pkg string, mapping map[string]string) { 606 ctx.BeginTrace(metrics.RunSoong, name) 607 defer ctx.EndTrace() 608 cfg := microfactory.Config{TrimPath: absPath(ctx, ".")} 609 for pkgPrefix, pathPrefix := range mapping { 610 cfg.Map(pkgPrefix, pathPrefix) 611 } 612 613 exePath := filepath.Join(config.SoongOutDir(), name) 614 dir := filepath.Dir(exePath) 615 if err := os.MkdirAll(dir, 0777); err != nil { 616 ctx.Fatalf("cannot create %s: %s", dir, err) 617 } 618 if _, err := microfactory.Build(&cfg, exePath, pkg); err != nil { 619 ctx.Fatalf("failed to build %s: %s", name, err) 620 } 621} 622