[refactor] More post-NSS WebCrypto cleanups (utility functions).
[chromium-blink-merge.git] / tools / gn / commands.cc
blobab22ebe43c8851b799c8d899404c199567781442
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 // Not a valid label, assume this must be a file.
93 err = Err();
94 file_matches->push_back(current_dir.ResolveRelativeFile(
95 Value(nullptr, input), &err, setup->build_settings().root_path_utf8()));
96 if (err.has_error()) {
97 err.PrintToStdout();
98 return false;
100 return true;
103 const Item* item = setup->builder()->GetItem(label);
104 if (item) {
105 if (const Config* as_config = item->AsConfig())
106 config_matches->push_back(as_config);
107 else if (const Target* as_target = item->AsTarget())
108 target_matches->push_back(as_target);
109 else if (const Toolchain* as_toolchain = item->AsToolchain())
110 toolchain_matches->push_back(as_toolchain);
111 } else {
112 // Not an item, assume this must be a file.
113 file_matches->push_back(current_dir.ResolveRelativeFile(
114 Value(nullptr, input), &err, setup->build_settings().root_path_utf8()));
115 if (err.has_error()) {
116 err.PrintToStdout();
117 return false;
121 return true;
124 enum TargetPrintingMode {
125 TARGET_PRINT_BUILDFILE,
126 TARGET_PRINT_LABEL,
127 TARGET_PRINT_OUTPUT,
130 // Retrieves the target printing mode based on the command line flags for the
131 // current process. Returns true on success. On error, prints a message to the
132 // console and returns false.
133 bool GetTargetPrintingMode(TargetPrintingMode* mode) {
134 std::string switch_key = "as";
135 const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
137 if (!cmdline->HasSwitch(switch_key)) {
138 // Default to labels.
139 *mode = TARGET_PRINT_LABEL;
140 return true;
143 std::string value = cmdline->GetSwitchValueASCII(switch_key);
144 if (value == "buildfile") {
145 *mode = TARGET_PRINT_BUILDFILE;
146 return true;
148 if (value == "label") {
149 *mode = TARGET_PRINT_LABEL;
150 return true;
152 if (value == "output") {
153 *mode = TARGET_PRINT_OUTPUT;
154 return true;
157 Err(Location(), "Invalid value for \"--as\".",
158 "I was expecting \"buildfile\", \"label\", or \"output\" but you\n"
159 "said \"" + value + "\".").PrintToStdout();
160 return false;
163 // Returns the target type filter based on the command line flags for the
164 // current process. Returns true on success. On error, prints a message to the
165 // console and returns false.
167 // Target::UNKNOWN will be set if there is no filter. Target::ACTION_FOREACH
168 // will never be returned. Code applying the filters should apply Target::ACTION
169 // to both ACTION and ACTION_FOREACH.
170 bool GetTargetTypeFilter(Target::OutputType* type) {
171 std::string switch_key = "type";
172 const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
174 if (!cmdline->HasSwitch(switch_key)) {
175 // Default to unknown -> no filtering.
176 *type = Target::UNKNOWN;
177 return true;
180 std::string value = cmdline->GetSwitchValueASCII(switch_key);
181 if (value == "group") {
182 *type = Target::GROUP;
183 return true;
185 if (value == "executable") {
186 *type = Target::EXECUTABLE;
187 return true;
189 if (value == "shared_library") {
190 *type = Target::SHARED_LIBRARY;
191 return true;
193 if (value == "static_library") {
194 *type = Target::STATIC_LIBRARY;
195 return true;
197 if (value == "source_set") {
198 *type = Target::SOURCE_SET;
199 return true;
201 if (value == "copy") {
202 *type = Target::COPY_FILES;
203 return true;
205 if (value == "action") {
206 *type = Target::ACTION;
207 return true;
210 Err(Location(), "Invalid value for \"--type\".").PrintToStdout();
211 return false;
215 // Applies any testonly filtering specified on the command line to the given
216 // target set. On failure, prints an error and returns false.
217 bool ApplyTestonlyFilter(std::vector<const Target*>* targets) {
218 const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
219 std::string testonly_key = "testonly";
221 if (targets->empty() || !cmdline->HasSwitch(testonly_key))
222 return true;
224 std::string testonly_value = cmdline->GetSwitchValueASCII(testonly_key);
225 bool testonly = false;
226 if (testonly_value == "true") {
227 testonly = true;
228 } else if (testonly_value != "false") {
229 Err(Location(), "Bad value for --testonly.",
230 "I was expecting --testonly=true or --testonly=false.")
231 .PrintToStdout();
232 return false;
235 // Filter into a copy of the vector, then swap to output.
236 std::vector<const Target*> result;
237 result.reserve(targets->size());
239 for (const Target* target : *targets) {
240 if (target->testonly() == testonly)
241 result.push_back(target);
244 targets->swap(result);
245 return true;
248 // Applies any target type filtering specified on the command line to the given
249 // target set. On failure, prints an error and returns false.
250 bool ApplyTypeFilter(std::vector<const Target*>* targets) {
251 Target::OutputType type = Target::UNKNOWN;
252 if (!GetTargetTypeFilter(&type))
253 return false;
254 if (targets->empty() || type == Target::UNKNOWN)
255 return true; // Nothing to filter out.
257 // Filter into a copy of the vector, then swap to output.
258 std::vector<const Target*> result;
259 result.reserve(targets->size());
261 for (const Target* target : *targets) {
262 // Make "action" also apply to ACTION_FOREACH.
263 if (target->output_type() == type ||
264 (type == Target::ACTION &&
265 target->output_type() == Target::ACTION_FOREACH))
266 result.push_back(target);
269 targets->swap(result);
270 return true;
273 // Returns the file path generating this item.
274 base::FilePath BuildFileForItem(const Item* item) {
275 return item->defined_from()->GetRange().begin().file()->physical_name();
278 void PrintTargetsAsBuildfiles(bool indent,
279 const std::vector<const Target*>& targets) {
280 // Output the set of unique source files.
281 std::set<std::string> unique_files;
282 for (const Target* target : targets)
283 unique_files.insert(FilePathToUTF8(BuildFileForItem(target)));
285 for (const std::string& file : unique_files) {
286 if (indent)
287 OutputString(" ");
288 OutputString(file + "\n");
292 void PrintTargetsAsLabels(bool indent,
293 const std::vector<const Target*>& targets) {
294 // Putting the labels into a set automatically sorts them for us.
295 std::set<Label> unique_labels;
296 for (const auto& target : targets)
297 unique_labels.insert(target->label());
299 // Grab the label of the default toolchain from the first target.
300 Label default_tc_label =
301 targets[0]->settings()->default_toolchain_label();
303 for (const Label& label : unique_labels) {
304 // Print toolchain only for ones not in the default toolchain.
305 if (indent)
306 OutputString(" ");
307 OutputString(label.GetUserVisibleName(
308 label.GetToolchainLabel() != default_tc_label));
309 OutputString("\n");
313 void PrintTargetsAsOutputs(bool indent,
314 const std::vector<const Target*>& targets) {
315 if (targets.empty())
316 return;
318 // Grab the build settings from a random target.
319 const BuildSettings* build_settings =
320 targets[0]->settings()->build_settings();
322 SourceDir current_dir = SourceDirForCurrentDirectory(
323 build_settings->root_path());
324 for (const Target* target : targets) {
325 // Use the link output file if there is one, otherwise fall back to the
326 // dependency output file (for actions, for example).
327 OutputFile output_file = target->link_output_file();
328 if (output_file.value().empty())
329 output_file = target->dependency_output_file();
331 SourceFile output_as_source =
332 output_file.AsSourceFile(build_settings);
333 std::string result = RebasePath(output_as_source.value(), current_dir,
334 build_settings->root_path_utf8());
335 if (indent)
336 OutputString(" ");
337 OutputString(result);
338 OutputString("\n");
342 } // namespace
344 CommandInfo::CommandInfo()
345 : help_short(nullptr),
346 help(nullptr),
347 runner(nullptr) {
350 CommandInfo::CommandInfo(const char* in_help_short,
351 const char* in_help,
352 CommandRunner in_runner)
353 : help_short(in_help_short),
354 help(in_help),
355 runner(in_runner) {
358 const CommandInfoMap& GetCommands() {
359 static CommandInfoMap info_map;
360 if (info_map.empty()) {
361 #define INSERT_COMMAND(cmd) \
362 info_map[k##cmd] = CommandInfo(k##cmd##_HelpShort, \
363 k##cmd##_Help, \
364 &Run##cmd);
366 INSERT_COMMAND(Args)
367 INSERT_COMMAND(Check)
368 INSERT_COMMAND(Clean)
369 INSERT_COMMAND(Desc)
370 INSERT_COMMAND(Gen)
371 INSERT_COMMAND(Format)
372 INSERT_COMMAND(Help)
373 INSERT_COMMAND(Ls)
374 INSERT_COMMAND(Path)
375 INSERT_COMMAND(Refs)
377 #undef INSERT_COMMAND
379 return info_map;
382 const Target* ResolveTargetFromCommandLineString(
383 Setup* setup,
384 const std::string& label_string) {
385 // Need to resolve the label after we know the default toolchain.
386 Label default_toolchain = setup->loader()->default_toolchain_label();
387 Value arg_value(nullptr, label_string);
388 Err err;
389 Label label = Label::Resolve(SourceDirForCurrentDirectory(
390 setup->build_settings().root_path()),
391 default_toolchain, arg_value, &err);
392 if (err.has_error()) {
393 err.PrintToStdout();
394 return nullptr;
397 const Item* item = setup->builder()->GetItem(label);
398 if (!item) {
399 Err(Location(), "Label not found.",
400 label.GetUserVisibleName(false) + " not found.").PrintToStdout();
401 return nullptr;
404 const Target* target = item->AsTarget();
405 if (!target) {
406 Err(Location(), "Not a target.",
407 "The \"" + label.GetUserVisibleName(false) + "\" thing\n"
408 "is not a target. Somebody should probably implement this command for "
409 "other\nitem types.").PrintToStdout();
410 return nullptr;
413 return target;
416 bool ResolveFromCommandLineInput(
417 Setup* setup,
418 const std::vector<std::string>& input,
419 bool all_toolchains,
420 UniqueVector<const Target*>* target_matches,
421 UniqueVector<const Config*>* config_matches,
422 UniqueVector<const Toolchain*>* toolchain_matches,
423 UniqueVector<SourceFile>* file_matches) {
424 if (input.empty()) {
425 Err(Location(), "You need to specify a label, file, or pattern.")
426 .PrintToStdout();
427 return false;
430 SourceDir cur_dir =
431 SourceDirForCurrentDirectory(setup->build_settings().root_path());
432 for (const auto& cur : input) {
433 if (!ResolveStringFromCommandLineInput(setup, cur_dir, cur,
434 all_toolchains, target_matches,
435 config_matches, toolchain_matches,
436 file_matches))
437 return false;
439 return true;
442 void FilterTargetsByPatterns(const std::vector<const Target*>& input,
443 const std::vector<LabelPattern>& filter,
444 std::vector<const Target*>* output) {
445 for (const auto& target : input) {
446 for (const auto& pattern : filter) {
447 if (pattern.Matches(target->label())) {
448 output->push_back(target);
449 break;
455 void FilterTargetsByPatterns(const std::vector<const Target*>& input,
456 const std::vector<LabelPattern>& filter,
457 UniqueVector<const Target*>* output) {
458 for (const auto& target : input) {
459 for (const auto& pattern : filter) {
460 if (pattern.Matches(target->label())) {
461 output->push_back(target);
462 break;
468 void FilterAndPrintTargets(bool indent, std::vector<const Target*>* targets) {
469 if (targets->empty())
470 return;
472 if (!ApplyTestonlyFilter(targets))
473 return;
474 if (!ApplyTypeFilter(targets))
475 return;
477 TargetPrintingMode printing_mode = TARGET_PRINT_LABEL;
478 if (targets->empty() || !GetTargetPrintingMode(&printing_mode))
479 return;
480 switch (printing_mode) {
481 case TARGET_PRINT_BUILDFILE:
482 PrintTargetsAsBuildfiles(indent, *targets);
483 break;
484 case TARGET_PRINT_LABEL:
485 PrintTargetsAsLabels(indent, *targets);
486 break;
487 case TARGET_PRINT_OUTPUT:
488 PrintTargetsAsOutputs(indent, *targets);
489 break;
493 void FilterAndPrintTargetSet(bool indent,
494 const std::set<const Target*>& targets) {
495 std::vector<const Target*> target_vector(targets.begin(), targets.end());
496 FilterAndPrintTargets(indent, &target_vector);
499 } // namespace commands