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