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()) {
96 const Item
* item
= setup
->builder()->GetItem(label
);
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
);
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()));
113 enum TargetPrintingMode
{
114 TARGET_PRINT_BUILDFILE
,
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
;
132 std::string value
= cmdline
->GetSwitchValueASCII(switch_key
);
133 if (value
== "buildfile") {
134 *mode
= TARGET_PRINT_BUILDFILE
;
137 if (value
== "label") {
138 *mode
= TARGET_PRINT_LABEL
;
141 if (value
== "output") {
142 *mode
= TARGET_PRINT_OUTPUT
;
146 Err(Location(), "Invalid value for \"--as\".",
147 "I was expecting \"buildfile\", \"label\", or \"output\" but you\n"
148 "said \"" + value
+ "\".").PrintToStdout();
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
;
169 std::string value
= cmdline
->GetSwitchValueASCII(switch_key
);
170 if (value
== "group") {
171 *type
= Target::GROUP
;
174 if (value
== "executable") {
175 *type
= Target::EXECUTABLE
;
178 if (value
== "shared_library") {
179 *type
= Target::SHARED_LIBRARY
;
182 if (value
== "static_library") {
183 *type
= Target::STATIC_LIBRARY
;
186 if (value
== "source_set") {
187 *type
= Target::SOURCE_SET
;
190 if (value
== "copy") {
191 *type
= Target::COPY_FILES
;
194 if (value
== "action") {
195 *type
= Target::ACTION
;
199 Err(Location(), "Invalid value for \"--type\".").PrintToStdout();
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
))
213 std::string testonly_value
= cmdline
->GetSwitchValueASCII(testonly_key
);
214 bool testonly
= false;
215 if (testonly_value
== "true") {
217 } else if (testonly_value
!= "false") {
218 Err(Location(), "Bad value for --testonly.",
219 "I was expecting --testonly=true or --testonly=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
);
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
))
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
);
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
) {
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.
296 OutputString(label
.GetUserVisibleName(
297 label
.GetToolchainLabel() != default_tc_label
));
302 void PrintTargetsAsOutputs(bool indent
,
303 const std::vector
<const Target
*>& targets
) {
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());
326 OutputString(result
);
333 CommandInfo::CommandInfo()
334 : help_short(nullptr),
339 CommandInfo::CommandInfo(const char* in_help_short
,
341 CommandRunner in_runner
)
342 : help_short(in_help_short
),
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, \
356 INSERT_COMMAND(Check
)
357 INSERT_COMMAND(Clean
)
360 INSERT_COMMAND(Format
)
365 #undef INSERT_COMMAND
370 const Target
* ResolveTargetFromCommandLineString(
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
);
377 Label label
= Label::Resolve(SourceDirForCurrentDirectory(
378 setup
->build_settings().root_path()),
379 default_toolchain
, arg_value
, &err
);
380 if (err
.has_error()) {
385 const Item
* item
= setup
->builder()->GetItem(label
);
387 Err(Location(), "Label not found.",
388 label
.GetUserVisibleName(false) + " not found.").PrintToStdout();
392 const Target
* target
= item
->AsTarget();
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.");
404 bool ResolveFromCommandLineInput(
406 const std::vector
<std::string
>& input
,
408 UniqueVector
<const Target
*>* target_matches
,
409 UniqueVector
<const Config
*>* config_matches
,
410 UniqueVector
<const Toolchain
*>* toolchain_matches
,
411 UniqueVector
<SourceFile
>* file_matches
) {
413 Err(Location(), "You need to specify a label, file, or pattern.")
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
,
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
);
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
);
456 void FilterAndPrintTargets(bool indent
, std::vector
<const Target
*>* targets
) {
457 if (targets
->empty())
460 if (!ApplyTestonlyFilter(targets
))
462 if (!ApplyTypeFilter(targets
))
465 TargetPrintingMode printing_mode
= TARGET_PRINT_LABEL
;
466 if (targets
->empty() || !GetTargetPrintingMode(&printing_mode
))
468 switch (printing_mode
) {
469 case TARGET_PRINT_BUILDFILE
:
470 PrintTargetsAsBuildfiles(indent
, *targets
);
472 case TARGET_PRINT_LABEL
:
473 PrintTargetsAsLabels(indent
, *targets
);
475 case TARGET_PRINT_OUTPUT
:
476 PrintTargetsAsOutputs(indent
, *targets
);
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