1*333d2b36SAndroid Build Coastguard Worker// Copyright 2015 Google Inc. All rights reserved. 2*333d2b36SAndroid Build Coastguard Worker// 3*333d2b36SAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License"); 4*333d2b36SAndroid Build Coastguard Worker// you may not use this file except in compliance with the License. 5*333d2b36SAndroid Build Coastguard Worker// You may obtain a copy of the License at 6*333d2b36SAndroid Build Coastguard Worker// 7*333d2b36SAndroid Build Coastguard Worker// http://www.apache.org/licenses/LICENSE-2.0 8*333d2b36SAndroid Build Coastguard Worker// 9*333d2b36SAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software 10*333d2b36SAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS, 11*333d2b36SAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*333d2b36SAndroid Build Coastguard Worker// See the License for the specific language governing permissions and 13*333d2b36SAndroid Build Coastguard Worker// limitations under the License. 14*333d2b36SAndroid Build Coastguard Worker 15*333d2b36SAndroid Build Coastguard Workerpackage main 16*333d2b36SAndroid Build Coastguard Worker 17*333d2b36SAndroid Build Coastguard Workerimport ( 18*333d2b36SAndroid Build Coastguard Worker "encoding/json" 19*333d2b36SAndroid Build Coastguard Worker "errors" 20*333d2b36SAndroid Build Coastguard Worker "flag" 21*333d2b36SAndroid Build Coastguard Worker "fmt" 22*333d2b36SAndroid Build Coastguard Worker "os" 23*333d2b36SAndroid Build Coastguard Worker "path/filepath" 24*333d2b36SAndroid Build Coastguard Worker "strings" 25*333d2b36SAndroid Build Coastguard Worker "time" 26*333d2b36SAndroid Build Coastguard Worker 27*333d2b36SAndroid Build Coastguard Worker "android/soong/android" 28*333d2b36SAndroid Build Coastguard Worker "android/soong/android/allowlists" 29*333d2b36SAndroid Build Coastguard Worker "android/soong/shared" 30*333d2b36SAndroid Build Coastguard Worker 31*333d2b36SAndroid Build Coastguard Worker "github.com/google/blueprint" 32*333d2b36SAndroid Build Coastguard Worker "github.com/google/blueprint/bootstrap" 33*333d2b36SAndroid Build Coastguard Worker "github.com/google/blueprint/deptools" 34*333d2b36SAndroid Build Coastguard Worker "github.com/google/blueprint/metrics" 35*333d2b36SAndroid Build Coastguard Worker "github.com/google/blueprint/pathtools" 36*333d2b36SAndroid Build Coastguard Worker "github.com/google/blueprint/proptools" 37*333d2b36SAndroid Build Coastguard Worker androidProtobuf "google.golang.org/protobuf/android" 38*333d2b36SAndroid Build Coastguard Worker) 39*333d2b36SAndroid Build Coastguard Worker 40*333d2b36SAndroid Build Coastguard Workervar ( 41*333d2b36SAndroid Build Coastguard Worker topDir string 42*333d2b36SAndroid Build Coastguard Worker availableEnvFile string 43*333d2b36SAndroid Build Coastguard Worker usedEnvFile string 44*333d2b36SAndroid Build Coastguard Worker 45*333d2b36SAndroid Build Coastguard Worker delveListen string 46*333d2b36SAndroid Build Coastguard Worker delvePath string 47*333d2b36SAndroid Build Coastguard Worker 48*333d2b36SAndroid Build Coastguard Worker cmdlineArgs android.CmdArgs 49*333d2b36SAndroid Build Coastguard Worker) 50*333d2b36SAndroid Build Coastguard Worker 51*333d2b36SAndroid Build Coastguard Workerconst configCacheFile = "config.cache" 52*333d2b36SAndroid Build Coastguard Worker 53*333d2b36SAndroid Build Coastguard Workertype ConfigCache struct { 54*333d2b36SAndroid Build Coastguard Worker EnvDepsHash uint64 55*333d2b36SAndroid Build Coastguard Worker ProductVariableFileTimestamp int64 56*333d2b36SAndroid Build Coastguard Worker SoongBuildFileTimestamp int64 57*333d2b36SAndroid Build Coastguard Worker} 58*333d2b36SAndroid Build Coastguard Worker 59*333d2b36SAndroid Build Coastguard Workerfunc init() { 60*333d2b36SAndroid Build Coastguard Worker // Flags that make sense in every mode 61*333d2b36SAndroid Build Coastguard Worker flag.StringVar(&topDir, "top", "", "Top directory of the Android source tree") 62*333d2b36SAndroid Build Coastguard Worker flag.StringVar(&cmdlineArgs.SoongOutDir, "soong_out", "", "Soong output directory (usually $TOP/out/soong)") 63*333d2b36SAndroid Build Coastguard Worker flag.StringVar(&availableEnvFile, "available_env", "", "File containing available environment variables") 64*333d2b36SAndroid Build Coastguard Worker flag.StringVar(&usedEnvFile, "used_env", "", "File containing used environment variables") 65*333d2b36SAndroid Build Coastguard Worker flag.StringVar(&cmdlineArgs.OutDir, "out", "", "the ninja builddir directory") 66*333d2b36SAndroid Build Coastguard Worker flag.StringVar(&cmdlineArgs.ModuleListFile, "l", "", "file that lists filepaths to parse") 67*333d2b36SAndroid Build Coastguard Worker 68*333d2b36SAndroid Build Coastguard Worker // Debug flags 69*333d2b36SAndroid Build Coastguard Worker flag.StringVar(&delveListen, "delve_listen", "", "Delve port to listen on for debugging") 70*333d2b36SAndroid Build Coastguard Worker flag.StringVar(&delvePath, "delve_path", "", "Path to Delve. Only used if --delve_listen is set") 71*333d2b36SAndroid Build Coastguard Worker flag.StringVar(&cmdlineArgs.Cpuprofile, "cpuprofile", "", "write cpu profile to file") 72*333d2b36SAndroid Build Coastguard Worker flag.StringVar(&cmdlineArgs.TraceFile, "trace", "", "write trace to file") 73*333d2b36SAndroid Build Coastguard Worker flag.StringVar(&cmdlineArgs.Memprofile, "memprofile", "", "write memory profile to file") 74*333d2b36SAndroid Build Coastguard Worker flag.BoolVar(&cmdlineArgs.NoGC, "nogc", false, "turn off GC for debugging") 75*333d2b36SAndroid Build Coastguard Worker 76*333d2b36SAndroid Build Coastguard Worker // Flags representing various modes soong_build can run in 77*333d2b36SAndroid Build Coastguard Worker flag.StringVar(&cmdlineArgs.ModuleGraphFile, "module_graph_file", "", "JSON module graph file to output") 78*333d2b36SAndroid Build Coastguard Worker flag.StringVar(&cmdlineArgs.ModuleActionsFile, "module_actions_file", "", "JSON file to output inputs/outputs of actions of modules") 79*333d2b36SAndroid Build Coastguard Worker flag.StringVar(&cmdlineArgs.DocFile, "soong_docs", "", "build documentation file to output") 80*333d2b36SAndroid Build Coastguard Worker flag.StringVar(&cmdlineArgs.OutFile, "o", "build.ninja", "the Ninja file to output") 81*333d2b36SAndroid Build Coastguard Worker flag.StringVar(&cmdlineArgs.SoongVariables, "soong_variables", "soong.variables", "the file contains all build variables") 82*333d2b36SAndroid Build Coastguard Worker flag.BoolVar(&cmdlineArgs.EmptyNinjaFile, "empty-ninja-file", false, "write out a 0-byte ninja file") 83*333d2b36SAndroid Build Coastguard Worker flag.BoolVar(&cmdlineArgs.BuildFromSourceStub, "build-from-source-stub", false, "build Java stubs from source files instead of API text files") 84*333d2b36SAndroid Build Coastguard Worker flag.BoolVar(&cmdlineArgs.EnsureAllowlistIntegrity, "ensure-allowlist-integrity", false, "verify that allowlisted modules are mixed-built") 85*333d2b36SAndroid Build Coastguard Worker flag.StringVar(&cmdlineArgs.ModuleDebugFile, "soong_module_debug", "", "soong module debug info file to write") 86*333d2b36SAndroid Build Coastguard Worker // Flags that probably shouldn't be flags of soong_build, but we haven't found 87*333d2b36SAndroid Build Coastguard Worker // the time to remove them yet 88*333d2b36SAndroid Build Coastguard Worker flag.BoolVar(&cmdlineArgs.RunGoTests, "t", false, "build and run go tests during bootstrap") 89*333d2b36SAndroid Build Coastguard Worker flag.BoolVar(&cmdlineArgs.IncrementalBuildActions, "incremental-build-actions", false, "generate build actions incrementally") 90*333d2b36SAndroid Build Coastguard Worker 91*333d2b36SAndroid Build Coastguard Worker // Disable deterministic randomization in the protobuf package, so incremental 92*333d2b36SAndroid Build Coastguard Worker // builds with unrelated Soong changes don't trigger large rebuilds (since we 93*333d2b36SAndroid Build Coastguard Worker // write out text protos in command lines, and command line changes trigger 94*333d2b36SAndroid Build Coastguard Worker // rebuilds). 95*333d2b36SAndroid Build Coastguard Worker androidProtobuf.DisableRand() 96*333d2b36SAndroid Build Coastguard Worker} 97*333d2b36SAndroid Build Coastguard Worker 98*333d2b36SAndroid Build Coastguard Workerfunc newNameResolver(config android.Config) *android.NameResolver { 99*333d2b36SAndroid Build Coastguard Worker return android.NewNameResolver(config) 100*333d2b36SAndroid Build Coastguard Worker} 101*333d2b36SAndroid Build Coastguard Worker 102*333d2b36SAndroid Build Coastguard Workerfunc newContext(configuration android.Config) *android.Context { 103*333d2b36SAndroid Build Coastguard Worker ctx := android.NewContext(configuration) 104*333d2b36SAndroid Build Coastguard Worker ctx.SetNameInterface(newNameResolver(configuration)) 105*333d2b36SAndroid Build Coastguard Worker ctx.SetAllowMissingDependencies(configuration.AllowMissingDependencies()) 106*333d2b36SAndroid Build Coastguard Worker ctx.AddSourceRootDirs(configuration.SourceRootDirs()...) 107*333d2b36SAndroid Build Coastguard Worker return ctx 108*333d2b36SAndroid Build Coastguard Worker} 109*333d2b36SAndroid Build Coastguard Worker 110*333d2b36SAndroid Build Coastguard Workerfunc needToWriteNinjaHint(ctx *android.Context) bool { 111*333d2b36SAndroid Build Coastguard Worker switch ctx.Config().GetenvWithDefault("SOONG_GENERATES_NINJA_HINT", "") { 112*333d2b36SAndroid Build Coastguard Worker case "always": 113*333d2b36SAndroid Build Coastguard Worker return true 114*333d2b36SAndroid Build Coastguard Worker case "depend": 115*333d2b36SAndroid Build Coastguard Worker if _, err := os.Stat(filepath.Join(topDir, ctx.Config().OutDir(), ".ninja_log")); errors.Is(err, os.ErrNotExist) { 116*333d2b36SAndroid Build Coastguard Worker return true 117*333d2b36SAndroid Build Coastguard Worker } 118*333d2b36SAndroid Build Coastguard Worker } 119*333d2b36SAndroid Build Coastguard Worker return false 120*333d2b36SAndroid Build Coastguard Worker} 121*333d2b36SAndroid Build Coastguard Worker 122*333d2b36SAndroid Build Coastguard Workerfunc writeNinjaHint(ctx *android.Context) error { 123*333d2b36SAndroid Build Coastguard Worker ctx.BeginEvent("ninja_hint") 124*333d2b36SAndroid Build Coastguard Worker defer ctx.EndEvent("ninja_hint") 125*333d2b36SAndroid Build Coastguard Worker // The current predictor focuses on reducing false negatives. 126*333d2b36SAndroid Build Coastguard Worker // If there are too many false positives (e.g., most modules are marked as positive), 127*333d2b36SAndroid Build Coastguard Worker // real long-running jobs cannot run early. 128*333d2b36SAndroid Build Coastguard Worker // Therefore, the model should be adjusted in this case. 129*333d2b36SAndroid Build Coastguard Worker // The model should also be adjusted if there are critical false negatives. 130*333d2b36SAndroid Build Coastguard Worker predicate := func(j *blueprint.JsonModule) (prioritized bool, weight int) { 131*333d2b36SAndroid Build Coastguard Worker prioritized = false 132*333d2b36SAndroid Build Coastguard Worker weight = 0 133*333d2b36SAndroid Build Coastguard Worker for prefix, w := range allowlists.HugeModuleTypePrefixMap { 134*333d2b36SAndroid Build Coastguard Worker if strings.HasPrefix(j.Type, prefix) { 135*333d2b36SAndroid Build Coastguard Worker prioritized = true 136*333d2b36SAndroid Build Coastguard Worker weight = w 137*333d2b36SAndroid Build Coastguard Worker return 138*333d2b36SAndroid Build Coastguard Worker } 139*333d2b36SAndroid Build Coastguard Worker } 140*333d2b36SAndroid Build Coastguard Worker dep_count := len(j.Deps) 141*333d2b36SAndroid Build Coastguard Worker src_count := 0 142*333d2b36SAndroid Build Coastguard Worker for _, a := range j.Module["Actions"].([]blueprint.JSONAction) { 143*333d2b36SAndroid Build Coastguard Worker src_count += len(a.Inputs) 144*333d2b36SAndroid Build Coastguard Worker } 145*333d2b36SAndroid Build Coastguard Worker input_size := dep_count + src_count 146*333d2b36SAndroid Build Coastguard Worker 147*333d2b36SAndroid Build Coastguard Worker // Current threshold is an arbitrary value which only consider recall rather than accuracy. 148*333d2b36SAndroid Build Coastguard Worker if input_size > allowlists.INPUT_SIZE_THRESHOLD { 149*333d2b36SAndroid Build Coastguard Worker prioritized = true 150*333d2b36SAndroid Build Coastguard Worker weight += ((input_size) / allowlists.INPUT_SIZE_THRESHOLD) * allowlists.DEFAULT_PRIORITIZED_WEIGHT 151*333d2b36SAndroid Build Coastguard Worker 152*333d2b36SAndroid Build Coastguard Worker // To prevent some modules from having too large a priority value. 153*333d2b36SAndroid Build Coastguard Worker if weight > allowlists.HIGH_PRIORITIZED_WEIGHT { 154*333d2b36SAndroid Build Coastguard Worker weight = allowlists.HIGH_PRIORITIZED_WEIGHT 155*333d2b36SAndroid Build Coastguard Worker } 156*333d2b36SAndroid Build Coastguard Worker } 157*333d2b36SAndroid Build Coastguard Worker return 158*333d2b36SAndroid Build Coastguard Worker } 159*333d2b36SAndroid Build Coastguard Worker 160*333d2b36SAndroid Build Coastguard Worker outputsMap := ctx.Context.GetWeightedOutputsFromPredicate(predicate) 161*333d2b36SAndroid Build Coastguard Worker var outputBuilder strings.Builder 162*333d2b36SAndroid Build Coastguard Worker for output, weight := range outputsMap { 163*333d2b36SAndroid Build Coastguard Worker outputBuilder.WriteString(fmt.Sprintf("%s,%d\n", output, weight)) 164*333d2b36SAndroid Build Coastguard Worker } 165*333d2b36SAndroid Build Coastguard Worker weightListFile := filepath.Join(topDir, ctx.Config().OutDir(), ".ninja_weight_list") 166*333d2b36SAndroid Build Coastguard Worker 167*333d2b36SAndroid Build Coastguard Worker err := os.WriteFile(weightListFile, []byte(outputBuilder.String()), 0644) 168*333d2b36SAndroid Build Coastguard Worker if err != nil { 169*333d2b36SAndroid Build Coastguard Worker return fmt.Errorf("could not write ninja weight list file %s", err) 170*333d2b36SAndroid Build Coastguard Worker } 171*333d2b36SAndroid Build Coastguard Worker return nil 172*333d2b36SAndroid Build Coastguard Worker} 173*333d2b36SAndroid Build Coastguard Worker 174*333d2b36SAndroid Build Coastguard Workerfunc writeMetrics(configuration android.Config, eventHandler *metrics.EventHandler, metricsDir string) { 175*333d2b36SAndroid Build Coastguard Worker if len(metricsDir) < 1 { 176*333d2b36SAndroid Build Coastguard Worker fmt.Fprintf(os.Stderr, "\nMissing required env var for generating soong metrics: LOG_DIR\n") 177*333d2b36SAndroid Build Coastguard Worker os.Exit(1) 178*333d2b36SAndroid Build Coastguard Worker } 179*333d2b36SAndroid Build Coastguard Worker metricsFile := filepath.Join(metricsDir, "soong_build_metrics.pb") 180*333d2b36SAndroid Build Coastguard Worker err := android.WriteMetrics(configuration, eventHandler, metricsFile) 181*333d2b36SAndroid Build Coastguard Worker maybeQuit(err, "error writing soong_build metrics %s", metricsFile) 182*333d2b36SAndroid Build Coastguard Worker} 183*333d2b36SAndroid Build Coastguard Worker 184*333d2b36SAndroid Build Coastguard Workerfunc writeJsonModuleGraphAndActions(ctx *android.Context, cmdArgs android.CmdArgs) { 185*333d2b36SAndroid Build Coastguard Worker graphFile, graphErr := os.Create(shared.JoinPath(topDir, cmdArgs.ModuleGraphFile)) 186*333d2b36SAndroid Build Coastguard Worker maybeQuit(graphErr, "graph err") 187*333d2b36SAndroid Build Coastguard Worker defer graphFile.Close() 188*333d2b36SAndroid Build Coastguard Worker actionsFile, actionsErr := os.Create(shared.JoinPath(topDir, cmdArgs.ModuleActionsFile)) 189*333d2b36SAndroid Build Coastguard Worker maybeQuit(actionsErr, "actions err") 190*333d2b36SAndroid Build Coastguard Worker defer actionsFile.Close() 191*333d2b36SAndroid Build Coastguard Worker ctx.Context.PrintJSONGraphAndActions(graphFile, actionsFile) 192*333d2b36SAndroid Build Coastguard Worker} 193*333d2b36SAndroid Build Coastguard Worker 194*333d2b36SAndroid Build Coastguard Workerfunc writeDepFile(outputFile string, eventHandler *metrics.EventHandler, ninjaDeps []string) { 195*333d2b36SAndroid Build Coastguard Worker eventHandler.Begin("ninja_deps") 196*333d2b36SAndroid Build Coastguard Worker defer eventHandler.End("ninja_deps") 197*333d2b36SAndroid Build Coastguard Worker depFile := shared.JoinPath(topDir, outputFile+".d") 198*333d2b36SAndroid Build Coastguard Worker err := deptools.WriteDepFile(depFile, outputFile, ninjaDeps) 199*333d2b36SAndroid Build Coastguard Worker maybeQuit(err, "error writing depfile '%s'", depFile) 200*333d2b36SAndroid Build Coastguard Worker} 201*333d2b36SAndroid Build Coastguard Worker 202*333d2b36SAndroid Build Coastguard Worker// Check if there are changes to the environment file, product variable file and 203*333d2b36SAndroid Build Coastguard Worker// soong_build binary, in which case no incremental will be performed. For env 204*333d2b36SAndroid Build Coastguard Worker// variables we check the used env file, which will be removed in soong ui if 205*333d2b36SAndroid Build Coastguard Worker// there is any changes to the env variables used last time, in which case the 206*333d2b36SAndroid Build Coastguard Worker// check below will fail and a full build will be attempted. If any new env 207*333d2b36SAndroid Build Coastguard Worker// variables are added in the new run, soong ui won't be able to detect it, the 208*333d2b36SAndroid Build Coastguard Worker// used env file check below will pass. But unless there is a soong build code 209*333d2b36SAndroid Build Coastguard Worker// change, in which case the soong build binary check will fail, otherwise the 210*333d2b36SAndroid Build Coastguard Worker// new env variables shouldn't have any affect. 211*333d2b36SAndroid Build Coastguard Workerfunc incrementalValid(config android.Config, configCacheFile string) (*ConfigCache, bool) { 212*333d2b36SAndroid Build Coastguard Worker var newConfigCache ConfigCache 213*333d2b36SAndroid Build Coastguard Worker data, err := os.ReadFile(shared.JoinPath(topDir, usedEnvFile)) 214*333d2b36SAndroid Build Coastguard Worker if err != nil { 215*333d2b36SAndroid Build Coastguard Worker // Clean build 216*333d2b36SAndroid Build Coastguard Worker if os.IsNotExist(err) { 217*333d2b36SAndroid Build Coastguard Worker data = []byte{} 218*333d2b36SAndroid Build Coastguard Worker } else { 219*333d2b36SAndroid Build Coastguard Worker maybeQuit(err, "") 220*333d2b36SAndroid Build Coastguard Worker } 221*333d2b36SAndroid Build Coastguard Worker } 222*333d2b36SAndroid Build Coastguard Worker 223*333d2b36SAndroid Build Coastguard Worker newConfigCache.EnvDepsHash, err = proptools.CalculateHash(data) 224*333d2b36SAndroid Build Coastguard Worker newConfigCache.ProductVariableFileTimestamp = getFileTimestamp(filepath.Join(topDir, cmdlineArgs.SoongVariables)) 225*333d2b36SAndroid Build Coastguard Worker newConfigCache.SoongBuildFileTimestamp = getFileTimestamp(filepath.Join(topDir, config.HostToolDir(), "soong_build")) 226*333d2b36SAndroid Build Coastguard Worker //TODO(b/344917959): out/soong/dexpreopt.config might need to be checked as well. 227*333d2b36SAndroid Build Coastguard Worker 228*333d2b36SAndroid Build Coastguard Worker file, err := os.Open(configCacheFile) 229*333d2b36SAndroid Build Coastguard Worker if err != nil && os.IsNotExist(err) { 230*333d2b36SAndroid Build Coastguard Worker return &newConfigCache, false 231*333d2b36SAndroid Build Coastguard Worker } 232*333d2b36SAndroid Build Coastguard Worker maybeQuit(err, "") 233*333d2b36SAndroid Build Coastguard Worker defer file.Close() 234*333d2b36SAndroid Build Coastguard Worker 235*333d2b36SAndroid Build Coastguard Worker var configCache ConfigCache 236*333d2b36SAndroid Build Coastguard Worker decoder := json.NewDecoder(file) 237*333d2b36SAndroid Build Coastguard Worker err = decoder.Decode(&configCache) 238*333d2b36SAndroid Build Coastguard Worker maybeQuit(err, "") 239*333d2b36SAndroid Build Coastguard Worker 240*333d2b36SAndroid Build Coastguard Worker return &newConfigCache, newConfigCache == configCache 241*333d2b36SAndroid Build Coastguard Worker} 242*333d2b36SAndroid Build Coastguard Worker 243*333d2b36SAndroid Build Coastguard Workerfunc getFileTimestamp(file string) int64 { 244*333d2b36SAndroid Build Coastguard Worker stat, err := os.Stat(file) 245*333d2b36SAndroid Build Coastguard Worker if err == nil { 246*333d2b36SAndroid Build Coastguard Worker return stat.ModTime().UnixMilli() 247*333d2b36SAndroid Build Coastguard Worker } else if !os.IsNotExist(err) { 248*333d2b36SAndroid Build Coastguard Worker maybeQuit(err, "") 249*333d2b36SAndroid Build Coastguard Worker } 250*333d2b36SAndroid Build Coastguard Worker return 0 251*333d2b36SAndroid Build Coastguard Worker} 252*333d2b36SAndroid Build Coastguard Worker 253*333d2b36SAndroid Build Coastguard Workerfunc writeConfigCache(configCache *ConfigCache, configCacheFile string) { 254*333d2b36SAndroid Build Coastguard Worker file, err := os.Create(configCacheFile) 255*333d2b36SAndroid Build Coastguard Worker maybeQuit(err, "") 256*333d2b36SAndroid Build Coastguard Worker defer file.Close() 257*333d2b36SAndroid Build Coastguard Worker 258*333d2b36SAndroid Build Coastguard Worker encoder := json.NewEncoder(file) 259*333d2b36SAndroid Build Coastguard Worker err = encoder.Encode(*configCache) 260*333d2b36SAndroid Build Coastguard Worker maybeQuit(err, "") 261*333d2b36SAndroid Build Coastguard Worker} 262*333d2b36SAndroid Build Coastguard Worker 263*333d2b36SAndroid Build Coastguard Worker// runSoongOnlyBuild runs the standard Soong build in a number of different modes. 264*333d2b36SAndroid Build Coastguard Worker// It returns the path to the output file (usually the ninja file) and the deps that need 265*333d2b36SAndroid Build Coastguard Worker// to trigger a soong rerun. 266*333d2b36SAndroid Build Coastguard Workerfunc runSoongOnlyBuild(ctx *android.Context) (string, []string) { 267*333d2b36SAndroid Build Coastguard Worker ctx.EventHandler.Begin("soong_build") 268*333d2b36SAndroid Build Coastguard Worker defer ctx.EventHandler.End("soong_build") 269*333d2b36SAndroid Build Coastguard Worker 270*333d2b36SAndroid Build Coastguard Worker var stopBefore bootstrap.StopBefore 271*333d2b36SAndroid Build Coastguard Worker switch ctx.Config().BuildMode { 272*333d2b36SAndroid Build Coastguard Worker case android.GenerateModuleGraph: 273*333d2b36SAndroid Build Coastguard Worker stopBefore = bootstrap.StopBeforeWriteNinja 274*333d2b36SAndroid Build Coastguard Worker case android.GenerateDocFile: 275*333d2b36SAndroid Build Coastguard Worker stopBefore = bootstrap.StopBeforePrepareBuildActions 276*333d2b36SAndroid Build Coastguard Worker default: 277*333d2b36SAndroid Build Coastguard Worker stopBefore = bootstrap.DoEverything 278*333d2b36SAndroid Build Coastguard Worker } 279*333d2b36SAndroid Build Coastguard Worker 280*333d2b36SAndroid Build Coastguard Worker ninjaDeps, err := bootstrap.RunBlueprint(cmdlineArgs.Args, stopBefore, ctx.Context, ctx.Config()) 281*333d2b36SAndroid Build Coastguard Worker maybeQuit(err, "") 282*333d2b36SAndroid Build Coastguard Worker 283*333d2b36SAndroid Build Coastguard Worker // Convert the Soong module graph into Bazel BUILD files. 284*333d2b36SAndroid Build Coastguard Worker switch ctx.Config().BuildMode { 285*333d2b36SAndroid Build Coastguard Worker case android.GenerateModuleGraph: 286*333d2b36SAndroid Build Coastguard Worker writeJsonModuleGraphAndActions(ctx, cmdlineArgs) 287*333d2b36SAndroid Build Coastguard Worker return cmdlineArgs.ModuleGraphFile, ninjaDeps 288*333d2b36SAndroid Build Coastguard Worker case android.GenerateDocFile: 289*333d2b36SAndroid Build Coastguard Worker // TODO: we could make writeDocs() return the list of documentation files 290*333d2b36SAndroid Build Coastguard Worker // written and add them to the .d file. Then soong_docs would be re-run 291*333d2b36SAndroid Build Coastguard Worker // whenever one is deleted. 292*333d2b36SAndroid Build Coastguard Worker err := writeDocs(ctx, shared.JoinPath(topDir, cmdlineArgs.DocFile)) 293*333d2b36SAndroid Build Coastguard Worker maybeQuit(err, "error building Soong documentation") 294*333d2b36SAndroid Build Coastguard Worker return cmdlineArgs.DocFile, ninjaDeps 295*333d2b36SAndroid Build Coastguard Worker default: 296*333d2b36SAndroid Build Coastguard Worker // The actual output (build.ninja) was written in the RunBlueprint() call 297*333d2b36SAndroid Build Coastguard Worker // above 298*333d2b36SAndroid Build Coastguard Worker if needToWriteNinjaHint(ctx) { 299*333d2b36SAndroid Build Coastguard Worker writeNinjaHint(ctx) 300*333d2b36SAndroid Build Coastguard Worker } 301*333d2b36SAndroid Build Coastguard Worker return cmdlineArgs.OutFile, ninjaDeps 302*333d2b36SAndroid Build Coastguard Worker } 303*333d2b36SAndroid Build Coastguard Worker} 304*333d2b36SAndroid Build Coastguard Worker 305*333d2b36SAndroid Build Coastguard Worker// soong_ui dumps the available environment variables to 306*333d2b36SAndroid Build Coastguard Worker// soong.environment.available . Then soong_build itself is run with an empty 307*333d2b36SAndroid Build Coastguard Worker// environment so that the only way environment variables can be accessed is 308*333d2b36SAndroid Build Coastguard Worker// using Config, which tracks access to them. 309*333d2b36SAndroid Build Coastguard Worker 310*333d2b36SAndroid Build Coastguard Worker// At the end of the build, a file called soong.environment.used is written 311*333d2b36SAndroid Build Coastguard Worker// containing the current value of all used environment variables. The next 312*333d2b36SAndroid Build Coastguard Worker// time soong_ui is run, it checks whether any environment variables that was 313*333d2b36SAndroid Build Coastguard Worker// used had changed and if so, it deletes soong.environment.used to cause a 314*333d2b36SAndroid Build Coastguard Worker// rebuild. 315*333d2b36SAndroid Build Coastguard Worker// 316*333d2b36SAndroid Build Coastguard Worker// The dependency of build.ninja on soong.environment.used is declared in 317*333d2b36SAndroid Build Coastguard Worker// build.ninja.d 318*333d2b36SAndroid Build Coastguard Workerfunc parseAvailableEnv() map[string]string { 319*333d2b36SAndroid Build Coastguard Worker if availableEnvFile == "" { 320*333d2b36SAndroid Build Coastguard Worker fmt.Fprintf(os.Stderr, "--available_env not set\n") 321*333d2b36SAndroid Build Coastguard Worker os.Exit(1) 322*333d2b36SAndroid Build Coastguard Worker } 323*333d2b36SAndroid Build Coastguard Worker result, err := shared.EnvFromFile(shared.JoinPath(topDir, availableEnvFile)) 324*333d2b36SAndroid Build Coastguard Worker maybeQuit(err, "error reading available environment file '%s'", availableEnvFile) 325*333d2b36SAndroid Build Coastguard Worker return result 326*333d2b36SAndroid Build Coastguard Worker} 327*333d2b36SAndroid Build Coastguard Worker 328*333d2b36SAndroid Build Coastguard Workerfunc main() { 329*333d2b36SAndroid Build Coastguard Worker flag.Parse() 330*333d2b36SAndroid Build Coastguard Worker 331*333d2b36SAndroid Build Coastguard Worker soongStartTime := time.Now() 332*333d2b36SAndroid Build Coastguard Worker 333*333d2b36SAndroid Build Coastguard Worker shared.ReexecWithDelveMaybe(delveListen, delvePath) 334*333d2b36SAndroid Build Coastguard Worker android.InitSandbox(topDir) 335*333d2b36SAndroid Build Coastguard Worker 336*333d2b36SAndroid Build Coastguard Worker availableEnv := parseAvailableEnv() 337*333d2b36SAndroid Build Coastguard Worker configuration, err := android.NewConfig(cmdlineArgs, availableEnv) 338*333d2b36SAndroid Build Coastguard Worker maybeQuit(err, "") 339*333d2b36SAndroid Build Coastguard Worker if configuration.Getenv("ALLOW_MISSING_DEPENDENCIES") == "true" { 340*333d2b36SAndroid Build Coastguard Worker configuration.SetAllowMissingDependencies() 341*333d2b36SAndroid Build Coastguard Worker } 342*333d2b36SAndroid Build Coastguard Worker 343*333d2b36SAndroid Build Coastguard Worker // Bypass configuration.Getenv, as LOG_DIR does not need to be dependency tracked. By definition, it will 344*333d2b36SAndroid Build Coastguard Worker // change between every CI build, so tracking it would require re-running Soong for every build. 345*333d2b36SAndroid Build Coastguard Worker metricsDir := availableEnv["LOG_DIR"] 346*333d2b36SAndroid Build Coastguard Worker 347*333d2b36SAndroid Build Coastguard Worker ctx := newContext(configuration) 348*333d2b36SAndroid Build Coastguard Worker android.StartBackgroundMetrics(configuration) 349*333d2b36SAndroid Build Coastguard Worker 350*333d2b36SAndroid Build Coastguard Worker var configCache *ConfigCache 351*333d2b36SAndroid Build Coastguard Worker configFile := filepath.Join(topDir, ctx.Config().OutDir(), configCacheFile) 352*333d2b36SAndroid Build Coastguard Worker incremental := false 353*333d2b36SAndroid Build Coastguard Worker ctx.SetIncrementalEnabled(cmdlineArgs.IncrementalBuildActions) 354*333d2b36SAndroid Build Coastguard Worker if cmdlineArgs.IncrementalBuildActions { 355*333d2b36SAndroid Build Coastguard Worker configCache, incremental = incrementalValid(ctx.Config(), configFile) 356*333d2b36SAndroid Build Coastguard Worker } 357*333d2b36SAndroid Build Coastguard Worker ctx.SetIncrementalAnalysis(incremental) 358*333d2b36SAndroid Build Coastguard Worker 359*333d2b36SAndroid Build Coastguard Worker ctx.Register() 360*333d2b36SAndroid Build Coastguard Worker finalOutputFile, ninjaDeps := runSoongOnlyBuild(ctx) 361*333d2b36SAndroid Build Coastguard Worker 362*333d2b36SAndroid Build Coastguard Worker ninjaDeps = append(ninjaDeps, configuration.ProductVariablesFileName) 363*333d2b36SAndroid Build Coastguard Worker ninjaDeps = append(ninjaDeps, usedEnvFile) 364*333d2b36SAndroid Build Coastguard Worker if shared.IsDebugging() { 365*333d2b36SAndroid Build Coastguard Worker // Add a non-existent file to the dependencies so that soong_build will rerun when the debugger is 366*333d2b36SAndroid Build Coastguard Worker // enabled even if it completed successfully. 367*333d2b36SAndroid Build Coastguard Worker ninjaDeps = append(ninjaDeps, filepath.Join(configuration.SoongOutDir(), "always_rerun_for_delve")) 368*333d2b36SAndroid Build Coastguard Worker } 369*333d2b36SAndroid Build Coastguard Worker 370*333d2b36SAndroid Build Coastguard Worker writeDepFile(finalOutputFile, ctx.EventHandler, ninjaDeps) 371*333d2b36SAndroid Build Coastguard Worker 372*333d2b36SAndroid Build Coastguard Worker if ctx.GetIncrementalEnabled() { 373*333d2b36SAndroid Build Coastguard Worker data, err := shared.EnvFileContents(configuration.EnvDeps()) 374*333d2b36SAndroid Build Coastguard Worker maybeQuit(err, "") 375*333d2b36SAndroid Build Coastguard Worker configCache.EnvDepsHash, err = proptools.CalculateHash(data) 376*333d2b36SAndroid Build Coastguard Worker maybeQuit(err, "") 377*333d2b36SAndroid Build Coastguard Worker writeConfigCache(configCache, configFile) 378*333d2b36SAndroid Build Coastguard Worker } 379*333d2b36SAndroid Build Coastguard Worker 380*333d2b36SAndroid Build Coastguard Worker writeMetrics(configuration, ctx.EventHandler, metricsDir) 381*333d2b36SAndroid Build Coastguard Worker 382*333d2b36SAndroid Build Coastguard Worker writeUsedEnvironmentFile(configuration) 383*333d2b36SAndroid Build Coastguard Worker 384*333d2b36SAndroid Build Coastguard Worker err = writeGlobFile(ctx.EventHandler, finalOutputFile, ctx.Globs(), soongStartTime) 385*333d2b36SAndroid Build Coastguard Worker maybeQuit(err, "") 386*333d2b36SAndroid Build Coastguard Worker 387*333d2b36SAndroid Build Coastguard Worker // Touch the output file so that it's the newest file created by soong_build. 388*333d2b36SAndroid Build Coastguard Worker // This is necessary because, if soong_build generated any files which 389*333d2b36SAndroid Build Coastguard Worker // are ninja inputs to the main output file, then ninja would superfluously 390*333d2b36SAndroid Build Coastguard Worker // rebuild this output file on the next build invocation. 391*333d2b36SAndroid Build Coastguard Worker touch(shared.JoinPath(topDir, finalOutputFile)) 392*333d2b36SAndroid Build Coastguard Worker} 393*333d2b36SAndroid Build Coastguard Worker 394*333d2b36SAndroid Build Coastguard Workerfunc writeUsedEnvironmentFile(configuration android.Config) { 395*333d2b36SAndroid Build Coastguard Worker if usedEnvFile == "" { 396*333d2b36SAndroid Build Coastguard Worker return 397*333d2b36SAndroid Build Coastguard Worker } 398*333d2b36SAndroid Build Coastguard Worker 399*333d2b36SAndroid Build Coastguard Worker path := shared.JoinPath(topDir, usedEnvFile) 400*333d2b36SAndroid Build Coastguard Worker data, err := shared.EnvFileContents(configuration.EnvDeps()) 401*333d2b36SAndroid Build Coastguard Worker maybeQuit(err, "error writing used environment file '%s'\n", usedEnvFile) 402*333d2b36SAndroid Build Coastguard Worker 403*333d2b36SAndroid Build Coastguard Worker err = pathtools.WriteFileIfChanged(path, data, 0666) 404*333d2b36SAndroid Build Coastguard Worker maybeQuit(err, "error writing used environment file '%s'", usedEnvFile) 405*333d2b36SAndroid Build Coastguard Worker} 406*333d2b36SAndroid Build Coastguard Worker 407*333d2b36SAndroid Build Coastguard Workerfunc writeGlobFile(eventHandler *metrics.EventHandler, finalOutFile string, globs pathtools.MultipleGlobResults, soongStartTime time.Time) error { 408*333d2b36SAndroid Build Coastguard Worker eventHandler.Begin("writeGlobFile") 409*333d2b36SAndroid Build Coastguard Worker defer eventHandler.End("writeGlobFile") 410*333d2b36SAndroid Build Coastguard Worker 411*333d2b36SAndroid Build Coastguard Worker globsFile, err := os.Create(shared.JoinPath(topDir, finalOutFile+".globs")) 412*333d2b36SAndroid Build Coastguard Worker if err != nil { 413*333d2b36SAndroid Build Coastguard Worker return err 414*333d2b36SAndroid Build Coastguard Worker } 415*333d2b36SAndroid Build Coastguard Worker defer globsFile.Close() 416*333d2b36SAndroid Build Coastguard Worker globsFileEncoder := json.NewEncoder(globsFile) 417*333d2b36SAndroid Build Coastguard Worker for _, glob := range globs { 418*333d2b36SAndroid Build Coastguard Worker if err := globsFileEncoder.Encode(glob); err != nil { 419*333d2b36SAndroid Build Coastguard Worker return err 420*333d2b36SAndroid Build Coastguard Worker } 421*333d2b36SAndroid Build Coastguard Worker } 422*333d2b36SAndroid Build Coastguard Worker 423*333d2b36SAndroid Build Coastguard Worker return os.WriteFile( 424*333d2b36SAndroid Build Coastguard Worker shared.JoinPath(topDir, finalOutFile+".globs_time"), 425*333d2b36SAndroid Build Coastguard Worker []byte(fmt.Sprintf("%d\n", soongStartTime.UnixMicro())), 426*333d2b36SAndroid Build Coastguard Worker 0666, 427*333d2b36SAndroid Build Coastguard Worker ) 428*333d2b36SAndroid Build Coastguard Worker} 429*333d2b36SAndroid Build Coastguard Worker 430*333d2b36SAndroid Build Coastguard Workerfunc touch(path string) { 431*333d2b36SAndroid Build Coastguard Worker f, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666) 432*333d2b36SAndroid Build Coastguard Worker maybeQuit(err, "Error touching '%s'", path) 433*333d2b36SAndroid Build Coastguard Worker err = f.Close() 434*333d2b36SAndroid Build Coastguard Worker maybeQuit(err, "Error touching '%s'", path) 435*333d2b36SAndroid Build Coastguard Worker 436*333d2b36SAndroid Build Coastguard Worker currentTime := time.Now().Local() 437*333d2b36SAndroid Build Coastguard Worker err = os.Chtimes(path, currentTime, currentTime) 438*333d2b36SAndroid Build Coastguard Worker maybeQuit(err, "error touching '%s'", path) 439*333d2b36SAndroid Build Coastguard Worker} 440*333d2b36SAndroid Build Coastguard Worker 441*333d2b36SAndroid Build Coastguard Workerfunc maybeQuit(err error, format string, args ...interface{}) { 442*333d2b36SAndroid Build Coastguard Worker if err == nil { 443*333d2b36SAndroid Build Coastguard Worker return 444*333d2b36SAndroid Build Coastguard Worker } 445*333d2b36SAndroid Build Coastguard Worker if format != "" { 446*333d2b36SAndroid Build Coastguard Worker fmt.Fprintln(os.Stderr, fmt.Sprintf(format, args...)+": "+err.Error()) 447*333d2b36SAndroid Build Coastguard Worker } else { 448*333d2b36SAndroid Build Coastguard Worker fmt.Fprintln(os.Stderr, err) 449*333d2b36SAndroid Build Coastguard Worker } 450*333d2b36SAndroid Build Coastguard Worker os.Exit(1) 451*333d2b36SAndroid Build Coastguard Worker} 452