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"
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(
30 const std::string
& label_pattern
,
32 std::vector
<const Target
*>* matches
) {
33 Value
pattern_value(nullptr, label_pattern
);
36 LabelPattern pattern
= LabelPattern::GetPattern(
37 SourceDirForCurrentDirectory(setup
->build_settings().root_path()),
40 if (err
.has_error()) {
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
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
);
63 // If there's an error, it will be printed and false will be returned.
64 bool ResolveStringFromCommandLineInput(
66 const SourceDir
& current_dir
,
67 const std::string
& input
,
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
))
81 for (const Target
* target
: target_match_vector
)
82 target_matches
->push_back(target
);
86 // Try to figure out what this thing is.
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 file_matches
->push_back(current_dir
.ResolveRelativeFile(
94 input
, setup
->build_settings().root_path_utf8()));
98 const Item
* item
= setup
->builder()->GetItem(label
);
100 if (const Config
* as_config
= item
->AsConfig())
101 config_matches
->push_back(as_config
);
102 else if (const Target
* as_target
= item
->AsTarget())
103 target_matches
->push_back(as_target
);
104 else if (const Toolchain
* as_toolchain
= item
->AsToolchain())
105 toolchain_matches
->push_back(as_toolchain
);
107 // Not an item, assume this must be a file.
108 file_matches
->push_back(current_dir
.ResolveRelativeFile(
109 input
, setup
->build_settings().root_path_utf8()));
115 enum TargetPrintingMode
{
116 TARGET_PRINT_BUILDFILE
,
121 // Retrieves the target printing mode based on the command line flags for the
122 // current process. Returns true on success. On error, prints a message to the
123 // console and returns false.
124 bool GetTargetPrintingMode(TargetPrintingMode
* mode
) {
125 std::string switch_key
= "as";
126 const base::CommandLine
* cmdline
= base::CommandLine::ForCurrentProcess();
128 if (!cmdline
->HasSwitch(switch_key
)) {
129 // Default to labels.
130 *mode
= TARGET_PRINT_LABEL
;
134 std::string value
= cmdline
->GetSwitchValueASCII(switch_key
);
135 if (value
== "buildfile") {
136 *mode
= TARGET_PRINT_BUILDFILE
;
139 if (value
== "label") {
140 *mode
= TARGET_PRINT_LABEL
;
143 if (value
== "output") {
144 *mode
= TARGET_PRINT_OUTPUT
;
148 Err(Location(), "Invalid value for \"--as\".",
149 "I was expecting \"buildfile\", \"label\", or \"output\" but you\n"
150 "said \"" + value
+ "\".").PrintToStdout();
154 // Returns the target type filter based on the command line flags for the
155 // current process. Returns true on success. On error, prints a message to the
156 // console and returns false.
158 // Target::UNKNOWN will be set if there is no filter. Target::ACTION_FOREACH
159 // will never be returned. Code applying the filters should apply Target::ACTION
160 // to both ACTION and ACTION_FOREACH.
161 bool GetTargetTypeFilter(Target::OutputType
* type
) {
162 std::string switch_key
= "type";
163 const base::CommandLine
* cmdline
= base::CommandLine::ForCurrentProcess();
165 if (!cmdline
->HasSwitch(switch_key
)) {
166 // Default to unknown -> no filtering.
167 *type
= Target::UNKNOWN
;
171 std::string value
= cmdline
->GetSwitchValueASCII(switch_key
);
172 if (value
== "group") {
173 *type
= Target::GROUP
;
176 if (value
== "executable") {
177 *type
= Target::EXECUTABLE
;
180 if (value
== "shared_library") {
181 *type
= Target::SHARED_LIBRARY
;
184 if (value
== "static_library") {
185 *type
= Target::STATIC_LIBRARY
;
188 if (value
== "source_set") {
189 *type
= Target::SOURCE_SET
;
192 if (value
== "copy") {
193 *type
= Target::COPY_FILES
;
196 if (value
== "action") {
197 *type
= Target::ACTION
;
201 Err(Location(), "Invalid value for \"--type\".").PrintToStdout();
206 // Applies any testonly filtering specified on the command line to the given
207 // target set. On failure, prints an error and returns false.
208 bool ApplyTestonlyFilter(std::vector
<const Target
*>* targets
) {
209 const base::CommandLine
* cmdline
= base::CommandLine::ForCurrentProcess();
210 std::string testonly_key
= "testonly";
212 if (targets
->empty() || !cmdline
->HasSwitch(testonly_key
))
215 std::string testonly_value
= cmdline
->GetSwitchValueASCII(testonly_key
);
216 bool testonly
= false;
217 if (testonly_value
== "true") {
219 } else if (testonly_value
!= "false") {
220 Err(Location(), "Bad value for --testonly.",
221 "I was expecting --testonly=true or --testonly=false.")
226 // Filter into a copy of the vector, then swap to output.
227 std::vector
<const Target
*> result
;
228 result
.reserve(targets
->size());
230 for (const Target
* target
: *targets
) {
231 if (target
->testonly() == testonly
)
232 result
.push_back(target
);
235 targets
->swap(result
);
239 // Applies any target type filtering specified on the command line to the given
240 // target set. On failure, prints an error and returns false.
241 bool ApplyTypeFilter(std::vector
<const Target
*>* targets
) {
242 Target::OutputType type
= Target::UNKNOWN
;
243 if (!GetTargetTypeFilter(&type
))
245 if (targets
->empty() || type
== Target::UNKNOWN
)
246 return true; // Nothing to filter out.
248 // Filter into a copy of the vector, then swap to output.
249 std::vector
<const Target
*> result
;
250 result
.reserve(targets
->size());
252 for (const Target
* target
: *targets
) {
253 // Make "action" also apply to ACTION_FOREACH.
254 if (target
->output_type() == type
||
255 (type
== Target::ACTION
&&
256 target
->output_type() == Target::ACTION_FOREACH
))
257 result
.push_back(target
);
260 targets
->swap(result
);
264 // Returns the file path generating this item.
265 base::FilePath
BuildFileForItem(const Item
* item
) {
266 return item
->defined_from()->GetRange().begin().file()->physical_name();
269 void PrintTargetsAsBuildfiles(bool indent
,
270 const std::vector
<const Target
*>& targets
) {
271 // Output the set of unique source files.
272 std::set
<std::string
> unique_files
;
273 for (const Target
* target
: targets
)
274 unique_files
.insert(FilePathToUTF8(BuildFileForItem(target
)));
276 for (const std::string
& file
: unique_files
) {
279 OutputString(file
+ "\n");
283 void PrintTargetsAsLabels(bool indent
,
284 const std::vector
<const Target
*>& targets
) {
285 // Putting the labels into a set automatically sorts them for us.
286 std::set
<Label
> unique_labels
;
287 for (const auto& target
: targets
)
288 unique_labels
.insert(target
->label());
290 // Grab the label of the default toolchain from the first target.
291 Label default_tc_label
=
292 targets
[0]->settings()->default_toolchain_label();
294 for (const Label
& label
: unique_labels
) {
295 // Print toolchain only for ones not in the default toolchain.
298 OutputString(label
.GetUserVisibleName(
299 label
.GetToolchainLabel() != default_tc_label
));
304 void PrintTargetsAsOutputs(bool indent
,
305 const std::vector
<const Target
*>& targets
) {
309 // Grab the build settings from a random target.
310 const BuildSettings
* build_settings
=
311 targets
[0]->settings()->build_settings();
313 SourceDir current_dir
= SourceDirForCurrentDirectory(
314 build_settings
->root_path());
315 for (const Target
* target
: targets
) {
316 // Use the link output file if there is one, otherwise fall back to the
317 // dependency output file (for actions, for example).
318 OutputFile output_file
= target
->link_output_file();
319 if (output_file
.value().empty())
320 output_file
= target
->dependency_output_file();
322 SourceFile output_as_source
=
323 output_file
.AsSourceFile(build_settings
);
324 std::string result
= RebasePath(output_as_source
.value(), current_dir
,
325 build_settings
->root_path_utf8());
328 OutputString(result
);
335 CommandInfo::CommandInfo()
336 : help_short(nullptr),
341 CommandInfo::CommandInfo(const char* in_help_short
,
343 CommandRunner in_runner
)
344 : help_short(in_help_short
),
349 const CommandInfoMap
& GetCommands() {
350 static CommandInfoMap info_map
;
351 if (info_map
.empty()) {
352 #define INSERT_COMMAND(cmd) \
353 info_map[k##cmd] = CommandInfo(k##cmd##_HelpShort, \
358 INSERT_COMMAND(Check
)
359 INSERT_COMMAND(Clean
)
362 INSERT_COMMAND(Format
)
367 #undef INSERT_COMMAND
372 const Target
* ResolveTargetFromCommandLineString(
374 const std::string
& label_string
) {
375 // Need to resolve the label after we know the default toolchain.
376 Label default_toolchain
= setup
->loader()->default_toolchain_label();
377 Value
arg_value(nullptr, label_string
);
379 Label label
= Label::Resolve(SourceDirForCurrentDirectory(
380 setup
->build_settings().root_path()),
381 default_toolchain
, arg_value
, &err
);
382 if (err
.has_error()) {
387 const Item
* item
= setup
->builder()->GetItem(label
);
389 Err(Location(), "Label not found.",
390 label
.GetUserVisibleName(false) + " not found.").PrintToStdout();
394 const Target
* target
= item
->AsTarget();
396 Err(Location(), "Not a target.",
397 "The \"" + label
.GetUserVisibleName(false) + "\" thing\n"
398 "is not a target. Somebody should probably implement this command for "
399 "other\nitem types.");
406 bool ResolveFromCommandLineInput(
408 const std::vector
<std::string
>& input
,
410 UniqueVector
<const Target
*>* target_matches
,
411 UniqueVector
<const Config
*>* config_matches
,
412 UniqueVector
<const Toolchain
*>* toolchain_matches
,
413 UniqueVector
<SourceFile
>* file_matches
) {
415 Err(Location(), "You need to specify a label, file, or pattern.")
421 SourceDirForCurrentDirectory(setup
->build_settings().root_path());
422 for (const auto& cur
: input
) {
423 if (!ResolveStringFromCommandLineInput(setup
, cur_dir
, cur
,
424 all_toolchains
, target_matches
,
425 config_matches
, toolchain_matches
,
432 void FilterTargetsByPatterns(const std::vector
<const Target
*>& input
,
433 const std::vector
<LabelPattern
>& filter
,
434 std::vector
<const Target
*>* output
) {
435 for (const auto& target
: input
) {
436 for (const auto& pattern
: filter
) {
437 if (pattern
.Matches(target
->label())) {
438 output
->push_back(target
);
445 void FilterTargetsByPatterns(const std::vector
<const Target
*>& input
,
446 const std::vector
<LabelPattern
>& filter
,
447 UniqueVector
<const Target
*>* output
) {
448 for (const auto& target
: input
) {
449 for (const auto& pattern
: filter
) {
450 if (pattern
.Matches(target
->label())) {
451 output
->push_back(target
);
458 void FilterAndPrintTargets(bool indent
, std::vector
<const Target
*>* targets
) {
459 if (targets
->empty())
462 if (!ApplyTestonlyFilter(targets
))
464 if (!ApplyTypeFilter(targets
))
467 TargetPrintingMode printing_mode
= TARGET_PRINT_LABEL
;
468 if (targets
->empty() || !GetTargetPrintingMode(&printing_mode
))
470 switch (printing_mode
) {
471 case TARGET_PRINT_BUILDFILE
:
472 PrintTargetsAsBuildfiles(indent
, *targets
);
474 case TARGET_PRINT_LABEL
:
475 PrintTargetsAsLabels(indent
, *targets
);
477 case TARGET_PRINT_OUTPUT
:
478 PrintTargetsAsOutputs(indent
, *targets
);
483 void FilterAndPrintTargetSet(bool indent
,
484 const std::set
<const Target
*>& targets
) {
485 std::vector
<const Target
*> target_vector(targets
.begin(), targets
.end());
486 FilterAndPrintTargets(indent
, &target_vector
);
489 } // namespace commands