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 "tools/gn/args.h"
6
7#include "build/build_config.h"
8#include "tools/gn/variables.h"
9
10#if defined(OS_WIN)
11#include "base/win/windows_version.h"
12#endif
13
14const char kBuildArgs_Help[] =
15    "Build Arguments Overview\n"
16    "\n"
17    "  Build arguments are variables passed in from outside of the build\n"
18    "  that build files can query to determine how the build works.\n"
19    "\n"
20    "How build arguments are set\n"
21    "\n"
22    "  First, system default arguments are set based on the current system.\n"
23    "  The built-in arguments are:\n"
24    "   - cpu_arch (by default this is the same as \"default_cpu_arch\")\n"
25    "   - default_cpu_arch\n"
26    "   - default_os\n"
27    "   - os (by default this is the same as \"default_os\")\n"
28    "\n"
29    "  If specified, arguments from the --args command line flag are used. If\n"
30    "  that flag is not specified, args from previous builds in the build\n"
31    "  directory will be used (this is in the file args.gn in the build\n"
32    "  directory).\n"
33    "\n"
34    "  Last, for targets being compiled with a non-default toolchain, the\n"
35    "  toolchain overrides are applied. These are specified in the\n"
36    "  toolchain_args section of a toolchain definition. The use-case for\n"
37    "  this is that a toolchain may be building code for a different\n"
38    "  platform, and that it may want to always specify Posix, for example.\n"
39    "  See \"gn help toolchain_args\" for more.\n"
40    "\n"
41    "  If you specify an override for a build argument that never appears in\n"
42    "  a \"declare_args\" call, a nonfatal error will be displayed.\n"
43    "\n"
44    "Examples\n"
45    "\n"
46    "  gn args out/FooBar\n"
47    "      Create the directory out/FooBar and open an editor. You would type\n"
48    "      something like this into that file:\n"
49    "          enable_doom_melon=false\n"
50    "          os=\"android\"\n"
51    "\n"
52    "  gn gen out/FooBar --args=\"enable_doom_melon=true os=\\\"android\\\"\"\n"
53    "      This will overwrite the build directory with the given arguments.\n"
54    "      (Note that the quotes inside the args command will usually need to\n"
55    "      be escaped for your shell to pass through strings values.)\n"
56    "\n"
57    "How build arguments are used\n"
58    "\n"
59    "  If you want to use an argument, you use declare_args() and specify\n"
60    "  default values. These default values will apply if none of the steps\n"
61    "  listed in the \"How build arguments are set\" section above apply to\n"
62    "  the given argument, but the defaults will not override any of these.\n"
63    "\n"
64    "  Often, the root build config file will declare global arguments that\n"
65    "  will be passed to all buildfiles. Individual build files can also\n"
66    "  specify arguments that apply only to those files. It is also useful\n"
67    "  to specify build args in an \"import\"-ed file if you want such\n"
68    "  arguments to apply to multiple buildfiles.\n";
69
70Args::Args() {
71}
72
73Args::Args(const Args& other)
74    : overrides_(other.overrides_),
75      all_overrides_(other.all_overrides_),
76      declared_arguments_(other.declared_arguments_) {
77}
78
79Args::~Args() {
80}
81
82void Args::AddArgOverride(const char* name, const Value& value) {
83  base::AutoLock lock(lock_);
84
85  overrides_[base::StringPiece(name)] = value;
86  all_overrides_[base::StringPiece(name)] = value;
87}
88
89void Args::AddArgOverrides(const Scope::KeyValueMap& overrides) {
90  base::AutoLock lock(lock_);
91
92  for (Scope::KeyValueMap::const_iterator i = overrides.begin();
93       i != overrides.end(); ++i) {
94    overrides_[i->first] = i->second;
95    all_overrides_[i->first] = i->second;
96  }
97}
98
99const Value* Args::GetArgOverride(const char* name) const {
100  base::AutoLock lock(lock_);
101
102  Scope::KeyValueMap::const_iterator found =
103      all_overrides_.find(base::StringPiece(name));
104  if (found == all_overrides_.end())
105    return NULL;
106  return &found->second;
107}
108
109Scope::KeyValueMap Args::GetAllOverrides() const {
110  base::AutoLock lock(lock_);
111  return all_overrides_;
112}
113
114void Args::SetupRootScope(Scope* dest,
115                          const Scope::KeyValueMap& toolchain_overrides) const {
116  base::AutoLock lock(lock_);
117
118  SetSystemVarsLocked(dest);
119  ApplyOverridesLocked(overrides_, dest);
120  ApplyOverridesLocked(toolchain_overrides, dest);
121  SaveOverrideRecordLocked(toolchain_overrides);
122}
123
124bool Args::DeclareArgs(const Scope::KeyValueMap& args,
125                       Scope* scope_to_set,
126                       Err* err) const {
127  base::AutoLock lock(lock_);
128
129  for (Scope::KeyValueMap::const_iterator i = args.begin();
130       i != args.end(); ++i) {
131    // Verify that the value hasn't already been declared. We want each value
132    // to be declared only once.
133    //
134    // The tricky part is that a buildfile can be interpreted multiple times
135    // when used from different toolchains, so we can't just check that we've
136    // seen it before. Instead, we check that the location matches.
137    Scope::KeyValueMap::iterator previously_declared =
138        declared_arguments_.find(i->first);
139    if (previously_declared != declared_arguments_.end()) {
140      if (previously_declared->second.origin() != i->second.origin()) {
141        // Declaration location mismatch.
142        *err = Err(i->second.origin(), "Duplicate build argument declaration.",
143            "Here you're declaring an argument that was already declared "
144            "elsewhere.\nYou can only declare each argument once in the entire "
145            "build so there is one\ncanonical place for documentation and the "
146            "default value. Either move this\nargument to the build config "
147            "file (for visibility everywhere) or to a .gni file\nthat you "
148            "\"import\" from the files where you need it (preferred).");
149        err->AppendSubErr(Err(previously_declared->second.origin(),
150                              "Previous declaration.",
151                              "See also \"gn help buildargs\" for more on how "
152                              "build arguments work."));
153        return false;
154      }
155    } else {
156      declared_arguments_.insert(*i);
157    }
158
159    // Only set on the current scope to the new value if it hasn't been already
160    // set. Mark the variable used so the build script can override it in
161    // certain cases without getting unused value errors.
162    if (!scope_to_set->GetValue(i->first)) {
163      scope_to_set->SetValue(i->first, i->second, i->second.origin());
164      scope_to_set->MarkUsed(i->first);
165    }
166  }
167
168  return true;
169}
170
171bool Args::VerifyAllOverridesUsed(Err* err) const {
172  base::AutoLock lock(lock_);
173  return VerifyAllOverridesUsed(all_overrides_, declared_arguments_, err);
174}
175
176bool Args::VerifyAllOverridesUsed(
177    const Scope::KeyValueMap& overrides,
178    const Scope::KeyValueMap& declared_arguments,
179    Err* err) {
180  for (Scope::KeyValueMap::const_iterator i = overrides.begin();
181       i != overrides.end(); ++i) {
182    if (declared_arguments.find(i->first) == declared_arguments.end()) {
183      // Get a list of all possible overrides for help with error finding.
184      //
185      // It might be nice to do edit distance checks to see if we can find one
186      // close to what you typed.
187      std::string all_declared_str;
188      for (Scope::KeyValueMap::const_iterator cur_str =
189               declared_arguments.begin();
190           cur_str != declared_arguments.end(); ++cur_str) {
191        if (cur_str != declared_arguments.begin())
192          all_declared_str += ", ";
193        all_declared_str += cur_str->first.as_string();
194      }
195
196      *err = Err(i->second.origin(), "Build argument has no effect.",
197          "The variable \"" + i->first.as_string() + "\" was set as a build "
198          "argument\nbut never appeared in a declare_args() block in any "
199          "buildfile.\n\nPossible arguments: " + all_declared_str);
200      return false;
201    }
202  }
203  return true;
204}
205
206void Args::MergeDeclaredArguments(Scope::KeyValueMap* dest) const {
207  base::AutoLock lock(lock_);
208
209  for (Scope::KeyValueMap::const_iterator i = declared_arguments_.begin();
210       i != declared_arguments_.end(); ++i)
211    (*dest)[i->first] = i->second;
212}
213
214void Args::SetSystemVarsLocked(Scope* dest) const {
215  lock_.AssertAcquired();
216
217  // Host OS.
218  const char* os = NULL;
219#if defined(OS_WIN)
220  os = "win";
221#elif defined(OS_MACOSX)
222  os = "mac";
223#elif defined(OS_LINUX)
224  os = "linux";
225#elif defined(OS_ANDROID)
226  os = "android";
227#else
228  #error Unknown OS type.
229#endif
230  Value os_val(NULL, std::string(os));
231  dest->SetValue(variables::kBuildOs, os_val, NULL);
232  dest->SetValue(variables::kOs, os_val, NULL);
233
234  // Host architecture.
235  static const char kX86[] = "x86";
236  static const char kX64[] = "x64";
237  const char* arch = NULL;
238#if defined(OS_WIN)
239  // ...on Windows, set the CPU architecture based on the underlying OS, not
240  // whatever the current bit-tedness of the GN binary is.
241  const base::win::OSInfo* os_info = base::win::OSInfo::GetInstance();
242  switch (os_info->architecture()) {
243    case base::win::OSInfo::X86_ARCHITECTURE:
244      arch = kX86;
245      break;
246    case base::win::OSInfo::X64_ARCHITECTURE:
247      arch = kX64;
248      break;
249    default:
250      CHECK(false) << "Windows architecture not handled.";
251      break;
252  }
253#else
254  // ...on all other platforms, just use the bit-tedness of the current
255  // process.
256  #if defined(ARCH_CPU_X86_64)
257    arch = kX64;
258  #elif defined(ARCH_CPU_X86)
259    arch = kX86;
260  #elif defined(ARCH_CPU_ARMEL)
261    static const char kArm[] = "arm";
262    arch = kArm;
263  #else
264    #error Unknown architecture.
265  #endif
266#endif
267  // Avoid unused var warning.
268  (void)kX86;
269  (void)kX64;
270
271  Value arch_val(NULL, std::string(arch));
272  dest->SetValue(variables::kBuildCpuArch, arch_val, NULL);
273  dest->SetValue(variables::kCpuArch, arch_val, NULL);
274
275  // Save the OS and architecture as build arguments that are implicitly
276  // declared. This is so they can be overridden in a toolchain build args
277  // override, and so that they will appear in the "gn args" output.
278  //
279  // Do not declare the build* variants since these shouldn't be changed.
280  //
281  // Mark these variables used so the build config file can override them
282  // without geting a warning about overwriting an unused variable.
283  declared_arguments_[variables::kOs] = os_val;
284  declared_arguments_[variables::kCpuArch] = arch_val;
285  dest->MarkUsed(variables::kCpuArch);
286  dest->MarkUsed(variables::kOs);
287}
288
289void Args::ApplyOverridesLocked(const Scope::KeyValueMap& values,
290                                Scope* scope) const {
291  lock_.AssertAcquired();
292  for (Scope::KeyValueMap::const_iterator i = values.begin();
293       i != values.end(); ++i)
294    scope->SetValue(i->first, i->second, i->second.origin());
295}
296
297void Args::SaveOverrideRecordLocked(const Scope::KeyValueMap& values) const {
298  lock_.AssertAcquired();
299  for (Scope::KeyValueMap::const_iterator i = values.begin();
300       i != values.end(); ++i)
301    all_overrides_[i->first] = i->second;
302}
303