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