• 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/parse_tree.h"
27 #include "gn/parser.h"
28 #include "gn/source_dir.h"
29 #include "gn/source_file.h"
30 #include "gn/standard_out.h"
31 #include "gn/switches.h"
32 #include "gn/tokenizer.h"
33 #include "gn/trace.h"
34 #include "gn/value.h"
35 #include "gn/value_extractors.h"
36 #include "util/build_config.h"
37 
38 #if defined(OS_WIN)
39 #include <windows.h>
40 
41 #include "base/win/scoped_process_information.h"
42 #include "base/win/win_util.h"
43 #endif
44 
45 const char kDotfile_Help[] =
46     R"(.gn file
47 
48   When gn starts, it will search the current directory and parent directories
49   for a file called ".gn". This indicates the source root. You can override
50   this detection by using the --root command-line argument
51 
52   The .gn file in the source root will be executed. The syntax is the same as a
53   buildfile, but with very limited build setup-specific meaning.
54 
55   If you specify --root, by default GN will look for the file .gn in that
56   directory. If you want to specify a different file, you can additionally pass
57   --dotfile:
58 
59     gn gen out/Debug --root=/home/build --dotfile=/home/my_gn_file.gn
60 
61 Variables
62 
63   arg_file_template [optional]
64       Path to a file containing the text that should be used as the default
65       args.gn content when you run `gn args`.
66 
67   buildconfig [required]
68       Path to the build config file. This file will be used to set up the
69       build file execution environment for each toolchain.
70 
71   check_targets [optional]
72       A list of labels and label patterns that should be checked when running
73       "gn check" or "gn gen --check". If unspecified, all targets will be
74       checked. If it is the empty list, no targets will be checked. To
75       bypass this list, request an explicit check of targets, like "//*".
76 
77       The format of this list is identical to that of "visibility" so see "gn
78       help visibility" for examples.
79 
80   check_system_includes [optional]
81       Boolean to control whether system style includes are checked by default
82       when running "gn check" or "gn gen --check".  System style includes are
83       includes that use angle brackets <> instead of double quotes "". If this
84       setting is omitted or set to false, these includes will be ignored by
85       default. They can be checked explicitly by running
86       "gn check --check-system" or "gn gen --check=system"
87 
88   exec_script_whitelist [optional]
89       A list of .gn/.gni files (not labels) that have permission to call the
90       exec_script function. If this list is defined, calls to exec_script will
91       be checked against this list and GN will fail if the current file isn't
92       in the list.
93 
94       This is to allow the use of exec_script to be restricted since is easy to
95       use inappropriately. Wildcards are not supported. Files in the
96       secondary_source tree (if defined) should be referenced by ignoring the
97       secondary tree and naming them as if they are in the main tree.
98 
99       If unspecified, the ability to call exec_script is unrestricted.
100 
101       Example:
102         exec_script_whitelist = [
103           "//base/BUILD.gn",
104           "//build/my_config.gni",
105         ]
106 
107   root [optional]
108       Label of the root build target. The GN build will start by loading the
109       build file containing this target name. This defaults to "//:" which will
110       cause the file //BUILD.gn to be loaded.
111 
112   script_executable [optional]
113       Path to specific Python executable or other interpreter to use in
114       action targets and exec_script calls. By default GN searches the
115       PATH for Python to execute these scripts.
116 
117       If set to the empty string, the path specified in action targets
118       and exec_script calls will be executed directly.
119 
120   secondary_source [optional]
121       Label of an alternate directory tree to find input files. When searching
122       for a BUILD.gn file (or the build config file discussed above), the file
123       will first be looked for in the source root. If it's not found, the
124       secondary source root will be checked (which would contain a parallel
125       directory hierarchy).
126 
127       This behavior is intended to be used when BUILD.gn files can't be checked
128       in to certain source directories for whatever reason.
129 
130       The secondary source root must be inside the main source tree.
131 
132   default_args [optional]
133       Scope containing the default overrides for declared arguments. These
134       overrides take precedence over the default values specified in the
135       declare_args() block, but can be overriden using --args or the
136       args.gn file.
137 
138       This is intended to be used when subprojects declare arguments with
139       default values that need to be changed for whatever reason.
140 
141 Example .gn file contents
142 
143   buildconfig = "//build/config/BUILDCONFIG.gn"
144 
145   check_targets = [
146     "//doom_melon/*",  # Check everything in this subtree.
147     "//tools:mind_controlling_ant",  # Check this specific target.
148   ]
149 
150   root = "//:root"
151 
152   secondary_source = "//build/config/temporary_buildfiles/"
153 
154   default_args = {
155     # Default to release builds for this project.
156     is_debug = false
157     is_component_build = false
158   }
159 )";
160 
161 namespace {
162 
163 const base::FilePath::CharType kGnFile[] = FILE_PATH_LITERAL(".gn");
164 const char kDefaultArgsGn[] =
165     "# Set build arguments here. See `gn help buildargs`.";
166 
FindDotFile(const base::FilePath & current_dir)167 base::FilePath FindDotFile(const base::FilePath& current_dir) {
168   base::FilePath try_this_file = current_dir.Append(kGnFile);
169   if (base::PathExists(try_this_file))
170     return try_this_file;
171 
172   base::FilePath with_no_slash = current_dir.StripTrailingSeparators();
173   base::FilePath up_one_dir = with_no_slash.DirName();
174   if (up_one_dir == current_dir)
175     return base::FilePath();  // Got to the top.
176 
177   return FindDotFile(up_one_dir);
178 }
179 
180 // Called on any thread. Post the item to the builder on the main thread.
181 void ItemDefinedCallback(MsgLoop* task_runner,
182                          Builder* builder_call_on_main_thread_only,
183                          std::unique_ptr<Item> item) {
184   DCHECK(item);
185 
186   // Increment the work count for the duration of defining the item with the
187   // builder. Otherwise finishing this callback will race finishing loading
188   // files. If there is no other pending work at any point in the middle of
189   // this call completing on the main thread, the 'Complete' function will
190   // be signaled and we'll stop running with an incomplete build.
191   g_scheduler->IncrementWorkCount();
192 
193   // Work around issue binding a unique_ptr with std::function by moving into a
194   // shared_ptr.
195   auto item_shared = std::make_shared<std::unique_ptr<Item>>(std::move(item));
196   task_runner->PostTask(
__anone9eb8e6b0202() 197       [builder_call_on_main_thread_only, item_shared]() mutable {
198         builder_call_on_main_thread_only->ItemDefined(std::move(*item_shared));
199         g_scheduler->DecrementWorkCount();
200       });
201 }
202 
DecrementWorkCount()203 void DecrementWorkCount() {
204   g_scheduler->DecrementWorkCount();
205 }
206 
207 #if defined(OS_WIN)
208 
SysMultiByteTo16(std::string_view mb)209 std::u16string SysMultiByteTo16(std::string_view mb) {
210   if (mb.empty())
211     return std::u16string();
212 
213   int mb_length = static_cast<int>(mb.length());
214   // Compute the length of the buffer.
215   int charcount = MultiByteToWideChar(CP_ACP, 0, mb.data(), mb_length, NULL, 0);
216   if (charcount == 0)
217     return std::u16string();
218 
219   std::u16string wide;
220   wide.resize(charcount);
221   MultiByteToWideChar(CP_ACP, 0, mb.data(), mb_length, base::ToWCharT(&wide[0]),
222                       charcount);
223 
224   return wide;
225 }
226 
227 // Given the path to a batch file that runs Python, extracts the name of the
228 // executable actually implementing Python. Generally people write a batch file
229 // to put something named "python" on the path, which then just redirects to
230 // a python.exe somewhere else. This step decodes that setup. On failure,
231 // returns empty path.
PythonBatToExe(const base::FilePath & bat_path)232 base::FilePath PythonBatToExe(const base::FilePath& bat_path) {
233   // Note exciting double-quoting to allow spaces. The /c switch seems to check
234   // for quotes around the whole thing and then deletes them. If you want to
235   // quote the first argument in addition (to allow for spaces in the Python
236   // path, you need *another* set of quotes around that, likewise, we need
237   // two quotes at the end.
238   std::u16string command = u"cmd.exe /c \"\"";
239   command.append(bat_path.value());
240   command.append(u"\" -c \"import sys; print sys.executable\"\"");
241 
242   std::string python_path;
243   std::string std_err;
244   int exit_code;
245   base::FilePath cwd;
246   GetCurrentDirectory(&cwd);
247   if (internal::ExecProcess(command, cwd, &python_path, &std_err, &exit_code) &&
248       exit_code == 0 && std_err.empty()) {
249     base::TrimWhitespaceASCII(python_path, base::TRIM_ALL, &python_path);
250 
251     // Python uses the system multibyte code page for sys.executable.
252     base::FilePath exe_path(SysMultiByteTo16(python_path));
253 
254     // Check for reasonable output, cmd may have output an error message.
255     if (base::PathExists(exe_path))
256       return exe_path;
257   }
258   return base::FilePath();
259 }
260 
261 const char16_t kPythonExeName[] = u"python.exe";
262 const char16_t kPythonBatName[] = u"python.bat";
263 
FindWindowsPython()264 base::FilePath FindWindowsPython() {
265   char16_t current_directory[MAX_PATH];
266   ::GetCurrentDirectory(MAX_PATH, reinterpret_cast<LPWSTR>(current_directory));
267 
268   // First search for python.exe in the current directory.
269   base::FilePath cur_dir_candidate_exe =
270       base::FilePath(current_directory).Append(kPythonExeName);
271   if (base::PathExists(cur_dir_candidate_exe))
272     return cur_dir_candidate_exe;
273 
274   // Get the path.
275   const char16_t kPathEnvVarName[] = u"Path";
276   DWORD path_length = ::GetEnvironmentVariable(
277       reinterpret_cast<LPCWSTR>(kPathEnvVarName), nullptr, 0);
278   if (path_length == 0)
279     return base::FilePath();
280   std::unique_ptr<char16_t[]> full_path(new char16_t[path_length]);
281   DWORD actual_path_length = ::GetEnvironmentVariable(
282       reinterpret_cast<LPCWSTR>(kPathEnvVarName),
283       reinterpret_cast<LPWSTR>(full_path.get()), path_length);
284   CHECK_EQ(path_length, actual_path_length + 1);
285 
286   // Search for python.exe in the path.
287   for (const auto& component : base::SplitStringPiece(
288            std::u16string_view(full_path.get(), path_length), u";",
289            base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) {
290     base::FilePath candidate_exe =
291         base::FilePath(component).Append(kPythonExeName);
292     if (base::PathExists(candidate_exe))
293       return candidate_exe;
294 
295     // Also allow python.bat, but convert into the .exe.
296     base::FilePath candidate_bat =
297         base::FilePath(component).Append(kPythonBatName);
298     if (base::PathExists(candidate_bat)) {
299       base::FilePath python_exe = PythonBatToExe(candidate_bat);
300       if (!python_exe.empty())
301         return python_exe;
302     }
303   }
304   return base::FilePath();
305 }
306 #endif
307 
308 }  // namespace
309 
310 const char Setup::kBuildArgFileName[] = "args.gn";
311 
Setup()312 Setup::Setup()
313     : build_settings_(),
314       loader_(new LoaderImpl(&build_settings_)),
315       builder_(loader_.get()),
316       root_build_file_("//BUILD.gn"),
317       dotfile_settings_(&build_settings_, std::string()),
318       dotfile_scope_(&dotfile_settings_) {
319   dotfile_settings_.set_toolchain_label(Label());
320 
321   build_settings_.set_item_defined_callback(
322       [task_runner = scheduler_.task_runner(),
323        builder = &builder_](std::unique_ptr<Item> item) {
324         ItemDefinedCallback(task_runner, builder, std::move(item));
325       });
326 
327   loader_->set_complete_callback(&DecrementWorkCount);
328   // The scheduler's task runner wasn't created when the Loader was created, so
329   // we need to set it now.
330   loader_->set_task_runner(scheduler_.task_runner());
331 }
332 
DoSetup(const std::string & build_dir,bool force_create)333 bool Setup::DoSetup(const std::string& build_dir, bool force_create) {
334   return DoSetup(build_dir, force_create,
335                  *base::CommandLine::ForCurrentProcess());
336 }
337 
DoSetup(const std::string & build_dir,bool force_create,const base::CommandLine & cmdline)338 bool Setup::DoSetup(const std::string& build_dir,
339                     bool force_create,
340                     const base::CommandLine& cmdline) {
341   scheduler_.set_verbose_logging(cmdline.HasSwitch(switches::kVerbose));
342   if (cmdline.HasSwitch(switches::kTime) ||
343       cmdline.HasSwitch(switches::kTracelog))
344     EnableTracing();
345 
346   ScopedTrace setup_trace(TraceItem::TRACE_SETUP, "DoSetup");
347 
348   if (!FillSourceDir(cmdline))
349     return false;
350   if (!RunConfigFile())
351     return false;
352   if (!FillOtherConfig(cmdline))
353     return false;
354 
355   // Must be after FillSourceDir to resolve.
356   if (!FillBuildDir(build_dir, !force_create))
357     return false;
358 
359   // Apply project-specific default (if specified).
360   // Must happen before FillArguments().
361   if (default_args_) {
362     Scope::KeyValueMap overrides;
363     default_args_->GetCurrentScopeValues(&overrides);
364     build_settings_.build_args().AddDefaultArgOverrides(overrides);
365   }
366 
367   if (fill_arguments_) {
368     if (!FillArguments(cmdline))
369       return false;
370   }
371   if (!FillPythonPath(cmdline))
372     return false;
373 
374   // Check for unused variables in the .gn file.
375   Err err;
376   if (!dotfile_scope_.CheckForUnusedVars(&err)) {
377     err.PrintToStdout();
378     return false;
379   }
380 
381   return true;
382 }
383 
Run()384 bool Setup::Run() {
385   return Run(*base::CommandLine::ForCurrentProcess());
386 }
387 
Run(const base::CommandLine & cmdline)388 bool Setup::Run(const base::CommandLine& cmdline) {
389   RunPreMessageLoop();
390   if (!scheduler_.Run())
391     return false;
392   return RunPostMessageLoop(cmdline);
393 }
394 
GetBuildArgFile() const395 SourceFile Setup::GetBuildArgFile() const {
396   return SourceFile(build_settings_.build_dir().value() + kBuildArgFileName);
397 }
398 
RunPreMessageLoop()399 void Setup::RunPreMessageLoop() {
400   // Will be decremented with the loader is drained.
401   g_scheduler->IncrementWorkCount();
402 
403   // Load the root build file.
404   loader_->Load(root_build_file_, LocationRange(), Label());
405 }
406 
RunPostMessageLoop(const base::CommandLine & cmdline)407 bool Setup::RunPostMessageLoop(const base::CommandLine& cmdline) {
408   Err err;
409   if (!builder_.CheckForBadItems(&err)) {
410     err.PrintToStdout();
411     return false;
412   }
413 
414   if (!build_settings_.build_args().VerifyAllOverridesUsed(&err)) {
415     if (cmdline.HasSwitch(switches::kFailOnUnusedArgs)) {
416       err.PrintToStdout();
417       return false;
418     }
419     err.PrintNonfatalToStdout();
420     OutputString(
421         "\nThe build continued as if that argument was "
422         "unspecified.\n\n");
423     // Nonfatal error.
424   }
425 
426   if (check_public_headers_) {
427     std::vector<const Target*> all_targets = builder_.GetAllResolvedTargets();
428     std::vector<const Target*> to_check;
429     if (check_patterns()) {
430       commands::FilterTargetsByPatterns(all_targets, *check_patterns(),
431                                         &to_check);
432     } else {
433       to_check = all_targets;
434     }
435 
436     if (!commands::CheckPublicHeaders(&build_settings_, all_targets, to_check,
437                                       false, false, check_system_includes_)) {
438       return false;
439     }
440   }
441 
442   // Write out tracing and timing if requested.
443   if (cmdline.HasSwitch(switches::kTime))
444     PrintLongHelp(SummarizeTraces());
445   if (cmdline.HasSwitch(switches::kTracelog))
446     SaveTraces(cmdline.GetSwitchValuePath(switches::kTracelog));
447 
448   return true;
449 }
450 
FillArguments(const base::CommandLine & cmdline)451 bool Setup::FillArguments(const base::CommandLine& cmdline) {
452   // Use the args on the command line if specified, and save them. Do this even
453   // if the list is empty (this means clear any defaults).
454   // If --args is not set, args.gn file does not exist and gen_empty_args
455   // is set, generate an empty args.gn file with default comments.
456 
457   base::FilePath build_arg_file =
458       build_settings_.GetFullPath(GetBuildArgFile());
459   auto switch_value = cmdline.GetSwitchValueASCII(switches::kArgs);
460   if (cmdline.HasSwitch(switches::kArgs) ||
461       (gen_empty_args_ && !PathExists(build_arg_file))) {
462     if (!FillArgsFromCommandLine(switch_value.empty() ? kDefaultArgsGn
463                                                       : switch_value)) {
464       return false;
465     }
466     SaveArgsToFile();
467     return true;
468   }
469 
470   // No command line args given, use the arguments from the build dir (if any).
471   return FillArgsFromFile();
472 }
473 
FillArgsFromCommandLine(const std::string & args)474 bool Setup::FillArgsFromCommandLine(const std::string& args) {
475   args_input_file_ = std::make_unique<InputFile>(SourceFile());
476   args_input_file_->SetContents(args);
477   args_input_file_->set_friendly_name("the command-line \"--args\"");
478   return FillArgsFromArgsInputFile();
479 }
480 
FillArgsFromFile()481 bool Setup::FillArgsFromFile() {
482   ScopedTrace setup_trace(TraceItem::TRACE_SETUP, "Load args file");
483 
484   SourceFile build_arg_source_file = GetBuildArgFile();
485   base::FilePath build_arg_file =
486       build_settings_.GetFullPath(build_arg_source_file);
487 
488   std::string contents;
489   if (!base::ReadFileToString(build_arg_file, &contents))
490     return true;  // File doesn't exist, continue with default args.
491 
492   // Add a dependency on the build arguments file. If this changes, we want
493   // to re-generate the build.
494   g_scheduler->AddGenDependency(build_arg_file);
495 
496   if (contents.empty())
497     return true;  // Empty file, do nothing.
498 
499   args_input_file_ = std::make_unique<InputFile>(build_arg_source_file);
500   args_input_file_->SetContents(contents);
501   args_input_file_->set_friendly_name(
502       "build arg file (use \"gn args <out_dir>\" to edit)");
503 
504   setup_trace.Done();  // Only want to count the load as part of the trace.
505   return FillArgsFromArgsInputFile();
506 }
507 
FillArgsFromArgsInputFile()508 bool Setup::FillArgsFromArgsInputFile() {
509   ScopedTrace setup_trace(TraceItem::TRACE_SETUP, "Parse args");
510 
511   Err err;
512   args_tokens_ = Tokenizer::Tokenize(args_input_file_.get(), &err);
513   if (err.has_error()) {
514     err.PrintToStdout();
515     return false;
516   }
517 
518   args_root_ = Parser::Parse(args_tokens_, &err);
519   if (err.has_error()) {
520     err.PrintToStdout();
521     return false;
522   }
523 
524   Scope arg_scope(&dotfile_settings_);
525   // Set soure dir so relative imports in args work.
526   SourceDir root_source_dir =
527       SourceDirForCurrentDirectory(build_settings_.root_path());
528   arg_scope.set_source_dir(root_source_dir);
529   args_root_->Execute(&arg_scope, &err);
530   if (err.has_error()) {
531     err.PrintToStdout();
532     return false;
533   }
534 
535   // Save the result of the command args.
536   Scope::KeyValueMap overrides;
537   arg_scope.GetCurrentScopeValues(&overrides);
538   build_settings_.build_args().AddArgOverrides(overrides);
539   build_settings_.build_args().set_build_args_dependency_files(
540       arg_scope.build_dependency_files());
541   return true;
542 }
543 
SaveArgsToFile()544 bool Setup::SaveArgsToFile() {
545   ScopedTrace setup_trace(TraceItem::TRACE_SETUP, "Save args file");
546 
547   // For the first run, the build output dir might not be created yet, so do
548   // that so we can write a file into it. Ignore errors, we'll catch the error
549   // when we try to write a file to it below.
550   base::FilePath build_arg_file =
551       build_settings_.GetFullPath(GetBuildArgFile());
552   base::CreateDirectory(build_arg_file.DirName());
553 
554   std::string contents = args_input_file_->contents();
555   commands::FormatStringToString(contents, commands::TreeDumpMode::kInactive,
556                                  &contents);
557 #if defined(OS_WIN)
558   // Use Windows lineendings for this file since it will often open in
559   // Notepad which can't handle Unix ones.
560   base::ReplaceSubstringsAfterOffset(&contents, 0, "\n", "\r\n");
561 #endif
562   if (base::WriteFile(build_arg_file, contents.c_str(),
563                       static_cast<int>(contents.size())) == -1) {
564     Err(Location(), "Args file could not be written.",
565         "The file is \"" + FilePathToUTF8(build_arg_file) + "\"")
566         .PrintToStdout();
567     return false;
568   }
569 
570   // Add a dependency on the build arguments file. If this changes, we want
571   // to re-generate the build.
572   g_scheduler->AddGenDependency(build_arg_file);
573 
574   return true;
575 }
576 
FillSourceDir(const base::CommandLine & cmdline)577 bool Setup::FillSourceDir(const base::CommandLine& cmdline) {
578   // Find the .gn file.
579   base::FilePath root_path;
580 
581   // Prefer the command line args to the config file.
582   base::FilePath relative_root_path =
583       cmdline.GetSwitchValuePath(switches::kRoot);
584   if (!relative_root_path.empty()) {
585     root_path = base::MakeAbsoluteFilePath(relative_root_path);
586     if (root_path.empty()) {
587       Err(Location(), "Root source path not found.",
588           "The path \"" + FilePathToUTF8(relative_root_path) +
589               "\" doesn't exist.")
590           .PrintToStdout();
591       return false;
592     }
593 
594     // When --root is specified, an alternate --dotfile can also be set.
595     // --dotfile should be a real file path and not a "//foo" source-relative
596     // path.
597     base::FilePath dotfile_path =
598         cmdline.GetSwitchValuePath(switches::kDotfile);
599     if (dotfile_path.empty()) {
600       dotfile_name_ = root_path.Append(kGnFile);
601     } else {
602       dotfile_name_ = base::MakeAbsoluteFilePath(dotfile_path);
603       if (dotfile_name_.empty()) {
604         Err(Location(), "Could not load dotfile.",
605             "The file \"" + FilePathToUTF8(dotfile_path) +
606                 "\" couldn't be loaded.")
607             .PrintToStdout();
608         return false;
609       }
610       // Only set dotfile_name if it was passed explicitly.
611       build_settings_.set_dotfile_name(dotfile_name_);
612     }
613   } else {
614     // In the default case, look for a dotfile and that also tells us where the
615     // source root is.
616     base::FilePath cur_dir;
617     base::GetCurrentDirectory(&cur_dir);
618     dotfile_name_ = FindDotFile(cur_dir);
619     if (dotfile_name_.empty()) {
620       Err(Location(), "Can't find source root.",
621           "I could not find a \".gn\" file in the current directory or any "
622           "parent,\nand the --root command-line argument was not specified.")
623           .PrintToStdout();
624       return false;
625     }
626     root_path = dotfile_name_.DirName();
627   }
628 
629   base::FilePath root_realpath = base::MakeAbsoluteFilePath(root_path);
630   if (root_realpath.empty()) {
631     Err(Location(), "Can't get the real root path.",
632         "I could not get the real path of \"" + FilePathToUTF8(root_path) +
633             "\".")
634         .PrintToStdout();
635     return false;
636   }
637   if (scheduler_.verbose_logging())
638     scheduler_.Log("Using source root", FilePathToUTF8(root_realpath));
639   build_settings_.SetRootPath(root_realpath);
640 
641   return true;
642 }
643 
FillBuildDir(const std::string & build_dir,bool require_exists)644 bool Setup::FillBuildDir(const std::string& build_dir, bool require_exists) {
645   Err err;
646   SourceDir resolved =
647       SourceDirForCurrentDirectory(build_settings_.root_path())
648           .ResolveRelativeDir(Value(nullptr, build_dir), &err,
649                               build_settings_.root_path_utf8());
650   if (err.has_error()) {
651     err.PrintToStdout();
652     return false;
653   }
654 
655   base::FilePath build_dir_path = build_settings_.GetFullPath(resolved);
656   if (!base::CreateDirectory(build_dir_path)) {
657     Err(Location(), "Can't create the build dir.",
658         "I could not create the build dir \"" + FilePathToUTF8(build_dir_path) +
659             "\".")
660         .PrintToStdout();
661     return false;
662   }
663   base::FilePath build_dir_realpath =
664       base::MakeAbsoluteFilePath(build_dir_path);
665   if (build_dir_realpath.empty()) {
666     Err(Location(), "Can't get the real build dir path.",
667         "I could not get the real path of \"" + FilePathToUTF8(build_dir_path) +
668             "\".")
669         .PrintToStdout();
670     return false;
671   }
672   resolved = SourceDirForPath(build_settings_.root_path(), build_dir_realpath);
673 
674   if (scheduler_.verbose_logging())
675     scheduler_.Log("Using build dir", resolved.value());
676 
677   if (require_exists) {
678     if (!base::PathExists(
679             build_dir_path.Append(FILE_PATH_LITERAL("build.ninja")))) {
680       Err(Location(), "Not a build directory.",
681           "This command requires an existing build directory. I interpreted "
682           "your input\n\"" +
683               build_dir + "\" as:\n  " + FilePathToUTF8(build_dir_path) +
684               "\nwhich doesn't seem to contain a previously-generated build.")
685           .PrintToStdout();
686       return false;
687     }
688   }
689 
690   build_settings_.SetBuildDir(resolved);
691   return true;
692 }
693 
FillPythonPath(const base::CommandLine & cmdline)694 bool Setup::FillPythonPath(const base::CommandLine& cmdline) {
695   // Trace this since it tends to be a bit slow on Windows.
696   ScopedTrace setup_trace(TraceItem::TRACE_SETUP, "Fill Python Path");
697   const Value* value = dotfile_scope_.GetValue("script_executable", true);
698   if (cmdline.HasSwitch(switches::kScriptExecutable)) {
699     build_settings_.set_python_path(
700         cmdline.GetSwitchValuePath(switches::kScriptExecutable));
701   } else if (value) {
702     Err err;
703     if (!value->VerifyTypeIs(Value::STRING, &err)) {
704       err.PrintToStdout();
705       return false;
706     }
707     build_settings_.set_python_path(
708         base::FilePath(UTF8ToFilePath(value->string_value())));
709   } else {
710 #if defined(OS_WIN)
711     base::FilePath python_path = FindWindowsPython();
712     if (python_path.empty()) {
713       scheduler_.Log("WARNING",
714                      "Could not find python on path, using "
715                      "just \"python.exe\"");
716       python_path = base::FilePath(kPythonExeName);
717     }
718     build_settings_.set_python_path(python_path.NormalizePathSeparatorsTo('/'));
719 #else
720     build_settings_.set_python_path(base::FilePath("python"));
721 #endif
722   }
723   return true;
724 }
725 
RunConfigFile()726 bool Setup::RunConfigFile() {
727   if (scheduler_.verbose_logging())
728     scheduler_.Log("Got dotfile", FilePathToUTF8(dotfile_name_));
729 
730   dotfile_input_file_ = std::make_unique<InputFile>(SourceFile("//.gn"));
731   if (!dotfile_input_file_->Load(dotfile_name_)) {
732     Err(Location(), "Could not load dotfile.",
733         "The file \"" + FilePathToUTF8(dotfile_name_) + "\" couldn't be loaded")
734         .PrintToStdout();
735     return false;
736   }
737 
738   Err err;
739   dotfile_tokens_ = Tokenizer::Tokenize(dotfile_input_file_.get(), &err);
740   if (err.has_error()) {
741     err.PrintToStdout();
742     return false;
743   }
744 
745   dotfile_root_ = Parser::Parse(dotfile_tokens_, &err);
746   if (err.has_error()) {
747     err.PrintToStdout();
748     return false;
749   }
750 
751   // Add a dependency on the build arguments file. If this changes, we want
752   // to re-generate the build. This causes the dotfile to make it into
753   // build.ninja.d.
754   g_scheduler->AddGenDependency(dotfile_name_);
755 
756   // Also add a build dependency to the scope, which is used by `gn analyze`.
757   dotfile_scope_.AddBuildDependencyFile(SourceFile("//.gn"));
758   dotfile_root_->Execute(&dotfile_scope_, &err);
759   if (err.has_error()) {
760     err.PrintToStdout();
761     return false;
762   }
763 
764   return true;
765 }
766 
FillOtherConfig(const base::CommandLine & cmdline)767 bool Setup::FillOtherConfig(const base::CommandLine& cmdline) {
768   Err err;
769   SourceDir current_dir("//");
770   Label root_target_label(current_dir, "");
771 
772   // Secondary source path, read from the config file if present.
773   // Read from the config file if present.
774   const Value* secondary_value =
775       dotfile_scope_.GetValue("secondary_source", true);
776   if (secondary_value) {
777     if (!secondary_value->VerifyTypeIs(Value::STRING, &err)) {
778       err.PrintToStdout();
779       return false;
780     }
781     build_settings_.SetSecondarySourcePath(
782         SourceDir(secondary_value->string_value()));
783   }
784 
785   // Root build file.
786   const Value* root_value = dotfile_scope_.GetValue("root", true);
787   if (root_value) {
788     if (!root_value->VerifyTypeIs(Value::STRING, &err)) {
789       err.PrintToStdout();
790       return false;
791     }
792 
793     root_target_label = Label::Resolve(current_dir, std::string_view(), Label(),
794                                        *root_value, &err);
795     if (err.has_error()) {
796       err.PrintToStdout();
797       return false;
798     }
799 
800     root_build_file_ = Loader::BuildFileForLabel(root_target_label);
801   }
802   build_settings_.SetRootTargetLabel(root_target_label);
803 
804   // Build config file.
805   const Value* build_config_value =
806       dotfile_scope_.GetValue("buildconfig", true);
807   if (!build_config_value) {
808     Err(Location(), "No build config file.",
809         "Your .gn file (\"" + FilePathToUTF8(dotfile_name_) +
810             "\")\n"
811             "didn't specify a \"buildconfig\" value.")
812         .PrintToStdout();
813     return false;
814   } else if (!build_config_value->VerifyTypeIs(Value::STRING, &err)) {
815     err.PrintToStdout();
816     return false;
817   }
818   build_settings_.set_build_config_file(
819       SourceFile(build_config_value->string_value()));
820 
821   // Targets to check.
822   const Value* check_targets_value =
823       dotfile_scope_.GetValue("check_targets", true);
824   if (check_targets_value) {
825     check_patterns_.reset(new std::vector<LabelPattern>);
826     ExtractListOfLabelPatterns(&build_settings_, *check_targets_value,
827                                current_dir, check_patterns_.get(), &err);
828     if (err.has_error()) {
829       err.PrintToStdout();
830       return false;
831     }
832   }
833 
834   const Value* check_system_includes_value =
835       dotfile_scope_.GetValue("check_system_includes", true);
836   if (check_system_includes_value) {
837     if (!check_system_includes_value->VerifyTypeIs(Value::BOOLEAN, &err)) {
838       err.PrintToStdout();
839       return false;
840     }
841     check_system_includes_ = check_system_includes_value->boolean_value();
842   }
843 
844   // Fill exec_script_whitelist.
845   const Value* exec_script_whitelist_value =
846       dotfile_scope_.GetValue("exec_script_whitelist", true);
847   if (exec_script_whitelist_value) {
848     // Fill the list of targets to check.
849     if (!exec_script_whitelist_value->VerifyTypeIs(Value::LIST, &err)) {
850       err.PrintToStdout();
851       return false;
852     }
853     std::unique_ptr<SourceFileSet> whitelist =
854         std::make_unique<SourceFileSet>();
855     for (const auto& item : exec_script_whitelist_value->list_value()) {
856       if (!item.VerifyTypeIs(Value::STRING, &err)) {
857         err.PrintToStdout();
858         return false;
859       }
860       whitelist->insert(current_dir.ResolveRelativeFile(item, &err));
861       if (err.has_error()) {
862         err.PrintToStdout();
863         return false;
864       }
865     }
866     build_settings_.set_exec_script_whitelist(std::move(whitelist));
867   }
868 
869   // Fill optional default_args.
870   const Value* default_args_value =
871       dotfile_scope_.GetValue("default_args", true);
872   if (default_args_value) {
873     if (!default_args_value->VerifyTypeIs(Value::SCOPE, &err)) {
874       err.PrintToStdout();
875       return false;
876     }
877 
878     default_args_ = default_args_value->scope_value();
879   }
880 
881   const Value* arg_file_template_value =
882       dotfile_scope_.GetValue("arg_file_template", true);
883   if (arg_file_template_value) {
884     if (!arg_file_template_value->VerifyTypeIs(Value::STRING, &err)) {
885       err.PrintToStdout();
886       return false;
887     }
888     SourceFile path(arg_file_template_value->string_value());
889     build_settings_.set_arg_file_template_path(path);
890   }
891 
892   return true;
893 }
894