Add Media Router Action stub implementation.
[chromium-blink-merge.git] / tools / gn / commands.cc
blob81adf3450e12a7d37dad2fae193925038c2a06cc
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.
5 #include "tools/gn/commands.h"
7 #include "base/command_line.h"
8 #include "tools/gn/builder.h"
9 #include "tools/gn/filesystem_utils.h"
10 #include "tools/gn/item.h"
11 #include "tools/gn/label.h"
12 #include "tools/gn/label_pattern.h"
13 #include "tools/gn/setup.h"
14 #include "tools/gn/standard_out.h"
15 #include "tools/gn/target.h"
17 namespace commands {
19 namespace {
21 // Like above but the input string can be a pattern that matches multiple
22 // targets. If the input does not parse as a pattern, prints and error and
23 // returns false. If the pattern is valid, fills the vector (which might be
24 // empty if there are no matches) and returns true.
26 // If all_toolchains is false, a pattern with an unspecified toolchain will
27 // match the default toolchain only. If true, all toolchains will be matched.
28 bool ResolveTargetsFromCommandLinePattern(
29 Setup* setup,
30 const std::string& label_pattern,
31 bool all_toolchains,
32 std::vector<const Target*>* matches) {
33 Value pattern_value(nullptr, label_pattern);
35 Err err;
36 LabelPattern pattern = LabelPattern::GetPattern(
37 SourceDirForCurrentDirectory(setup->build_settings().root_path()),
38 pattern_value,
39 &err);
40 if (err.has_error()) {
41 err.PrintToStdout();
42 return false;
45 if (!all_toolchains) {
46 // By default a pattern with an empty toolchain will match all toolchains.
47 // If the caller wants to default to the main toolchain only, set it
48 // explicitly.
49 if (pattern.toolchain().is_null()) {
50 // No explicit toolchain set.
51 pattern.set_toolchain(setup->loader()->default_toolchain_label());
55 std::vector<LabelPattern> pattern_vector;
56 pattern_vector.push_back(pattern);
57 FilterTargetsByPatterns(setup->builder()->GetAllResolvedTargets(),
58 pattern_vector, matches);
59 return true;
63 // If there's an error, it will be printed and false will be returned.
64 bool ResolveStringFromCommandLineInput(
65 Setup* setup,
66 const SourceDir& current_dir,
67 const std::string& input,
68 bool all_toolchains,
69 UniqueVector<const Target*>* target_matches,
70 UniqueVector<const Config*>* config_matches,
71 UniqueVector<const Toolchain*>* toolchain_matches,
72 UniqueVector<SourceFile>* file_matches) {
73 if (LabelPattern::HasWildcard(input)) {
74 // For now, only match patterns against targets. It might be nice in the
75 // future to allow the user to specify which types of things they want to
76 // match, but it should probably only match targets by default.
77 std::vector<const Target*> target_match_vector;
78 if (!ResolveTargetsFromCommandLinePattern(setup, input, all_toolchains,
79 &target_match_vector))
80 return false;
81 for (const Target* target : target_match_vector)
82 target_matches->push_back(target);
83 return true;
86 // Try to figure out what this thing is.
87 Err err;
88 Label label = Label::Resolve(current_dir,
89 setup->loader()->default_toolchain_label(),
90 Value(nullptr, input), &err);
91 if (err.has_error()) {
92 err.PrintToStdout();
93 return false;
96 const Item* item = setup->builder()->GetItem(label);
97 if (item) {
98 if (const Config* as_config = item->AsConfig())
99 config_matches->push_back(as_config);
100 else if (const Target* as_target = item->AsTarget())
101 target_matches->push_back(as_target);
102 else if (const Toolchain* as_toolchain = item->AsToolchain())
103 toolchain_matches->push_back(as_toolchain);
104 } else {
105 // Not an item, assume this must be a file.
106 file_matches->push_back(current_dir.ResolveRelativeFile(
107 input, setup->build_settings().root_path_utf8()));
110 return true;
113 enum TargetPrintingMode {
114 TARGET_PRINT_BUILDFILE,
115 TARGET_PRINT_LABEL,
116 TARGET_PRINT_OUTPUT,
119 // Retrieves the target printing mode based on the command line flags for the
120 // current process. Returns true on success. On error, prints a message to the
121 // console and returns false.
122 bool GetTargetPrintingMode(TargetPrintingMode* mode) {
123 std::string switch_key = "as";
124 const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
126 if (!cmdline->HasSwitch(switch_key)) {
127 // Default to labels.
128 *mode = TARGET_PRINT_LABEL;
129 return true;
132 std::string value = cmdline->GetSwitchValueASCII(switch_key);
133 if (value == "buildfile") {
134 *mode = TARGET_PRINT_BUILDFILE;
135 return true;
137 if (value == "label") {
138 *mode = TARGET_PRINT_LABEL;
139 return true;
141 if (value == "output") {
142 *mode = TARGET_PRINT_OUTPUT;
143 return true;
146 Err(Location(), "Invalid value for \"--as\".",
147 "I was expecting \"buildfile\", \"label\", or \"output\" but you\n"
148 "said \"" + value + "\".").PrintToStdout();
149 return false;
152 // Returns the target type filter based on the command line flags for the
153 // current process. Returns true on success. On error, prints a message to the
154 // console and returns false.
156 // Target::UNKNOWN will be set if there is no filter. Target::ACTION_FOREACH
157 // will never be returned. Code applying the filters should apply Target::ACTION
158 // to both ACTION and ACTION_FOREACH.
159 bool GetTargetTypeFilter(Target::OutputType* type) {
160 std::string switch_key = "type";
161 const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
163 if (!cmdline->HasSwitch(switch_key)) {
164 // Default to unknown -> no filtering.
165 *type = Target::UNKNOWN;
166 return true;
169 std::string value = cmdline->GetSwitchValueASCII(switch_key);
170 if (value == "group") {
171 *type = Target::GROUP;
172 return true;
174 if (value == "executable") {
175 *type = Target::EXECUTABLE;
176 return true;
178 if (value == "shared_library") {
179 *type = Target::SHARED_LIBRARY;
180 return true;
182 if (value == "static_library") {
183 *type = Target::STATIC_LIBRARY;
184 return true;
186 if (value == "source_set") {
187 *type = Target::SOURCE_SET;
188 return true;
190 if (value == "copy") {
191 *type = Target::COPY_FILES;
192 return true;
194 if (value == "action") {
195 *type = Target::ACTION;
196 return true;
199 Err(Location(), "Invalid value for \"--type\".").PrintToStdout();
200 return false;
204 // Applies any testonly filtering specified on the command line to the given
205 // target set. On failure, prints an error and returns false.
206 bool ApplyTestonlyFilter(std::vector<const Target*>* targets) {
207 const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
208 std::string testonly_key = "testonly";
210 if (targets->empty() || !cmdline->HasSwitch(testonly_key))
211 return true;
213 std::string testonly_value = cmdline->GetSwitchValueASCII(testonly_key);
214 bool testonly = false;
215 if (testonly_value == "true") {
216 testonly = true;
217 } else if (testonly_value != "false") {
218 Err(Location(), "Bad value for --testonly.",
219 "I was expecting --testonly=true or --testonly=false.")
220 .PrintToStdout();
221 return false;
224 // Filter into a copy of the vector, then swap to output.
225 std::vector<const Target*> result;
226 result.reserve(targets->size());
228 for (const Target* target : *targets) {
229 if (target->testonly() == testonly)
230 result.push_back(target);
233 targets->swap(result);
234 return true;
237 // Applies any target type filtering specified on the command line to the given
238 // target set. On failure, prints an error and returns false.
239 bool ApplyTypeFilter(std::vector<const Target*>* targets) {
240 Target::OutputType type = Target::UNKNOWN;
241 if (!GetTargetTypeFilter(&type))
242 return false;
243 if (targets->empty() || type == Target::UNKNOWN)
244 return true; // Nothing to filter out.
246 // Filter into a copy of the vector, then swap to output.
247 std::vector<const Target*> result;
248 result.reserve(targets->size());
250 for (const Target* target : *targets) {
251 // Make "action" also apply to ACTION_FOREACH.
252 if (target->output_type() == type ||
253 (type == Target::ACTION &&
254 target->output_type() == Target::ACTION_FOREACH))
255 result.push_back(target);
258 targets->swap(result);
259 return true;
262 // Returns the file path generating this item.
263 base::FilePath BuildFileForItem(const Item* item) {
264 return item->defined_from()->GetRange().begin().file()->physical_name();
267 void PrintTargetsAsBuildfiles(bool indent,
268 const std::vector<const Target*>& targets) {
269 // Output the set of unique source files.
270 std::set<std::string> unique_files;
271 for (const Target* target : targets)
272 unique_files.insert(FilePathToUTF8(BuildFileForItem(target)));
274 for (const std::string& file : unique_files) {
275 if (indent)
276 OutputString(" ");
277 OutputString(file + "\n");
281 void PrintTargetsAsLabels(bool indent,
282 const std::vector<const Target*>& targets) {
283 // Putting the labels into a set automatically sorts them for us.
284 std::set<Label> unique_labels;
285 for (const auto& target : targets)
286 unique_labels.insert(target->label());
288 // Grab the label of the default toolchain from the first target.
289 Label default_tc_label =
290 targets[0]->settings()->default_toolchain_label();
292 for (const Label& label : unique_labels) {
293 // Print toolchain only for ones not in the default toolchain.
294 if (indent)
295 OutputString(" ");
296 OutputString(label.GetUserVisibleName(
297 label.GetToolchainLabel() != default_tc_label));
298 OutputString("\n");
302 void PrintTargetsAsOutputs(bool indent,
303 const std::vector<const Target*>& targets) {
304 if (targets.empty())
305 return;
307 // Grab the build settings from a random target.
308 const BuildSettings* build_settings =
309 targets[0]->settings()->build_settings();
311 SourceDir current_dir = SourceDirForCurrentDirectory(
312 build_settings->root_path());
313 for (const Target* target : targets) {
314 // Use the link output file if there is one, otherwise fall back to the
315 // dependency output file (for actions, for example).
316 OutputFile output_file = target->link_output_file();
317 if (output_file.value().empty())
318 output_file = target->dependency_output_file();
320 SourceFile output_as_source =
321 output_file.AsSourceFile(build_settings);
322 std::string result = RebasePath(output_as_source.value(), current_dir,
323 build_settings->root_path_utf8());
324 if (indent)
325 OutputString(" ");
326 OutputString(result);
327 OutputString("\n");
331 } // namespace
333 CommandInfo::CommandInfo()
334 : help_short(nullptr),
335 help(nullptr),
336 runner(nullptr) {
339 CommandInfo::CommandInfo(const char* in_help_short,
340 const char* in_help,
341 CommandRunner in_runner)
342 : help_short(in_help_short),
343 help(in_help),
344 runner(in_runner) {
347 const CommandInfoMap& GetCommands() {
348 static CommandInfoMap info_map;
349 if (info_map.empty()) {
350 #define INSERT_COMMAND(cmd) \
351 info_map[k##cmd] = CommandInfo(k##cmd##_HelpShort, \
352 k##cmd##_Help, \
353 &Run##cmd);
355 INSERT_COMMAND(Args)
356 INSERT_COMMAND(Check)
357 INSERT_COMMAND(Clean)
358 INSERT_COMMAND(Desc)
359 INSERT_COMMAND(Gen)
360 INSERT_COMMAND(Format)
361 INSERT_COMMAND(Help)
362 INSERT_COMMAND(Ls)
363 INSERT_COMMAND(Refs)
365 #undef INSERT_COMMAND
367 return info_map;
370 const Target* ResolveTargetFromCommandLineString(
371 Setup* setup,
372 const std::string& label_string) {
373 // Need to resolve the label after we know the default toolchain.
374 Label default_toolchain = setup->loader()->default_toolchain_label();
375 Value arg_value(nullptr, label_string);
376 Err err;
377 Label label = Label::Resolve(SourceDirForCurrentDirectory(
378 setup->build_settings().root_path()),
379 default_toolchain, arg_value, &err);
380 if (err.has_error()) {
381 err.PrintToStdout();
382 return nullptr;
385 const Item* item = setup->builder()->GetItem(label);
386 if (!item) {
387 Err(Location(), "Label not found.",
388 label.GetUserVisibleName(false) + " not found.").PrintToStdout();
389 return nullptr;
392 const Target* target = item->AsTarget();
393 if (!target) {
394 Err(Location(), "Not a target.",
395 "The \"" + label.GetUserVisibleName(false) + "\" thing\n"
396 "is not a target. Somebody should probably implement this command for "
397 "other\nitem types.");
398 return nullptr;
401 return target;
404 bool ResolveFromCommandLineInput(
405 Setup* setup,
406 const std::vector<std::string>& input,
407 bool all_toolchains,
408 UniqueVector<const Target*>* target_matches,
409 UniqueVector<const Config*>* config_matches,
410 UniqueVector<const Toolchain*>* toolchain_matches,
411 UniqueVector<SourceFile>* file_matches) {
412 if (input.empty()) {
413 Err(Location(), "You need to specify a label, file, or pattern.")
414 .PrintToStdout();
415 return false;
418 SourceDir cur_dir =
419 SourceDirForCurrentDirectory(setup->build_settings().root_path());
420 for (const auto& cur : input) {
421 if (!ResolveStringFromCommandLineInput(setup, cur_dir, cur,
422 all_toolchains, target_matches,
423 config_matches, toolchain_matches,
424 file_matches))
425 return false;
427 return true;
430 void FilterTargetsByPatterns(const std::vector<const Target*>& input,
431 const std::vector<LabelPattern>& filter,
432 std::vector<const Target*>* output) {
433 for (const auto& target : input) {
434 for (const auto& pattern : filter) {
435 if (pattern.Matches(target->label())) {
436 output->push_back(target);
437 break;
443 void FilterTargetsByPatterns(const std::vector<const Target*>& input,
444 const std::vector<LabelPattern>& filter,
445 UniqueVector<const Target*>* output) {
446 for (const auto& target : input) {
447 for (const auto& pattern : filter) {
448 if (pattern.Matches(target->label())) {
449 output->push_back(target);
450 break;
456 void FilterAndPrintTargets(bool indent, std::vector<const Target*>* targets) {
457 if (targets->empty())
458 return;
460 if (!ApplyTestonlyFilter(targets))
461 return;
462 if (!ApplyTypeFilter(targets))
463 return;
465 TargetPrintingMode printing_mode = TARGET_PRINT_LABEL;
466 if (targets->empty() || !GetTargetPrintingMode(&printing_mode))
467 return;
468 switch (printing_mode) {
469 case TARGET_PRINT_BUILDFILE:
470 PrintTargetsAsBuildfiles(indent, *targets);
471 break;
472 case TARGET_PRINT_LABEL:
473 PrintTargetsAsLabels(indent, *targets);
474 break;
475 case TARGET_PRINT_OUTPUT:
476 PrintTargetsAsOutputs(indent, *targets);
477 break;
481 void FilterAndPrintTargetSet(bool indent,
482 const std::set<const Target*>& targets) {
483 std::vector<const Target*> target_vector(targets.begin(), targets.end());
484 FilterAndPrintTargets(indent, &target_vector);
487 } // namespace commands