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