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.
94 file_matches
->push_back(current_dir
.ResolveRelativeFile(
95 Value(nullptr, input
), &err
, setup
->build_settings().root_path_utf8()));
96 if (err
.has_error()) {
103 const Item
* item
= setup
->builder()->GetItem(label
);
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
);
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()) {
124 enum TargetPrintingMode
{
125 TARGET_PRINT_BUILDFILE
,
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
;
143 std::string value
= cmdline
->GetSwitchValueASCII(switch_key
);
144 if (value
== "buildfile") {
145 *mode
= TARGET_PRINT_BUILDFILE
;
148 if (value
== "label") {
149 *mode
= TARGET_PRINT_LABEL
;
152 if (value
== "output") {
153 *mode
= TARGET_PRINT_OUTPUT
;
157 Err(Location(), "Invalid value for \"--as\".",
158 "I was expecting \"buildfile\", \"label\", or \"output\" but you\n"
159 "said \"" + value
+ "\".").PrintToStdout();
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
;
180 std::string value
= cmdline
->GetSwitchValueASCII(switch_key
);
181 if (value
== "group") {
182 *type
= Target::GROUP
;
185 if (value
== "executable") {
186 *type
= Target::EXECUTABLE
;
189 if (value
== "shared_library") {
190 *type
= Target::SHARED_LIBRARY
;
193 if (value
== "static_library") {
194 *type
= Target::STATIC_LIBRARY
;
197 if (value
== "source_set") {
198 *type
= Target::SOURCE_SET
;
201 if (value
== "copy") {
202 *type
= Target::COPY_FILES
;
205 if (value
== "action") {
206 *type
= Target::ACTION
;
210 Err(Location(), "Invalid value for \"--type\".").PrintToStdout();
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
))
224 std::string testonly_value
= cmdline
->GetSwitchValueASCII(testonly_key
);
225 bool testonly
= false;
226 if (testonly_value
== "true") {
228 } else if (testonly_value
!= "false") {
229 Err(Location(), "Bad value for --testonly.",
230 "I was expecting --testonly=true or --testonly=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
);
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
))
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
);
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
) {
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.
307 OutputString(label
.GetUserVisibleName(
308 label
.GetToolchainLabel() != default_tc_label
));
313 void PrintTargetsAsOutputs(bool indent
,
314 const std::vector
<const Target
*>& targets
) {
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());
337 OutputString(result
);
344 CommandInfo::CommandInfo()
345 : help_short(nullptr),
350 CommandInfo::CommandInfo(const char* in_help_short
,
352 CommandRunner in_runner
)
353 : help_short(in_help_short
),
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, \
367 INSERT_COMMAND(Check
)
368 INSERT_COMMAND(Clean
)
371 INSERT_COMMAND(Format
)
377 #undef INSERT_COMMAND
382 const Target
* ResolveTargetFromCommandLineString(
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
);
389 Label label
= Label::Resolve(SourceDirForCurrentDirectory(
390 setup
->build_settings().root_path()),
391 default_toolchain
, arg_value
, &err
);
392 if (err
.has_error()) {
397 const Item
* item
= setup
->builder()->GetItem(label
);
399 Err(Location(), "Label not found.",
400 label
.GetUserVisibleName(false) + " not found.").PrintToStdout();
404 const Target
* target
= item
->AsTarget();
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();
416 bool ResolveFromCommandLineInput(
418 const std::vector
<std::string
>& input
,
420 UniqueVector
<const Target
*>* target_matches
,
421 UniqueVector
<const Config
*>* config_matches
,
422 UniqueVector
<const Toolchain
*>* toolchain_matches
,
423 UniqueVector
<SourceFile
>* file_matches
) {
425 Err(Location(), "You need to specify a label, file, or pattern.")
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
,
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
);
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
);
468 void FilterAndPrintTargets(bool indent
, std::vector
<const Target
*>* targets
) {
469 if (targets
->empty())
472 if (!ApplyTestonlyFilter(targets
))
474 if (!ApplyTypeFilter(targets
))
477 TargetPrintingMode printing_mode
= TARGET_PRINT_LABEL
;
478 if (targets
->empty() || !GetTargetPrintingMode(&printing_mode
))
480 switch (printing_mode
) {
481 case TARGET_PRINT_BUILDFILE
:
482 PrintTargetsAsBuildfiles(indent
, *targets
);
484 case TARGET_PRINT_LABEL
:
485 PrintTargetsAsLabels(indent
, *targets
);
487 case TARGET_PRINT_OUTPUT
:
488 PrintTargetsAsOutputs(indent
, *targets
);
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