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