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