1// Copyright 2015 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 main 16 17import ( 18 "flag" 19 "fmt" 20 "io/ioutil" 21 "os" 22 "path/filepath" 23 "strings" 24 "time" 25 26 "android/soong/android" 27 "android/soong/bp2build" 28 "android/soong/shared" 29 "android/soong/ui/metrics/bp2build_metrics_proto" 30 31 "github.com/google/blueprint/bootstrap" 32 "github.com/google/blueprint/deptools" 33 "github.com/google/blueprint/metrics" 34 androidProtobuf "google.golang.org/protobuf/android" 35) 36 37var ( 38 topDir string 39 outDir string 40 soongOutDir string 41 availableEnvFile string 42 usedEnvFile string 43 44 runGoTests bool 45 46 globFile string 47 globListDir string 48 delveListen string 49 delvePath string 50 51 moduleGraphFile string 52 moduleActionsFile string 53 docFile string 54 bazelQueryViewDir string 55 bp2buildMarker string 56 57 cmdlineArgs bootstrap.Args 58) 59 60func init() { 61 // Flags that make sense in every mode 62 flag.StringVar(&topDir, "top", "", "Top directory of the Android source tree") 63 flag.StringVar(&soongOutDir, "soong_out", "", "Soong output directory (usually $TOP/out/soong)") 64 flag.StringVar(&availableEnvFile, "available_env", "", "File containing available environment variables") 65 flag.StringVar(&usedEnvFile, "used_env", "", "File containing used environment variables") 66 flag.StringVar(&globFile, "globFile", "build-globs.ninja", "the Ninja file of globs to output") 67 flag.StringVar(&globListDir, "globListDir", "", "the directory containing the glob list files") 68 flag.StringVar(&outDir, "out", "", "the ninja builddir directory") 69 flag.StringVar(&cmdlineArgs.ModuleListFile, "l", "", "file that lists filepaths to parse") 70 71 // Debug flags 72 flag.StringVar(&delveListen, "delve_listen", "", "Delve port to listen on for debugging") 73 flag.StringVar(&delvePath, "delve_path", "", "Path to Delve. Only used if --delve_listen is set") 74 flag.StringVar(&cmdlineArgs.Cpuprofile, "cpuprofile", "", "write cpu profile to file") 75 flag.StringVar(&cmdlineArgs.TraceFile, "trace", "", "write trace to file") 76 flag.StringVar(&cmdlineArgs.Memprofile, "memprofile", "", "write memory profile to file") 77 flag.BoolVar(&cmdlineArgs.NoGC, "nogc", false, "turn off GC for debugging") 78 79 // Flags representing various modes soong_build can run in 80 flag.StringVar(&moduleGraphFile, "module_graph_file", "", "JSON module graph file to output") 81 flag.StringVar(&moduleActionsFile, "module_actions_file", "", "JSON file to output inputs/outputs of actions of modules") 82 flag.StringVar(&docFile, "soong_docs", "", "build documentation file to output") 83 flag.StringVar(&bazelQueryViewDir, "bazel_queryview_dir", "", "path to the bazel queryview directory relative to --top") 84 flag.StringVar(&bp2buildMarker, "bp2build_marker", "", "If set, run bp2build, touch the specified marker file then exit") 85 flag.StringVar(&cmdlineArgs.OutFile, "o", "build.ninja", "the Ninja file to output") 86 flag.BoolVar(&cmdlineArgs.EmptyNinjaFile, "empty-ninja-file", false, "write out a 0-byte ninja file") 87 88 // Flags that probably shouldn't be flags of soong_build but we haven't found 89 // the time to remove them yet 90 flag.BoolVar(&runGoTests, "t", false, "build and run go tests during bootstrap") 91 92 // Disable deterministic randomization in the protobuf package, so incremental 93 // builds with unrelated Soong changes don't trigger large rebuilds (since we 94 // write out text protos in command lines, and command line changes trigger 95 // rebuilds). 96 androidProtobuf.DisableRand() 97} 98 99func newNameResolver(config android.Config) *android.NameResolver { 100 namespacePathsToExport := make(map[string]bool) 101 102 for _, namespaceName := range config.ExportedNamespaces() { 103 namespacePathsToExport[namespaceName] = true 104 } 105 106 namespacePathsToExport["."] = true // always export the root namespace 107 108 exportFilter := func(namespace *android.Namespace) bool { 109 return namespacePathsToExport[namespace.Path] 110 } 111 112 return android.NewNameResolver(exportFilter) 113} 114 115func newContext(configuration android.Config) *android.Context { 116 ctx := android.NewContext(configuration) 117 ctx.Register() 118 ctx.SetNameInterface(newNameResolver(configuration)) 119 ctx.SetAllowMissingDependencies(configuration.AllowMissingDependencies()) 120 ctx.AddIncludeTags(configuration.IncludeTags()...) 121 return ctx 122} 123 124func newConfig(availableEnv map[string]string) android.Config { 125 configuration, err := android.NewConfig(cmdlineArgs.ModuleListFile, runGoTests, outDir, soongOutDir, availableEnv) 126 if err != nil { 127 fmt.Fprintf(os.Stderr, "%s", err) 128 os.Exit(1) 129 } 130 return configuration 131} 132 133// Bazel-enabled mode. Soong runs in two passes. 134// First pass: Analyze the build tree, but only store all bazel commands 135// needed to correctly evaluate the tree in the second pass. 136// TODO(cparsons): Don't output any ninja file, as the second pass will overwrite 137// the incorrect results from the first pass, and file I/O is expensive. 138func runMixedModeBuild(configuration android.Config, firstCtx *android.Context, extraNinjaDeps []string) { 139 firstCtx.EventHandler.Begin("mixed_build") 140 defer firstCtx.EventHandler.End("mixed_build") 141 142 firstCtx.EventHandler.Begin("prepare") 143 bootstrap.RunBlueprint(cmdlineArgs, bootstrap.StopBeforeWriteNinja, firstCtx.Context, configuration) 144 firstCtx.EventHandler.End("prepare") 145 146 firstCtx.EventHandler.Begin("bazel") 147 // Invoke bazel commands and save results for second pass. 148 if err := configuration.BazelContext.InvokeBazel(); err != nil { 149 fmt.Fprintf(os.Stderr, "%s", err) 150 os.Exit(1) 151 } 152 // Second pass: Full analysis, using the bazel command results. Output ninja file. 153 secondConfig, err := android.ConfigForAdditionalRun(configuration) 154 if err != nil { 155 fmt.Fprintf(os.Stderr, "%s", err) 156 os.Exit(1) 157 } 158 firstCtx.EventHandler.End("bazel") 159 160 secondCtx := newContext(secondConfig) 161 secondCtx.EventHandler = firstCtx.EventHandler 162 secondCtx.EventHandler.Begin("analyze") 163 ninjaDeps := bootstrap.RunBlueprint(cmdlineArgs, bootstrap.DoEverything, secondCtx.Context, secondConfig) 164 ninjaDeps = append(ninjaDeps, extraNinjaDeps...) 165 secondCtx.EventHandler.End("analyze") 166 167 globListFiles := writeBuildGlobsNinjaFile(secondCtx, configuration.SoongOutDir(), configuration) 168 ninjaDeps = append(ninjaDeps, globListFiles...) 169 170 writeDepFile(cmdlineArgs.OutFile, *secondCtx.EventHandler, ninjaDeps) 171} 172 173// Run the code-generation phase to convert BazelTargetModules to BUILD files. 174func runQueryView(queryviewDir, queryviewMarker string, configuration android.Config, ctx *android.Context) { 175 ctx.EventHandler.Begin("queryview") 176 defer ctx.EventHandler.End("queryview") 177 codegenContext := bp2build.NewCodegenContext(configuration, *ctx, bp2build.QueryView) 178 absoluteQueryViewDir := shared.JoinPath(topDir, queryviewDir) 179 if err := createBazelQueryView(codegenContext, absoluteQueryViewDir); err != nil { 180 fmt.Fprintf(os.Stderr, "%s", err) 181 os.Exit(1) 182 } 183 184 touch(shared.JoinPath(topDir, queryviewMarker)) 185} 186 187func writeMetrics(configuration android.Config, eventHandler metrics.EventHandler) { 188 metricsDir := configuration.Getenv("LOG_DIR") 189 if len(metricsDir) < 1 { 190 fmt.Fprintf(os.Stderr, "\nMissing required env var for generating soong metrics: LOG_DIR\n") 191 os.Exit(1) 192 } 193 metricsFile := filepath.Join(metricsDir, "soong_build_metrics.pb") 194 err := android.WriteMetrics(configuration, eventHandler, metricsFile) 195 if err != nil { 196 fmt.Fprintf(os.Stderr, "error writing soong_build metrics %s: %s", metricsFile, err) 197 os.Exit(1) 198 } 199} 200 201func writeJsonModuleGraphAndActions(ctx *android.Context, graphPath string, actionsPath string) { 202 graphFile, graphErr := os.Create(shared.JoinPath(topDir, graphPath)) 203 actionsFile, actionsErr := os.Create(shared.JoinPath(topDir, actionsPath)) 204 if graphErr != nil || actionsErr != nil { 205 fmt.Fprintf(os.Stderr, "Graph err: %s, actions err: %s", graphErr, actionsErr) 206 os.Exit(1) 207 } 208 209 defer graphFile.Close() 210 defer actionsFile.Close() 211 ctx.Context.PrintJSONGraphAndActions(graphFile, actionsFile) 212} 213 214func writeBuildGlobsNinjaFile(ctx *android.Context, buildDir string, config interface{}) []string { 215 ctx.EventHandler.Begin("globs_ninja_file") 216 defer ctx.EventHandler.End("globs_ninja_file") 217 218 globDir := bootstrap.GlobDirectory(buildDir, globListDir) 219 bootstrap.WriteBuildGlobsNinjaFile(&bootstrap.GlobSingleton{ 220 GlobLister: ctx.Globs, 221 GlobFile: globFile, 222 GlobDir: globDir, 223 SrcDir: ctx.SrcDir(), 224 }, config) 225 return bootstrap.GlobFileListFiles(globDir) 226} 227 228func writeDepFile(outputFile string, eventHandler metrics.EventHandler, ninjaDeps []string) { 229 eventHandler.Begin("ninja_deps") 230 defer eventHandler.End("ninja_deps") 231 depFile := shared.JoinPath(topDir, outputFile+".d") 232 err := deptools.WriteDepFile(depFile, outputFile, ninjaDeps) 233 if err != nil { 234 fmt.Fprintf(os.Stderr, "Error writing depfile '%s': %s\n", depFile, err) 235 os.Exit(1) 236 } 237} 238 239// doChosenActivity runs Soong for a specific activity, like bp2build, queryview 240// or the actual Soong build for the build.ninja file. Returns the top level 241// output file of the specific activity. 242func doChosenActivity(configuration android.Config, extraNinjaDeps []string) string { 243 mixedModeBuild := configuration.BazelContext.BazelEnabled() 244 generateBazelWorkspace := bp2buildMarker != "" 245 generateQueryView := bazelQueryViewDir != "" 246 generateModuleGraphFile := moduleGraphFile != "" 247 generateDocFile := docFile != "" 248 249 if generateBazelWorkspace { 250 // Run the alternate pipeline of bp2build mutators and singleton to convert 251 // Blueprint to BUILD files before everything else. 252 runBp2Build(configuration, extraNinjaDeps) 253 return bp2buildMarker 254 } 255 256 blueprintArgs := cmdlineArgs 257 258 ctx := newContext(configuration) 259 if mixedModeBuild { 260 runMixedModeBuild(configuration, ctx, extraNinjaDeps) 261 } else { 262 var stopBefore bootstrap.StopBefore 263 if generateModuleGraphFile { 264 stopBefore = bootstrap.StopBeforeWriteNinja 265 } else if generateQueryView { 266 stopBefore = bootstrap.StopBeforePrepareBuildActions 267 } else if generateDocFile { 268 stopBefore = bootstrap.StopBeforePrepareBuildActions 269 } else { 270 stopBefore = bootstrap.DoEverything 271 } 272 273 ninjaDeps := bootstrap.RunBlueprint(blueprintArgs, stopBefore, ctx.Context, configuration) 274 ninjaDeps = append(ninjaDeps, extraNinjaDeps...) 275 276 globListFiles := writeBuildGlobsNinjaFile(ctx, configuration.SoongOutDir(), configuration) 277 ninjaDeps = append(ninjaDeps, globListFiles...) 278 279 // Convert the Soong module graph into Bazel BUILD files. 280 if generateQueryView { 281 queryviewMarkerFile := bazelQueryViewDir + ".marker" 282 runQueryView(bazelQueryViewDir, queryviewMarkerFile, configuration, ctx) 283 writeDepFile(queryviewMarkerFile, *ctx.EventHandler, ninjaDeps) 284 return queryviewMarkerFile 285 } else if generateModuleGraphFile { 286 writeJsonModuleGraphAndActions(ctx, moduleGraphFile, moduleActionsFile) 287 writeDepFile(moduleGraphFile, *ctx.EventHandler, ninjaDeps) 288 return moduleGraphFile 289 } else if generateDocFile { 290 // TODO: we could make writeDocs() return the list of documentation files 291 // written and add them to the .d file. Then soong_docs would be re-run 292 // whenever one is deleted. 293 if err := writeDocs(ctx, shared.JoinPath(topDir, docFile)); err != nil { 294 fmt.Fprintf(os.Stderr, "error building Soong documentation: %s\n", err) 295 os.Exit(1) 296 } 297 writeDepFile(docFile, *ctx.EventHandler, ninjaDeps) 298 return docFile 299 } else { 300 // The actual output (build.ninja) was written in the RunBlueprint() call 301 // above 302 writeDepFile(cmdlineArgs.OutFile, *ctx.EventHandler, ninjaDeps) 303 } 304 } 305 306 writeMetrics(configuration, *ctx.EventHandler) 307 return cmdlineArgs.OutFile 308} 309 310// soong_ui dumps the available environment variables to 311// soong.environment.available . Then soong_build itself is run with an empty 312// environment so that the only way environment variables can be accessed is 313// using Config, which tracks access to them. 314 315// At the end of the build, a file called soong.environment.used is written 316// containing the current value of all used environment variables. The next 317// time soong_ui is run, it checks whether any environment variables that was 318// used had changed and if so, it deletes soong.environment.used to cause a 319// rebuild. 320// 321// The dependency of build.ninja on soong.environment.used is declared in 322// build.ninja.d 323func parseAvailableEnv() map[string]string { 324 if availableEnvFile == "" { 325 fmt.Fprintf(os.Stderr, "--available_env not set\n") 326 os.Exit(1) 327 } 328 329 result, err := shared.EnvFromFile(shared.JoinPath(topDir, availableEnvFile)) 330 if err != nil { 331 fmt.Fprintf(os.Stderr, "error reading available environment file '%s': %s\n", availableEnvFile, err) 332 os.Exit(1) 333 } 334 335 return result 336} 337 338func main() { 339 flag.Parse() 340 341 shared.ReexecWithDelveMaybe(delveListen, delvePath) 342 android.InitSandbox(topDir) 343 344 availableEnv := parseAvailableEnv() 345 346 configuration := newConfig(availableEnv) 347 extraNinjaDeps := []string{ 348 configuration.ProductVariablesFileName, 349 usedEnvFile, 350 } 351 352 if configuration.Getenv("ALLOW_MISSING_DEPENDENCIES") == "true" { 353 configuration.SetAllowMissingDependencies() 354 } 355 356 if shared.IsDebugging() { 357 // Add a non-existent file to the dependencies so that soong_build will rerun when the debugger is 358 // enabled even if it completed successfully. 359 extraNinjaDeps = append(extraNinjaDeps, filepath.Join(configuration.SoongOutDir(), "always_rerun_for_delve")) 360 } 361 362 finalOutputFile := doChosenActivity(configuration, extraNinjaDeps) 363 364 writeUsedEnvironmentFile(configuration, finalOutputFile) 365} 366 367func writeUsedEnvironmentFile(configuration android.Config, finalOutputFile string) { 368 if usedEnvFile == "" { 369 return 370 } 371 372 path := shared.JoinPath(topDir, usedEnvFile) 373 data, err := shared.EnvFileContents(configuration.EnvDeps()) 374 if err != nil { 375 fmt.Fprintf(os.Stderr, "error writing used environment file '%s': %s\n", usedEnvFile, err) 376 os.Exit(1) 377 } 378 379 err = ioutil.WriteFile(path, data, 0666) 380 if err != nil { 381 fmt.Fprintf(os.Stderr, "error writing used environment file '%s': %s\n", usedEnvFile, err) 382 os.Exit(1) 383 } 384 385 // Touch the output file so that it's not older than the file we just 386 // wrote. We can't write the environment file earlier because one an access 387 // new environment variables while writing it. 388 touch(shared.JoinPath(topDir, finalOutputFile)) 389} 390 391func touch(path string) { 392 f, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666) 393 if err != nil { 394 fmt.Fprintf(os.Stderr, "Error touching '%s': %s\n", path, err) 395 os.Exit(1) 396 } 397 398 err = f.Close() 399 if err != nil { 400 fmt.Fprintf(os.Stderr, "Error touching '%s': %s\n", path, err) 401 os.Exit(1) 402 } 403 404 currentTime := time.Now().Local() 405 err = os.Chtimes(path, currentTime, currentTime) 406 if err != nil { 407 fmt.Fprintf(os.Stderr, "error touching '%s': %s\n", path, err) 408 os.Exit(1) 409 } 410} 411 412// Find BUILD files in the srcDir which... 413// 414// - are not on the allow list (android/bazel.go#ShouldKeepExistingBuildFileForDir()) 415// 416// - won't be overwritten by corresponding bp2build generated files 417// 418// And return their paths so they can be left out of the Bazel workspace dir (i.e. ignored) 419func getPathsToIgnoredBuildFiles(topDir string, generatedRoot string, srcDirBazelFiles []string, verbose bool) []string { 420 paths := make([]string, 0) 421 422 for _, srcDirBazelFileRelativePath := range srcDirBazelFiles { 423 srcDirBazelFileFullPath := shared.JoinPath(topDir, srcDirBazelFileRelativePath) 424 fileInfo, err := os.Stat(srcDirBazelFileFullPath) 425 if err != nil { 426 // Warn about error, but continue trying to check files 427 fmt.Fprintf(os.Stderr, "WARNING: Error accessing path '%s', err: %s\n", srcDirBazelFileFullPath, err) 428 continue 429 } 430 if fileInfo.IsDir() { 431 // Don't ignore entire directories 432 continue 433 } 434 if !(fileInfo.Name() == "BUILD" || fileInfo.Name() == "BUILD.bazel") { 435 // Don't ignore this file - it is not a build file 436 continue 437 } 438 srcDirBazelFileDir := filepath.Dir(srcDirBazelFileRelativePath) 439 if android.ShouldKeepExistingBuildFileForDir(srcDirBazelFileDir) { 440 // Don't ignore this existing build file 441 continue 442 } 443 correspondingBp2BuildFile := shared.JoinPath(topDir, generatedRoot, srcDirBazelFileRelativePath) 444 if _, err := os.Stat(correspondingBp2BuildFile); err == nil { 445 // If bp2build generated an alternate BUILD file, don't exclude this workspace path 446 // BUILD file clash resolution happens later in the symlink forest creation 447 continue 448 } 449 if verbose { 450 fmt.Fprintf(os.Stderr, "Ignoring existing BUILD file: %s\n", srcDirBazelFileRelativePath) 451 } 452 paths = append(paths, srcDirBazelFileRelativePath) 453 } 454 455 return paths 456} 457 458// Returns temporary symlink forest excludes necessary for bazel build //external/... (and bazel build //frameworks/...) to work 459func getTemporaryExcludes() []string { 460 excludes := make([]string, 0) 461 462 // FIXME: 'autotest_lib' is a symlink back to external/autotest, and this causes an infinite symlink expansion error for Bazel 463 excludes = append(excludes, "external/autotest/venv/autotest_lib") 464 465 // FIXME: The external/google-fruit/extras/bazel_root/third_party/fruit dir is poison 466 // It contains several symlinks back to real source dirs, and those source dirs contain BUILD files we want to ignore 467 excludes = append(excludes, "external/google-fruit/extras/bazel_root/third_party/fruit") 468 469 // FIXME: 'frameworks/compile/slang' has a filegroup error due to an escaping issue 470 excludes = append(excludes, "frameworks/compile/slang") 471 472 return excludes 473} 474 475// Read the bazel.list file that the Soong Finder already dumped earlier (hopefully) 476// It contains the locations of BUILD files, BUILD.bazel files, etc. in the source dir 477func getExistingBazelRelatedFiles(topDir string) ([]string, error) { 478 bazelFinderFile := filepath.Join(filepath.Dir(cmdlineArgs.ModuleListFile), "bazel.list") 479 if !filepath.IsAbs(bazelFinderFile) { 480 // Assume this was a relative path under topDir 481 bazelFinderFile = filepath.Join(topDir, bazelFinderFile) 482 } 483 data, err := ioutil.ReadFile(bazelFinderFile) 484 if err != nil { 485 return nil, err 486 } 487 files := strings.Split(strings.TrimSpace(string(data)), "\n") 488 return files, nil 489} 490 491// Run Soong in the bp2build mode. This creates a standalone context that registers 492// an alternate pipeline of mutators and singletons specifically for generating 493// Bazel BUILD files instead of Ninja files. 494func runBp2Build(configuration android.Config, extraNinjaDeps []string) { 495 eventHandler := metrics.EventHandler{} 496 eventHandler.Begin("bp2build") 497 498 // Register an alternate set of singletons and mutators for bazel 499 // conversion for Bazel conversion. 500 bp2buildCtx := android.NewContext(configuration) 501 502 // Soong internals like LoadHooks behave differently when running as 503 // bp2build. This is the bit to differentiate between Soong-as-Soong and 504 // Soong-as-bp2build. 505 bp2buildCtx.SetRunningAsBp2build() 506 507 // Propagate "allow misssing dependencies" bit. This is normally set in 508 // newContext(), but we create bp2buildCtx without calling that method. 509 bp2buildCtx.SetAllowMissingDependencies(configuration.AllowMissingDependencies()) 510 bp2buildCtx.SetNameInterface(newNameResolver(configuration)) 511 bp2buildCtx.RegisterForBazelConversion() 512 513 // The bp2build process is a purely functional process that only depends on 514 // Android.bp files. It must not depend on the values of per-build product 515 // configurations or variables, since those will generate different BUILD 516 // files based on how the user has configured their tree. 517 bp2buildCtx.SetModuleListFile(cmdlineArgs.ModuleListFile) 518 modulePaths, err := bp2buildCtx.ListModulePaths(".") 519 if err != nil { 520 panic(err) 521 } 522 523 extraNinjaDeps = append(extraNinjaDeps, modulePaths...) 524 525 // Run the loading and analysis pipeline to prepare the graph of regular 526 // Modules parsed from Android.bp files, and the BazelTargetModules mapped 527 // from the regular Modules. 528 blueprintArgs := cmdlineArgs 529 ninjaDeps := bootstrap.RunBlueprint(blueprintArgs, bootstrap.StopBeforePrepareBuildActions, bp2buildCtx.Context, configuration) 530 ninjaDeps = append(ninjaDeps, extraNinjaDeps...) 531 532 globListFiles := writeBuildGlobsNinjaFile(bp2buildCtx, configuration.SoongOutDir(), configuration) 533 ninjaDeps = append(ninjaDeps, globListFiles...) 534 535 // Run the code-generation phase to convert BazelTargetModules to BUILD files 536 // and print conversion metrics to the user. 537 codegenContext := bp2build.NewCodegenContext(configuration, *bp2buildCtx, bp2build.Bp2Build) 538 metrics := bp2build.Codegen(codegenContext) 539 540 generatedRoot := shared.JoinPath(configuration.SoongOutDir(), "bp2build") 541 workspaceRoot := shared.JoinPath(configuration.SoongOutDir(), "workspace") 542 543 excludes := []string{ 544 "bazel-bin", 545 "bazel-genfiles", 546 "bazel-out", 547 "bazel-testlogs", 548 "bazel-" + filepath.Base(topDir), 549 } 550 551 if outDir[0] != '/' { 552 excludes = append(excludes, outDir) 553 } 554 555 existingBazelRelatedFiles, err := getExistingBazelRelatedFiles(topDir) 556 if err != nil { 557 fmt.Fprintf(os.Stderr, "Error determining existing Bazel-related files: %s\n", err) 558 os.Exit(1) 559 } 560 561 pathsToIgnoredBuildFiles := getPathsToIgnoredBuildFiles(topDir, generatedRoot, existingBazelRelatedFiles, configuration.IsEnvTrue("BP2BUILD_VERBOSE")) 562 excludes = append(excludes, pathsToIgnoredBuildFiles...) 563 564 excludes = append(excludes, getTemporaryExcludes()...) 565 566 symlinkForestDeps := bp2build.PlantSymlinkForest( 567 topDir, workspaceRoot, generatedRoot, ".", excludes) 568 569 ninjaDeps = append(ninjaDeps, codegenContext.AdditionalNinjaDeps()...) 570 ninjaDeps = append(ninjaDeps, symlinkForestDeps...) 571 572 writeDepFile(bp2buildMarker, eventHandler, ninjaDeps) 573 574 // Create an empty bp2build marker file. 575 touch(shared.JoinPath(topDir, bp2buildMarker)) 576 577 eventHandler.End("bp2build") 578 579 // Only report metrics when in bp2build mode. The metrics aren't relevant 580 // for queryview, since that's a total repo-wide conversion and there's a 581 // 1:1 mapping for each module. 582 metrics.Print() 583 writeBp2BuildMetrics(&metrics, configuration, eventHandler) 584} 585 586// Write Bp2Build metrics into $LOG_DIR 587func writeBp2BuildMetrics(codegenMetrics *bp2build.CodegenMetrics, 588 configuration android.Config, eventHandler metrics.EventHandler) { 589 for _, event := range eventHandler.CompletedEvents() { 590 codegenMetrics.Events = append(codegenMetrics.Events, 591 &bp2build_metrics_proto.Event{ 592 Name: event.Id, 593 StartTime: uint64(event.Start.UnixNano()), 594 RealTime: event.RuntimeNanoseconds(), 595 }) 596 } 597 metricsDir := configuration.Getenv("LOG_DIR") 598 if len(metricsDir) < 1 { 599 fmt.Fprintf(os.Stderr, "\nMissing required env var for generating bp2build metrics: LOG_DIR\n") 600 os.Exit(1) 601 } 602 codegenMetrics.Write(metricsDir) 603} 604