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