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 "base/command_line.h"
6 #include "gn/args.h"
7 #include "gn/commands.h"
8 #include "gn/err.h"
9 #include "gn/functions.h"
10 #include "gn/input_conversion.h"
11 #include "gn/label.h"
12 #include "gn/label_pattern.h"
13 #include "gn/metadata.h"
14 #include "gn/ninja_build_writer.h"
15 #include "gn/output_conversion.h"
16 #include "gn/parser.h"
17 #include "gn/runtime_deps.h"
18 #include "gn/setup.h"
19 #include "gn/standard_out.h"
20 #include "gn/string_utils.h"
21 #include "gn/substitution_writer.h"
22 #include "gn/switches.h"
23 #include "gn/target.h"
24 #include "gn/variables.h"
25
26 namespace commands {
27
28 namespace {
29
30 // Some names exist in multiple sections, these prefixes are used for the
31 // internal links to disambiguate when writing markdown.
32 const char kCommandLinkPrefix[] = "cmd_";
33 const char kFunctionLinkPrefix[] = "func_";
34 const char kVariableLinkPrefix[] = "var_";
35
PrintToplevelHelp()36 void PrintToplevelHelp() {
37 PrintSectionHelp("Commands", "<command>", "commands");
38 for (const auto& cmd : commands::GetCommands())
39 PrintShortHelp(cmd.second.help_short, kCommandLinkPrefix + cmd.first);
40
41 // Target declarations.
42 PrintSectionHelp("Target declarations", "<function>", "targets");
43 for (const auto& func : functions::GetFunctions()) {
44 if (func.second.is_target)
45 PrintShortHelp(func.second.help_short, kFunctionLinkPrefix + func.first);
46 }
47
48 // Functions.
49 PrintSectionHelp("Buildfile functions", "<function>", "functions");
50 for (const auto& func : functions::GetFunctions()) {
51 if (!func.second.is_target)
52 PrintShortHelp(func.second.help_short, kFunctionLinkPrefix + func.first);
53 }
54
55 // Built-in variables.
56 PrintSectionHelp("Built-in predefined variables", "<variable>",
57 "predefined_variables");
58 for (const auto& builtin : variables::GetBuiltinVariables()) {
59 PrintShortHelp(builtin.second.help_short,
60 kVariableLinkPrefix + builtin.first);
61 }
62
63 // Target variables.
64 PrintSectionHelp("Variables you set in targets", "<variable>",
65 "target_variables");
66 for (const auto& target : variables::GetTargetVariables()) {
67 PrintShortHelp(target.second.help_short,
68 kVariableLinkPrefix + target.first);
69 }
70
71 PrintSectionHelp("Other help topics", "", "other");
72 PrintShortHelp("all: Print all the help at once");
73 PrintShortHelp("buildargs: How build arguments work.", "buildargs");
74 PrintShortHelp("dotfile: Info about the toplevel .gn file.", "dotfile");
75 PrintShortHelp("execution: Build graph and execution overview.", "execution");
76 PrintShortHelp("grammar: Language and grammar for GN build files.",
77 "grammar");
78 PrintShortHelp(
79 "input_conversion: Processing input from exec_script and read_file.",
80 "io_conversion");
81 PrintShortHelp("label_pattern: Matching more than one label.",
82 "label_pattern");
83 PrintShortHelp("labels: About labels.", "labels");
84 PrintShortHelp("metadata_collection: About metadata and its collection.",
85 "metadata_collection");
86 PrintShortHelp("ninja_rules: How Ninja build rules are named.",
87 "ninja_rules");
88 PrintShortHelp("nogncheck: Annotating includes for checking.", "nogncheck");
89 PrintShortHelp(
90 "output_conversion: Specifies how to transform a value to output.",
91 "io_conversion");
92 PrintShortHelp("runtime_deps: How runtime dependency computation works.",
93 "runtime_deps");
94 PrintShortHelp("source_expansion: Map sources to outputs for scripts.",
95 "source_expansion");
96 PrintShortHelp("switches: Show available command-line switches.",
97 "switch_list");
98 }
99
PrintSwitchHelp()100 void PrintSwitchHelp() {
101 const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
102 bool is_markdown = cmdline->HasSwitch(switches::kMarkdown);
103
104 // This uses "switch_list" for the tag because Markdown seems to generate
105 // implicit tags for headings that match the strings, and some headings are
106 // labeled "switches".
107 PrintLongHelp(R"(Available global switches
108
109 Do "gn help --the_switch_you_want_help_on" for more. Individual commands may
110 take command-specific switches not listed here. See the help on your specific
111 command for more.
112 )",
113 "switch_list");
114
115 if (is_markdown)
116 OutputString("```\n", DECORATION_NONE);
117
118 for (const auto& s : switches::GetSwitches())
119 PrintShortHelp(s.second.short_help);
120
121 if (is_markdown)
122 OutputString("```\n", DECORATION_NONE);
123
124 OutputString("\n");
125 }
126
PrintAllHelp()127 void PrintAllHelp() {
128 const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
129 bool is_markdown = cmdline->HasSwitch(switches::kMarkdown);
130
131 if (is_markdown) {
132 OutputString("# GN Reference\n\n");
133 OutputString(
134 "*This page is automatically generated from* "
135 "`gn help --markdown all`.\n\n");
136
137 // Generate our own table of contents so that we have more control
138 // over what's in and out.
139 OutputString("## Contents\n\n");
140 }
141
142 PrintToplevelHelp();
143
144 OutputString("\n");
145
146 if (is_markdown) {
147 OutputString("## <a name=\"commands\"></a>Commands\n\n", DECORATION_NONE,
148 NO_ESCAPING);
149 }
150 for (const auto& c : commands::GetCommands())
151 PrintLongHelp(c.second.help, kCommandLinkPrefix + c.first);
152
153 if (is_markdown) {
154 OutputString("## <a name=\"targets\"></a>Target declarations\n\n",
155 DECORATION_NONE, NO_ESCAPING);
156 }
157 for (const auto& f : functions::GetFunctions()) {
158 if (f.second.is_target)
159 PrintLongHelp(f.second.help, kFunctionLinkPrefix + f.first);
160 }
161
162 if (is_markdown) {
163 OutputString("## <a name=\"functions\"></a>Buildfile functions\n\n",
164 DECORATION_NONE, NO_ESCAPING);
165 }
166 for (const auto& f : functions::GetFunctions()) {
167 if (!f.second.is_target)
168 PrintLongHelp(f.second.help, kFunctionLinkPrefix + f.first);
169 }
170
171 if (is_markdown) {
172 OutputString(
173 "## <a name=\"predefined_variables\"></a>"
174 "Built-in predefined variables\n\n",
175 DECORATION_NONE, NO_ESCAPING);
176 }
177 for (const auto& v : variables::GetBuiltinVariables())
178 PrintLongHelp(v.second.help, kVariableLinkPrefix + v.first);
179
180 if (is_markdown) {
181 OutputString(
182 "## <a name=\"target_variables\"></a>"
183 "Variables you set in targets\n\n",
184 DECORATION_NONE, NO_ESCAPING);
185 }
186 for (const auto& v : variables::GetTargetVariables())
187 PrintLongHelp(v.second.help, kVariableLinkPrefix + v.first);
188
189 if (is_markdown) {
190 OutputString("## <a name=\"other\"></a>Other help topics\n\n",
191 DECORATION_NONE, NO_ESCAPING);
192 }
193 PrintLongHelp(kBuildArgs_Help, "buildargs");
194 PrintLongHelp(kDotfile_Help, "dotfile");
195 PrintLongHelp(kExecution_Help, "execution");
196 PrintLongHelp(kGrammar_Help, "grammar");
197 PrintLongHelp(kInputOutputConversion_Help, "io_conversion");
198 PrintLongHelp(kLabelPattern_Help, "label_pattern");
199 PrintLongHelp(kLabels_Help, "labels");
200 PrintLongHelp(kMetadata_Help, "metadata_collection");
201 PrintLongHelp(kNinjaRules_Help, "ninja_rules");
202 PrintLongHelp(kNoGnCheck_Help, "nogncheck");
203 PrintLongHelp(kRuntimeDeps_Help, "runtime_deps");
204 PrintLongHelp(kSourceExpansion_Help, "source_expansion");
205
206 PrintSwitchHelp();
207 }
208
209 // Prints help on the given switch. There should be no leading hyphens. Returns
210 // true if the switch was found and help was printed. False means the switch is
211 // unknown.
PrintHelpOnSwitch(const std::string & what)212 bool PrintHelpOnSwitch(const std::string& what) {
213 const switches::SwitchInfoMap& all = switches::GetSwitches();
214 switches::SwitchInfoMap::const_iterator found =
215 all.find(std::string_view(what));
216 if (found == all.end())
217 return false;
218 PrintLongHelp(found->second.long_help);
219 return true;
220 }
221
222 // Special-case help for ambiguous "args" case.
PrintArgsHelp()223 void PrintArgsHelp() {
224 PrintLongHelp(
225 "The string \"args\" is both a command and a variable for action "
226 "targets.\nShowing help for both...\n\n");
227 PrintLongHelp(commands::kArgs_Help);
228 PrintLongHelp(
229 "\n----------------------------------------------------------------------"
230 "---------\n\n");
231 PrintLongHelp(variables::kArgs_Help);
232 }
233
234 } // namespace
235
236 const char kHelp[] = "help";
237 const char kHelp_HelpShort[] = "help: Does what you think.";
238 const char kHelp_Help[] =
239 R"(gn help <anything>
240
241 Yo dawg, I heard you like help on your help so I put help on the help in the
242 help.
243
244 You can also use "all" as the parameter to get all help at once.
245
246 Switches
247
248 --markdown
249 Format output in markdown syntax.
250
251 Example
252
253 gn help --markdown all
254 Dump all help to stdout in markdown format.
255 )";
256
RunHelp(const std::vector<std::string> & args)257 int RunHelp(const std::vector<std::string>& args) {
258 std::string what;
259 if (args.size() == 0) {
260 // If no argument is specified, check for switches to allow things like
261 // "gn help --args" for help on the args switch.
262 const base::CommandLine::SwitchMap& switches =
263 base::CommandLine::ForCurrentProcess()->GetSwitches();
264 if (switches.empty()) {
265 // Still nothing, show help overview.
266 PrintToplevelHelp();
267 return 0;
268 }
269
270 // Switch help needs to be done separately. The CommandLine will strip the
271 // switch separators so --args will come out as "args" which is then
272 // ambiguous with the variable named "args".
273 if (!PrintHelpOnSwitch(switches.begin()->first))
274 PrintToplevelHelp();
275 return 0;
276 } else {
277 what = args[0];
278 }
279
280 std::vector<std::string_view> all_help_topics;
281
282 // Special-case ambiguous topics.
283 if (what == "args") {
284 PrintArgsHelp();
285 return 0;
286 }
287
288 // Check commands.
289 const commands::CommandInfoMap& command_map = commands::GetCommands();
290 auto found_command = command_map.find(what);
291 if (found_command != command_map.end()) {
292 PrintLongHelp(found_command->second.help);
293 return 0;
294 }
295 for (const auto& entry : command_map)
296 all_help_topics.push_back(entry.first);
297
298 // Check functions.
299 const functions::FunctionInfoMap& function_map = functions::GetFunctions();
300 auto found_function = function_map.find(what);
301 if (found_function != function_map.end())
302 PrintLongHelp(found_function->second.help);
303 for (const auto& entry : function_map)
304 all_help_topics.push_back(entry.first);
305
306 // Builtin variables.
307 const variables::VariableInfoMap& builtin_vars =
308 variables::GetBuiltinVariables();
309 auto found_builtin_var = builtin_vars.find(what);
310 if (found_builtin_var != builtin_vars.end())
311 PrintLongHelp(found_builtin_var->second.help);
312 for (const auto& entry : builtin_vars)
313 all_help_topics.push_back(entry.first);
314
315 // Target variables.
316 const variables::VariableInfoMap& target_vars =
317 variables::GetTargetVariables();
318 auto found_target_var = target_vars.find(what);
319 if (found_target_var != target_vars.end())
320 PrintLongHelp(found_target_var->second.help);
321 for (const auto& entry : target_vars)
322 all_help_topics.push_back(entry.first);
323
324 if (found_function != function_map.end() ||
325 found_builtin_var != builtin_vars.end() ||
326 found_target_var != target_vars.end())
327 return 0;
328
329 // Random other topics.
330 std::map<std::string, void (*)()> random_topics;
331 random_topics["all"] = PrintAllHelp;
332 random_topics["execution"] = []() { PrintLongHelp(kExecution_Help); };
333 random_topics["buildargs"] = []() { PrintLongHelp(kBuildArgs_Help); };
334 random_topics["dotfile"] = []() { PrintLongHelp(kDotfile_Help); };
335 random_topics["grammar"] = []() { PrintLongHelp(kGrammar_Help); };
336 random_topics["io_conversion"] = []() {
337 PrintLongHelp(kInputOutputConversion_Help);
338 };
339 random_topics["label_pattern"] = []() { PrintLongHelp(kLabelPattern_Help); };
340 random_topics["labels"] = []() { PrintLongHelp(kLabels_Help); };
341 random_topics["metadata_collection"] = []() {
342 PrintLongHelp(kMetadata_Help);
343 };
344 random_topics["ninja_rules"] = []() { PrintLongHelp(kNinjaRules_Help); };
345 random_topics["nogncheck"] = []() { PrintLongHelp(kNoGnCheck_Help); };
346 random_topics["runtime_deps"] = []() { PrintLongHelp(kRuntimeDeps_Help); };
347 random_topics["source_expansion"] = []() {
348 PrintLongHelp(kSourceExpansion_Help);
349 };
350 random_topics["switches"] = PrintSwitchHelp;
351 auto found_random_topic = random_topics.find(what);
352 if (found_random_topic != random_topics.end()) {
353 found_random_topic->second();
354 return 0;
355 }
356 for (const auto& entry : random_topics)
357 all_help_topics.push_back(entry.first);
358
359 // No help on this.
360 Err(Location(), "No help on \"" + what + "\".").PrintToStdout();
361 std::string_view suggestion = SpellcheckString(what, all_help_topics);
362 if (suggestion.empty()) {
363 OutputString("Run `gn help` for a list of available topics.\n",
364 DECORATION_NONE);
365 } else {
366 OutputString("Did you mean `gn help " + std::string(suggestion) + "`?\n",
367 DECORATION_NONE);
368 }
369 return 1;
370 }
371
372 } // namespace commands
373