• 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 "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