• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/setup.h"
6 
7 #include <stdlib.h>
8 
9 #include <algorithm>
10 #include <memory>
11 #include <sstream>
12 #include <utility>
13 
14 #include "base/command_line.h"
15 #include "base/files/file_path.h"
16 #include "base/files/file_util.h"
17 #include "base/memory/ref_counted.h"
18 #include "base/strings/string_split.h"
19 #include "base/strings/string_util.h"
20 #include "base/strings/utf_string_conversions.h"
21 #include "gn/command_format.h"
22 #include "gn/commands.h"
23 #include "gn/exec_process.h"
24 #include "gn/filesystem_utils.h"
25 #include "gn/input_file.h"
26 #include "gn/label_pattern.h"
27 #include "gn/ohos_components_checker.h"
28 #include "gn/parse_tree.h"
29 #include "gn/parser.h"
30 #include "gn/source_dir.h"
31 #include "gn/source_file.h"
32 #include "gn/standard_out.h"
33 #include "gn/switches.h"
34 #include "gn/tokenizer.h"
35 #include "gn/trace.h"
36 #include "gn/value.h"
37 #include "gn/value_extractors.h"
38 #include "util/build_config.h"
39 
40 #if defined(OS_WIN)
41 #include <windows.h>
42 
43 #include "base/win/scoped_process_information.h"
44 #include "base/win/win_util.h"
45 #endif
46 
47 const char kDotfile_Help[] =
48     R"(.gn file
49 
50   When gn starts, it will search the current directory and parent directories
51   for a file called ".gn". This indicates the source root. You can override
52   this detection by using the --root command-line argument
53 
54   The .gn file in the source root will be executed. The syntax is the same as a
55   buildfile, but with very limited build setup-specific meaning.
56 
57   If you specify --root, by default GN will look for the file .gn in that
58   directory. If you want to specify a different file, you can additionally pass
59   --dotfile:
60 
61     gn gen out/Debug --root=/home/build --dotfile=/home/my_gn_file.gn
62 
63 Variables
64 
65   arg_file_template [optional]
66       Path to a file containing the text that should be used as the default
67       args.gn content when you run `gn args`.
68 
69   buildconfig [required]
70       Path to the build config file. This file will be used to set up the
71       build file execution environment for each toolchain.
72 
73   check_targets [optional]
74       A list of labels and label patterns that should be checked when running
75       "gn check" or "gn gen --check". If neither check_targets or
76       no_check_targets (see below) is specified, all targets will be checked.
77       It is an error to specify both check_targets and no_check_targets. If it
78       is the empty list, no targets will be checked. To bypass this list,
79       request an explicit check of targets, like "//*".
80 
81       The format of this list is identical to that of "visibility" so see "gn
82       help visibility" for examples.
83 
84   no_check_targets [optional]
85       A list of labels and label patterns that should *not* be checked when
86       running "gn check" or "gn gen --check". All other targets will be checked.
87       If neither check_targets (see above) or no_check_targets is specified, all
88       targets will be checked. It is an error to specify both check_targets and
89       no_check_targets.
90 
91       The format of this list is identical to that of "visibility" so see "gn
92       help visibility" for examples.
93 
94   check_system_includes [optional]
95       Boolean to control whether system style includes are checked by default
96       when running "gn check" or "gn gen --check".  System style includes are
97       includes that use angle brackets <> instead of double quotes "". If this
98       setting is omitted or set to false, these includes will be ignored by
99       default. They can be checked explicitly by running
100       "gn check --check-system" or "gn gen --check=system"
101 
102   exec_script_whitelist [optional]
103       A list of .gn/.gni files (not labels) that have permission to call the
104       exec_script function. If this list is defined, calls to exec_script will
105       be checked against this list and GN will fail if the current file isn't
106       in the list.
107 
108       This is to allow the use of exec_script to be restricted since is easy to
109       use inappropriately. Wildcards are not supported. Files in the
110       secondary_source tree (if defined) should be referenced by ignoring the
111       secondary tree and naming them as if they are in the main tree.
112 
113       If unspecified, the ability to call exec_script is unrestricted.
114 
115       Example:
116         exec_script_whitelist = [
117           "//base/BUILD.gn",
118           "//build/my_config.gni",
119         ]
120 
121   export_compile_commands [optional]
122       A list of label patterns for which to generate a Clang compilation
123       database (see "gn help label_pattern" for the string format).
124 
125       When specified, GN will generate a compile_commands.json file in the root
126       of the build directory containing information on how to compile each
127       source file reachable from any label matching any pattern in the list.
128       This is used for Clang-based tooling and some editor integration. See
129       https://clang.llvm.org/docs/JSONCompilationDatabase.html
130 
131       The switch --add-export-compile-commands to "gn gen" (see "gn help gen")
132       appends to this value which provides a per-user way to customize it.
133 
134       The deprecated switch --export-compile-commands to "gn gen" (see "gn help
135       gen") adds to the export target list using a different format.
136 
137       Example:
138         export_compile_commands = [
139           "//base/*",
140           "//tools:doom_melon",
141         ]
142 
143   root [optional]
144       Label of the root build target. The GN build will start by loading the
145       build file containing this target name. This defaults to "//:" which will
146       cause the file //BUILD.gn to be loaded. Note that build_file_extension
147       applies to the default case as well.
148 
149       The command-line switch --root-target will override this value (see "gn
150       help --root-target").
151 
152   root_patterns [optional]
153       A list of label pattern strings. When not defined or empty, the GN build
154       graph will contain all targets from any BUILD.gn evaluated in the default
155       toolchain context, and their transitive dependencies.
156 
157       When set to a non empty list, only the targets in the default toolchain
158       matching these patterns, and their transitive dependencies, will be defined
159       instead.
160 
161       The command-line switch --root-pattern will override this value (see
162       "gn help --root-pattern")
163 
164   script_executable [optional]
165       By default, GN runs the scripts used in action targets and exec_script
166       calls using the Python interpreter found in PATH. This value specifies the
167       Python executable or other interpreter to use instead.
168 
169       If set to the empty string, the scripts will be executed directly.
170 
171       The command-line switch --script-executable will override this value (see
172       "gn help --script-executable")
173 
174   secondary_source [optional]
175       Label of an alternate directory tree to find input files. When searching
176       for a BUILD.gn file (or the build config file discussed above), the file
177       will first be looked for in the source root. If it's not found, the
178       secondary source root will be checked (which would contain a parallel
179       directory hierarchy).
180 
181       This behavior is intended to be used when BUILD.gn files can't be checked
182       in to certain source directories for whatever reason.
183 
184       The secondary source root must be inside the main source tree.
185 
186   default_args [optional]
187       Scope containing the default overrides for declared arguments. These
188       overrides take precedence over the default values specified in the
189       declare_args() block, but can be overridden using --args or the
190       args.gn file.
191 
192       This is intended to be used when subprojects declare arguments with
193       default values that need to be changed for whatever reason.
194 
195   build_file_extension [optional]
196       If set to a non-empty string, this is added to the name of all build files
197       to load.
198       GN will look for build files named "BUILD.$build_file_extension.gn".
199       This is intended to be used during migrations or other situations where
200       there are two independent GN builds in the same directories.
201 
202   ninja_required_version [optional]
203       When set specifies the minimum required version of Ninja. The default
204       required version is 1.7.2. Specifying a higher version might enable the
205       use of some of newer features that can make the build more efficient.
206 
207   ohos_components_support [optional]
208         This parameter enable support for OpenHarmony components.
209         When enabled, gn will load components information from "build_configs/"
210         directory in the root_out_directory.
211 
212   The following files will be loaded:
213       out/build_configs/parts_info/inner_kits_info.json (required):
214         Required InnerAPI information file for each OHOS component.
215       out/build_configs/component_override_map.json (optional):
216         Optional overrided component maps file.
217 
218   For OpenHarmony system build, this value must be enabled to support
219     external_deps (see "gn help external_deps") and public_external_deps
220     (see "gn help public_external_deps").
221 
222 Example .gn file contents
223 
224   buildconfig = "//build/config/BUILDCONFIG.gn"
225 
226   check_targets = [
227     "//doom_melon/*",  # Check everything in this subtree.
228     "//tools:mind_controlling_ant",  # Check this specific target.
229   ]
230 
231   root = "//:root"
232 
233   secondary_source = "//build/config/temporary_buildfiles/"
234 
235   default_args = {
236     # Default to release builds for this project.
237     is_debug = false
238     is_component_build = false
239   }
240 )";
241 
242 namespace {
243 
244 const base::FilePath::CharType kGnFile[] = FILE_PATH_LITERAL(".gn");
245 const char kDefaultArgsGn[] =
246     "# Set build arguments here. See `gn help buildargs`.";
247 
FindDotFile(const base::FilePath & current_dir)248 base::FilePath FindDotFile(const base::FilePath& current_dir) {
249   base::FilePath try_this_file = current_dir.Append(kGnFile);
250   if (base::PathExists(try_this_file))
251     return try_this_file;
252 
253   base::FilePath with_no_slash = current_dir.StripTrailingSeparators();
254   base::FilePath up_one_dir = with_no_slash.DirName();
255   if (up_one_dir == current_dir)
256     return base::FilePath();  // Got to the top.
257 
258   return FindDotFile(up_one_dir);
259 }
260 
261 // Called on any thread. Post the item to the builder on the main thread.
262 void ItemDefinedCallback(MsgLoop* task_runner,
263                          Builder* builder_call_on_main_thread_only,
264                          std::unique_ptr<Item> item) {
265   DCHECK(item);
266 
267   // Increment the work count for the duration of defining the item with the
268   // builder. Otherwise finishing this callback will race finishing loading
269   // files. If there is no other pending work at any point in the middle of
270   // this call completing on the main thread, the 'Complete' function will
271   // be signaled and we'll stop running with an incomplete build.
272   g_scheduler->IncrementWorkCount();
273 
274   // Work around issue binding a unique_ptr with std::function by moving into a
275   // shared_ptr.
276   auto item_shared = std::make_shared<std::unique_ptr<Item>>(std::move(item));
277   task_runner->PostTask(
__anon76bf66c60202() 278       [builder_call_on_main_thread_only, item_shared]() mutable {
279         builder_call_on_main_thread_only->ItemDefined(std::move(*item_shared));
280         g_scheduler->DecrementWorkCount();
281       });
282 }
283 
DecrementWorkCount()284 void DecrementWorkCount() {
285   g_scheduler->DecrementWorkCount();
286 }
287 
288 #if defined(OS_WIN)
289 
SysMultiByteTo16(std::string_view mb)290 std::u16string SysMultiByteTo16(std::string_view mb) {
291   if (mb.empty())
292     return std::u16string();
293 
294   int mb_length = static_cast<int>(mb.length());
295   // Compute the length of the buffer.
296   int charcount = MultiByteToWideChar(CP_ACP, 0, mb.data(), mb_length, NULL, 0);
297   if (charcount == 0)
298     return std::u16string();
299 
300   std::u16string wide;
301   wide.resize(charcount);
302   MultiByteToWideChar(CP_ACP, 0, mb.data(), mb_length, base::ToWCharT(&wide[0]),
303                       charcount);
304 
305   return wide;
306 }
307 
308 // Given the path to a batch file that runs Python, extracts the name of the
309 // executable actually implementing Python. Generally people write a batch file
310 // to put something named "python" on the path, which then just redirects to
311 // a python.exe somewhere else. This step decodes that setup. On failure,
312 // returns empty path.
PythonBatToExe(const base::FilePath & bat_path)313 base::FilePath PythonBatToExe(const base::FilePath& bat_path) {
314   // Note exciting double-quoting to allow spaces. The /c switch seems to check
315   // for quotes around the whole thing and then deletes them. If you want to
316   // quote the first argument in addition (to allow for spaces in the Python
317   // path, you need *another* set of quotes around that, likewise, we need
318   // two quotes at the end.
319   std::u16string command = u"cmd.exe /c \"\"";
320   command.append(bat_path.value());
321   command.append(u"\" -c \"import sys; print(sys.executable)\"\"");
322 
323   std::string python_path;
324   std::string std_err;
325   int exit_code;
326   base::FilePath cwd;
327   GetCurrentDirectory(&cwd);
328   if (internal::ExecProcess(command, cwd, &python_path, &std_err, &exit_code) &&
329       exit_code == 0 && std_err.empty()) {
330     base::TrimWhitespaceASCII(python_path, base::TRIM_ALL, &python_path);
331 
332     // Python uses the system multibyte code page for sys.executable.
333     base::FilePath exe_path(SysMultiByteTo16(python_path));
334 
335     // Check for reasonable output, cmd may have output an error message.
336     if (base::PathExists(exe_path))
337       return exe_path;
338   }
339   return base::FilePath();
340 }
341 
342 // python_exe_name and python_bat_name can be empty but cannot be absolute
343 // paths. They should be "python.exe" or "", etc., and "python.bat" or "", etc.
FindWindowsPython(const base::FilePath & python_exe_name,const base::FilePath & python_bat_name)344 base::FilePath FindWindowsPython(const base::FilePath& python_exe_name,
345                                  const base::FilePath& python_bat_name) {
346   char16_t current_directory[MAX_PATH];
347   ::GetCurrentDirectory(MAX_PATH, reinterpret_cast<LPWSTR>(current_directory));
348 
349   // First search for python.exe in the current directory.
350   if (!python_exe_name.empty()) {
351     CHECK(python_exe_name.FinalExtension() == u".exe");
352     CHECK_EQ(python_exe_name.IsAbsolute(), false);
353     base::FilePath cur_dir_candidate_exe =
354         base::FilePath(current_directory).Append(python_exe_name);
355     if (base::PathExists(cur_dir_candidate_exe))
356       return cur_dir_candidate_exe;
357   }
358 
359   // Get the path.
360   const char16_t kPathEnvVarName[] = u"Path";
361   DWORD path_length = ::GetEnvironmentVariable(
362       reinterpret_cast<LPCWSTR>(kPathEnvVarName), nullptr, 0);
363   if (path_length == 0)
364     return base::FilePath();
365   std::unique_ptr<char16_t[]> full_path(new char16_t[path_length]);
366   DWORD actual_path_length = ::GetEnvironmentVariable(
367       reinterpret_cast<LPCWSTR>(kPathEnvVarName),
368       reinterpret_cast<LPWSTR>(full_path.get()), path_length);
369   CHECK_EQ(path_length, actual_path_length + 1);
370 
371   // Search for python.exe in the path.
372   for (const auto& component : base::SplitStringPiece(
373            std::u16string_view(full_path.get(), path_length), u";",
374            base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) {
375     if (!python_exe_name.empty()) {
376       base::FilePath candidate_exe =
377           base::FilePath(component).Append(python_exe_name);
378       if (base::PathExists(candidate_exe))
379         return candidate_exe;
380     }
381 
382     // Also allow python.bat, but convert into the .exe.
383     if (!python_bat_name.empty()) {
384       CHECK(python_bat_name.FinalExtension() == u".bat");
385       CHECK_EQ(python_bat_name.IsAbsolute(), false);
386       base::FilePath candidate_bat =
387           base::FilePath(component).Append(python_bat_name);
388       if (base::PathExists(candidate_bat)) {
389         base::FilePath python_exe = PythonBatToExe(candidate_bat);
390         if (!python_exe.empty())
391           return python_exe;
392       }
393     }
394   }
395   return base::FilePath();
396 }
397 #endif
398 
399 }  // namespace
400 
401 const char Setup::kBuildArgFileName[] = "args.gn";
402 
Setup()403 Setup::Setup()
404     : build_settings_(),
405       loader_(new LoaderImpl(&build_settings_)),
406       builder_(loader_.get()),
407       dotfile_settings_(&build_settings_, std::string()),
408       dotfile_scope_(&dotfile_settings_) {
409   dotfile_settings_.set_toolchain_label(Label());
410 
411   build_settings_.set_item_defined_callback(
412       [task_runner = scheduler_.task_runner(),
413        builder = &builder_](std::unique_ptr<Item> item) {
414         ItemDefinedCallback(task_runner, builder, std::move(item));
415       });
416 
417   loader_->set_complete_callback(&DecrementWorkCount);
418   // The scheduler's task runner wasn't created when the Loader was created, so
419   // we need to set it now.
420   loader_->set_task_runner(scheduler_.task_runner());
421 }
422 
DoSetup(const std::string & build_dir,bool force_create)423 bool Setup::DoSetup(const std::string& build_dir, bool force_create) {
424   return DoSetup(build_dir, force_create,
425                  *base::CommandLine::ForCurrentProcess());
426 }
427 
DoSetup(const std::string & build_dir,bool force_create,const base::CommandLine & cmdline)428 bool Setup::DoSetup(const std::string& build_dir,
429                     bool force_create,
430                     const base::CommandLine& cmdline) {
431   Err err;
432   if (!DoSetupWithErr(build_dir, force_create, cmdline, &err)) {
433     err.PrintToStdout();
434     return false;
435   }
436   DCHECK(!err.has_error());
437   return true;
438 }
439 
FillOhosComponentsInfo(const std::string & build_dir,Err * err)440 bool Setup::FillOhosComponentsInfo(const std::string& build_dir, Err* err)
441 {
442   // Load OpenHarmony system components definition file.
443   const Value *support =
444       dotfile_scope_.GetValue("ohos_components_support", true);
445   const Value *independent = build_settings_.build_args().GetArgOverride("ohos_indep_compiler_enable");
446   const Value *product = build_settings_.build_args().GetArgOverride("product_name");
447   if (!ohos_components_.LoadOhosComponents(build_dir, support, independent, product, err)) {
448     return false;
449   }
450 
451   if (ohos_components_.isOhosComponentsLoaded()) {
452     build_settings_.SetOhosComponentsInfo(&ohos_components_);
453   }
454 
455   const Value* checkType = build_settings_.build_args().GetArgOverride("ohos_components_checktype");
456   const Value* ruleSwitch = build_settings_.build_args().GetArgOverride("ohos_interception_rule_switch");
457   if (ruleSwitch && ruleSwitch->type() == Value::INTEGER) {
458     if (checkType && checkType->type() == Value::INTEGER) {
459       ohos_components_.LoadOhosComponentsChecker(build_dir, support, checkType->int_value(), ruleSwitch->int_value());
460     } else {
461       ohos_components_.LoadOhosComponentsChecker(build_dir, support,
462           OhosComponentChecker::CheckType::INTERCEPT_IGNORE_TEST, ruleSwitch->int_value());
463     }
464   } else {
465     if (checkType && checkType->type() == Value::INTEGER) {
466       const unsigned int INTERCEPT_ALL_RULE = (1 << (OhosComponentChecker::BinaryLeftShift::ALL - 1)) - 1;
467       ohos_components_.LoadOhosComponentsChecker(build_dir, support, checkType->int_value(), INTERCEPT_ALL_RULE);
468     } else {
469       ohos_components_.LoadOhosComponentsChecker(build_dir, support, OhosComponentChecker::CheckType::NONE,
470           OhosComponentChecker::BinaryLeftShift::UNKNOWN);
471     }
472   }
473   if (independent) {
474       ohos_components_.LoadOhosComponentsMapping(build_dir, support, independent);
475   }
476   return true;
477 }
478 
DoSetupWithErr(const std::string & build_dir,bool force_create,const base::CommandLine & cmdline,Err * err)479 bool Setup::DoSetupWithErr(const std::string& build_dir,
480                            bool force_create,
481                            const base::CommandLine& cmdline,
482                            Err* err) {
483   scheduler_.set_verbose_logging(cmdline.HasSwitch(switches::kVerbose));
484   if (cmdline.HasSwitch(switches::kTime) ||
485       cmdline.HasSwitch(switches::kTracelog))
486     EnableTracing();
487 
488   ScopedTrace setup_trace(TraceItem::TRACE_SETUP, "DoSetup");
489 
490   if (!FillSourceDir(cmdline, err))
491     return false;
492   if (!RunConfigFile(err))
493     return false;
494   if (!FillOtherConfig(cmdline, err))
495     return false;
496 
497   // Must be after FillSourceDir to resolve.
498   if (!FillBuildDir(build_dir, !force_create, err))
499     return false;
500 
501   // Apply project-specific default (if specified).
502   // Must happen before FillArguments().
503   if (default_args_) {
504     Scope::KeyValueMap overrides;
505     default_args_->GetCurrentScopeValues(&overrides);
506     build_settings_.build_args().AddDefaultArgOverrides(overrides);
507   }
508 
509   if (fill_arguments_) {
510     if (!FillArguments(cmdline, err))
511       return false;
512   }
513 
514   if (!FillOhosComponentsInfo(build_dir, err)) {
515     return false;
516   }
517 
518   if (!FillPythonPath(cmdline, err))
519     return false;
520 
521   // Check for unused variables in the .gn file.
522   if (!dotfile_scope_.CheckForUnusedVars(err)) {
523     return false;
524   }
525 
526   return true;
527 }
528 
Run()529 bool Setup::Run() {
530   return Run(*base::CommandLine::ForCurrentProcess());
531 }
532 
Run(const base::CommandLine & cmdline)533 bool Setup::Run(const base::CommandLine& cmdline) {
534   RunPreMessageLoop();
535   if (!scheduler_.Run())
536     return false;
537   return RunPostMessageLoop(cmdline);
538 }
539 
GetBuildArgFile() const540 SourceFile Setup::GetBuildArgFile() const {
541   return SourceFile(build_settings_.build_dir().value() + kBuildArgFileName);
542 }
543 
RunPreMessageLoop()544 void Setup::RunPreMessageLoop() {
545   // Will be decremented with the loader is drained.
546   g_scheduler->IncrementWorkCount();
547 
548   // Load the root build file.
549   loader_->Load(root_build_file_, LocationRange(), Label());
550 }
551 
RunPostMessageLoop(const base::CommandLine & cmdline)552 bool Setup::RunPostMessageLoop(const base::CommandLine& cmdline) {
553   Err err;
554   if (!builder_.CheckForBadItems(&err)) {
555     err.PrintToStdout();
556     return false;
557   }
558 
559   if (!build_settings_.build_args().VerifyAllOverridesUsed(&err)) {
560     if (cmdline.HasSwitch(switches::kFailOnUnusedArgs)) {
561       err.PrintToStdout();
562       return false;
563     }
564     err.PrintNonfatalToStdout();
565     OutputString(
566         "\nThe build continued as if that argument was "
567         "unspecified.\n\n");
568     // Nonfatal error.
569   }
570 
571   if (check_public_headers_) {
572     std::vector<const Target*> all_targets = builder_.GetAllResolvedTargets();
573     std::vector<const Target*> to_check;
574     if (check_patterns()) {
575       commands::FilterTargetsByPatterns(all_targets, *check_patterns(),
576                                         &to_check);
577     } else if (no_check_patterns()) {
578       commands::FilterOutTargetsByPatterns(all_targets, *no_check_patterns(),
579                                            &to_check);
580     } else {
581       to_check = all_targets;
582     }
583 
584     if (!commands::CheckPublicHeaders(&build_settings_, all_targets, to_check,
585                                       false, false, check_system_includes_)) {
586       return false;
587     }
588   }
589 
590   // Write out tracing and timing if requested.
591   if (cmdline.HasSwitch(switches::kTime))
592     PrintLongHelp(SummarizeTraces());
593   if (cmdline.HasSwitch(switches::kTracelog))
594     SaveTraces(cmdline.GetSwitchValuePath(switches::kTracelog));
595 
596   return true;
597 }
598 
FillArguments(const base::CommandLine & cmdline,Err * err)599 bool Setup::FillArguments(const base::CommandLine& cmdline, Err* err) {
600   // Use the args on the command line if specified, and save them. Do this even
601   // if the list is empty (this means clear any defaults).
602   // If --args is not set, args.gn file does not exist and gen_empty_args
603   // is set, generate an empty args.gn file with default comments.
604 
605   base::FilePath build_arg_file =
606       build_settings_.GetFullPath(GetBuildArgFile());
607   auto switch_value = cmdline.GetSwitchValueString(switches::kArgs);
608   if (cmdline.HasSwitch(switches::kArgs) ||
609       (gen_empty_args_ && !PathExists(build_arg_file))) {
610     if (!FillArgsFromCommandLine(
611             switch_value.empty() ? kDefaultArgsGn : switch_value, err)) {
612       return false;
613     }
614     SaveArgsToFile();
615     return true;
616   }
617 
618   // No command line args given, use the arguments from the build dir (if any).
619   return FillArgsFromFile(err);
620 }
621 
FillArgsFromCommandLine(const std::string & args,Err * err)622 bool Setup::FillArgsFromCommandLine(const std::string& args, Err* err) {
623   args_input_file_ = std::make_unique<InputFile>(SourceFile());
624   args_input_file_->SetContents(args);
625   args_input_file_->set_friendly_name("the command-line \"--args\"");
626   return FillArgsFromArgsInputFile(err);
627 }
628 
FillArgsFromFile(Err * err)629 bool Setup::FillArgsFromFile(Err* err) {
630   ScopedTrace setup_trace(TraceItem::TRACE_SETUP, "Load args file");
631 
632   SourceFile build_arg_source_file = GetBuildArgFile();
633   base::FilePath build_arg_file =
634       build_settings_.GetFullPath(build_arg_source_file);
635 
636   std::string contents;
637   if (!base::ReadFileToString(build_arg_file, &contents))
638     return true;  // File doesn't exist, continue with default args.
639 
640   // Add a dependency on the build arguments file. If this changes, we want
641   // to re-generate the build.
642   g_scheduler->AddGenDependency(build_arg_file);
643 
644   if (contents.empty())
645     return true;  // Empty file, do nothing.
646 
647   args_input_file_ = std::make_unique<InputFile>(build_arg_source_file);
648   args_input_file_->SetContents(contents);
649   args_input_file_->set_friendly_name(
650       "build arg file (use \"gn args <out_dir>\" to edit)");
651 
652   setup_trace.Done();  // Only want to count the load as part of the trace.
653   return FillArgsFromArgsInputFile(err);
654 }
655 
FillArgsFromArgsInputFile(Err * err)656 bool Setup::FillArgsFromArgsInputFile(Err* err) {
657   ScopedTrace setup_trace(TraceItem::TRACE_SETUP, "Parse args");
658 
659   args_tokens_ = Tokenizer::Tokenize(args_input_file_.get(), err);
660   if (err->has_error()) {
661     return false;
662   }
663 
664   args_root_ = Parser::Parse(args_tokens_, err);
665   if (err->has_error()) {
666     return false;
667   }
668 
669   Scope arg_scope(&dotfile_settings_);
670   // Set soure dir so relative imports in args work.
671   SourceDir root_source_dir =
672       SourceDirForCurrentDirectory(build_settings_.root_path());
673   arg_scope.set_source_dir(root_source_dir);
674   args_root_->Execute(&arg_scope, err);
675   if (err->has_error()) {
676     return false;
677   }
678 
679   // Save the result of the command args.
680   Scope::KeyValueMap overrides;
681   arg_scope.GetCurrentScopeValues(&overrides);
682   build_settings_.build_args().AddArgOverrides(overrides);
683   build_settings_.build_args().set_build_args_dependency_files(
684       arg_scope.build_dependency_files());
685   return true;
686 }
687 
SaveArgsToFile()688 bool Setup::SaveArgsToFile() {
689   ScopedTrace setup_trace(TraceItem::TRACE_SETUP, "Save args file");
690 
691   // For the first run, the build output dir might not be created yet, so do
692   // that so we can write a file into it. Ignore errors, we'll catch the error
693   // when we try to write a file to it below.
694   base::FilePath build_arg_file =
695       build_settings_.GetFullPath(GetBuildArgFile());
696   base::CreateDirectory(build_arg_file.DirName());
697 
698   std::string contents = args_input_file_->contents();
699   commands::FormatStringToString(contents, commands::TreeDumpMode::kInactive,
700                                  &contents, nullptr);
701 #if defined(OS_WIN)
702   // Use Windows lineendings for this file since it will often open in
703   // Notepad which can't handle Unix ones.
704   base::ReplaceSubstringsAfterOffset(&contents, 0, "\n", "\r\n");
705 #endif
706   if (base::WriteFile(build_arg_file, contents.c_str(),
707                       static_cast<int>(contents.size())) == -1) {
708     Err(Location(), "Args file could not be written.",
709         "The file is \"" + FilePathToUTF8(build_arg_file) + "\"")
710         .PrintToStdout();
711     return false;
712   }
713 
714   // Add a dependency on the build arguments file. If this changes, we want
715   // to re-generate the build.
716   g_scheduler->AddGenDependency(build_arg_file);
717 
718   return true;
719 }
720 
FillSourceDir(const base::CommandLine & cmdline,Err * err)721 bool Setup::FillSourceDir(const base::CommandLine& cmdline, Err* err) {
722   // Find the .gn file.
723   base::FilePath root_path;
724 
725   // Prefer the command line args to the config file.
726   base::FilePath relative_root_path =
727       cmdline.GetSwitchValuePath(switches::kRoot);
728   if (!relative_root_path.empty()) {
729     root_path = base::MakeAbsoluteFilePath(relative_root_path);
730     if (root_path.empty()) {
731       *err = Err(Location(), "Root source path not found.",
732                  "The path \"" + FilePathToUTF8(relative_root_path) +
733                      "\" doesn't exist.");
734       return false;
735     }
736 
737     // When --root is specified, an alternate --dotfile can also be set.
738     // --dotfile should be a real file path and not a "//foo" source-relative
739     // path.
740     base::FilePath dotfile_path =
741         cmdline.GetSwitchValuePath(switches::kDotfile);
742     if (dotfile_path.empty()) {
743       dotfile_name_ = root_path.Append(kGnFile);
744     } else {
745       dotfile_name_ = base::MakeAbsoluteFilePath(dotfile_path);
746       if (dotfile_name_.empty()) {
747         *err = Err(Location(), "Could not load dotfile.",
748                    "The file \"" + FilePathToUTF8(dotfile_path) +
749                        "\" couldn't be loaded.");
750         return false;
751       }
752       // Only set dotfile_name if it was passed explicitly.
753       build_settings_.set_dotfile_name(dotfile_name_);
754     }
755   } else {
756     // In the default case, look for a dotfile and that also tells us where the
757     // source root is.
758     base::FilePath cur_dir;
759     base::GetCurrentDirectory(&cur_dir);
760     dotfile_name_ = FindDotFile(cur_dir);
761     if (dotfile_name_.empty()) {
762       *err = Err(
763           Location(), "Can't find source root.",
764           "I could not find a \".gn\" file in the current directory or any "
765           "parent,\nand the --root command-line argument was not specified.");
766       return false;
767     }
768     root_path = dotfile_name_.DirName();
769   }
770 
771   base::FilePath root_realpath = base::MakeAbsoluteFilePath(root_path);
772   if (root_realpath.empty()) {
773     *err = Err(Location(), "Can't get the real root path.",
774                "I could not get the real path of \"" +
775                    FilePathToUTF8(root_path) + "\".");
776     return false;
777   }
778   if (scheduler_.verbose_logging())
779     scheduler_.Log("Using source root", FilePathToUTF8(root_realpath));
780   build_settings_.SetRootPath(root_realpath);
781 
782   return true;
783 }
784 
FillBuildDir(const std::string & build_dir,bool require_exists,Err * err)785 bool Setup::FillBuildDir(const std::string& build_dir,
786                          bool require_exists,
787                          Err* err) {
788   SourceDir resolved =
789       SourceDirForCurrentDirectory(build_settings_.root_path())
790           .ResolveRelativeDir(Value(nullptr, build_dir), err,
791                               build_settings_.root_path_utf8());
792   if (err->has_error()) {
793     return false;
794   }
795 
796   base::FilePath build_dir_path = build_settings_.GetFullPath(resolved);
797   if (!base::CreateDirectory(build_dir_path)) {
798     *err = Err(Location(), "Can't create the build dir.",
799                "I could not create the build dir \"" +
800                    FilePathToUTF8(build_dir_path) + "\".");
801     return false;
802   }
803   base::FilePath build_dir_realpath =
804       base::MakeAbsoluteFilePath(build_dir_path);
805   if (build_dir_realpath.empty()) {
806     *err = Err(Location(), "Can't get the real build dir path.",
807                "I could not get the real path of \"" +
808                    FilePathToUTF8(build_dir_path) + "\".");
809     return false;
810   }
811   resolved = SourceDirForPath(build_settings_.root_path(), build_dir_realpath);
812 
813   if (scheduler_.verbose_logging())
814     scheduler_.Log("Using build dir", resolved.value());
815 
816   if (require_exists) {
817     if (!base::PathExists(
818             build_dir_path.Append(FILE_PATH_LITERAL("build.ninja")))) {
819       *err = Err(
820           Location(), "Not a build directory.",
821           "This command requires an existing build directory. I interpreted "
822           "your input\n\"" +
823               build_dir + "\" as:\n  " + FilePathToUTF8(build_dir_path) +
824               "\nwhich doesn't seem to contain a previously-generated build.");
825       return false;
826     }
827   }
828 
829   build_settings_.SetBuildDir(resolved);
830   return true;
831 }
832 
833 // On Chromium repositories on Windows the Python executable can be specified as
834 // python, python.bat, or python.exe (ditto for python3, and with or without a
835 // full path specification). This handles all of these cases and returns a fully
836 // specified path to a .exe file.
837 // This is currently a NOP on other platforms.
ProcessFileExtensions(base::FilePath script_executable)838 base::FilePath ProcessFileExtensions(base::FilePath script_executable) {
839 #if defined(OS_WIN)
840   // If we have a relative path with no extension such as "python" or
841   // "python3" then do a path search on the name with .exe and .bat appended.
842   auto extension = script_executable.FinalExtension();
843   if (script_executable.IsAbsolute()) {
844     // Do translation from .bat to .exe but otherwise just pass through.
845     if (extension == u".bat")
846       script_executable = PythonBatToExe(script_executable);
847   } else {
848     if (extension == u"") {
849       // If no extension is specified then search the path for .exe and .bat
850       // variants.
851       script_executable =
852           FindWindowsPython(script_executable.ReplaceExtension(u".exe"),
853                             script_executable.ReplaceExtension(u".bat"));
854     } else if (extension == u".bat") {
855       // Search the path just for the specified .bat.
856       script_executable =
857           FindWindowsPython(base::FilePath(), script_executable);
858     } else if (extension == u".exe") {
859       // Search the path just for the specified .exe.
860       script_executable =
861           FindWindowsPython(script_executable, base::FilePath());
862     }
863   }
864   script_executable = script_executable.NormalizePathSeparatorsTo('/');
865 #endif
866   return script_executable;
867 }
868 
FillPythonPath(const base::CommandLine & cmdline,Err * err)869 bool Setup::FillPythonPath(const base::CommandLine& cmdline, Err* err) {
870   // Trace this since it tends to be a bit slow on Windows.
871   ScopedTrace setup_trace(TraceItem::TRACE_SETUP, "Fill Python Path");
872   const Value* value = dotfile_scope_.GetValue("script_executable", true);
873   if (cmdline.HasSwitch(switches::kScriptExecutable)) {
874     auto script_executable =
875         cmdline.GetSwitchValuePath(switches::kScriptExecutable);
876     build_settings_.set_python_path(ProcessFileExtensions(script_executable));
877   } else if (value) {
878     if (!value->VerifyTypeIs(Value::STRING, err)) {
879       return false;
880     }
881     // Note that an empty string value is valid, and means that the scripts
882     // invoked by actions will be run directly.
883     base::FilePath python_path;
884     if (!value->string_value().empty()) {
885       python_path =
886           ProcessFileExtensions(UTF8ToFilePath(value->string_value()));
887       if (python_path.empty()) {
888         *err = Err(Location(), "Could not find \"" + value->string_value() +
889                                    "\" from dotfile in PATH.");
890         return false;
891       }
892     }
893     build_settings_.set_python_path(python_path);
894   } else {
895 #if defined(OS_WIN)
896     base::FilePath python_path =
897         ProcessFileExtensions(base::FilePath(u"python"));
898     if (!python_path.IsAbsolute()) {
899       scheduler_.Log("WARNING",
900                      "Could not find python on path, using "
901                      "just \"python.exe\"");
902       python_path = base::FilePath(u"python.exe");
903     }
904     build_settings_.set_python_path(python_path);
905 #else
906     build_settings_.set_python_path(base::FilePath("python"));
907 #endif
908   }
909   return true;
910 }
911 
RunConfigFile(Err * err)912 bool Setup::RunConfigFile(Err* err) {
913   if (scheduler_.verbose_logging())
914     scheduler_.Log("Got dotfile", FilePathToUTF8(dotfile_name_));
915 
916   dotfile_input_file_ = std::make_unique<InputFile>(SourceFile("//.gn"));
917   if (!dotfile_input_file_->Load(dotfile_name_)) {
918     *err = Err(Location(), "Could not load dotfile.",
919                "The file \"" + FilePathToUTF8(dotfile_name_) +
920                    "\" couldn't be loaded");
921     return false;
922   }
923 
924   dotfile_tokens_ = Tokenizer::Tokenize(dotfile_input_file_.get(), err);
925   if (err->has_error()) {
926     return false;
927   }
928 
929   dotfile_root_ = Parser::Parse(dotfile_tokens_, err);
930   if (err->has_error()) {
931     return false;
932   }
933 
934   // Add a dependency on the build arguments file. If this changes, we want
935   // to re-generate the build. This causes the dotfile to make it into
936   // build.ninja.d.
937   g_scheduler->AddGenDependency(dotfile_name_);
938 
939   // Also add a build dependency to the scope, which is used by `gn analyze`.
940   dotfile_scope_.AddBuildDependencyFile(SourceFile("//.gn"));
941   dotfile_root_->Execute(&dotfile_scope_, err);
942   if (err->has_error()) {
943     return false;
944   }
945 
946   return true;
947 }
948 
FillOtherConfig(const base::CommandLine & cmdline,Err * err)949 bool Setup::FillOtherConfig(const base::CommandLine& cmdline, Err* err) {
950   SourceDir current_dir("//");
951   Label root_target_label(current_dir, "");
952   std::vector<LabelPattern> root_patterns;
953 
954   // Secondary source path, read from the config file if present.
955   // Read from the config file if present.
956   const Value* secondary_value =
957       dotfile_scope_.GetValue("secondary_source", true);
958   if (secondary_value) {
959     if (!secondary_value->VerifyTypeIs(Value::STRING, err)) {
960       return false;
961     }
962     build_settings_.SetSecondarySourcePath(
963         SourceDir(secondary_value->string_value()));
964   }
965 
966   // Build file names.
967   const Value* build_file_extension_value =
968       dotfile_scope_.GetValue("build_file_extension", true);
969   if (build_file_extension_value) {
970     if (!build_file_extension_value->VerifyTypeIs(Value::STRING, err)) {
971       return false;
972     }
973 
974     std::string extension = build_file_extension_value->string_value();
975     auto normalized_extension = UTF8ToFilePath(extension).value();
976     if (normalized_extension.find_first_of(base::FilePath::kSeparators) !=
977         base::FilePath::StringType::npos) {
978       *err = Err(Location(), "Build file extension '" + extension +
979                                  "' cannot " + "contain a path separator");
980       return false;
981     }
982     loader_->set_build_file_extension(extension);
983   }
984 
985   // Ninja required version.
986   const Value* ninja_required_version_value =
987       dotfile_scope_.GetValue("ninja_required_version", true);
988   if (ninja_required_version_value) {
989     if (!ninja_required_version_value->VerifyTypeIs(Value::STRING, err)) {
990       return false;
991     }
992     std::optional<Version> version =
993         Version::FromString(ninja_required_version_value->string_value());
994     if (!version) {
995       Err(Location(), "Invalid Ninja version '" +
996                           ninja_required_version_value->string_value() + "'")
997           .PrintToStdout();
998       return false;
999     }
1000     build_settings_.set_ninja_required_version(*version);
1001   }
1002 
1003   // Root build file.
1004   if (cmdline.HasSwitch(switches::kRootTarget)) {
1005     auto switch_value = cmdline.GetSwitchValueString(switches::kRootTarget);
1006     Value root_value(nullptr, switch_value);
1007     root_target_label = Label::Resolve(current_dir, std::string_view(), Label(),
1008                                        root_value, err);
1009     if (err->has_error()) {
1010       return false;
1011     }
1012     if (dotfile_scope_.GetValue("root", true)) {
1013       // The "kRootTarget" switch overwrites the "root" variable in ".gn".
1014       dotfile_scope_.MarkUsed("root");
1015     }
1016   } else {
1017     const Value* root_value = dotfile_scope_.GetValue("root", true);
1018     if (root_value) {
1019       if (!root_value->VerifyTypeIs(Value::STRING, err)) {
1020         return false;
1021       }
1022 
1023       root_target_label = Label::Resolve(current_dir, std::string_view(),
1024                                          Label(), *root_value, err);
1025       if (err->has_error()) {
1026         return false;
1027       }
1028     }
1029   }
1030 
1031   if (cmdline.HasSwitch(switches::kRootPattern)) {
1032     auto& switches = cmdline.GetSwitches();
1033     for (auto it = switches.find(switches::kRootPattern);
1034          it != switches.end() && it->first == switches::kRootPattern; ++it) {
1035       std::string pattern = base::CommandLine::StringTypeToUTF8(it->second);
1036       LabelPattern pat = LabelPattern::GetPattern(
1037           SourceDir("//"), build_settings_.root_path_utf8(),
1038           Value(nullptr, pattern), err);
1039       if (err->has_error()) {
1040         err->AppendSubErr(
1041             Err(Location(),
1042                 "for the command-line switch --root-pattern=" + pattern));
1043         return false;
1044       }
1045       if (!pat.toolchain().is_null()) {
1046         *err = Err(Location(),
1047                    "Root pattern cannot have toolchain suffix: " + pattern);
1048         return false;
1049       }
1050       root_patterns.push_back(std::move(pat));
1051     }
1052     // Ensure GN does not complain about the .gn root_patterns value being
1053     // ignored if it is set.
1054     (void)dotfile_scope_.GetValue("root_patterns", true);
1055   } else {
1056     const Value* root_patterns_value =
1057         dotfile_scope_.GetValue("root_patterns", true);
1058     if (root_patterns_value) {
1059       if (!root_patterns_value->VerifyTypeIs(Value::LIST, err)) {
1060         return false;
1061       }
1062       for (const auto& pattern_value : root_patterns_value->list_value()) {
1063         if (!pattern_value.VerifyTypeIs(Value::STRING, err))
1064           return false;
1065 
1066         LabelPattern pat = LabelPattern::GetPattern(
1067             SourceDir("//"), build_settings_.root_path_utf8(), pattern_value,
1068             err);
1069         if (err->has_error())
1070           return false;
1071         if (!pat.toolchain().is_null()) {
1072           *err =
1073               Err(pattern_value, "Root pattern cannot have toolchain suffix: " +
1074                                      pattern_value.string_value());
1075           return false;
1076         }
1077         root_patterns.push_back(std::move(pat));
1078       }
1079     }
1080   }
1081 
1082   // Set the root build file here in order to take into account the values of
1083   // "build_file_extension" and "root".
1084   root_build_file_ = loader_->BuildFileForLabel(root_target_label);
1085   build_settings_.SetRootTargetLabel(root_target_label);
1086   build_settings_.SetRootPatterns(std::move(root_patterns));
1087 
1088   // Build config file.
1089   const Value* build_config_value =
1090       dotfile_scope_.GetValue("buildconfig", true);
1091   if (!build_config_value) {
1092     Err(Location(), "No build config file.",
1093         "Your .gn file (\"" + FilePathToUTF8(dotfile_name_) +
1094             "\")\n"
1095             "didn't specify a \"buildconfig\" value.")
1096         .PrintToStdout();
1097     return false;
1098   } else if (!build_config_value->VerifyTypeIs(Value::STRING, err)) {
1099     return false;
1100   }
1101   build_settings_.set_build_config_file(
1102       SourceFile(build_config_value->string_value()));
1103 
1104   // Targets to check.
1105   const Value* check_targets_value =
1106       dotfile_scope_.GetValue("check_targets", true);
1107   if (check_targets_value) {
1108     check_patterns_ = std::make_unique<std::vector<LabelPattern>>();
1109     ExtractListOfLabelPatterns(&build_settings_, *check_targets_value,
1110                                current_dir, check_patterns_.get(), err);
1111     if (err->has_error()) {
1112       return false;
1113     }
1114   }
1115 
1116   // Targets not to check.
1117   const Value* no_check_targets_value =
1118       dotfile_scope_.GetValue("no_check_targets", true);
1119   if (no_check_targets_value) {
1120     if (check_targets_value) {
1121       Err(Location(), "Conflicting check settings.",
1122           "Your .gn file (\"" + FilePathToUTF8(dotfile_name_) +
1123               "\")\n"
1124               "specified both check_targets and no_check_targets and at most "
1125               "one is allowed.")
1126           .PrintToStdout();
1127       return false;
1128     }
1129     no_check_patterns_ = std::make_unique<std::vector<LabelPattern>>();
1130     ExtractListOfLabelPatterns(&build_settings_, *no_check_targets_value,
1131                                current_dir, no_check_patterns_.get(), err);
1132     if (err->has_error()) {
1133       return false;
1134     }
1135   }
1136 
1137   const Value* check_system_includes_value =
1138       dotfile_scope_.GetValue("check_system_includes", true);
1139   if (check_system_includes_value) {
1140     if (!check_system_includes_value->VerifyTypeIs(Value::BOOLEAN, err)) {
1141       return false;
1142     }
1143     check_system_includes_ = check_system_includes_value->boolean_value();
1144   }
1145 
1146   // Fill exec_script_whitelist.
1147   const Value* exec_script_whitelist_value =
1148       dotfile_scope_.GetValue("exec_script_whitelist", true);
1149   if (exec_script_whitelist_value) {
1150     // Fill the list of targets to check.
1151     if (!exec_script_whitelist_value->VerifyTypeIs(Value::LIST, err)) {
1152       return false;
1153     }
1154     std::unique_ptr<SourceFileSet> whitelist =
1155         std::make_unique<SourceFileSet>();
1156     for (const auto& item : exec_script_whitelist_value->list_value()) {
1157       if (!item.VerifyTypeIs(Value::STRING, err)) {
1158         return false;
1159       }
1160       whitelist->insert(current_dir.ResolveRelativeFile(item, err));
1161       if (err->has_error()) {
1162         return false;
1163       }
1164     }
1165     build_settings_.set_exec_script_whitelist(std::move(whitelist));
1166   }
1167 
1168   // Fill optional default_args.
1169   const Value* default_args_value =
1170       dotfile_scope_.GetValue("default_args", true);
1171   if (default_args_value) {
1172     if (!default_args_value->VerifyTypeIs(Value::SCOPE, err)) {
1173       return false;
1174     }
1175 
1176     default_args_ = default_args_value->scope_value();
1177   }
1178 
1179   const Value* arg_file_template_value =
1180       dotfile_scope_.GetValue("arg_file_template", true);
1181   if (arg_file_template_value) {
1182     if (!arg_file_template_value->VerifyTypeIs(Value::STRING, err)) {
1183       return false;
1184     }
1185     SourceFile path(arg_file_template_value->string_value());
1186     build_settings_.set_arg_file_template_path(path);
1187   }
1188 
1189   // No stamp files.
1190   const Value* no_stamp_files_value =
1191       dotfile_scope_.GetValue("no_stamp_files", true);
1192   if (no_stamp_files_value) {
1193     if (!no_stamp_files_value->VerifyTypeIs(Value::BOOLEAN, err)) {
1194       return false;
1195     }
1196     build_settings_.set_no_stamp_files(no_stamp_files_value->boolean_value());
1197     CHECK(!build_settings_.no_stamp_files())
1198         << "no_stamp_files does not work yet!";
1199   }
1200 
1201   // Export compile commands.
1202   const Value* export_cc_value =
1203       dotfile_scope_.GetValue("export_compile_commands", true);
1204   if (export_cc_value) {
1205     if (!ExtractListOfLabelPatterns(&build_settings_, *export_cc_value,
1206                                     SourceDir("//"), &export_compile_commands_,
1207                                     err)) {
1208       return false;
1209     }
1210   }
1211 
1212   // Append any additional export compile command patterns from the cmdline.
1213   for (const std::string& cur :
1214        cmdline.GetSwitchValueStrings(switches::kAddExportCompileCommands)) {
1215     LabelPattern pat = LabelPattern::GetPattern(
1216         SourceDir("//"), build_settings_.root_path_utf8(), Value(nullptr, cur),
1217         err);
1218     if (err->has_error()) {
1219       err->AppendSubErr(Err(
1220           Location(),
1221           "for the command-line switch --add-export-compile-commands=" + cur));
1222       return false;
1223     }
1224     export_compile_commands_.push_back(std::move(pat));
1225   }
1226 
1227   return true;
1228 }
1229