• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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