1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "gn/args.h"
6
7 #include "gn/settings.h"
8 #include "gn/source_file.h"
9 #include "gn/string_utils.h"
10 #include "gn/variables.h"
11 #include "util/build_config.h"
12 #include "util/sys_info.h"
13
14 const char kBuildArgs_Help[] =
15 R"(Build Arguments Overview
16
17 Build arguments are variables passed in from outside of the build that build
18 files can query to determine how the build works.
19
20 How build arguments are set
21
22 First, system default arguments are set based on the current system. The
23 built-in arguments are:
24 - host_cpu
25 - host_os
26 - current_cpu
27 - current_os
28 - target_cpu
29 - target_os
30
31 Next, project-specific overrides are applied. These are specified inside
32 the default_args variable of //.gn. See "gn help dotfile" for more.
33
34 If specified, arguments from the --args command line flag are used. If that
35 flag is not specified, args from previous builds in the build directory will
36 be used (this is in the file args.gn in the build directory).
37
38 Last, for targets being compiled with a non-default toolchain, the toolchain
39 overrides are applied. These are specified in the toolchain_args section of a
40 toolchain definition. The use-case for this is that a toolchain may be
41 building code for a different platform, and that it may want to always
42 specify Posix, for example. See "gn help toolchain" for more.
43
44 If you specify an override for a build argument that never appears in a
45 "declare_args" call, a nonfatal error will be displayed.
46
47 Examples
48
49 gn args out/FooBar
50 Create the directory out/FooBar and open an editor. You would type
51 something like this into that file:
52 enable_doom_melon=false
53 os="android"
54
55 gn gen out/FooBar --args="enable_doom_melon=true os=\"android\""
56 This will overwrite the build directory with the given arguments. (Note
57 that the quotes inside the args command will usually need to be escaped
58 for your shell to pass through strings values.)
59
60 How build arguments are used
61
62 If you want to use an argument, you use declare_args() and specify default
63 values. These default values will apply if none of the steps listed in the
64 "How build arguments are set" section above apply to the given argument, but
65 the defaults will not override any of these.
66
67 Often, the root build config file will declare global arguments that will be
68 passed to all buildfiles. Individual build files can also specify arguments
69 that apply only to those files. It is also useful to specify build args in an
70 "import"-ed file if you want such arguments to apply to multiple buildfiles.
71 )";
72
73 namespace {
74
75 // Removes all entries in |overrides| that are in |declared_overrides|.
RemoveDeclaredOverrides(const Scope::KeyValueMap & declared_arguments,Scope::KeyValueMap * overrides)76 void RemoveDeclaredOverrides(const Scope::KeyValueMap& declared_arguments,
77 Scope::KeyValueMap* overrides) {
78 for (Scope::KeyValueMap::iterator override = overrides->begin();
79 override != overrides->end();) {
80 if (declared_arguments.find(override->first) == declared_arguments.end())
81 ++override;
82 else
83 overrides->erase(override++);
84 }
85 }
86
87 } // namespace
88
89 Args::ValueWithOverride::ValueWithOverride()
90 : default_value(), has_override(false), override_value() {}
91
92 Args::ValueWithOverride::ValueWithOverride(const Value& def_val)
93 : default_value(def_val), has_override(false), override_value() {}
94
95 Args::ValueWithOverride::~ValueWithOverride() = default;
96
97 Args::Args() = default;
98
99 Args::Args(const Args& other)
100 : overrides_(other.overrides_),
101 all_overrides_(other.all_overrides_),
102 declared_arguments_per_toolchain_(
103 other.declared_arguments_per_toolchain_),
104 toolchain_overrides_(other.toolchain_overrides_) {}
105
106 Args::~Args() = default;
107
108 void Args::AddArgOverride(const char* name, const Value& value) {
109 std::lock_guard<std::mutex> lock(lock_);
110
111 overrides_[std::string_view(name)] = value;
112 all_overrides_[std::string_view(name)] = value;
113 }
114
115 void Args::AddArgOverrides(const Scope::KeyValueMap& overrides) {
116 std::lock_guard<std::mutex> lock(lock_);
117
118 for (const auto& cur_override : overrides) {
119 overrides_[cur_override.first] = cur_override.second;
120 all_overrides_[cur_override.first] = cur_override.second;
121 }
122 }
123
124 void Args::AddDefaultArgOverrides(const Scope::KeyValueMap& overrides) {
125 std::lock_guard<std::mutex> lock(lock_);
126 for (const auto& cur_override : overrides)
127 overrides_[cur_override.first] = cur_override.second;
128 }
129
130 const Value* Args::GetArgOverride(const char* name) const {
131 std::lock_guard<std::mutex> lock(lock_);
132
133 Scope::KeyValueMap::const_iterator found =
134 all_overrides_.find(std::string_view(name));
135 if (found == all_overrides_.end())
136 return nullptr;
137 return &found->second;
138 }
139
140 void Args::SetupRootScope(Scope* dest,
141 const Scope::KeyValueMap& toolchain_overrides) const {
142 std::lock_guard<std::mutex> lock(lock_);
143
144 SetSystemVarsLocked(dest);
145
146 // Apply overrides for already declared args.
147 // (i.e. the system vars we set above)
148 ApplyOverridesLocked(overrides_, dest);
149 ApplyOverridesLocked(toolchain_overrides, dest);
150
151 OverridesForToolchainLocked(dest) = toolchain_overrides;
152
153 SaveOverrideRecordLocked(toolchain_overrides);
154 }
155
156 bool Args::DeclareArgs(const Scope::KeyValueMap& args,
157 Scope* scope_to_set,
158 Err* err) const {
159 std::lock_guard<std::mutex> lock(lock_);
160
161 Scope::KeyValueMap& declared_arguments(
162 DeclaredArgumentsForToolchainLocked(scope_to_set));
163
164 const Scope::KeyValueMap& toolchain_overrides(
165 OverridesForToolchainLocked(scope_to_set));
166
167 for (const auto& arg : args) {
168 // Verify that the value hasn't already been declared. We want each value
169 // to be declared only once.
170 //
171 // The tricky part is that a buildfile can be interpreted multiple times
172 // when used from different toolchains, so we can't just check that we've
173 // seen it before. Instead, we check that the location matches.
174 Scope::KeyValueMap::iterator previously_declared =
175 declared_arguments.find(arg.first);
176 if (previously_declared != declared_arguments.end()) {
177 if (previously_declared->second.origin() != arg.second.origin()) {
178 // Declaration location mismatch.
179 *err = Err(
180 arg.second.origin(), "Duplicate build argument declaration.",
181 "Here you're declaring an argument that was already declared "
182 "elsewhere.\nYou can only declare each argument once in the entire "
183 "build so there is one\ncanonical place for documentation and the "
184 "default value. Either move this\nargument to the build config "
185 "file (for visibility everywhere) or to a .gni file\nthat you "
186 "\"import\" from the files where you need it (preferred).");
187 err->AppendSubErr(Err(previously_declared->second.origin(),
188 "Previous declaration.",
189 "See also \"gn help buildargs\" for more on how "
190 "build arguments work."));
191 return false;
192 }
193 } else {
194 declared_arguments.insert(arg);
195 }
196
197 // In all the cases below, mark the variable used. If a variable is set
198 // that's only used in one toolchain, we don't want to report unused
199 // variable errors in other toolchains. Also, in some cases it's reasonable
200 // for the build file to overwrite the value with a different value based
201 // on some other condition without dereferencing the value first.
202
203 // Check whether this argument has been overridden on the toolchain level
204 // and use the override instead.
205 Scope::KeyValueMap::const_iterator toolchain_override =
206 toolchain_overrides.find(arg.first);
207 if (toolchain_override != toolchain_overrides.end()) {
208 scope_to_set->SetValue(toolchain_override->first,
209 toolchain_override->second,
210 toolchain_override->second.origin());
211 scope_to_set->MarkUsed(arg.first);
212 continue;
213 }
214
215 // Check whether this argument has been overridden and use the override
216 // instead.
217 Scope::KeyValueMap::const_iterator override = overrides_.find(arg.first);
218 if (override != overrides_.end()) {
219 scope_to_set->SetValue(override->first, override->second,
220 override->second.origin());
221 scope_to_set->MarkUsed(override->first);
222 continue;
223 }
224
225 scope_to_set->SetValue(arg.first, arg.second, arg.second.origin());
226 scope_to_set->MarkUsed(arg.first);
227 }
228
229 return true;
230 }
231
VerifyAllOverridesUsed(Err * err) const232 bool Args::VerifyAllOverridesUsed(Err* err) const {
233 std::lock_guard<std::mutex> lock(lock_);
234 Scope::KeyValueMap unused_overrides(all_overrides_);
235 for (const auto& map_pair : declared_arguments_per_toolchain_)
236 RemoveDeclaredOverrides(map_pair.second, &unused_overrides);
237
238 if (unused_overrides.empty())
239 return true;
240
241 // Some assignments in args.gn had no effect. Show an error for the first
242 // unused assignment.
243 std::string_view name = unused_overrides.begin()->first;
244 const Value& value = unused_overrides.begin()->second;
245
246 std::string err_help(
247 "The variable \"" + name +
248 "\" was set as a build argument\n"
249 "but never appeared in a declare_args() block in any buildfile.\n\n"
250 "To view all possible args, run \"gn args --list <out_dir>\"");
251
252 // Use all declare_args for a spelling suggestion.
253 std::vector<std::string_view> candidates;
254 for (const auto& map_pair : declared_arguments_per_toolchain_) {
255 for (const auto& declared_arg : map_pair.second)
256 candidates.push_back(declared_arg.first);
257 }
258 std::string_view suggestion = SpellcheckString(name, candidates);
259 if (!suggestion.empty())
260 err_help = "Did you mean \"" + suggestion + "\"?\n\n" + err_help;
261
262 *err = Err(value.origin(), "Build argument has no effect.", err_help);
263 return false;
264 }
265
GetAllArguments() const266 Args::ValueWithOverrideMap Args::GetAllArguments() const {
267 ValueWithOverrideMap result;
268
269 std::lock_guard<std::mutex> lock(lock_);
270
271 // Sort the keys from declared_arguments_per_toolchain_ so
272 // the return value will be deterministic.
273 std::vector<const Settings*> keys;
274 keys.reserve(declared_arguments_per_toolchain_.size());
275 for (const auto& map_pair : declared_arguments_per_toolchain_) {
276 keys.push_back(map_pair.first);
277 }
278 std::sort(keys.begin(), keys.end(),
279 [](const Settings* a, const Settings* b) -> bool {
280 return a->toolchain_label() < b->toolchain_label();
281 });
282
283 // Default values.
284 for (const auto& key : keys) {
285 const auto& value = declared_arguments_per_toolchain_[key];
286 for (const auto& arg : value)
287 result.insert(std::make_pair(arg.first, ValueWithOverride(arg.second)));
288 }
289
290 // Merge in overrides.
291 for (const auto& over : overrides_) {
292 auto found = result.find(over.first);
293 if (found != result.end()) {
294 found->second.has_override = true;
295 found->second.override_value = over.second;
296 }
297 }
298
299 return result;
300 }
301
SetSystemVarsLocked(Scope * dest) const302 void Args::SetSystemVarsLocked(Scope* dest) const {
303 // Host OS.
304 const char* os = nullptr;
305 #if defined(OS_WIN) || defined(OS_MSYS)
306 os = "win";
307 #elif defined(OS_MACOSX)
308 os = "mac";
309 #elif defined(OS_LINUX)
310 os = "linux";
311 #elif defined(OS_FREEBSD)
312 os = "freebsd";
313 #elif defined(OS_AIX)
314 os = "aix";
315 #elif defined(OS_OPENBSD)
316 os = "openbsd";
317 #elif defined(OS_HAIKU)
318 os = "haiku";
319 #elif defined(OS_SOLARIS)
320 os = "solaris";
321 #elif defined(OS_NETBSD)
322 os = "netbsd";
323 #elif defined(OS_ZOS)
324 os = "zos";
325 #else
326 #error Unknown OS type.
327 #endif
328 // NOTE: Adding a new port? Please follow
329 // https://chromium.googlesource.com/chromium/src/+/master/docs/new_port_policy.md
330
331 // Host architecture.
332 static const char kX86[] = "x86";
333 static const char kX64[] = "x64";
334 static const char kArm[] = "arm";
335 static const char kArm64[] = "arm64";
336 static const char kMips[] = "mipsel";
337 static const char kMips64[] = "mips64el";
338 static const char kS390X[] = "s390x";
339 static const char kPPC64[] = "ppc64";
340 static const char kRISCV32[] = "riscv32";
341 static const char kRISCV64[] = "riscv64";
342 static const char kE2K[] = "e2k";
343 static const char kLOONG64[] = "loong64";
344 const char* arch = nullptr;
345
346 // Set the host CPU architecture based on the underlying OS, not
347 // whatever the current bit-tedness of the GN binary is.
348 std::string os_arch = OperatingSystemArchitecture();
349 if (os_arch == "x86" || os_arch == "BePC")
350 arch = kX86;
351 else if (os_arch == "x86_64")
352 arch = kX64;
353 else if (os_arch == "aarch64" || os_arch == "arm64")
354 arch = kArm64;
355 else if (os_arch.substr(0, 3) == "arm")
356 arch = kArm;
357 else if (os_arch == "mips")
358 arch = kMips;
359 else if (os_arch == "mips64")
360 arch = kMips64;
361 else if (os_arch == "s390x")
362 arch = kS390X;
363 else if (os_arch == "ppc64" || os_arch == "ppc64le")
364 // We handle the endianness inside //build/config/host_byteorder.gni.
365 // This allows us to use the same toolchain as ppc64 BE
366 // and specific flags are included using the host_byteorder logic.
367 arch = kPPC64;
368 else if (os_arch == "riscv32")
369 arch = kRISCV32;
370 else if (os_arch == "riscv64")
371 arch = kRISCV64;
372 else if (os_arch == "e2k")
373 arch = kE2K;
374 else if (os_arch == "loongarch64")
375 arch = kLOONG64;
376 else
377 CHECK(false) << "OS architecture not handled. (" << os_arch << ")";
378
379 // Save the OS and architecture as build arguments that are implicitly
380 // declared. This is so they can be overridden in a toolchain build args
381 // override, and so that they will appear in the "gn args" output.
382 Value empty_string(nullptr, std::string());
383
384 Value os_val(nullptr, std::string(os));
385 dest->SetValue(variables::kHostOs, os_val, nullptr);
386 dest->SetValue(variables::kTargetOs, empty_string, nullptr);
387 dest->SetValue(variables::kCurrentOs, empty_string, nullptr);
388
389 Value arch_val(nullptr, std::string(arch));
390 dest->SetValue(variables::kHostCpu, arch_val, nullptr);
391 dest->SetValue(variables::kTargetCpu, empty_string, nullptr);
392 dest->SetValue(variables::kCurrentCpu, empty_string, nullptr);
393
394 Scope::KeyValueMap& declared_arguments(
395 DeclaredArgumentsForToolchainLocked(dest));
396 declared_arguments[variables::kHostOs] = os_val;
397 declared_arguments[variables::kCurrentOs] = empty_string;
398 declared_arguments[variables::kTargetOs] = empty_string;
399 declared_arguments[variables::kHostCpu] = arch_val;
400 declared_arguments[variables::kCurrentCpu] = empty_string;
401 declared_arguments[variables::kTargetCpu] = empty_string;
402
403 // Mark these variables used so the build config file can override them
404 // without getting a warning about overwriting an unused variable.
405 dest->MarkUsed(variables::kHostCpu);
406 dest->MarkUsed(variables::kCurrentCpu);
407 dest->MarkUsed(variables::kTargetCpu);
408 dest->MarkUsed(variables::kHostOs);
409 dest->MarkUsed(variables::kCurrentOs);
410 dest->MarkUsed(variables::kTargetOs);
411 }
412
ApplyOverridesLocked(const Scope::KeyValueMap & values,Scope * scope) const413 void Args::ApplyOverridesLocked(const Scope::KeyValueMap& values,
414 Scope* scope) const {
415 const Scope::KeyValueMap& declared_arguments(
416 DeclaredArgumentsForToolchainLocked(scope));
417
418 // Only set a value if it has been declared.
419 for (const auto& val : values) {
420 Scope::KeyValueMap::const_iterator declared =
421 declared_arguments.find(val.first);
422
423 if (declared == declared_arguments.end())
424 continue;
425
426 scope->SetValue(val.first, val.second, val.second.origin());
427 }
428 }
429
SaveOverrideRecordLocked(const Scope::KeyValueMap & values) const430 void Args::SaveOverrideRecordLocked(const Scope::KeyValueMap& values) const {
431 for (const auto& val : values)
432 all_overrides_[val.first] = val.second;
433 }
434
DeclaredArgumentsForToolchainLocked(Scope * scope) const435 Scope::KeyValueMap& Args::DeclaredArgumentsForToolchainLocked(
436 Scope* scope) const {
437 return declared_arguments_per_toolchain_[scope->settings()];
438 }
439
OverridesForToolchainLocked(Scope * scope) const440 Scope::KeyValueMap& Args::OverridesForToolchainLocked(Scope* scope) const {
441 return toolchain_overrides_[scope->settings()];
442 }
443