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 toolchains from declared_arguments_per_toolchain_ so
272 // the return value will be deterministic. Always prioritize
273 // the default toolchain.
274 std::vector<const Settings*> toolchains;
275 toolchains.reserve(declared_arguments_per_toolchain_.size());
276 for (const auto& map_pair : declared_arguments_per_toolchain_) {
277 toolchains.push_back(map_pair.first);
278 }
279 std::sort(toolchains.begin(), toolchains.end(),
280 [](const Settings* a, const Settings* b) -> bool {
281 // NOTE: There can be multiple default toolchains in the map!
282 // which happens when declare_args() blocks are found in args.gn
283 // or some of its imports. This uses a Settings instance with
284 // an empty label, where `is_default()` returns true.
285 if (a->is_default() != b->is_default())
286 return a->is_default();
287 return a->toolchain_label() < b->toolchain_label();
288 });
289
290 // Default values.
291 for (const auto& toolchain : toolchains) {
292 const auto& value_map = declared_arguments_per_toolchain_[toolchain];
293 for (const auto& arg : value_map) {
294 result.insert(std::make_pair(arg.first, ValueWithOverride(arg.second)));
295 }
296 }
297
298 // Merge in overrides.
299 for (const auto& over : overrides_) {
300 auto found = result.find(over.first);
301 if (found != result.end()) {
302 found->second.has_override = true;
303 found->second.override_value = over.second;
304 }
305 }
306
307 return result;
308 }
309
SetSystemVarsLocked(Scope * dest) const310 void Args::SetSystemVarsLocked(Scope* dest) const {
311 // Host OS.
312 const char* os = nullptr;
313 #if defined(OS_WIN) || defined(OS_MSYS)
314 os = "win";
315 #elif defined(OS_MACOSX)
316 os = "mac";
317 #elif defined(OS_LINUX)
318 os = "linux";
319 #elif defined(OS_FREEBSD)
320 os = "freebsd";
321 #elif defined(OS_AIX)
322 os = "aix";
323 #elif defined(OS_OPENBSD)
324 os = "openbsd";
325 #elif defined(OS_HAIKU)
326 os = "haiku";
327 #elif defined(OS_SOLARIS)
328 os = "solaris";
329 #elif defined(OS_NETBSD)
330 os = "netbsd";
331 #elif defined(OS_ZOS)
332 os = "zos";
333 #elif defined(OS_SERENITY)
334 os = "serenity";
335 #else
336 #error Unknown OS type.
337 #endif
338
339 // Host architecture.
340 static const char kX86[] = "x86";
341 static const char kX64[] = "x64";
342 static const char kArm[] = "arm";
343 static const char kArm64[] = "arm64";
344 static const char kMips[] = "mipsel";
345 static const char kMips64[] = "mips64el";
346 static const char kS390X[] = "s390x";
347 static const char kPPC64[] = "ppc64";
348 static const char kRISCV32[] = "riscv32";
349 static const char kRISCV64[] = "riscv64";
350 static const char kE2K[] = "e2k";
351 static const char kLOONG64[] = "loong64";
352 const char* arch = nullptr;
353
354 // Set the host CPU architecture based on the underlying OS, not
355 // whatever the current bit-tedness of the GN binary is.
356 std::string os_arch = OperatingSystemArchitecture();
357 if (os_arch == "x86" || os_arch == "BePC")
358 arch = kX86;
359 else if (os_arch == "x86_64")
360 arch = kX64;
361 else if (os_arch == "aarch64" || os_arch == "arm64")
362 arch = kArm64;
363 else if (os_arch.substr(0, 3) == "arm")
364 arch = kArm;
365 else if (os_arch == "mips")
366 arch = kMips;
367 else if (os_arch == "mips64")
368 arch = kMips64;
369 else if (os_arch == "s390x")
370 arch = kS390X;
371 else if (os_arch == "ppc64" || os_arch == "ppc64le")
372 // We handle the endianness inside //build/config/host_byteorder.gni.
373 // This allows us to use the same toolchain as ppc64 BE
374 // and specific flags are included using the host_byteorder logic.
375 arch = kPPC64;
376 else if (os_arch == "riscv32")
377 arch = kRISCV32;
378 else if (os_arch == "riscv64")
379 arch = kRISCV64;
380 else if (os_arch == "e2k")
381 arch = kE2K;
382 else if (os_arch == "loongarch64")
383 arch = kLOONG64;
384 else
385 CHECK(false) << "OS architecture not handled. (" << os_arch << ")";
386
387 // Save the OS and architecture as build arguments that are implicitly
388 // declared. This is so they can be overridden in a toolchain build args
389 // override, and so that they will appear in the "gn args" output.
390 Value empty_string(nullptr, std::string());
391
392 Value os_val(nullptr, std::string(os));
393 dest->SetValue(variables::kHostOs, os_val, nullptr);
394 dest->SetValue(variables::kTargetOs, empty_string, nullptr);
395 dest->SetValue(variables::kCurrentOs, empty_string, nullptr);
396
397 Value arch_val(nullptr, std::string(arch));
398 dest->SetValue(variables::kHostCpu, arch_val, nullptr);
399 dest->SetValue(variables::kTargetCpu, empty_string, nullptr);
400 dest->SetValue(variables::kCurrentCpu, empty_string, nullptr);
401
402 Scope::KeyValueMap& declared_arguments(
403 DeclaredArgumentsForToolchainLocked(dest));
404 declared_arguments[variables::kHostOs] = os_val;
405 declared_arguments[variables::kCurrentOs] = empty_string;
406 declared_arguments[variables::kTargetOs] = empty_string;
407 declared_arguments[variables::kHostCpu] = arch_val;
408 declared_arguments[variables::kCurrentCpu] = empty_string;
409 declared_arguments[variables::kTargetCpu] = empty_string;
410
411 // Mark these variables used so the build config file can override them
412 // without getting a warning about overwriting an unused variable.
413 dest->MarkUsed(variables::kHostCpu);
414 dest->MarkUsed(variables::kCurrentCpu);
415 dest->MarkUsed(variables::kTargetCpu);
416 dest->MarkUsed(variables::kHostOs);
417 dest->MarkUsed(variables::kCurrentOs);
418 dest->MarkUsed(variables::kTargetOs);
419 }
420
ApplyOverridesLocked(const Scope::KeyValueMap & values,Scope * scope) const421 void Args::ApplyOverridesLocked(const Scope::KeyValueMap& values,
422 Scope* scope) const {
423 const Scope::KeyValueMap& declared_arguments(
424 DeclaredArgumentsForToolchainLocked(scope));
425
426 // Only set a value if it has been declared.
427 for (const auto& val : values) {
428 Scope::KeyValueMap::const_iterator declared =
429 declared_arguments.find(val.first);
430
431 if (declared == declared_arguments.end())
432 continue;
433
434 scope->SetValue(val.first, val.second, val.second.origin());
435 }
436 }
437
SaveOverrideRecordLocked(const Scope::KeyValueMap & values) const438 void Args::SaveOverrideRecordLocked(const Scope::KeyValueMap& values) const {
439 for (const auto& val : values)
440 all_overrides_[val.first] = val.second;
441 }
442
DeclaredArgumentsForToolchainLocked(Scope * scope) const443 Scope::KeyValueMap& Args::DeclaredArgumentsForToolchainLocked(
444 Scope* scope) const {
445 return declared_arguments_per_toolchain_[scope->settings()];
446 }
447
OverridesForToolchainLocked(Scope * scope) const448 Scope::KeyValueMap& Args::OverridesForToolchainLocked(Scope* scope) const {
449 return toolchain_overrides_[scope->settings()];
450 }
451