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