blob: a8be7ec1674b29876b6cf4e97cb92789be9dbc65 [file] [log] [blame]
Colin Cross3f40fa42015-01-30 17:27:36 -08001// 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 (
Usta Shrestha2ba28a32022-10-24 11:33:09 -040018 "bytes"
Yu Liufa297642024-06-11 00:13:02 +000019 "encoding/json"
Jeongik Chaa87506f2023-06-01 23:16:41 +090020 "errors"
Colin Cross3f40fa42015-01-30 17:27:36 -080021 "flag"
22 "fmt"
23 "os"
24 "path/filepath"
Jingwen Cheneb76c432021-01-28 08:22:12 -050025 "strings"
Lukacs T. Berkic99c9472021-03-24 10:50:06 +010026 "time"
Colin Cross3f40fa42015-01-30 17:27:36 -080027
Dan Willemsen66213a62021-09-21 17:50:30 -070028 "android/soong/android"
Jeongik Chae114e602023-03-19 00:12:39 +090029 "android/soong/android/allowlists"
Lukacs T. Berkif8e24282021-04-14 10:31:00 +020030 "android/soong/bp2build"
Lukacs T. Berki7690c092021-02-26 14:27:36 +010031 "android/soong/shared"
Jeongik Chab745e2e2023-04-11 14:28:43 +090032 "github.com/google/blueprint"
Colin Cross70b40592015-03-23 12:57:34 -070033 "github.com/google/blueprint/bootstrap"
Lukacs T. Berkid518e1a2021-04-14 13:49:50 +020034 "github.com/google/blueprint/deptools"
Chris Parsons715b08f2022-03-22 19:23:40 -040035 "github.com/google/blueprint/metrics"
Yu Liufa297642024-06-11 00:13:02 +000036 "github.com/google/blueprint/proptools"
Dan Willemsen66213a62021-09-21 17:50:30 -070037 androidProtobuf "google.golang.org/protobuf/android"
Colin Cross3f40fa42015-01-30 17:27:36 -080038)
39
Colin Crosse87040b2017-12-11 15:52:26 -080040var (
Lukacs T. Berkif8e24282021-04-14 10:31:00 +020041 topDir string
Lukacs T. Berkif8e24282021-04-14 10:31:00 +020042 availableEnvFile string
43 usedEnvFile string
44
Lukacs T. Berki809d2ed2021-08-18 10:55:32 +020045 globFile string
46 globListDir string
Lukacs T. Berkif8e24282021-04-14 10:31:00 +020047 delveListen string
48 delvePath string
49
Sasha Smundakaf5ca922022-12-12 21:23:34 -080050 cmdlineArgs android.CmdArgs
Colin Crosse87040b2017-12-11 15:52:26 -080051)
52
Yu Liufa297642024-06-11 00:13:02 +000053const configCacheFile = "config.cache"
54
55type ConfigCache struct {
56 EnvDepsHash uint64
57 ProductVariableFileTimestamp int64
58 SoongBuildFileTimestamp int64
59}
60
Colin Crosse87040b2017-12-11 15:52:26 -080061func init() {
Lukacs T. Berkif8e24282021-04-14 10:31:00 +020062 // Flags that make sense in every mode
Lukacs T. Berki7690c092021-02-26 14:27:36 +010063 flag.StringVar(&topDir, "top", "", "Top directory of the Android source tree")
Sasha Smundakaf5ca922022-12-12 21:23:34 -080064 flag.StringVar(&cmdlineArgs.SoongOutDir, "soong_out", "", "Soong output directory (usually $TOP/out/soong)")
Lukacs T. Berkif8e24282021-04-14 10:31:00 +020065 flag.StringVar(&availableEnvFile, "available_env", "", "File containing available environment variables")
66 flag.StringVar(&usedEnvFile, "used_env", "", "File containing used environment variables")
Lukacs T. Berkib078ade2021-08-31 10:42:08 +020067 flag.StringVar(&globFile, "globFile", "build-globs.ninja", "the Ninja file of globs to output")
68 flag.StringVar(&globListDir, "globListDir", "", "the directory containing the glob list files")
Sasha Smundakaf5ca922022-12-12 21:23:34 -080069 flag.StringVar(&cmdlineArgs.OutDir, "out", "", "the ninja builddir directory")
Lukacs T. Berkib078ade2021-08-31 10:42:08 +020070 flag.StringVar(&cmdlineArgs.ModuleListFile, "l", "", "file that lists filepaths to parse")
Lukacs T. Berkif8e24282021-04-14 10:31:00 +020071
72 // Debug flags
Lukacs T. Berki7d613bf2021-03-02 10:09:41 +010073 flag.StringVar(&delveListen, "delve_listen", "", "Delve port to listen on for debugging")
74 flag.StringVar(&delvePath, "delve_path", "", "Path to Delve. Only used if --delve_listen is set")
Lukacs T. Berkib078ade2021-08-31 10:42:08 +020075 flag.StringVar(&cmdlineArgs.Cpuprofile, "cpuprofile", "", "write cpu profile to file")
76 flag.StringVar(&cmdlineArgs.TraceFile, "trace", "", "write trace to file")
77 flag.StringVar(&cmdlineArgs.Memprofile, "memprofile", "", "write memory profile to file")
78 flag.BoolVar(&cmdlineArgs.NoGC, "nogc", false, "turn off GC for debugging")
Lukacs T. Berkif8e24282021-04-14 10:31:00 +020079
80 // Flags representing various modes soong_build can run in
Sasha Smundakaf5ca922022-12-12 21:23:34 -080081 flag.StringVar(&cmdlineArgs.ModuleGraphFile, "module_graph_file", "", "JSON module graph file to output")
82 flag.StringVar(&cmdlineArgs.ModuleActionsFile, "module_actions_file", "", "JSON file to output inputs/outputs of actions of modules")
83 flag.StringVar(&cmdlineArgs.DocFile, "soong_docs", "", "build documentation file to output")
84 flag.StringVar(&cmdlineArgs.BazelQueryViewDir, "bazel_queryview_dir", "", "path to the bazel queryview directory relative to --top")
Lukacs T. Berkif9008072021-08-16 15:24:48 +020085 flag.StringVar(&cmdlineArgs.OutFile, "o", "build.ninja", "the Ninja file to output")
Kiyoung Kima37d9ba2023-04-19 13:13:45 +090086 flag.StringVar(&cmdlineArgs.SoongVariables, "soong_variables", "soong.variables", "the file contains all build variables")
Lukacs T. Berkib078ade2021-08-31 10:42:08 +020087 flag.BoolVar(&cmdlineArgs.EmptyNinjaFile, "empty-ninja-file", false, "write out a 0-byte ninja file")
Jihoon Kang2a929ad2023-06-08 19:02:07 +000088 flag.BoolVar(&cmdlineArgs.BuildFromSourceStub, "build-from-source-stub", false, "build Java stubs from source files instead of API text files")
MarkDacekf47e1422023-04-19 16:47:36 +000089 flag.BoolVar(&cmdlineArgs.EnsureAllowlistIntegrity, "ensure-allowlist-integrity", false, "verify that allowlisted modules are mixed-built")
Joe Onoratoe5ed3472024-02-02 14:52:05 -080090 flag.StringVar(&cmdlineArgs.ModuleDebugFile, "soong_module_debug", "", "soong module debug info file to write")
Sasha Smundakaf5ca922022-12-12 21:23:34 -080091 // Flags that probably shouldn't be flags of soong_build, but we haven't found
Lukacs T. Berkib078ade2021-08-31 10:42:08 +020092 // the time to remove them yet
Sasha Smundakaf5ca922022-12-12 21:23:34 -080093 flag.BoolVar(&cmdlineArgs.RunGoTests, "t", false, "build and run go tests during bootstrap")
Yu Liufa297642024-06-11 00:13:02 +000094 flag.BoolVar(&cmdlineArgs.IncrementalBuildActions, "incremental-build-actions", false, "generate build actions incrementally")
Dan Willemsen66213a62021-09-21 17:50:30 -070095
96 // Disable deterministic randomization in the protobuf package, so incremental
97 // builds with unrelated Soong changes don't trigger large rebuilds (since we
98 // write out text protos in command lines, and command line changes trigger
99 // rebuilds).
100 androidProtobuf.DisableRand()
Colin Crosse87040b2017-12-11 15:52:26 -0800101}
102
Jeff Gaston088e29e2017-11-29 16:47:17 -0800103func newNameResolver(config android.Config) *android.NameResolver {
Paul Duffin3f7bf9f2022-11-08 12:21:15 +0000104 return android.NewNameResolver(config)
Jeff Gaston088e29e2017-11-29 16:47:17 -0800105}
106
Lukacs T. Berkiffc9e8d2021-09-07 17:54:38 +0200107func newContext(configuration android.Config) *android.Context {
Colin Crossae8600b2020-10-29 17:09:13 -0700108 ctx := android.NewContext(configuration)
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400109 ctx.SetNameInterface(newNameResolver(configuration))
110 ctx.SetAllowMissingDependencies(configuration.AllowMissingDependencies())
Sam Delmerico98a73292023-02-21 11:50:29 -0500111 ctx.AddSourceRootDirs(configuration.SourceRootDirs()...)
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400112 return ctx
113}
114
Jeongik Chaa87506f2023-06-01 23:16:41 +0900115func needToWriteNinjaHint(ctx *android.Context) bool {
116 switch ctx.Config().GetenvWithDefault("SOONG_GENERATES_NINJA_HINT", "") {
117 case "always":
118 return true
119 case "depend":
Cole Faust6bb28322024-05-13 11:57:16 -0700120 if _, err := os.Stat(filepath.Join(topDir, ctx.Config().OutDir(), ".ninja_log")); errors.Is(err, os.ErrNotExist) {
Jeongik Chaa87506f2023-06-01 23:16:41 +0900121 return true
122 }
123 }
124 return false
125}
126
Lukacs T. Berki6790ebc2021-04-01 17:55:58 +0200127// Run the code-generation phase to convert BazelTargetModules to BUILD files.
Sasha Smundak1845f422022-12-13 14:18:58 -0800128func runQueryView(queryviewDir, queryviewMarker string, ctx *android.Context) {
Chris Parsons715b08f2022-03-22 19:23:40 -0400129 ctx.EventHandler.Begin("queryview")
130 defer ctx.EventHandler.End("queryview")
Cole Faustb85d1a12022-11-08 18:14:01 -0800131 codegenContext := bp2build.NewCodegenContext(ctx.Config(), ctx, bp2build.QueryView, topDir)
Spandan Das98cb8562023-03-09 23:05:47 +0000132 err := createBazelWorkspace(codegenContext, shared.JoinPath(topDir, queryviewDir), false)
Sasha Smundak1845f422022-12-13 14:18:58 -0800133 maybeQuit(err, "")
Lukacs T. Berki3a821692021-09-06 17:08:02 +0200134 touch(shared.JoinPath(topDir, queryviewMarker))
Lukacs T. Berki6790ebc2021-04-01 17:55:58 +0200135}
136
Jeongik Chae114e602023-03-19 00:12:39 +0900137func writeNinjaHint(ctx *android.Context) error {
Jeongik Cha73d49112023-05-04 18:16:11 +0900138 ctx.BeginEvent("ninja_hint")
139 defer ctx.EndEvent("ninja_hint")
Jeongik Chab745e2e2023-04-11 14:28:43 +0900140 // The current predictor focuses on reducing false negatives.
141 // If there are too many false positives (e.g., most modules are marked as positive),
142 // real long-running jobs cannot run early.
143 // Therefore, the model should be adjusted in this case.
144 // The model should also be adjusted if there are critical false negatives.
145 predicate := func(j *blueprint.JsonModule) (prioritized bool, weight int) {
146 prioritized = false
147 weight = 0
148 for prefix, w := range allowlists.HugeModuleTypePrefixMap {
149 if strings.HasPrefix(j.Type, prefix) {
150 prioritized = true
151 weight = w
152 return
153 }
Jeongik Chae114e602023-03-19 00:12:39 +0900154 }
Jeongik Chab745e2e2023-04-11 14:28:43 +0900155 dep_count := len(j.Deps)
156 src_count := 0
157 for _, a := range j.Module["Actions"].([]blueprint.JSONAction) {
158 src_count += len(a.Inputs)
159 }
160 input_size := dep_count + src_count
161
162 // Current threshold is an arbitrary value which only consider recall rather than accuracy.
163 if input_size > allowlists.INPUT_SIZE_THRESHOLD {
164 prioritized = true
165 weight += ((input_size) / allowlists.INPUT_SIZE_THRESHOLD) * allowlists.DEFAULT_PRIORITIZED_WEIGHT
166
167 // To prevent some modules from having too large a priority value.
168 if weight > allowlists.HIGH_PRIORITIZED_WEIGHT {
169 weight = allowlists.HIGH_PRIORITIZED_WEIGHT
170 }
171 }
172 return
173 }
174
175 outputsMap := ctx.Context.GetWeightedOutputsFromPredicate(predicate)
176 var outputBuilder strings.Builder
177 for output, weight := range outputsMap {
178 outputBuilder.WriteString(fmt.Sprintf("%s,%d\n", output, weight))
Jeongik Chae114e602023-03-19 00:12:39 +0900179 }
180 weightListFile := filepath.Join(topDir, ctx.Config().OutDir(), ".ninja_weight_list")
181
182 err := os.WriteFile(weightListFile, []byte(outputBuilder.String()), 0644)
183 if err != nil {
184 return fmt.Errorf("could not write ninja weight list file %s", err)
185 }
186 return nil
187}
188
Paul Duffin780a1852022-11-05 10:17:12 +0000189func writeMetrics(configuration android.Config, eventHandler *metrics.EventHandler, metricsDir string) {
Chris Parsons715b08f2022-03-22 19:23:40 -0400190 if len(metricsDir) < 1 {
191 fmt.Fprintf(os.Stderr, "\nMissing required env var for generating soong metrics: LOG_DIR\n")
192 os.Exit(1)
193 }
194 metricsFile := filepath.Join(metricsDir, "soong_build_metrics.pb")
195 err := android.WriteMetrics(configuration, eventHandler, metricsFile)
Sasha Smundak1845f422022-12-13 14:18:58 -0800196 maybeQuit(err, "error writing soong_build metrics %s", metricsFile)
Lukacs T. Berki6790ebc2021-04-01 17:55:58 +0200197}
198
Sasha Smundakaf5ca922022-12-12 21:23:34 -0800199func writeJsonModuleGraphAndActions(ctx *android.Context, cmdArgs android.CmdArgs) {
200 graphFile, graphErr := os.Create(shared.JoinPath(topDir, cmdArgs.ModuleGraphFile))
Sasha Smundak1845f422022-12-13 14:18:58 -0800201 maybeQuit(graphErr, "graph err")
kgui67007242022-01-25 13:50:25 +0800202 defer graphFile.Close()
Sasha Smundak1845f422022-12-13 14:18:58 -0800203 actionsFile, actionsErr := os.Create(shared.JoinPath(topDir, cmdArgs.ModuleActionsFile))
204 maybeQuit(actionsErr, "actions err")
kgui67007242022-01-25 13:50:25 +0800205 defer actionsFile.Close()
206 ctx.Context.PrintJSONGraphAndActions(graphFile, actionsFile)
Lukacs T. Berki97bb9f12021-04-01 18:28:45 +0200207}
208
Cole Faust8c0b11e2024-01-02 17:02:52 -0800209func writeBuildGlobsNinjaFile(ctx *android.Context) {
Chris Parsons715b08f2022-03-22 19:23:40 -0400210 ctx.EventHandler.Begin("globs_ninja_file")
211 defer ctx.EventHandler.End("globs_ninja_file")
212
Sasha Smundak1845f422022-12-13 14:18:58 -0800213 globDir := bootstrap.GlobDirectory(ctx.Config().SoongOutDir(), globListDir)
Lukacs T. Berkic357c812023-06-20 09:30:06 +0000214 err := bootstrap.WriteBuildGlobsNinjaFile(&bootstrap.GlobSingleton{
Chris Parsons715b08f2022-03-22 19:23:40 -0400215 GlobLister: ctx.Globs,
Lukacs T. Berki809d2ed2021-08-18 10:55:32 +0200216 GlobFile: globFile,
217 GlobDir: globDir,
Chris Parsons715b08f2022-03-22 19:23:40 -0400218 SrcDir: ctx.SrcDir(),
Sasha Smundak1845f422022-12-13 14:18:58 -0800219 }, ctx.Config())
Lukacs T. Berkic357c812023-06-20 09:30:06 +0000220 maybeQuit(err, "")
Lukacs T. Berki809d2ed2021-08-18 10:55:32 +0200221}
222
Paul Duffin780a1852022-11-05 10:17:12 +0000223func writeDepFile(outputFile string, eventHandler *metrics.EventHandler, ninjaDeps []string) {
Chris Parsons715b08f2022-03-22 19:23:40 -0400224 eventHandler.Begin("ninja_deps")
225 defer eventHandler.End("ninja_deps")
Lukacs T. Berkie571dc32021-08-25 14:14:13 +0200226 depFile := shared.JoinPath(topDir, outputFile+".d")
227 err := deptools.WriteDepFile(depFile, outputFile, ninjaDeps)
Sasha Smundak1845f422022-12-13 14:18:58 -0800228 maybeQuit(err, "error writing depfile '%s'", depFile)
Paul Duffin0c09a432022-11-05 15:28:04 +0000229}
Lukacs T. Berki6790ebc2021-04-01 17:55:58 +0200230
Yu Liufa297642024-06-11 00:13:02 +0000231// Check if there are changes to the environment file, product variable file and
232// soong_build binary, in which case no incremental will be performed.
233func incrementalValid(config android.Config, configCacheFile string) (*ConfigCache, bool) {
234 var newConfigCache ConfigCache
235 data, err := os.ReadFile(shared.JoinPath(topDir, usedEnvFile))
236 if err != nil {
237 // Clean build
238 if os.IsNotExist(err) {
239 data = []byte{}
240 } else {
241 maybeQuit(err, "")
242 }
243 }
244
245 newConfigCache.EnvDepsHash, err = proptools.CalculateHash(data)
246 newConfigCache.ProductVariableFileTimestamp = getFileTimestamp(filepath.Join(topDir, cmdlineArgs.SoongVariables))
247 newConfigCache.SoongBuildFileTimestamp = getFileTimestamp(filepath.Join(topDir, config.HostToolDir(), "soong_build"))
248 //TODO(b/344917959): out/soong/dexpreopt.config might need to be checked as well.
249
250 file, err := os.Open(configCacheFile)
251 if err != nil && os.IsNotExist(err) {
252 return &newConfigCache, false
253 }
254 maybeQuit(err, "")
255 defer file.Close()
256
257 var configCache ConfigCache
258 decoder := json.NewDecoder(file)
259 err = decoder.Decode(&configCache)
260 maybeQuit(err, "")
261
262 return &newConfigCache, newConfigCache == configCache
263}
264
265func getFileTimestamp(file string) int64 {
266 stat, err := os.Stat(file)
267 if err == nil {
268 return stat.ModTime().UnixMilli()
269 } else if !os.IsNotExist(err) {
270 maybeQuit(err, "")
271 }
272 return 0
273}
274
275func writeConfigCache(configCache *ConfigCache, configCacheFile string) {
276 file, err := os.Create(configCacheFile)
277 maybeQuit(err, "")
278 defer file.Close()
279
280 encoder := json.NewEncoder(file)
281 err = encoder.Encode(*configCache)
282 maybeQuit(err, "")
283}
284
Paul Duffin0c09a432022-11-05 15:28:04 +0000285// runSoongOnlyBuild runs the standard Soong build in a number of different modes.
Sasha Smundak1845f422022-12-13 14:18:58 -0800286func runSoongOnlyBuild(ctx *android.Context, extraNinjaDeps []string) string {
Paul Duffin39eae8f2022-11-05 14:59:52 +0000287 ctx.EventHandler.Begin("soong_build")
288 defer ctx.EventHandler.End("soong_build")
289
Paul Duffin0c09a432022-11-05 15:28:04 +0000290 var stopBefore bootstrap.StopBefore
Sasha Smundak1845f422022-12-13 14:18:58 -0800291 switch ctx.Config().BuildMode {
292 case android.GenerateModuleGraph:
Paul Duffin0c09a432022-11-05 15:28:04 +0000293 stopBefore = bootstrap.StopBeforeWriteNinja
Usta Shrestha7fae6952022-12-21 11:46:28 -0500294 case android.GenerateQueryView, android.GenerateDocFile:
Paul Duffin0c09a432022-11-05 15:28:04 +0000295 stopBefore = bootstrap.StopBeforePrepareBuildActions
Sasha Smundak1845f422022-12-13 14:18:58 -0800296 default:
Paul Duffin0c09a432022-11-05 15:28:04 +0000297 stopBefore = bootstrap.DoEverything
298 }
299
Lukacs T. Berkic357c812023-06-20 09:30:06 +0000300 ninjaDeps, err := bootstrap.RunBlueprint(cmdlineArgs.Args, stopBefore, ctx.Context, ctx.Config())
301 maybeQuit(err, "")
Paul Duffin0c09a432022-11-05 15:28:04 +0000302 ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
303
Cole Faust8c0b11e2024-01-02 17:02:52 -0800304 writeBuildGlobsNinjaFile(ctx)
Paul Duffin0c09a432022-11-05 15:28:04 +0000305
306 // Convert the Soong module graph into Bazel BUILD files.
Sasha Smundak1845f422022-12-13 14:18:58 -0800307 switch ctx.Config().BuildMode {
308 case android.GenerateQueryView:
Sasha Smundakaf5ca922022-12-12 21:23:34 -0800309 queryviewMarkerFile := cmdlineArgs.BazelQueryViewDir + ".marker"
Sasha Smundak1845f422022-12-13 14:18:58 -0800310 runQueryView(cmdlineArgs.BazelQueryViewDir, queryviewMarkerFile, ctx)
Paul Duffin0c09a432022-11-05 15:28:04 +0000311 writeDepFile(queryviewMarkerFile, ctx.EventHandler, ninjaDeps)
312 return queryviewMarkerFile
Sasha Smundak1845f422022-12-13 14:18:58 -0800313 case android.GenerateModuleGraph:
Sasha Smundakaf5ca922022-12-12 21:23:34 -0800314 writeJsonModuleGraphAndActions(ctx, cmdlineArgs)
315 writeDepFile(cmdlineArgs.ModuleGraphFile, ctx.EventHandler, ninjaDeps)
316 return cmdlineArgs.ModuleGraphFile
Sasha Smundak1845f422022-12-13 14:18:58 -0800317 case android.GenerateDocFile:
Paul Duffin0c09a432022-11-05 15:28:04 +0000318 // TODO: we could make writeDocs() return the list of documentation files
319 // written and add them to the .d file. Then soong_docs would be re-run
320 // whenever one is deleted.
Sasha Smundak1845f422022-12-13 14:18:58 -0800321 err := writeDocs(ctx, shared.JoinPath(topDir, cmdlineArgs.DocFile))
322 maybeQuit(err, "error building Soong documentation")
Sasha Smundakaf5ca922022-12-12 21:23:34 -0800323 writeDepFile(cmdlineArgs.DocFile, ctx.EventHandler, ninjaDeps)
324 return cmdlineArgs.DocFile
Sasha Smundak1845f422022-12-13 14:18:58 -0800325 default:
Paul Duffin0c09a432022-11-05 15:28:04 +0000326 // The actual output (build.ninja) was written in the RunBlueprint() call
327 // above
328 writeDepFile(cmdlineArgs.OutFile, ctx.EventHandler, ninjaDeps)
Jeongik Chaa87506f2023-06-01 23:16:41 +0900329 if needToWriteNinjaHint(ctx) {
Jeongik Cha591366d2023-05-08 11:32:52 +0900330 writeNinjaHint(ctx)
331 }
Paul Duffinb713ddf2022-11-05 16:14:30 +0000332 return cmdlineArgs.OutFile
Paul Duffin0c09a432022-11-05 15:28:04 +0000333 }
Lukacs T. Berkif8e24282021-04-14 10:31:00 +0200334}
335
336// soong_ui dumps the available environment variables to
337// soong.environment.available . Then soong_build itself is run with an empty
338// environment so that the only way environment variables can be accessed is
339// using Config, which tracks access to them.
340
341// At the end of the build, a file called soong.environment.used is written
342// containing the current value of all used environment variables. The next
343// time soong_ui is run, it checks whether any environment variables that was
344// used had changed and if so, it deletes soong.environment.used to cause a
345// rebuild.
346//
347// The dependency of build.ninja on soong.environment.used is declared in
348// build.ninja.d
349func parseAvailableEnv() map[string]string {
350 if availableEnvFile == "" {
351 fmt.Fprintf(os.Stderr, "--available_env not set\n")
352 os.Exit(1)
353 }
Lukacs T. Berkif8e24282021-04-14 10:31:00 +0200354 result, err := shared.EnvFromFile(shared.JoinPath(topDir, availableEnvFile))
Sasha Smundak1845f422022-12-13 14:18:58 -0800355 maybeQuit(err, "error reading available environment file '%s'", availableEnvFile)
Lukacs T. Berkif8e24282021-04-14 10:31:00 +0200356 return result
Lukacs T. Berki6790ebc2021-04-01 17:55:58 +0200357}
358
Colin Cross3f40fa42015-01-30 17:27:36 -0800359func main() {
360 flag.Parse()
361
Lukacs T. Berki7d613bf2021-03-02 10:09:41 +0100362 shared.ReexecWithDelveMaybe(delveListen, delvePath)
Lukacs T. Berki7690c092021-02-26 14:27:36 +0100363 android.InitSandbox(topDir)
Lukacs T. Berki7690c092021-02-26 14:27:36 +0100364
Lukacs T. Berkif8e24282021-04-14 10:31:00 +0200365 availableEnv := parseAvailableEnv()
Sasha Smundakaf5ca922022-12-12 21:23:34 -0800366 configuration, err := android.NewConfig(cmdlineArgs, availableEnv)
Sasha Smundak1845f422022-12-13 14:18:58 -0800367 maybeQuit(err, "")
Lukacs T. Berkid1e3f1f2021-03-16 08:55:23 +0100368 if configuration.Getenv("ALLOW_MISSING_DEPENDENCIES") == "true" {
369 configuration.SetAllowMissingDependencies()
370 }
371
Sasha Smundakaf5ca922022-12-12 21:23:34 -0800372 extraNinjaDeps := []string{configuration.ProductVariablesFileName, usedEnvFile}
Lukacs T. Berki7d613bf2021-03-02 10:09:41 +0100373 if shared.IsDebugging() {
Colin Crossaa812d12019-06-19 13:33:24 -0700374 // Add a non-existent file to the dependencies so that soong_build will rerun when the debugger is
375 // enabled even if it completed successfully.
Lukacs T. Berki9f6c24a2021-08-26 15:07:24 +0200376 extraNinjaDeps = append(extraNinjaDeps, filepath.Join(configuration.SoongOutDir(), "always_rerun_for_delve"))
Colin Crossaa812d12019-06-19 13:33:24 -0700377 }
Jingwen Chendaa54bc2020-12-14 02:58:54 -0500378
Dan Willemsenccf36aa2022-04-20 23:11:43 -0700379 // Bypass configuration.Getenv, as LOG_DIR does not need to be dependency tracked. By definition, it will
380 // change between every CI build, so tracking it would require re-running Soong for every build.
Sasha Smundak1845f422022-12-13 14:18:58 -0800381 metricsDir := availableEnv["LOG_DIR"]
Dan Willemsenccf36aa2022-04-20 23:11:43 -0700382
Joe Onorato2e5e4012022-06-07 17:16:08 -0700383 ctx := newContext(configuration)
Colin Cross46b0c752023-10-27 14:56:12 -0700384 android.StartBackgroundMetrics(configuration)
Joe Onorato2e5e4012022-06-07 17:16:08 -0700385
Yu Liufa297642024-06-11 00:13:02 +0000386 var configCache *ConfigCache
387 configFile := filepath.Join(topDir, ctx.Config().OutDir(), configCacheFile)
388 incremental := false
389 ctx.SetIncrementalEnabled(cmdlineArgs.IncrementalBuildActions)
390 if cmdlineArgs.IncrementalBuildActions {
391 configCache, incremental = incrementalValid(ctx.Config(), configFile)
392 }
393 ctx.SetIncrementalAnalysis(incremental)
394
Colin Crossb63d7b32023-12-07 16:54:51 -0800395 ctx.Register()
396 finalOutputFile := runSoongOnlyBuild(ctx, extraNinjaDeps)
Yu Liufa297642024-06-11 00:13:02 +0000397
398 if ctx.GetIncrementalEnabled() {
399 data, err := shared.EnvFileContents(configuration.EnvDeps())
400 maybeQuit(err, "")
401 configCache.EnvDepsHash, err = proptools.CalculateHash(data)
402 maybeQuit(err, "")
403 writeConfigCache(configCache, configFile)
404 }
405
Colin Crossb63d7b32023-12-07 16:54:51 -0800406 writeMetrics(configuration, ctx.EventHandler, metricsDir)
Chris Parsonsc83398f2023-05-31 18:41:41 +0000407
Chris Parsonsa3ae0072023-05-10 21:10:08 +0000408 writeUsedEnvironmentFile(configuration)
409
410 // Touch the output file so that it's the newest file created by soong_build.
411 // This is necessary because, if soong_build generated any files which
412 // are ninja inputs to the main output file, then ninja would superfluously
413 // rebuild this output file on the next build invocation.
414 touch(shared.JoinPath(topDir, finalOutputFile))
Lukacs T. Berkif0b3b942021-03-23 11:46:47 +0100415}
416
Chris Parsonsa3ae0072023-05-10 21:10:08 +0000417func writeUsedEnvironmentFile(configuration android.Config) {
Lukacs T. Berkif8e24282021-04-14 10:31:00 +0200418 if usedEnvFile == "" {
419 return
420 }
421
422 path := shared.JoinPath(topDir, usedEnvFile)
Lukacs T. Berkif0b3b942021-03-23 11:46:47 +0100423 data, err := shared.EnvFileContents(configuration.EnvDeps())
Sasha Smundak1845f422022-12-13 14:18:58 -0800424 maybeQuit(err, "error writing used environment file '%s'\n", usedEnvFile)
Lukacs T. Berkif0b3b942021-03-23 11:46:47 +0100425
Usta Shrestha2ba28a32022-10-24 11:33:09 -0400426 if preexistingData, err := os.ReadFile(path); err != nil {
427 if !os.IsNotExist(err) {
Sasha Smundak1845f422022-12-13 14:18:58 -0800428 maybeQuit(err, "error reading used environment file '%s'", usedEnvFile)
Usta Shrestha2ba28a32022-10-24 11:33:09 -0400429 }
430 } else if bytes.Equal(preexistingData, data) {
431 // used environment file is unchanged
432 return
433 }
Sasha Smundak1845f422022-12-13 14:18:58 -0800434 err = os.WriteFile(path, data, 0666)
435 maybeQuit(err, "error writing used environment file '%s'", usedEnvFile)
Colin Cross3f40fa42015-01-30 17:27:36 -0800436}
Jingwen Chen5ba7e472020-07-15 10:06:41 +0000437
Lukacs T. Berkif8e24282021-04-14 10:31:00 +0200438func touch(path string) {
439 f, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)
Sasha Smundak1845f422022-12-13 14:18:58 -0800440 maybeQuit(err, "Error touching '%s'", path)
Lukacs T. Berkif8e24282021-04-14 10:31:00 +0200441 err = f.Close()
Sasha Smundak1845f422022-12-13 14:18:58 -0800442 maybeQuit(err, "Error touching '%s'", path)
Lukacs T. Berkif8e24282021-04-14 10:31:00 +0200443
444 currentTime := time.Now().Local()
445 err = os.Chtimes(path, currentTime, currentTime)
Sasha Smundak1845f422022-12-13 14:18:58 -0800446 maybeQuit(err, "error touching '%s'", path)
Rupert Shuttleworth00960792021-05-12 21:20:13 -0400447}
448
Sasha Smundak1845f422022-12-13 14:18:58 -0800449func maybeQuit(err error, format string, args ...interface{}) {
450 if err == nil {
451 return
452 }
453 if format != "" {
454 fmt.Fprintln(os.Stderr, fmt.Sprintf(format, args...)+": "+err.Error())
455 } else {
456 fmt.Fprintln(os.Stderr, err)
457 }
458 os.Exit(1)
MarkDacek0d5bca52022-10-10 20:07:48 +0000459}