• 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 <inttypes.h>
6 
7 #include <mutex>
8 #include <thread>
9 #include <unordered_map>
10 
11 #include "base/command_line.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/stringprintf.h"
14 #include "base/timer/elapsed_timer.h"
15 #include "gn/build_settings.h"
16 #include "gn/commands.h"
17 #include "gn/compile_commands_writer.h"
18 #include "gn/eclipse_writer.h"
19 #include "gn/filesystem_utils.h"
20 #include "gn/json_project_writer.h"
21 #include "gn/label_pattern.h"
22 #include "gn/ninja_outputs_writer.h"
23 #include "gn/ninja_target_writer.h"
24 #include "gn/ninja_tools.h"
25 #include "gn/ninja_writer.h"
26 #include "gn/qt_creator_writer.h"
27 #include "gn/runtime_deps.h"
28 #include "gn/rust_project_writer.h"
29 #include "gn/scheduler.h"
30 #include "gn/setup.h"
31 #include "gn/standard_out.h"
32 #include "gn/switches.h"
33 #include "gn/target.h"
34 #include "gn/visual_studio_writer.h"
35 #include "gn/xcode_writer.h"
36 
37 namespace commands {
38 
39 namespace {
40 
41 const char kSwitchCheck[] = "check";
42 const char kSwitchCleanStale[] = "clean-stale";
43 const char kSwitchFilters[] = "filters";
44 const char kSwitchIde[] = "ide";
45 const char kSwitchIdeValueEclipse[] = "eclipse";
46 const char kSwitchIdeValueQtCreator[] = "qtcreator";
47 const char kSwitchIdeValueVs[] = "vs";
48 const char kSwitchIdeValueVs2013[] = "vs2013";
49 const char kSwitchIdeValueVs2015[] = "vs2015";
50 const char kSwitchIdeValueVs2017[] = "vs2017";
51 const char kSwitchIdeValueVs2019[] = "vs2019";
52 const char kSwitchIdeValueVs2022[] = "vs2022";
53 const char kSwitchIdeValueWinSdk[] = "winsdk";
54 const char kSwitchIdeValueXcode[] = "xcode";
55 const char kSwitchIdeValueJson[] = "json";
56 const char kSwitchIdeRootTarget[] = "ide-root-target";
57 const char kSwitchNinjaExecutable[] = "ninja-executable";
58 const char kSwitchNinjaExtraArgs[] = "ninja-extra-args";
59 const char kSwitchNinjaOutputsFile[] = "ninja-outputs-file";
60 const char kSwitchNinjaOutputsScript[] = "ninja-outputs-script";
61 const char kSwitchNinjaOutputsScriptArgs[] = "ninja-outputs-script-args";
62 const char kSwitchNoDeps[] = "no-deps";
63 const char kSwitchSln[] = "sln";
64 const char kSwitchXcodeProject[] = "xcode-project";
65 const char kSwitchXcodeBuildSystem[] = "xcode-build-system";
66 const char kSwitchXcodeBuildsystemValueLegacy[] = "legacy";
67 const char kSwitchXcodeBuildsystemValueNew[] = "new";
68 const char kSwitchXcodeConfigurations[] = "xcode-configs";
69 const char kSwitchXcodeConfigurationBuildPath[] = "xcode-config-build-dir";
70 const char kSwitchXcodeAdditionalFilesPatterns[] =
71     "xcode-additional-files-patterns";
72 const char kSwitchXcodeAdditionalFilesRoots[] = "xcode-additional-files-roots";
73 const char kSwitchJsonFileName[] = "json-file-name";
74 const char kSwitchJsonIdeScript[] = "json-ide-script";
75 const char kSwitchJsonIdeScriptArgs[] = "json-ide-script-args";
76 const char kSwitchExportCompileCommands[] = "export-compile-commands";
77 const char kSwitchExportRustProject[] = "export-rust-project";
78 
79 // A map type used to implement --ide=ninja_outputs
80 using NinjaOutputsMap = NinjaOutputsWriter::MapType;
81 
82 // Collects Ninja rules for each toolchain. The lock protects the rules
83 struct TargetWriteInfo {
84   // Set this to true to populate |ninja_outputs_map| below.
85   bool want_ninja_outputs = false;
86 
87   std::mutex lock;
88   NinjaWriter::PerToolchainRules rules;
89 
90   NinjaOutputsMap ninja_outputs_map;
91 
92   using ResolvedMap = std::unordered_map<std::thread::id, ResolvedTargetData>;
93   std::unique_ptr<ResolvedMap> resolved_map = std::make_unique<ResolvedMap>();
94 
LeakOnPurposecommands::__anon55f5242d0111::TargetWriteInfo95   void LeakOnPurpose() { (void)resolved_map.release(); }
96 };
97 
98 // Called on worker thread to write the ninja file.
BackgroundDoWrite(TargetWriteInfo * write_info,const Target * target)99 void BackgroundDoWrite(TargetWriteInfo* write_info, const Target* target) {
100   ResolvedTargetData* resolved;
101   std::vector<OutputFile> target_ninja_outputs;
102   std::vector<OutputFile>* ninja_outputs =
103       write_info->want_ninja_outputs ? &target_ninja_outputs : nullptr;
104 
105   {
106     std::lock_guard<std::mutex> lock(write_info->lock);
107     resolved = &((*write_info->resolved_map)[std::this_thread::get_id()]);
108   }
109   std::string rule =
110       NinjaTargetWriter::RunAndWriteFile(target, resolved, ninja_outputs);
111 
112   DCHECK(!rule.empty());
113 
114   {
115     std::lock_guard<std::mutex> lock(write_info->lock);
116     write_info->rules[target->toolchain()].emplace_back(target,
117                                                         std::move(rule));
118 
119     if (write_info->want_ninja_outputs) {
120       write_info->ninja_outputs_map.emplace(target,
121                                             std::move(target_ninja_outputs));
122     }
123   }
124 }
125 
126 // Called on the main thread.
ItemResolvedAndGeneratedCallback(TargetWriteInfo * write_info,const BuilderRecord * record)127 void ItemResolvedAndGeneratedCallback(TargetWriteInfo* write_info,
128                                       const BuilderRecord* record) {
129   const Item* item = record->item();
130   const Target* target = item->AsTarget();
131   if (target) {
132     g_scheduler->ScheduleWork(
133         [write_info, target]() { BackgroundDoWrite(write_info, target); });
134   }
135 }
136 
137 // Returns a pointer to the target with the given file as an output, or null
138 // if no targets generate the file. This is brute force since this is an
139 // error condition and performance shouldn't matter.
FindTargetThatGeneratesFile(const Builder & builder,const SourceFile & file)140 const Target* FindTargetThatGeneratesFile(const Builder& builder,
141                                           const SourceFile& file) {
142   std::vector<const Target*> targets = builder.GetAllResolvedTargets();
143   if (targets.empty())
144     return nullptr;
145 
146   OutputFile output_file(targets[0]->settings()->build_settings(), file);
147   for (const Target* target : targets) {
148     for (const auto& cur_output : target->computed_outputs()) {
149       if (cur_output == output_file)
150         return target;
151     }
152   }
153   return nullptr;
154 }
155 
156 // Prints an error that the given file was present as a source or input in
157 // 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)158 void PrintInvalidGeneratedInput(const Builder& builder,
159                                 const SourceFile& file,
160                                 const std::vector<const Target*>& targets) {
161   std::string err;
162 
163   // Only show the toolchain labels (which can be confusing) if something
164   // isn't the default.
165   bool show_toolchains = false;
166   const Label& default_toolchain =
167       targets[0]->settings()->default_toolchain_label();
168   for (const Target* target : targets) {
169     if (target->settings()->toolchain_label() != default_toolchain) {
170       show_toolchains = true;
171       break;
172     }
173   }
174 
175   const Target* generator = FindTargetThatGeneratesFile(builder, file);
176   if (generator &&
177       generator->settings()->toolchain_label() != default_toolchain)
178     show_toolchains = true;
179 
180   const std::string target_str = targets.size() > 1 ? "targets" : "target";
181   err += "The file:\n";
182   err += "  " + file.value() + "\n";
183   err += "is listed as an input or source for the " + target_str + ":\n";
184   for (const Target* target : targets)
185     err += "  " + target->label().GetUserVisibleName(show_toolchains) + "\n";
186 
187   if (generator) {
188     err += "but this file was not generated by any dependencies of the " +
189            target_str + ". The target\nthat generates the file is:\n  ";
190     err += generator->label().GetUserVisibleName(show_toolchains);
191   } else {
192     err += "but no targets in the build generate that file.";
193   }
194 
195   Err(Location(), "Input to " + target_str + " not generated by a dependency.",
196       err)
197       .PrintToStdout();
198 }
199 
CheckForInvalidGeneratedInputs(Setup * setup)200 bool CheckForInvalidGeneratedInputs(Setup* setup) {
201   std::multimap<SourceFile, const Target*> unknown_inputs =
202       g_scheduler->GetUnknownGeneratedInputs();
203   if (unknown_inputs.empty())
204     return true;  // No bad files.
205 
206   int errors_found = 0;
207   auto cur = unknown_inputs.begin();
208   while (cur != unknown_inputs.end()) {
209     errors_found++;
210     auto end_of_range = unknown_inputs.upper_bound(cur->first);
211 
212     // Package the values more conveniently for printing.
213     SourceFile bad_input = cur->first;
214     std::vector<const Target*> targets;
215     while (cur != end_of_range)
216       targets.push_back((cur++)->second);
217 
218     PrintInvalidGeneratedInput(setup->builder(), bad_input, targets);
219     OutputString("\n");
220   }
221 
222   OutputString(
223       "If you have generated inputs, there needs to be a dependency path "
224       "between the\ntwo targets in addition to just listing the files. For "
225       "indirect dependencies,\nthe intermediate ones must be public_deps. "
226       "data_deps don't count since they're\nonly runtime dependencies. If "
227       "you think a dependency chain exists, it might be\nbecause the chain "
228       "is private. Try \"gn path\" to analyze.\n");
229 
230   if (errors_found > 1) {
231     OutputString(base::StringPrintf("\n%d generated input errors found.\n",
232                                     errors_found),
233                  DECORATION_YELLOW);
234   }
235   return false;
236 }
237 
RunIdeWriter(const std::string & ide,const BuildSettings * build_settings,const Builder & builder,Err * err)238 bool RunIdeWriter(const std::string& ide,
239                   const BuildSettings* build_settings,
240                   const Builder& builder,
241                   Err* err) {
242   const base::CommandLine* command_line =
243       base::CommandLine::ForCurrentProcess();
244   bool quiet = command_line->HasSwitch(switches::kQuiet);
245   base::ElapsedTimer timer;
246 
247   if (ide == kSwitchIdeValueEclipse) {
248     bool res = EclipseWriter::RunAndWriteFile(build_settings, builder, err);
249     if (res && !quiet) {
250       OutputString("Generating Eclipse settings took " +
251                    base::Int64ToString(timer.Elapsed().InMilliseconds()) +
252                    "ms\n");
253     }
254     return res;
255   } else if (ide == kSwitchIdeValueVs || ide == kSwitchIdeValueVs2013 ||
256              ide == kSwitchIdeValueVs2015 || ide == kSwitchIdeValueVs2017 ||
257              ide == kSwitchIdeValueVs2019 || ide == kSwitchIdeValueVs2022) {
258     VisualStudioWriter::Version version = VisualStudioWriter::Version::Vs2019;
259     if (ide == kSwitchIdeValueVs2013)
260       version = VisualStudioWriter::Version::Vs2013;
261     else if (ide == kSwitchIdeValueVs2015)
262       version = VisualStudioWriter::Version::Vs2015;
263     else if (ide == kSwitchIdeValueVs2017)
264       version = VisualStudioWriter::Version::Vs2017;
265     else if (ide == kSwitchIdeValueVs2022)
266       version = VisualStudioWriter::Version::Vs2022;
267 
268     std::string sln_name;
269     if (command_line->HasSwitch(kSwitchSln))
270       sln_name = command_line->GetSwitchValueString(kSwitchSln);
271     std::string filters;
272     if (command_line->HasSwitch(kSwitchFilters))
273       filters = command_line->GetSwitchValueString(kSwitchFilters);
274     std::string win_kit;
275     if (command_line->HasSwitch(kSwitchIdeValueWinSdk))
276       win_kit = command_line->GetSwitchValueString(kSwitchIdeValueWinSdk);
277     std::string ninja_extra_args;
278     if (command_line->HasSwitch(kSwitchNinjaExtraArgs)) {
279       ninja_extra_args =
280           command_line->GetSwitchValueString(kSwitchNinjaExtraArgs);
281     }
282     std::string ninja_executable;
283     if (command_line->HasSwitch(kSwitchNinjaExecutable)) {
284       ninja_executable =
285           command_line->GetSwitchValueString(kSwitchNinjaExecutable);
286     }
287     bool no_deps = command_line->HasSwitch(kSwitchNoDeps);
288     bool res = VisualStudioWriter::RunAndWriteFiles(
289         build_settings, builder, version, sln_name, filters, win_kit,
290         ninja_extra_args, ninja_executable, no_deps, err);
291     if (res && !quiet) {
292       OutputString("Generating Visual Studio projects took " +
293                    base::Int64ToString(timer.Elapsed().InMilliseconds()) +
294                    "ms\n");
295     }
296     return res;
297   } else if (ide == kSwitchIdeValueXcode) {
298     XcodeWriter::Options options = {
299         command_line->GetSwitchValueString(kSwitchXcodeProject),
300         command_line->GetSwitchValueString(kSwitchIdeRootTarget),
301         command_line->GetSwitchValueString(kSwitchNinjaExecutable),
302         command_line->GetSwitchValueString(kSwitchFilters),
303         command_line->GetSwitchValueString(kSwitchXcodeConfigurations),
304         command_line->GetSwitchValuePath(kSwitchXcodeConfigurationBuildPath),
305         command_line->GetSwitchValueNative(kSwitchXcodeAdditionalFilesPatterns),
306         command_line->GetSwitchValueNative(kSwitchXcodeAdditionalFilesRoots),
307         XcodeBuildSystem::kLegacy,
308     };
309 
310     if (options.project_name.empty()) {
311       options.project_name = "all";
312     }
313 
314     const std::string build_system =
315         command_line->GetSwitchValueString(kSwitchXcodeBuildSystem);
316     if (!build_system.empty()) {
317       if (build_system == kSwitchXcodeBuildsystemValueNew) {
318         options.build_system = XcodeBuildSystem::kNew;
319       } else if (build_system == kSwitchXcodeBuildsystemValueLegacy) {
320         options.build_system = XcodeBuildSystem::kLegacy;
321       } else {
322         *err = Err(Location(), "Unknown build system: " + build_system);
323         return false;
324       }
325     }
326 
327     bool res =
328         XcodeWriter::RunAndWriteFiles(build_settings, builder, options, err);
329     if (res && !quiet) {
330       OutputString("Generating Xcode projects took " +
331                    base::Int64ToString(timer.Elapsed().InMilliseconds()) +
332                    "ms\n");
333     }
334     return res;
335   } else if (ide == kSwitchIdeValueQtCreator) {
336     std::string root_target;
337     if (command_line->HasSwitch(kSwitchIdeRootTarget))
338       root_target = command_line->GetSwitchValueString(kSwitchIdeRootTarget);
339     bool res = QtCreatorWriter::RunAndWriteFile(build_settings, builder, err,
340                                                 root_target);
341     if (res && !quiet) {
342       OutputString("Generating QtCreator projects took " +
343                    base::Int64ToString(timer.Elapsed().InMilliseconds()) +
344                    "ms\n");
345     }
346     return res;
347   } else if (ide == kSwitchIdeValueJson) {
348     std::string file_name =
349         command_line->GetSwitchValueString(kSwitchJsonFileName);
350     if (file_name.empty())
351       file_name = "project.json";
352     std::string exec_script =
353         command_line->GetSwitchValueString(kSwitchJsonIdeScript);
354     std::string exec_script_extra_args =
355         command_line->GetSwitchValueString(kSwitchJsonIdeScriptArgs);
356     std::string filters = command_line->GetSwitchValueString(kSwitchFilters);
357 
358     bool res = JSONProjectWriter::RunAndWriteFiles(
359         build_settings, builder, file_name, exec_script, exec_script_extra_args,
360         filters, quiet, err);
361     if (res && !quiet) {
362       OutputString("Generating JSON projects took " +
363                    base::Int64ToString(timer.Elapsed().InMilliseconds()) +
364                    "ms\n");
365     }
366     return res;
367   }
368 
369   *err = Err(Location(), "Unknown IDE: " + ide);
370   return false;
371 }
372 
RunRustProjectWriter(const BuildSettings * build_settings,const Builder & builder,Err * err)373 bool RunRustProjectWriter(const BuildSettings* build_settings,
374                           const Builder& builder,
375                           Err* err) {
376   const base::CommandLine* command_line =
377       base::CommandLine::ForCurrentProcess();
378   bool quiet = command_line->HasSwitch(switches::kQuiet);
379   base::ElapsedTimer timer;
380 
381   std::string file_name = "rust-project.json";
382   bool res = RustProjectWriter::RunAndWriteFiles(build_settings, builder,
383                                                  file_name, quiet, err);
384   if (res && !quiet) {
385     OutputString("Generating rust-project.json took " +
386                  base::Int64ToString(timer.Elapsed().InMilliseconds()) +
387                  "ms\n");
388   }
389   return res;
390 }
391 
RunCompileCommandsWriter(Setup & setup,Err * err)392 bool RunCompileCommandsWriter(Setup& setup, Err* err) {
393   // The compilation database is written if either the .gn setting is set or if
394   // the command line flag is set. The command line flag takes precedence.
395   const base::CommandLine* command_line =
396       base::CommandLine::ForCurrentProcess();
397   bool has_legacy_switch =
398       command_line->HasSwitch(kSwitchExportCompileCommands);
399 
400   bool has_patterns = !setup.export_compile_commands().empty();
401   if (!has_legacy_switch && !has_patterns)
402     return true;  // No compilation database needs to be written.
403 
404   bool quiet = command_line->HasSwitch(switches::kQuiet);
405   base::ElapsedTimer timer;
406 
407   // The compilation database file goes in the build directory.
408   SourceFile output_file =
409       setup.build_settings().build_dir().ResolveRelativeFile(
410           Value(nullptr, "compile_commands.json"), err);
411   if (output_file.is_null())
412     return false;
413   base::FilePath output_path = setup.build_settings().GetFullPath(output_file);
414 
415   std::optional<std::string> legacy_target_filters;
416   if (has_legacy_switch) {
417     legacy_target_filters =
418         command_line->GetSwitchValueString(kSwitchExportCompileCommands);
419   }
420 
421   bool ok = CompileCommandsWriter::RunAndWriteFiles(
422       &setup.build_settings(), setup.builder().GetAllResolvedTargets(),
423       setup.export_compile_commands(), legacy_target_filters, output_path, err);
424   if (ok && !quiet) {
425     OutputString("Generating compile_commands took " +
426                  base::Int64ToString(timer.Elapsed().InMilliseconds()) +
427                  "ms\n");
428   }
429   return ok;
430 }
431 
RunNinjaPostProcessTools(const BuildSettings * build_settings,base::FilePath ninja_executable,bool is_regeneration,bool clean_stale,Err * err)432 bool RunNinjaPostProcessTools(const BuildSettings* build_settings,
433                               base::FilePath ninja_executable,
434                               bool is_regeneration,
435                               bool clean_stale,
436                               Err* err) {
437   // If the user did not specify an executable, skip running the post processing
438   // tools. Since these tools can re-write ninja build log and dep logs, it is
439   // really important that ninja executable used for tools matches the
440   // executable that is used for builds.
441   if (ninja_executable.empty()) {
442     if (clean_stale) {
443       *err = Err(Location(), "No --ninja-executable provided.",
444                  "--clean-stale requires a ninja executable to run. You can "
445                  "provide one on the command line via --ninja-executable.");
446       return false;
447     }
448 
449     return true;
450   }
451 
452   base::FilePath build_dir =
453       build_settings->GetFullPath(build_settings->build_dir());
454 
455   if (clean_stale) {
456     if (build_settings->ninja_required_version() < Version{1, 10, 0}) {
457       *err = Err(Location(), "Need a ninja executable at least version 1.10.0.",
458                  "--clean-stale requires a ninja executable of version 1.10.0 "
459                  "or later.");
460       return false;
461     }
462 
463     if (!InvokeNinjaCleanDeadTool(ninja_executable, build_dir, err)) {
464       return false;
465     }
466 
467     if (!InvokeNinjaRecompactTool(ninja_executable, build_dir, err)) {
468       return false;
469     }
470   }
471 
472   // If we have a ninja version that supports restat, we should restat the
473   // build.ninja or build.ninja.stamp files so the next ninja invocation
474   // will use the right mtimes. If gen is being invoked as part of a re-gen
475   // (ie, ninja is invoking gn gen), then we can elide this restat, as
476   // ninja will restat the appropriate file anyways after it is complete.
477   if (!is_regeneration &&
478       build_settings->ninja_required_version() >= Version{1, 10, 0}) {
479     std::vector<base::FilePath> files_to_restat{
480         base::FilePath(FILE_PATH_LITERAL("build.ninja")),
481         base::FilePath(FILE_PATH_LITERAL("build.ninja.stamp")),
482     };
483     if (!InvokeNinjaRestatTool(ninja_executable, build_dir, files_to_restat,
484                                err)) {
485       return false;
486     }
487   }
488   return true;
489 }
490 
491 }  // namespace
492 
493 const char kGen[] = "gen";
494 const char kGen_HelpShort[] = "gen: Generate ninja files.";
495 const char kGen_Help[] =
496     R"(gn gen [--check] [<ide options>] <out_dir>
497 
498   Generates ninja files from the current tree and puts them in the given output
499   directory.
500 
501   The output directory can be a source-repo-absolute path name such as:
502       //out/foo
503   Or it can be a directory relative to the current directory such as:
504       out/foo
505 
506   "gn gen --check" is the same as running "gn check". "gn gen --check=system" is
507   the same as running "gn check --check-system".  See "gn help check" for
508   documentation on that mode.
509 
510   See "gn help switches" for the common command-line switches.
511 
512 General options
513 
514   --ninja-executable=<string>
515       Can be used to specify the ninja executable to use. This executable will
516       be used as an IDE option to indicate which ninja to use for building. This
517       executable will also be used as part of the gen process for triggering a
518       restat on generated ninja files and for use with --clean-stale.
519 
520   --clean-stale
521       This option will cause no longer needed output files to be removed from
522       the build directory, and their records pruned from the ninja build log and
523       dependency database after the ninja build graph has been generated. This
524       option requires a ninja executable of at least version 1.10.0. It can be
525       provided by the --ninja-executable switch. Also see "gn help clean_stale".
526 
527 IDE options
528 
529   GN optionally generates files for IDE. Files won't be overwritten if their
530   contents don't change. Possibilities for <ide options>
531 
532   --ide=<ide_name>
533       Generate files for an IDE. Currently supported values:
534       "eclipse" - Eclipse CDT settings file.
535       "vs" - Visual Studio project/solution files.
536              (default Visual Studio version: 2019)
537       "vs2013" - Visual Studio 2013 project/solution files.
538       "vs2015" - Visual Studio 2015 project/solution files.
539       "vs2017" - Visual Studio 2017 project/solution files.
540       "vs2019" - Visual Studio 2019 project/solution files.
541       "vs2022" - Visual Studio 2022 project/solution files.
542       "xcode" - Xcode workspace/solution files.
543       "qtcreator" - QtCreator project files.
544       "json" - JSON file containing target information
545 
546   --filters=<path_prefixes>
547       Semicolon-separated list of label patterns used to limit the set of
548       generated projects (see "gn help label_pattern"). Only matching targets
549       and their dependencies will be included in the solution. Only used for
550       Visual Studio, Xcode and JSON.
551 
552 Visual Studio Flags
553 
554   --sln=<file_name>
555       Override default sln file name ("all"). Solution file is written to the
556       root build directory.
557 
558   --no-deps
559       Don't include targets dependencies to the solution. Changes the way how
560       --filters option works. Only directly matching targets are included.
561 
562   --winsdk=<sdk_version>
563       Use the specified Windows 10 SDK version to generate project files.
564       As an example, "10.0.15063.0" can be specified to use Creators Update SDK
565       instead of the default one.
566 
567   --ninja-executable=<string>
568       Can be used to specify the ninja executable to use when building.
569 
570   --ninja-extra-args=<string>
571       This string is passed without any quoting to the ninja invocation
572       command-line. Can be used to configure ninja flags, like "-j".
573 
574 Xcode Flags
575 
576   --xcode-project=<file_name>
577       Override default Xcode project file name ("all"). The project file is
578       written to the root build directory.
579 
580   --xcode-build-system=<value>
581       Configure the build system to use for the Xcode project. Supported
582       values are (default to "legacy"):
583       "legacy" - Legacy Build system
584       "new" - New Build System
585 
586   --xcode-configs=<config_name_list>
587       Configure the list of build configuration supported by the generated
588       project. If specified, must be a list of semicolon-separated strings.
589       If omitted, a single configuration will be used in the generated
590       project derived from the build directory.
591 
592   --xcode-config-build-dir=<string>
593       If present, must be a path relative to the source directory. It will
594       default to $root_out_dir if omitted. The path is assumed to point to
595       the directory where ninja needs to be invoked. This variable can be
596       used to build for multiple configuration / platform / environment from
597       the same generated Xcode project (assuming that the user has created a
598       gn build directory with the correct args.gn for each).
599 
600       One useful value is to use Xcode variables such as '${CONFIGURATION}'
601       or '${EFFECTIVE_PLATFORM}'.
602 
603   --xcode-additional-files-patterns=<pattern_list>
604       If present, must be a list of semicolon-separated file patterns. It
605       will be used to add all files matching the pattern located in the
606       source tree to the project. It can be used to add, e.g. documentation
607       files to the project to allow easily edit them.
608 
609   --xcode-additional-files-roots=<path_list>
610       If present, must be a list of semicolon-separated paths. It will be used
611       as roots when looking for additional files to add. If omitted, defaults
612       to "//".
613 
614   --ninja-executable=<string>
615       Can be used to specify the ninja executable to use when building.
616 
617   --ninja-extra-args=<string>
618       This string is passed without any quoting to the ninja invocation
619       command-line. Can be used to configure ninja flags, like "-j".
620 
621   --ide-root-target=<target_name>
622       Name of the target corresponding to "All" target in Xcode. If unset,
623       "All" invokes ninja without any target and builds everything.
624 
625 QtCreator Flags
626 
627   --ide-root-target=<target_name>
628       Name of the root target for which the QtCreator project will be generated
629       to contain files of it and its dependencies. If unset, the whole build
630       graph will be emitted.
631 
632 
633 Eclipse IDE Support
634 
635   GN DOES NOT generate Eclipse CDT projects. Instead, it generates a settings
636   file which can be imported into an Eclipse CDT project. The XML file contains
637   a list of include paths and defines. Because GN does not generate a full
638   .cproject definition, it is not possible to properly define includes/defines
639   for each file individually. Instead, one set of includes/defines is generated
640   for the entire project. This works fairly well but may still result in a few
641   indexer issues here and there.
642 
643 Generic JSON Output
644 
645   Dumps target information to a JSON file and optionally invokes a
646   python script on the generated file. See the comments at the beginning
647   of json_project_writer.cc and desc_builder.cc for an overview of the JSON
648   file format.
649 
650   --json-file-name=<json_file_name>
651       Overrides default file name (project.json) of generated JSON file.
652 
653   --json-ide-script=<path_to_python_script>
654       Executes python script after the JSON file is generated or updated with
655       new content. Path can be project absolute (//), system absolute (/) or
656       relative, in which case the output directory will be base. Path to
657       generated JSON file will be first argument when invoking script.
658 
659   --json-ide-script-args=<argument>
660       Optional second argument that will be passed to executed script.
661 
662 Ninja Outputs
663 
664   The --ninja-outputs-file=<FILE> option dumps a JSON file that maps GN labels
665   to their Ninja output paths. This can be later processed to build an index
666   to convert between Ninja targets and GN ones before or after the build itself.
667   It looks like:
668 
669     {
670       "label1": [
671         "path1",
672         "path2"
673       ],
674       "label2": [
675         "path3"
676       ]
677     }
678 
679   --ninja-outputs-script=<path_to_python_script>
680     Executes python script after the outputs file is generated or updated
681     with new content. Path can be project absolute (//), system absolute (/) or
682     relative, in which case the output directory will be base. Path to
683     generated file will be first argument when invoking script.
684 
685   --ninja-outputs-script-args=<argument>
686     Optional second argument that will be passed to executed script.
687 
688 Compilation Database
689 
690   --export-rust-project
691       Produces a rust-project.json file in the root of the build directory
692       This is used for various tools in the Rust ecosystem allowing for the
693       replay of individual compilations independent of the build system.
694       This is an unstable format and likely to change without warning.
695 
696   --add-export-compile-commands=<label_pattern>
697       Adds an additional label pattern (see "gn help label_pattern") of a
698       target to add to the compilation database. This pattern is appended to any
699       list values specified in the export_compile_commands variable in the
700       .gn file (see "gn help dotfile"). This allows the user to add additional
701       targets to the compilation database that the project doesn't add by default.
702 
703       To add more than one value, specify this switch more than once. Each
704       invocation adds an additional label pattern.
705 
706       Example:
707         --add-export-compile-commands=//tools:my_tool
708         --add-export-compile-commands="//base/*"
709 
710   --export-compile-commands[=<target_name1,target_name2...>]
711       DEPRECATED https://bugs.chromium.org/p/gn/issues/detail?id=302.
712       Please use --add-export-compile-commands for per-user configuration, and
713       the "export_compile_commands" value in the project-level .gn file (see
714       "gn help dotfile") for per-project configuration.
715 
716       Overrides the value of the export_compile_commands in the .gn file (see
717       "gn help dotfile") as well as the --add-export-compile-commands switch.
718 
719       Unlike the .gn setting, this switch takes a legacy format which is a list
720       of target names that are matched in any directory. For example, "foo" will
721       match:
722        - "//path/to/src:foo"
723        - "//other/path:foo"
724        - "//foo:foo"
725       and not match:
726        - "//foo:bar"
727 )";
728 
RunGen(const std::vector<std::string> & args)729 int RunGen(const std::vector<std::string>& args) {
730   base::ElapsedTimer timer;
731 
732   if (args.size() != 1) {
733     Err(Location(), "Need exactly one build directory to generate.",
734         "I expected something more like \"gn gen out/foo\"\n"
735         "You can also see \"gn help gen\".")
736         .PrintToStdout();
737     return 1;
738   }
739 
740   // Deliberately leaked to avoid expensive process teardown.
741   Setup* setup = new Setup();
742   // Generate an empty args.gn file if it does not exists
743   if (!base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kArgs)) {
744     setup->set_gen_empty_args(true);
745   }
746   if (!setup->DoSetup(args[0], true))
747     return 1;
748 
749   const base::CommandLine* command_line =
750       base::CommandLine::ForCurrentProcess();
751   if (command_line->HasSwitch(kSwitchCheck)) {
752     setup->set_check_public_headers(true);
753     if (command_line->GetSwitchValueString(kSwitchCheck) == "system")
754       setup->set_check_system_includes(true);
755   }
756 
757   // If this is a regeneration, replace existing build.ninja and build.ninja.d
758   // with just enough for ninja to call GN and regenerate ninja files. This
759   // removes any potential soon-to-be-dangling references and ensures that
760   // regeneration can be restarted if interrupted.
761   if (command_line->HasSwitch(switches::kRegeneration)) {
762     if (!commands::PrepareForRegeneration(&setup->build_settings())) {
763       return false;
764     }
765   }
766 
767   // Cause the load to also generate the ninja files for each target.
768   TargetWriteInfo write_info;
769   write_info.want_ninja_outputs =
770       command_line->HasSwitch(kSwitchNinjaOutputsFile);
771 
772   setup->builder().set_resolved_and_generated_callback(
773       [&write_info](const BuilderRecord* record) {
774         ItemResolvedAndGeneratedCallback(&write_info, record);
775       });
776 
777   // Do the actual load. This will also write out the target ninja files.
778   if (!setup->Run())
779     return 1;
780 
781   if (command_line->HasSwitch(switches::kVerbose))
782     OutputString("Build graph constructed in " +
783                  base::Int64ToString(timer.Elapsed().InMilliseconds()) +
784                  "ms\n");
785 
786   // Sort the targets in each toolchain according to their label. This makes
787   // the ninja files have deterministic content.
788   for (auto& cur_toolchain : write_info.rules) {
789     std::sort(cur_toolchain.second.begin(), cur_toolchain.second.end(),
790               [](const NinjaWriter::TargetRulePair& a,
791                  const NinjaWriter::TargetRulePair& b) {
792                 return a.first->label() < b.first->label();
793               });
794   }
795 
796   Err err;
797   // Write the root ninja files.
798   if (!NinjaWriter::RunAndWriteFiles(&setup->build_settings(), setup->builder(),
799                                      write_info.rules, &err)) {
800     err.PrintToStdout();
801     return 1;
802   }
803 
804   if (!RunNinjaPostProcessTools(
805           &setup->build_settings(),
806           command_line->GetSwitchValuePath(switches::kNinjaExecutable),
807           command_line->HasSwitch(switches::kRegeneration),
808           command_line->HasSwitch(kSwitchCleanStale), &err)) {
809     err.PrintToStdout();
810     return 1;
811   }
812 
813   if (write_info.want_ninja_outputs) {
814     ElapsedTimer outputs_timer;
815     std::string file_name =
816         command_line->GetSwitchValueString(kSwitchNinjaOutputsFile);
817     if (file_name.empty()) {
818       Err(Location(), "The --ninja-outputs-file argument cannot be empty!")
819           .PrintToStdout();
820       return 1;
821     }
822 
823     bool quiet = command_line->HasSwitch(switches::kQuiet);
824 
825     std::string exec_script =
826         command_line->GetSwitchValueString(kSwitchNinjaOutputsScript);
827 
828     std::string exec_script_extra_args =
829         command_line->GetSwitchValueString(kSwitchNinjaOutputsScriptArgs);
830 
831     bool res = NinjaOutputsWriter::RunAndWriteFiles(
832         write_info.ninja_outputs_map, &setup->build_settings(), file_name,
833         exec_script, exec_script_extra_args, quiet, &err);
834     if (!res) {
835       err.PrintToStdout();
836       return 1;
837     }
838     if (!command_line->HasSwitch(switches::kQuiet)) {
839       OutputString(base::StringPrintf(
840           "Generating Ninja outputs file took %" PRId64 "ms\n",
841           outputs_timer.Elapsed().InMilliseconds()));
842     }
843   }
844 
845   if (!WriteRuntimeDepsFilesIfNecessary(&setup->build_settings(),
846                                         setup->builder(), &err)) {
847     err.PrintToStdout();
848     return 1;
849   }
850 
851   if (!CheckForInvalidGeneratedInputs(setup))
852     return 1;
853 
854   if (command_line->HasSwitch(kSwitchIde) &&
855       !RunIdeWriter(command_line->GetSwitchValueString(kSwitchIde),
856                     &setup->build_settings(), setup->builder(), &err)) {
857     err.PrintToStdout();
858     return 1;
859   }
860 
861   if (!RunCompileCommandsWriter(*setup, &err)) {
862     err.PrintToStdout();
863     return 1;
864   }
865 
866   if (command_line->HasSwitch(kSwitchExportRustProject) &&
867       !RunRustProjectWriter(&setup->build_settings(), setup->builder(), &err)) {
868     err.PrintToStdout();
869     return 1;
870   }
871 
872   TickDelta elapsed_time = timer.Elapsed();
873 
874   if (!command_line->HasSwitch(switches::kQuiet)) {
875     OutputString("Done. ", DECORATION_GREEN);
876 
877     size_t targets_collected = 0;
878     for (const auto& rules : write_info.rules)
879       targets_collected += rules.second.size();
880 
881     std::string stats =
882         "Made " + base::NumberToString(targets_collected) + " targets from " +
883         base::IntToString(
884             setup->scheduler().input_file_manager()->GetInputFileCount()) +
885         " files in " + base::Int64ToString(elapsed_time.InMilliseconds()) +
886         "ms\n";
887     OutputString(stats);
888   }
889 
890   // Just like the build graph, leak the resolved data to avoid expensive
891   // process teardown here too.
892   write_info.LeakOnPurpose();
893 
894   return 0;
895 }
896 
897 }  // namespace commands
898