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