• 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 "gn/commands.h"
6 
7 #include "base/command_line.h"
8 #include "base/environment.h"
9 #include "base/strings/string_split.h"
10 #include "base/values.h"
11 #include "gn/builder.h"
12 #include "gn/filesystem_utils.h"
13 #include "gn/item.h"
14 #include "gn/label.h"
15 #include "gn/label_pattern.h"
16 #include "gn/setup.h"
17 #include "gn/standard_out.h"
18 #include "gn/target.h"
19 #include "util/build_config.h"
20 
21 namespace commands {
22 
23 namespace {
24 
25 // Like above but the input string can be a pattern that matches multiple
26 // targets. If the input does not parse as a pattern, prints and error and
27 // returns false. If the pattern is valid, fills the vector (which might be
28 // empty if there are no matches) and returns true.
29 //
30 // If all_toolchains is false, a pattern with an unspecified toolchain will
31 // match the default toolchain only. If true, all toolchains will be matched.
ResolveTargetsFromCommandLinePattern(Setup * setup,const std::string & label_pattern,bool all_toolchains,std::vector<const Target * > * matches)32 bool ResolveTargetsFromCommandLinePattern(Setup* setup,
33                                           const std::string& label_pattern,
34                                           bool all_toolchains,
35                                           std::vector<const Target*>* matches) {
36   Value pattern_value(nullptr, label_pattern);
37 
38   Err err;
39   LabelPattern pattern = LabelPattern::GetPattern(
40       SourceDirForCurrentDirectory(setup->build_settings().root_path()),
41       setup->build_settings().root_path_utf8(), pattern_value, &err);
42   if (err.has_error()) {
43     err.PrintToStdout();
44     return false;
45   }
46 
47   if (!all_toolchains) {
48     // By default a pattern with an empty toolchain will match all toolchains.
49     // If the caller wants to default to the main toolchain only, set it
50     // explicitly.
51     if (pattern.toolchain().is_null()) {
52       // No explicit toolchain set.
53       pattern.set_toolchain(setup->loader()->default_toolchain_label());
54     }
55   }
56 
57   std::vector<LabelPattern> pattern_vector;
58   pattern_vector.push_back(pattern);
59   FilterTargetsByPatterns(setup->builder().GetAllResolvedTargets(),
60                           pattern_vector, matches);
61   return true;
62 }
63 
64 // If there's an error, it will be printed and false will be returned.
ResolveStringFromCommandLineInput(Setup * setup,const SourceDir & current_dir,const std::string & input,bool all_toolchains,UniqueVector<const Target * > * target_matches,UniqueVector<const Config * > * config_matches,UniqueVector<const Toolchain * > * toolchain_matches,UniqueVector<SourceFile> * file_matches)65 bool ResolveStringFromCommandLineInput(
66     Setup* setup,
67     const SourceDir& current_dir,
68     const std::string& input,
69     bool all_toolchains,
70     UniqueVector<const Target*>* target_matches,
71     UniqueVector<const Config*>* config_matches,
72     UniqueVector<const Toolchain*>* toolchain_matches,
73     UniqueVector<SourceFile>* file_matches) {
74   if (LabelPattern::HasWildcard(input)) {
75     // For now, only match patterns against targets. It might be nice in the
76     // future to allow the user to specify which types of things they want to
77     // match, but it should probably only match targets by default.
78     std::vector<const Target*> target_match_vector;
79     if (!ResolveTargetsFromCommandLinePattern(setup, input, all_toolchains,
80                                               &target_match_vector))
81       return false;
82     for (const Target* target : target_match_vector)
83       target_matches->push_back(target);
84     return true;
85   }
86 
87   // Try to figure out what this thing is.
88   Err err;
89   Label label = Label::Resolve(
90       current_dir, setup->build_settings().root_path_utf8(),
91       setup->loader()->default_toolchain_label(), Value(nullptr, input), &err);
92   if (err.has_error()) {
93     // Not a valid label, assume this must be a file.
94     err = Err();
95     file_matches->push_back(current_dir.ResolveRelativeFile(
96         Value(nullptr, input), &err, setup->build_settings().root_path_utf8()));
97     if (err.has_error()) {
98       err.PrintToStdout();
99       return false;
100     }
101     return true;
102   }
103 
104   const Item* item = setup->builder().GetItem(label);
105   if (item) {
106     if (const Config* as_config = item->AsConfig())
107       config_matches->push_back(as_config);
108     else if (const Target* as_target = item->AsTarget())
109       target_matches->push_back(as_target);
110     else if (const Toolchain* as_toolchain = item->AsToolchain())
111       toolchain_matches->push_back(as_toolchain);
112   } else {
113     // Not an item, assume this must be a file.
114     file_matches->push_back(current_dir.ResolveRelativeFile(
115         Value(nullptr, input), &err, setup->build_settings().root_path_utf8()));
116     if (err.has_error()) {
117       err.PrintToStdout();
118       return false;
119     }
120   }
121 
122   return true;
123 }
124 
125 enum TargetPrintingMode {
126   TARGET_PRINT_BUILDFILE,
127   TARGET_PRINT_LABEL,
128   TARGET_PRINT_OUTPUT,
129 };
130 
131 // Retrieves the target printing mode based on the command line flags for the
132 // current process. Returns true on success. On error, prints a message to the
133 // console and returns false.
GetTargetPrintingMode(TargetPrintingMode * mode)134 bool GetTargetPrintingMode(TargetPrintingMode* mode) {
135   std::string switch_key = "as";
136   const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
137 
138   if (!cmdline->HasSwitch(switch_key)) {
139     // Default to labels.
140     *mode = TARGET_PRINT_LABEL;
141     return true;
142   }
143 
144   std::string value = cmdline->GetSwitchValueASCII(switch_key);
145   if (value == "buildfile") {
146     *mode = TARGET_PRINT_BUILDFILE;
147     return true;
148   }
149   if (value == "label") {
150     *mode = TARGET_PRINT_LABEL;
151     return true;
152   }
153   if (value == "output") {
154     *mode = TARGET_PRINT_OUTPUT;
155     return true;
156   }
157 
158   Err(Location(), "Invalid value for \"--as\".",
159       "I was expecting \"buildfile\", \"label\", or \"output\" but you\n"
160       "said \"" +
161           value + "\".")
162       .PrintToStdout();
163   return false;
164 }
165 
166 // Returns the target type filter based on the command line flags for the
167 // current process. Returns true on success. On error, prints a message to the
168 // console and returns false.
169 //
170 // Target::UNKNOWN will be set if there is no filter. Target::ACTION_FOREACH
171 // will never be returned. Code applying the filters should apply Target::ACTION
172 // to both ACTION and ACTION_FOREACH.
GetTargetTypeFilter(Target::OutputType * type)173 bool GetTargetTypeFilter(Target::OutputType* type) {
174   std::string switch_key = "type";
175   const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
176 
177   if (!cmdline->HasSwitch(switch_key)) {
178     // Default to unknown -> no filtering.
179     *type = Target::UNKNOWN;
180     return true;
181   }
182 
183   std::string value = cmdline->GetSwitchValueASCII(switch_key);
184   if (value == "group") {
185     *type = Target::GROUP;
186     return true;
187   }
188   if (value == "executable") {
189     *type = Target::EXECUTABLE;
190     return true;
191   }
192   if (value == "shared_library") {
193     *type = Target::SHARED_LIBRARY;
194     return true;
195   }
196   if (value == "loadable_module") {
197     *type = Target::LOADABLE_MODULE;
198     return true;
199   }
200   if (value == "static_library") {
201     *type = Target::STATIC_LIBRARY;
202     return true;
203   }
204   if (value == "source_set") {
205     *type = Target::SOURCE_SET;
206     return true;
207   }
208   if (value == "copy") {
209     *type = Target::COPY_FILES;
210     return true;
211   }
212   if (value == "action") {
213     *type = Target::ACTION;
214     return true;
215   }
216 
217   Err(Location(), "Invalid value for \"--type\".").PrintToStdout();
218   return false;
219 }
220 
221 // Applies any testonly filtering specified on the command line to the given
222 // target set. On failure, prints an error and returns false.
ApplyTestonlyFilter(std::vector<const Target * > * targets)223 bool ApplyTestonlyFilter(std::vector<const Target*>* targets) {
224   const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
225   std::string testonly_key = "testonly";
226 
227   if (targets->empty() || !cmdline->HasSwitch(testonly_key))
228     return true;
229 
230   std::string testonly_value = cmdline->GetSwitchValueASCII(testonly_key);
231   bool testonly = false;
232   if (testonly_value == "true") {
233     testonly = true;
234   } else if (testonly_value != "false") {
235     Err(Location(), "Bad value for --testonly.",
236         "I was expecting --testonly=true or --testonly=false.")
237         .PrintToStdout();
238     return false;
239   }
240 
241   // Filter into a copy of the vector, then replace the output.
242   std::vector<const Target*> result;
243   result.reserve(targets->size());
244 
245   for (const Target* target : *targets) {
246     if (target->testonly() == testonly)
247       result.push_back(target);
248   }
249 
250   *targets = std::move(result);
251   return true;
252 }
253 
254 // Applies any target type filtering specified on the command line to the given
255 // target set. On failure, prints an error and returns false.
ApplyTypeFilter(std::vector<const Target * > * targets)256 bool ApplyTypeFilter(std::vector<const Target*>* targets) {
257   Target::OutputType type = Target::UNKNOWN;
258   if (!GetTargetTypeFilter(&type))
259     return false;
260   if (targets->empty() || type == Target::UNKNOWN)
261     return true;  // Nothing to filter out.
262 
263   // Filter into a copy of the vector, then replace the output.
264   std::vector<const Target*> result;
265   result.reserve(targets->size());
266 
267   for (const Target* target : *targets) {
268     // Make "action" also apply to ACTION_FOREACH.
269     if (target->output_type() == type ||
270         (type == Target::ACTION &&
271          target->output_type() == Target::ACTION_FOREACH))
272       result.push_back(target);
273   }
274 
275   *targets = std::move(result);
276   return true;
277 }
278 
279 // Returns the file path generating this item.
BuildFileForItem(const Item * item)280 base::FilePath BuildFileForItem(const Item* item) {
281   return item->defined_from()->GetRange().begin().file()->physical_name();
282 }
283 
PrintTargetsAsBuildfiles(const std::vector<const Target * > & targets,base::ListValue * out)284 void PrintTargetsAsBuildfiles(const std::vector<const Target*>& targets,
285                               base::ListValue* out) {
286   // Output the set of unique source files.
287   std::set<std::string> unique_files;
288   for (const Target* target : targets)
289     unique_files.insert(FilePathToUTF8(BuildFileForItem(target)));
290 
291   for (const std::string& file : unique_files) {
292     out->AppendString(file);
293   }
294 }
295 
PrintTargetsAsLabels(const std::vector<const Target * > & targets,base::ListValue * out)296 void PrintTargetsAsLabels(const std::vector<const Target*>& targets,
297                           base::ListValue* out) {
298   // Putting the labels into a set automatically sorts them for us.
299   std::set<Label> unique_labels;
300   for (auto* target : targets)
301     unique_labels.insert(target->label());
302 
303   // Grab the label of the default toolchain from the first target.
304   Label default_tc_label = targets[0]->settings()->default_toolchain_label();
305 
306   for (const Label& label : unique_labels) {
307     // Print toolchain only for ones not in the default toolchain.
308     out->AppendString(label.GetUserVisibleName(label.GetToolchainLabel() !=
309                                                default_tc_label));
310   }
311 }
312 
PrintTargetsAsOutputs(const std::vector<const Target * > & targets,base::ListValue * out)313 void PrintTargetsAsOutputs(const std::vector<const Target*>& targets,
314                            base::ListValue* out) {
315   if (targets.empty())
316     return;
317 
318   // Grab the build settings from a random target.
319   const BuildSettings* build_settings =
320       targets[0]->settings()->build_settings();
321 
322   for (const Target* target : targets) {
323     // Use the link output file if there is one, otherwise fall back to the
324     // dependency output file (for actions, for example).
325     OutputFile output_file = target->link_output_file();
326     if (output_file.value().empty())
327       output_file = target->dependency_output_file();
328 
329     SourceFile output_as_source = output_file.AsSourceFile(build_settings);
330     std::string result =
331         RebasePath(output_as_source.value(), build_settings->build_dir(),
332                    build_settings->root_path_utf8());
333     out->AppendString(result);
334   }
335 }
336 
337 #if defined(OS_WIN)
338 // Git bash will remove the first "/" in "//" paths
339 // This also happens for labels assigned to command line parameters, e.g.
340 // --filters
341 // Fix "//" paths, but not absolute and relative paths
FixGitBashLabelEdit(const std::string & label)342 inline std::string FixGitBashLabelEdit(const std::string& label) {
343   static std::unique_ptr<base::Environment> git_bash_env;
344   if (!git_bash_env)
345     git_bash_env = base::Environment::Create();
346 
347   std::string temp_label(label);
348 
349   if (git_bash_env->HasVar(
350           "MSYSTEM") &&        // Only for MinGW based shells like Git Bash
351       temp_label[0] == '/' &&  // Only fix for //foo paths, not /f:oo paths
352       (temp_label.length() < 2 ||
353        (temp_label[1] != '/' &&
354         (temp_label.length() < 3 || temp_label[1] != ':'))))
355     temp_label.insert(0, "/");
356   return temp_label;
357 }
358 #else
359 // Only repair on Windows
FixGitBashLabelEdit(const std::string & label)360 inline std::string FixGitBashLabelEdit(const std::string& label) {
361   return label;
362 }
363 #endif
364 
365 }  // namespace
366 
CommandInfo()367 CommandInfo::CommandInfo()
368     : help_short(nullptr), help(nullptr), runner(nullptr) {}
369 
CommandInfo(const char * in_help_short,const char * in_help,CommandRunner in_runner)370 CommandInfo::CommandInfo(const char* in_help_short,
371                          const char* in_help,
372                          CommandRunner in_runner)
373     : help_short(in_help_short), help(in_help), runner(in_runner) {}
374 
GetCommands()375 const CommandInfoMap& GetCommands() {
376   static CommandInfoMap info_map;
377   if (info_map.empty()) {
378 #define INSERT_COMMAND(cmd) \
379   info_map[k##cmd] = CommandInfo(k##cmd##_HelpShort, k##cmd##_Help, &Run##cmd);
380 
381     INSERT_COMMAND(Analyze)
382     INSERT_COMMAND(Args)
383     INSERT_COMMAND(Check)
384     INSERT_COMMAND(Clean)
385     INSERT_COMMAND(Desc)
386     INSERT_COMMAND(Gen)
387     INSERT_COMMAND(Format)
388     INSERT_COMMAND(Help)
389     INSERT_COMMAND(Meta)
390     INSERT_COMMAND(Ls)
391     INSERT_COMMAND(Path)
392     INSERT_COMMAND(Refs)
393 
394 #undef INSERT_COMMAND
395   }
396   return info_map;
397 }
398 
ResolveTargetFromCommandLineString(Setup * setup,const std::string & label_string)399 const Target* ResolveTargetFromCommandLineString(
400     Setup* setup,
401     const std::string& label_string) {
402   // Need to resolve the label after we know the default toolchain.
403   Label default_toolchain = setup->loader()->default_toolchain_label();
404   Value arg_value(nullptr, FixGitBashLabelEdit(label_string));
405   Err err;
406   Label label = Label::Resolve(
407       SourceDirForCurrentDirectory(setup->build_settings().root_path()),
408       setup->build_settings().root_path_utf8(), default_toolchain, arg_value,
409       &err);
410   if (err.has_error()) {
411     err.PrintToStdout();
412     return nullptr;
413   }
414 
415   const Item* item = setup->builder().GetItem(label);
416   if (!item) {
417     Err(Location(), "Label not found.",
418         label.GetUserVisibleName(false) + " not found.")
419         .PrintToStdout();
420     return nullptr;
421   }
422 
423   const Target* target = item->AsTarget();
424   if (!target) {
425     Err(Location(), "Not a target.",
426         "The \"" + label.GetUserVisibleName(false) +
427             "\" thing\n"
428             "is not a target. Somebody should probably implement this command "
429             "for "
430             "other\nitem types.")
431         .PrintToStdout();
432     return nullptr;
433   }
434 
435   return target;
436 }
437 
ResolveFromCommandLineInput(Setup * setup,const std::vector<std::string> & input,bool all_toolchains,UniqueVector<const Target * > * target_matches,UniqueVector<const Config * > * config_matches,UniqueVector<const Toolchain * > * toolchain_matches,UniqueVector<SourceFile> * file_matches)438 bool ResolveFromCommandLineInput(
439     Setup* setup,
440     const std::vector<std::string>& input,
441     bool all_toolchains,
442     UniqueVector<const Target*>* target_matches,
443     UniqueVector<const Config*>* config_matches,
444     UniqueVector<const Toolchain*>* toolchain_matches,
445     UniqueVector<SourceFile>* file_matches) {
446   if (input.empty()) {
447     Err(Location(), "You need to specify a label, file, or pattern.")
448         .PrintToStdout();
449     return false;
450   }
451 
452   SourceDir cur_dir =
453       SourceDirForCurrentDirectory(setup->build_settings().root_path());
454   for (const auto& cur : input) {
455     if (!ResolveStringFromCommandLineInput(setup, cur_dir, cur, all_toolchains,
456                                            target_matches, config_matches,
457                                            toolchain_matches, file_matches))
458       return false;
459   }
460   return true;
461 }
462 
FilterTargetsByPatterns(const std::vector<const Target * > & input,const std::vector<LabelPattern> & filter,std::vector<const Target * > * output)463 void FilterTargetsByPatterns(const std::vector<const Target*>& input,
464                              const std::vector<LabelPattern>& filter,
465                              std::vector<const Target*>* output) {
466   for (auto* target : input) {
467     for (const auto& pattern : filter) {
468       if (pattern.Matches(target->label())) {
469         output->push_back(target);
470         break;
471       }
472     }
473   }
474 }
475 
FilterTargetsByPatterns(const std::vector<const Target * > & input,const std::vector<LabelPattern> & filter,UniqueVector<const Target * > * output)476 void FilterTargetsByPatterns(const std::vector<const Target*>& input,
477                              const std::vector<LabelPattern>& filter,
478                              UniqueVector<const Target*>* output) {
479   for (auto* target : input) {
480     for (const auto& pattern : filter) {
481       if (pattern.Matches(target->label())) {
482         output->push_back(target);
483         break;
484       }
485     }
486   }
487 }
488 
FilterPatternsFromString(const BuildSettings * build_settings,const std::string & label_list_string,std::vector<LabelPattern> * filters,Err * err)489 bool FilterPatternsFromString(const BuildSettings* build_settings,
490                               const std::string& label_list_string,
491                               std::vector<LabelPattern>* filters,
492                               Err* err) {
493   std::vector<std::string> tokens = base::SplitString(
494       label_list_string, ";", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
495   SourceDir root_dir("//");
496 
497   filters->reserve(tokens.size());
498   for (const std::string& token : tokens) {
499     LabelPattern pattern = LabelPattern::GetPattern(
500         root_dir, build_settings->root_path_utf8(),
501         Value(nullptr, FixGitBashLabelEdit(token)), err);
502     if (err->has_error())
503       return false;
504     filters->push_back(pattern);
505   }
506 
507   return true;
508 }
509 
FilterAndPrintTargets(std::vector<const Target * > * targets,base::ListValue * out)510 void FilterAndPrintTargets(std::vector<const Target*>* targets,
511                            base::ListValue* out) {
512   if (targets->empty())
513     return;
514 
515   if (!ApplyTestonlyFilter(targets))
516     return;
517   if (!ApplyTypeFilter(targets))
518     return;
519 
520   TargetPrintingMode printing_mode = TARGET_PRINT_LABEL;
521   if (targets->empty() || !GetTargetPrintingMode(&printing_mode))
522     return;
523   switch (printing_mode) {
524     case TARGET_PRINT_BUILDFILE:
525       PrintTargetsAsBuildfiles(*targets, out);
526       break;
527     case TARGET_PRINT_LABEL:
528       PrintTargetsAsLabels(*targets, out);
529       break;
530     case TARGET_PRINT_OUTPUT:
531       PrintTargetsAsOutputs(*targets, out);
532       break;
533   }
534 }
535 
FilterAndPrintTargets(bool indent,std::vector<const Target * > * targets)536 void FilterAndPrintTargets(bool indent, std::vector<const Target*>* targets) {
537   base::ListValue tmp;
538   FilterAndPrintTargets(targets, &tmp);
539   for (const auto& value : tmp) {
540     std::string string;
541     value.GetAsString(&string);
542     if (indent)
543       OutputString("  ");
544     OutputString(string);
545     OutputString("\n");
546   }
547 }
548 
FilterAndPrintTargetSet(bool indent,const std::set<const Target * > & targets)549 void FilterAndPrintTargetSet(bool indent,
550                              const std::set<const Target*>& targets) {
551   std::vector<const Target*> target_vector(targets.begin(), targets.end());
552   FilterAndPrintTargets(indent, &target_vector);
553 }
554 
FilterAndPrintTargetSet(const std::set<const Target * > & targets,base::ListValue * out)555 void FilterAndPrintTargetSet(const std::set<const Target*>& targets,
556                              base::ListValue* out) {
557   std::vector<const Target*> target_vector(targets.begin(), targets.end());
558   FilterAndPrintTargets(&target_vector, out);
559 }
560 
561 }  // namespace commands
562