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