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 <mutex>
6
7 #include "base/command_line.h"
8 #include "base/strings/string_number_conversions.h"
9 #include "base/strings/stringprintf.h"
10 #include "base/timer/elapsed_timer.h"
11 #include "gn/build_settings.h"
12 #include "gn/commands.h"
13 #include "gn/compile_commands_writer.h"
14 #include "gn/eclipse_writer.h"
15 #include "gn/filesystem_utils.h"
16 #include "gn/json_project_writer.h"
17 #include "gn/ninja_target_writer.h"
18 #include "gn/ninja_tools.h"
19 #include "gn/ninja_writer.h"
20 #include "gn/qt_creator_writer.h"
21 #include "gn/runtime_deps.h"
22 #include "gn/rust_project_writer.h"
23 #include "gn/scheduler.h"
24 #include "gn/setup.h"
25 #include "gn/standard_out.h"
26 #include "gn/switches.h"
27 #include "gn/target.h"
28 #include "gn/visual_studio_writer.h"
29 #include "gn/xcode_writer.h"
30
31 namespace commands {
32
33 namespace {
34
35 const char kSwitchCheck[] = "check";
36 const char kSwitchCleanStale[] = "clean-stale";
37 const char kSwitchFilters[] = "filters";
38 const char kSwitchIde[] = "ide";
39 const char kSwitchIdeValueEclipse[] = "eclipse";
40 const char kSwitchIdeValueQtCreator[] = "qtcreator";
41 const char kSwitchIdeValueVs[] = "vs";
42 const char kSwitchIdeValueVs2013[] = "vs2013";
43 const char kSwitchIdeValueVs2015[] = "vs2015";
44 const char kSwitchIdeValueVs2017[] = "vs2017";
45 const char kSwitchIdeValueVs2019[] = "vs2019";
46 const char kSwitchIdeValueVs2022[] = "vs2022";
47 const char kSwitchIdeValueWinSdk[] = "winsdk";
48 const char kSwitchIdeValueXcode[] = "xcode";
49 const char kSwitchIdeValueJson[] = "json";
50 const char kSwitchIdeRootTarget[] = "ide-root-target";
51 const char kSwitchNinjaExecutable[] = "ninja-executable";
52 const char kSwitchNinjaExtraArgs[] = "ninja-extra-args";
53 const char kSwitchNoDeps[] = "no-deps";
54 const char kSwitchSln[] = "sln";
55 const char kSwitchXcodeProject[] = "xcode-project";
56 const char kSwitchXcodeBuildSystem[] = "xcode-build-system";
57 const char kSwitchXcodeBuildsystemValueLegacy[] = "legacy";
58 const char kSwitchXcodeBuildsystemValueNew[] = "new";
59 const char kSwitchJsonFileName[] = "json-file-name";
60 const char kSwitchJsonIdeScript[] = "json-ide-script";
61 const char kSwitchJsonIdeScriptArgs[] = "json-ide-script-args";
62 const char kSwitchExportCompileCommands[] = "export-compile-commands";
63 const char kSwitchExportRustProject[] = "export-rust-project";
64
65 // Collects Ninja rules for each toolchain. The lock protectes the rules.
66 struct TargetWriteInfo {
67 std::mutex lock;
68 NinjaWriter::PerToolchainRules rules;
69 };
70
71 // Called on worker thread to write the ninja file.
BackgroundDoWrite(TargetWriteInfo * write_info,const Target * target)72 void BackgroundDoWrite(TargetWriteInfo* write_info, const Target* target) {
73 std::string rule = NinjaTargetWriter::RunAndWriteFile(target);
74 DCHECK(!rule.empty());
75
76 {
77 std::lock_guard<std::mutex> lock(write_info->lock);
78 write_info->rules[target->toolchain()].emplace_back(target,
79 std::move(rule));
80 }
81 }
82
83 // Called on the main thread.
ItemResolvedAndGeneratedCallback(TargetWriteInfo * write_info,const BuilderRecord * record)84 void ItemResolvedAndGeneratedCallback(TargetWriteInfo* write_info,
85 const BuilderRecord* record) {
86 const Item* item = record->item();
87 const Target* target = item->AsTarget();
88 if (target) {
89 g_scheduler->ScheduleWork(
90 [write_info, target]() { BackgroundDoWrite(write_info, target); });
91 }
92 }
93
94 // Returns a pointer to the target with the given file as an output, or null
95 // if no targets generate the file. This is brute force since this is an
96 // error condition and performance shouldn't matter.
FindTargetThatGeneratesFile(const Builder & builder,const SourceFile & file)97 const Target* FindTargetThatGeneratesFile(const Builder& builder,
98 const SourceFile& file) {
99 std::vector<const Target*> targets = builder.GetAllResolvedTargets();
100 if (targets.empty())
101 return nullptr;
102
103 OutputFile output_file(targets[0]->settings()->build_settings(), file);
104 for (const Target* target : targets) {
105 for (const auto& cur_output : target->computed_outputs()) {
106 if (cur_output == output_file)
107 return target;
108 }
109 }
110 return nullptr;
111 }
112
113 // Prints an error that the given file was present as a source or input in
114 // the given target(s) but was not generated by any of its dependencies.
PrintInvalidGeneratedInput(const Builder & builder,const SourceFile & file,const std::vector<const Target * > & targets)115 void PrintInvalidGeneratedInput(const Builder& builder,
116 const SourceFile& file,
117 const std::vector<const Target*>& targets) {
118 std::string err;
119
120 // Only show the toolchain labels (which can be confusing) if something
121 // isn't the default.
122 bool show_toolchains = false;
123 const Label& default_toolchain =
124 targets[0]->settings()->default_toolchain_label();
125 for (const Target* target : targets) {
126 if (target->settings()->toolchain_label() != default_toolchain) {
127 show_toolchains = true;
128 break;
129 }
130 }
131
132 const Target* generator = FindTargetThatGeneratesFile(builder, file);
133 if (generator &&
134 generator->settings()->toolchain_label() != default_toolchain)
135 show_toolchains = true;
136
137 const std::string target_str = targets.size() > 1 ? "targets" : "target";
138 err += "The file:\n";
139 err += " " + file.value() + "\n";
140 err += "is listed as an input or source for the " + target_str + ":\n";
141 for (const Target* target : targets)
142 err += " " + target->label().GetUserVisibleName(show_toolchains) + "\n";
143
144 if (generator) {
145 err += "but this file was not generated by any dependencies of the " +
146 target_str + ". The target\nthat generates the file is:\n ";
147 err += generator->label().GetUserVisibleName(show_toolchains);
148 } else {
149 err += "but no targets in the build generate that file.";
150 }
151
152 Err(Location(), "Input to " + target_str + " not generated by a dependency.",
153 err)
154 .PrintToStdout();
155 }
156
CheckForInvalidGeneratedInputs(Setup * setup)157 bool CheckForInvalidGeneratedInputs(Setup* setup) {
158 std::multimap<SourceFile, const Target*> unknown_inputs =
159 g_scheduler->GetUnknownGeneratedInputs();
160 if (unknown_inputs.empty())
161 return true; // No bad files.
162
163 int errors_found = 0;
164 auto cur = unknown_inputs.begin();
165 while (cur != unknown_inputs.end()) {
166 errors_found++;
167 auto end_of_range = unknown_inputs.upper_bound(cur->first);
168
169 // Package the values more conveniently for printing.
170 SourceFile bad_input = cur->first;
171 std::vector<const Target*> targets;
172 while (cur != end_of_range)
173 targets.push_back((cur++)->second);
174
175 PrintInvalidGeneratedInput(setup->builder(), bad_input, targets);
176 OutputString("\n");
177 }
178
179 OutputString(
180 "If you have generated inputs, there needs to be a dependency path "
181 "between the\ntwo targets in addition to just listing the files. For "
182 "indirect dependencies,\nthe intermediate ones must be public_deps. "
183 "data_deps don't count since they're\nonly runtime dependencies. If "
184 "you think a dependency chain exists, it might be\nbecause the chain "
185 "is private. Try \"gn path\" to analyze.\n");
186
187 if (errors_found > 1) {
188 OutputString(base::StringPrintf("\n%d generated input errors found.\n",
189 errors_found),
190 DECORATION_YELLOW);
191 }
192 return false;
193 }
194
RunIdeWriter(const std::string & ide,const BuildSettings * build_settings,const Builder & builder,Err * err)195 bool RunIdeWriter(const std::string& ide,
196 const BuildSettings* build_settings,
197 const Builder& builder,
198 Err* err) {
199 const base::CommandLine* command_line =
200 base::CommandLine::ForCurrentProcess();
201 bool quiet = command_line->HasSwitch(switches::kQuiet);
202 base::ElapsedTimer timer;
203
204 if (ide == kSwitchIdeValueEclipse) {
205 bool res = EclipseWriter::RunAndWriteFile(build_settings, builder, err);
206 if (res && !quiet) {
207 OutputString("Generating Eclipse settings took " +
208 base::Int64ToString(timer.Elapsed().InMilliseconds()) +
209 "ms\n");
210 }
211 return res;
212 } else if (ide == kSwitchIdeValueVs || ide == kSwitchIdeValueVs2013 ||
213 ide == kSwitchIdeValueVs2015 || ide == kSwitchIdeValueVs2017 ||
214 ide == kSwitchIdeValueVs2019 || ide == kSwitchIdeValueVs2022) {
215 VisualStudioWriter::Version version = VisualStudioWriter::Version::Vs2019;
216 if (ide == kSwitchIdeValueVs2013)
217 version = VisualStudioWriter::Version::Vs2013;
218 else if (ide == kSwitchIdeValueVs2015)
219 version = VisualStudioWriter::Version::Vs2015;
220 else if (ide == kSwitchIdeValueVs2017)
221 version = VisualStudioWriter::Version::Vs2017;
222 else if (ide == kSwitchIdeValueVs2022)
223 version = VisualStudioWriter::Version::Vs2022;
224
225 std::string sln_name;
226 if (command_line->HasSwitch(kSwitchSln))
227 sln_name = command_line->GetSwitchValueASCII(kSwitchSln);
228 std::string filters;
229 if (command_line->HasSwitch(kSwitchFilters))
230 filters = command_line->GetSwitchValueASCII(kSwitchFilters);
231 std::string win_kit;
232 if (command_line->HasSwitch(kSwitchIdeValueWinSdk))
233 win_kit = command_line->GetSwitchValueASCII(kSwitchIdeValueWinSdk);
234 std::string ninja_extra_args;
235 if (command_line->HasSwitch(kSwitchNinjaExtraArgs)) {
236 ninja_extra_args =
237 command_line->GetSwitchValueASCII(kSwitchNinjaExtraArgs);
238 }
239 std::string ninja_executable;
240 if (command_line->HasSwitch(kSwitchNinjaExecutable)) {
241 ninja_executable =
242 command_line->GetSwitchValueASCII(kSwitchNinjaExecutable);
243 }
244 bool no_deps = command_line->HasSwitch(kSwitchNoDeps);
245 bool res = VisualStudioWriter::RunAndWriteFiles(
246 build_settings, builder, version, sln_name, filters, win_kit,
247 ninja_extra_args, ninja_executable, no_deps, err);
248 if (res && !quiet) {
249 OutputString("Generating Visual Studio projects took " +
250 base::Int64ToString(timer.Elapsed().InMilliseconds()) +
251 "ms\n");
252 }
253 return res;
254 } else if (ide == kSwitchIdeValueXcode) {
255 XcodeWriter::Options options = {
256 command_line->GetSwitchValueASCII(kSwitchXcodeProject),
257 command_line->GetSwitchValueASCII(kSwitchIdeRootTarget),
258 command_line->GetSwitchValueASCII(kSwitchNinjaExecutable),
259 command_line->GetSwitchValueASCII(kSwitchFilters),
260 XcodeBuildSystem::kLegacy,
261 };
262
263 if (options.project_name.empty()) {
264 options.project_name = "all";
265 }
266
267 const std::string build_system =
268 command_line->GetSwitchValueASCII(kSwitchXcodeBuildSystem);
269 if (!build_system.empty()) {
270 if (build_system == kSwitchXcodeBuildsystemValueNew) {
271 options.build_system = XcodeBuildSystem::kNew;
272 } else if (build_system == kSwitchXcodeBuildsystemValueLegacy) {
273 options.build_system = XcodeBuildSystem::kLegacy;
274 } else {
275 *err = Err(Location(), "Unknown build system: " + build_system);
276 return false;
277 }
278 }
279
280 bool res =
281 XcodeWriter::RunAndWriteFiles(build_settings, builder, options, err);
282 if (res && !quiet) {
283 OutputString("Generating Xcode projects took " +
284 base::Int64ToString(timer.Elapsed().InMilliseconds()) +
285 "ms\n");
286 }
287 return res;
288 } else if (ide == kSwitchIdeValueQtCreator) {
289 std::string root_target;
290 if (command_line->HasSwitch(kSwitchIdeRootTarget))
291 root_target = command_line->GetSwitchValueASCII(kSwitchIdeRootTarget);
292 bool res = QtCreatorWriter::RunAndWriteFile(build_settings, builder, err,
293 root_target);
294 if (res && !quiet) {
295 OutputString("Generating QtCreator projects took " +
296 base::Int64ToString(timer.Elapsed().InMilliseconds()) +
297 "ms\n");
298 }
299 return res;
300 } else if (ide == kSwitchIdeValueJson) {
301 std::string file_name =
302 command_line->GetSwitchValueASCII(kSwitchJsonFileName);
303 if (file_name.empty())
304 file_name = "project.json";
305 std::string exec_script =
306 command_line->GetSwitchValueASCII(kSwitchJsonIdeScript);
307 std::string exec_script_extra_args =
308 command_line->GetSwitchValueASCII(kSwitchJsonIdeScriptArgs);
309 std::string filters = command_line->GetSwitchValueASCII(kSwitchFilters);
310
311 bool res = JSONProjectWriter::RunAndWriteFiles(
312 build_settings, builder, file_name, exec_script, exec_script_extra_args,
313 filters, quiet, err);
314 if (res && !quiet) {
315 OutputString("Generating JSON projects took " +
316 base::Int64ToString(timer.Elapsed().InMilliseconds()) +
317 "ms\n");
318 }
319 return res;
320 }
321
322 *err = Err(Location(), "Unknown IDE: " + ide);
323 return false;
324 }
325
RunRustProjectWriter(const BuildSettings * build_settings,const Builder & builder,Err * err)326 bool RunRustProjectWriter(const BuildSettings* build_settings,
327 const Builder& builder,
328 Err* err) {
329 const base::CommandLine* command_line =
330 base::CommandLine::ForCurrentProcess();
331 bool quiet = command_line->HasSwitch(switches::kQuiet);
332 base::ElapsedTimer timer;
333
334 std::string file_name = "rust-project.json";
335 bool res = RustProjectWriter::RunAndWriteFiles(build_settings, builder,
336 file_name, quiet, err);
337 if (res && !quiet) {
338 OutputString("Generating rust-project.json took " +
339 base::Int64ToString(timer.Elapsed().InMilliseconds()) +
340 "ms\n");
341 }
342 return res;
343 }
344
RunCompileCommandsWriter(const BuildSettings * build_settings,const Builder & builder,Err * err)345 bool RunCompileCommandsWriter(const BuildSettings* build_settings,
346 const Builder& builder,
347 Err* err) {
348 const base::CommandLine* command_line =
349 base::CommandLine::ForCurrentProcess();
350 bool quiet = command_line->HasSwitch(switches::kQuiet);
351 base::ElapsedTimer timer;
352
353 std::string file_name = "compile_commands.json";
354 std::string target_filters =
355 command_line->GetSwitchValueASCII(kSwitchExportCompileCommands);
356
357 bool res = CompileCommandsWriter::RunAndWriteFiles(
358 build_settings, builder, file_name, target_filters, quiet, err);
359 if (res && !quiet) {
360 OutputString("Generating compile_commands took " +
361 base::Int64ToString(timer.Elapsed().InMilliseconds()) +
362 "ms\n");
363 }
364 return res;
365 }
366
RunNinjaPostProcessTools(const BuildSettings * build_settings,base::FilePath ninja_executable,bool is_regeneration,bool clean_stale,Err * err)367 bool RunNinjaPostProcessTools(const BuildSettings* build_settings,
368 base::FilePath ninja_executable,
369 bool is_regeneration,
370 bool clean_stale,
371 Err* err) {
372 // If the user did not specify an executable, skip running the post processing
373 // tools. Since these tools can re-write ninja build log and dep logs, it is
374 // really important that ninja executable used for tools matches the
375 // executable that is used for builds.
376 if (ninja_executable.empty()) {
377 if (clean_stale) {
378 *err = Err(Location(), "No --ninja-executable provided.",
379 "--clean-stale requires a ninja executable to run. You can "
380 "provide one on the command line via --ninja-executable.");
381 return false;
382 }
383
384 return true;
385 }
386
387 base::FilePath build_dir =
388 build_settings->GetFullPath(build_settings->build_dir());
389
390 if (clean_stale) {
391 if (build_settings->ninja_required_version() < Version{1, 10, 0}) {
392 *err = Err(Location(), "Need a ninja executable at least version 1.10.0.",
393 "--clean-stale requires a ninja executable of version 1.10.0 "
394 "or later.");
395 return false;
396 }
397
398 if (!InvokeNinjaCleanDeadTool(ninja_executable, build_dir, err)) {
399 return false;
400 }
401
402 if(!InvokeNinjaRecompactTool(ninja_executable, build_dir, err)) {
403 return false;
404 }
405 }
406
407 // If we have a ninja version that supports restat, we should restat the
408 // build.ninja file so the next ninja invocation will use the right mtime. If
409 // gen is being invoked as part of a re-gen (ie, ninja is invoking gn gen),
410 // then we can elide this restat, as ninja will restat build.ninja anyways
411 // after it is complete.
412 if (!is_regeneration &&
413 build_settings->ninja_required_version() >= Version{1, 10, 0}) {
414 std::vector<base::FilePath> files_to_restat{
415 base::FilePath(FILE_PATH_LITERAL("build.ninja"))};
416 if (!InvokeNinjaRestatTool(ninja_executable, build_dir, files_to_restat,
417 err)) {
418 return false;
419 }
420 }
421 return true;
422 }
423
424 } // namespace
425
426 const char kGen[] = "gen";
427 const char kGen_HelpShort[] = "gen: Generate ninja files.";
428 const char kGen_Help[] =
429 R"(gn gen [--check] [<ide options>] <out_dir>
430
431 Generates ninja files from the current tree and puts them in the given output
432 directory.
433
434 The output directory can be a source-repo-absolute path name such as:
435 //out/foo
436 Or it can be a directory relative to the current directory such as:
437 out/foo
438
439 "gn gen --check" is the same as running "gn check". "gn gen --check=system" is
440 the same as running "gn check --check-system". See "gn help check" for
441 documentation on that mode.
442
443 See "gn help switches" for the common command-line switches.
444
445 General options
446
447 --ninja-executable=<string>
448 Can be used to specify the ninja executable to use. This executable will
449 be used as an IDE option to indicate which ninja to use for building. This
450 executable will also be used as part of the gen process for triggering a
451 restat on generated ninja files and for use with --clean-stale.
452
453 --clean-stale
454 This option will cause no longer needed output files to be removed from
455 the build directory, and their records pruned from the ninja build log and
456 dependency database after the ninja build graph has been generated. This
457 option requires a ninja executable of at least version 1.10.0. It can be
458 provided by the --ninja-executable switch. Also see "gn help clean_stale".
459
460 IDE options
461
462 GN optionally generates files for IDE. Files won't be overwritten if their
463 contents don't change. Possibilities for <ide options>
464
465 --ide=<ide_name>
466 Generate files for an IDE. Currently supported values:
467 "eclipse" - Eclipse CDT settings file.
468 "vs" - Visual Studio project/solution files.
469 (default Visual Studio version: 2019)
470 "vs2013" - Visual Studio 2013 project/solution files.
471 "vs2015" - Visual Studio 2015 project/solution files.
472 "vs2017" - Visual Studio 2017 project/solution files.
473 "vs2019" - Visual Studio 2019 project/solution files.
474 "vs2022" - Visual Studio 2022 project/solution files.
475 "xcode" - Xcode workspace/solution files.
476 "qtcreator" - QtCreator project files.
477 "json" - JSON file containing target information
478
479 --filters=<path_prefixes>
480 Semicolon-separated list of label patterns used to limit the set of
481 generated projects (see "gn help label_pattern"). Only matching targets
482 and their dependencies will be included in the solution. Only used for
483 Visual Studio, Xcode and JSON.
484
485 Visual Studio Flags
486
487 --sln=<file_name>
488 Override default sln file name ("all"). Solution file is written to the
489 root build directory.
490
491 --no-deps
492 Don't include targets dependencies to the solution. Changes the way how
493 --filters option works. Only directly matching targets are included.
494
495 --winsdk=<sdk_version>
496 Use the specified Windows 10 SDK version to generate project files.
497 As an example, "10.0.15063.0" can be specified to use Creators Update SDK
498 instead of the default one.
499
500 --ninja-executable=<string>
501 Can be used to specify the ninja executable to use when building.
502
503 --ninja-extra-args=<string>
504 This string is passed without any quoting to the ninja invocation
505 command-line. Can be used to configure ninja flags, like "-j".
506
507 Xcode Flags
508
509 --xcode-project=<file_name>
510 Override default Xcode project file name ("all"). The project file is
511 written to the root build directory.
512
513 --xcode-build-system=<value>
514 Configure the build system to use for the Xcode project. Supported
515 values are (default to "legacy"):
516 "legacy" - Legacy Build system
517 "new" - New Build System
518
519 --ninja-executable=<string>
520 Can be used to specify the ninja executable to use when building.
521
522 --ninja-extra-args=<string>
523 This string is passed without any quoting to the ninja invocation
524 command-line. Can be used to configure ninja flags, like "-j".
525
526 --ide-root-target=<target_name>
527 Name of the target corresponding to "All" target in Xcode. If unset,
528 "All" invokes ninja without any target and builds everything.
529
530 QtCreator Flags
531
532 --ide-root-target=<target_name>
533 Name of the root target for which the QtCreator project will be generated
534 to contain files of it and its dependencies. If unset, the whole build
535 graph will be emitted.
536
537
538 Eclipse IDE Support
539
540 GN DOES NOT generate Eclipse CDT projects. Instead, it generates a settings
541 file which can be imported into an Eclipse CDT project. The XML file contains
542 a list of include paths and defines. Because GN does not generate a full
543 .cproject definition, it is not possible to properly define includes/defines
544 for each file individually. Instead, one set of includes/defines is generated
545 for the entire project. This works fairly well but may still result in a few
546 indexer issues here and there.
547
548 Generic JSON Output
549
550 Dumps target information to a JSON file and optionally invokes a
551 python script on the generated file. See the comments at the beginning
552 of json_project_writer.cc and desc_builder.cc for an overview of the JSON
553 file format.
554
555 --json-file-name=<json_file_name>
556 Overrides default file name (project.json) of generated JSON file.
557
558 --json-ide-script=<path_to_python_script>
559 Executes python script after the JSON file is generated or updated with
560 new content. Path can be project absolute (//), system absolute (/) or
561 relative, in which case the output directory will be base. Path to
562 generated JSON file will be first argument when invoking script.
563
564 --json-ide-script-args=<argument>
565 Optional second argument that will passed to executed script.
566
567 Compilation Database
568
569 --export-rust-project
570 Produces a rust-project.json file in the root of the build directory
571 This is used for various tools in the Rust ecosystem allowing for the
572 replay of individual compilations independent of the build system.
573 This is an unstable format and likely to change without warning.
574
575 --export-compile-commands[=<target_name1,target_name2...>]
576 Produces a compile_commands.json file in the root of the build directory
577 containing an array of “command objects”, where each command object
578 specifies one way a translation unit is compiled in the project. If a list
579 of target_name is supplied, only targets that are reachable from any
580 target in any build file whose name is target_name will be used for
581 “command objects” generation, otherwise all available targets will be used.
582 This is used for various Clang-based tooling, allowing for the replay of
583 individual compilations independent of the build system.
584 e.g. "foo" will match:
585 - "//path/to/src:foo"
586 - "//other/path:foo"
587 - "//foo:foo"
588 and not match:
589 - "//foo:bar"
590 )";
591
RunGen(const std::vector<std::string> & args)592 int RunGen(const std::vector<std::string>& args) {
593 base::ElapsedTimer timer;
594
595 if (args.size() != 1) {
596 Err(Location(), "Need exactly one build directory to generate.",
597 "I expected something more like \"gn gen out/foo\"\n"
598 "You can also see \"gn help gen\".")
599 .PrintToStdout();
600 return 1;
601 }
602
603 // Deliberately leaked to avoid expensive process teardown.
604 Setup* setup = new Setup();
605 // Generate an empty args.gn file if it does not exists
606 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kArgs)) {
607 setup->set_gen_empty_args(true);
608 }
609 if (!setup->DoSetup(args[0], true))
610 return 1;
611
612 const base::CommandLine* command_line =
613 base::CommandLine::ForCurrentProcess();
614 if (command_line->HasSwitch(kSwitchCheck)) {
615 setup->set_check_public_headers(true);
616 if (command_line->GetSwitchValueASCII(kSwitchCheck) == "system")
617 setup->set_check_system_includes(true);
618 }
619
620 // Cause the load to also generate the ninja files for each target.
621 TargetWriteInfo write_info;
622 setup->builder().set_resolved_and_generated_callback(
__anonc8c83aaf0302(const BuilderRecord* record) 623 [&write_info](const BuilderRecord* record) {
624 ItemResolvedAndGeneratedCallback(&write_info, record);
625 });
626
627 // Do the actual load. This will also write out the target ninja files.
628 if (!setup->Run())
629 return 1;
630
631 // Sort the targets in each toolchain according to their label. This makes
632 // the ninja files have deterministic content.
633 for (auto& cur_toolchain : write_info.rules) {
634 std::sort(cur_toolchain.second.begin(), cur_toolchain.second.end(),
635 [](const NinjaWriter::TargetRulePair& a,
__anonc8c83aaf0402(const NinjaWriter::TargetRulePair& a, const NinjaWriter::TargetRulePair& b) 636 const NinjaWriter::TargetRulePair& b) {
637 return a.first->label() < b.first->label();
638 });
639 }
640
641 Err err;
642 // Write the root ninja files.
643 if (!NinjaWriter::RunAndWriteFiles(&setup->build_settings(), setup->builder(),
644 write_info.rules, &err)) {
645 err.PrintToStdout();
646 return 1;
647 }
648
649 if (!RunNinjaPostProcessTools(
650 &setup->build_settings(),
651 command_line->GetSwitchValuePath(switches::kNinjaExecutable),
652 command_line->HasSwitch(switches::kRegeneration),
653 command_line->HasSwitch(kSwitchCleanStale), &err)) {
654 err.PrintToStdout();
655 return 1;
656 }
657
658 if (!WriteRuntimeDepsFilesIfNecessary(&setup->build_settings(),
659 setup->builder(), &err)) {
660 err.PrintToStdout();
661 return 1;
662 }
663
664 if (!CheckForInvalidGeneratedInputs(setup))
665 return 1;
666
667 if (command_line->HasSwitch(kSwitchIde) &&
668 !RunIdeWriter(command_line->GetSwitchValueASCII(kSwitchIde),
669 &setup->build_settings(), setup->builder(), &err)) {
670 err.PrintToStdout();
671 return 1;
672 }
673
674 if (command_line->HasSwitch(kSwitchExportCompileCommands) &&
675 !RunCompileCommandsWriter(&setup->build_settings(), setup->builder(),
676 &err)) {
677 err.PrintToStdout();
678 return 1;
679 }
680
681 if (command_line->HasSwitch(kSwitchExportRustProject) &&
682 !RunRustProjectWriter(&setup->build_settings(), setup->builder(), &err)) {
683 err.PrintToStdout();
684 return 1;
685 }
686
687 TickDelta elapsed_time = timer.Elapsed();
688
689 if (!command_line->HasSwitch(switches::kQuiet)) {
690 OutputString("Done. ", DECORATION_GREEN);
691
692 size_t targets_collected = 0;
693 for (const auto& rules : write_info.rules)
694 targets_collected += rules.second.size();
695
696 std::string stats =
697 "Made " + base::NumberToString(targets_collected) + " targets from " +
698 base::IntToString(
699 setup->scheduler().input_file_manager()->GetInputFileCount()) +
700 " files in " + base::Int64ToString(elapsed_time.InMilliseconds()) +
701 "ms\n";
702 OutputString(stats);
703 }
704
705 return 0;
706 }
707
708 } // namespace commands
709