Chromium Code Reviews| Index: tools/gn/commands.cc |
| diff --git a/tools/gn/commands.cc b/tools/gn/commands.cc |
| index ace78a7dff9c2a713e2f92383da72fb711638334..5c77d51d1a45d0457ac1e4eeb8af6aa24ad184f6 100644 |
| --- a/tools/gn/commands.cc |
| +++ b/tools/gn/commands.cc |
| @@ -2,6 +2,8 @@ |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| +#include "base/command_line.h" |
| +#include "tools/gn/builder.h" |
| #include "tools/gn/commands.h" |
| #include "tools/gn/filesystem_utils.h" |
| #include "tools/gn/item.h" |
| @@ -13,6 +15,320 @@ |
| namespace commands { |
| +namespace { |
| + |
| +// Like above but the input string can be a pattern that matches multiple |
| +// targets. If the input does not parse as a pattern, prints and error and |
| +// returns false. If the pattern is valid, fills the vector (which might be |
| +// empty if there are no matches) and returns true. |
| +// |
| +// If all_tolchains is false, a pattern with an unspecified toolchain will |
|
scottmg
2015/02/19 21:52:01
all_toolchains
|
| +// match the default toolchain only. If true, all toolchains will be matched. |
| +bool ResolveTargetsFromCommandLinePattern( |
| + Setup* setup, |
| + const std::string& label_pattern, |
| + bool all_toolchains, |
| + std::vector<const Target*>* matches) { |
| + Value pattern_value(nullptr, label_pattern); |
| + |
| + Err err; |
| + LabelPattern pattern = LabelPattern::GetPattern( |
| + SourceDirForCurrentDirectory(setup->build_settings().root_path()), |
| + pattern_value, |
| + &err); |
| + if (err.has_error()) { |
| + err.PrintToStdout(); |
| + return false; |
| + } |
| + |
| + if (!all_toolchains) { |
| + // By default a pattern with an empty toolchain will match all toolchains. |
| + // If the caller wants to default to the main toolchain only, set it |
| + // explicitly. |
| + if (pattern.toolchain().is_null()) { |
| + // No explicit toolchain set. |
| + pattern.set_toolchain(setup->loader()->default_toolchain_label()); |
| + } |
| + } |
| + |
| + std::vector<LabelPattern> pattern_vector; |
| + pattern_vector.push_back(pattern); |
| + FilterTargetsByPatterns(setup->builder()->GetAllResolvedTargets(), |
| + pattern_vector, matches); |
| + return true; |
| +} |
| + |
| + |
| +// If there's an error, it will be printed and false will be returned. |
| +bool ResolveStringFromCommandLineInput( |
| + Setup* setup, |
| + const SourceDir& current_dir, |
| + const std::string& input, |
| + bool all_toolchains, |
| + UniqueVector<const Target*>* target_matches, |
| + UniqueVector<const Config*>* config_matches, |
| + UniqueVector<const Toolchain*>* toolchain_matches, |
| + UniqueVector<SourceFile>* file_matches) { |
| + if (LabelPattern::HasWildcard(input)) { |
| + // For now, only match patterns against targets. It might be nice in the |
| + // future to allow the user to specify which types of things they want to |
| + // match, but it should probably only match targets by default. |
| + std::vector<const Target*> target_match_vector; |
| + if (!ResolveTargetsFromCommandLinePattern(setup, input, all_toolchains, |
| + &target_match_vector)) |
| + return false; |
| + for (const Target* target : target_match_vector) |
| + target_matches->push_back(target); |
| + return true; |
| + } |
| + |
| + // Try to figure out what this thing is. |
| + Err err; |
| + Label label = Label::Resolve(current_dir, |
| + setup->loader()->default_toolchain_label(), |
| + Value(nullptr, input), &err); |
| + if (err.has_error()) { |
| + err.PrintToStdout(); |
| + return false; |
| + } |
| + |
| + const Item* item = setup->builder()->GetItem(label); |
| + if (item) { |
| + if (const Config* as_config = item->AsConfig()) |
| + config_matches->push_back(as_config); |
| + else if (const Target* as_target = item->AsTarget()) |
| + target_matches->push_back(as_target); |
| + else if (const Toolchain* as_toolchain = item->AsToolchain()) |
| + toolchain_matches->push_back(as_toolchain); |
| + } else { |
| + // Not an item, assume this must be a file. |
| + file_matches->push_back(current_dir.ResolveRelativeFile( |
| + input, setup->build_settings().root_path_utf8())); |
| + } |
| + |
| + return true; |
| +} |
| + |
| +enum TargetPrintingMode { |
| + TARGET_PRINT_BUILDFILE, |
| + TARGET_PRINT_LABEL, |
| + TARGET_PRINT_OUTPUT, |
| +}; |
| + |
| +// Retrieves the target printing mode based on the command line flags for the |
| +// current process. Returns true on success. On error, prints a message to the |
| +// console and returns false. |
| +bool GetTargetPrintingMode(TargetPrintingMode* mode) { |
| + std::string switch_key = "as"; |
| + const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess(); |
| + |
| + if (!cmdline->HasSwitch(switch_key)) { |
| + // Default to labels. |
| + *mode = TARGET_PRINT_LABEL; |
| + return true; |
| + } |
| + |
| + std::string value = cmdline->GetSwitchValueASCII(switch_key); |
| + if (value == "buildfile") { |
| + *mode = TARGET_PRINT_BUILDFILE; |
| + return true; |
| + } |
| + if (value == "label") { |
| + *mode = TARGET_PRINT_LABEL; |
| + return true; |
| + } |
| + if (value == "output") { |
| + *mode = TARGET_PRINT_OUTPUT; |
| + return true; |
| + } |
| + |
| + Err(Location(), "Invalid value for \"--as\".", |
| + "I was expecting \"buildfile\", \"label\", or \"output\" but you\n" |
| + "said \"" + value + "\".").PrintToStdout(); |
| + return false; |
| +} |
| + |
| +// Returns the target type filter based on the command line flags for the |
| +// current process. Returns true on success. On error, prints a message to the |
| +// console and returns false. |
| +// |
| +// Target::UNKNOWN will be set if there is no filter. Target::ACTION_FOREACH |
| +// will never be returned. Code applying the filters should apply Target::ACTION |
| +// to both ACTION and ACTION_FOREACH. |
| +bool GetTargetTypeFilter(Target::OutputType* type) { |
| + std::string switch_key = "type"; |
| + const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess(); |
| + |
| + if (!cmdline->HasSwitch(switch_key)) { |
| + // Default to unknown -> no filtering. |
| + *type = Target::UNKNOWN; |
| + return true; |
| + } |
| + |
| + std::string value = cmdline->GetSwitchValueASCII(switch_key); |
| + if (value == "group") { |
| + *type = Target::GROUP; |
| + return true; |
| + } |
| + if (value == "executable") { |
| + *type = Target::EXECUTABLE; |
| + return true; |
| + } |
| + if (value == "shared_library") { |
| + *type = Target::SHARED_LIBRARY; |
| + return true; |
| + } |
| + if (value == "static_library") { |
| + *type = Target::STATIC_LIBRARY; |
| + return true; |
| + } |
| + if (value == "source_set") { |
| + *type = Target::SOURCE_SET; |
| + return true; |
| + } |
| + if (value == "copy") { |
| + *type = Target::COPY_FILES; |
| + return true; |
| + } |
| + if (value == "action") { |
| + *type = Target::ACTION; |
| + return true; |
| + } |
| + |
| + Err(Location(), "Invalid value for \"--type\".").PrintToStdout(); |
| + return false; |
| +} |
| + |
| + |
| +// Applies any testonly filtering specified on the command line to the given |
| +// target set. On failure, prints an error and returns false. |
| +bool ApplyTestonlyFilter(std::vector<const Target*>* targets) { |
| + const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess(); |
| + std::string testonly_key = "testonly"; |
| + |
| + if (targets->empty() || !cmdline->HasSwitch(testonly_key)) |
| + return true; |
| + |
| + std::string testonly_value = cmdline->GetSwitchValueASCII(testonly_key); |
| + bool testonly = false; |
| + if (testonly_value == "true") { |
| + testonly = true; |
| + } else if (testonly_value != "false") { |
| + Err(Location(), "Bad value for --testonly.", |
| + "I was expecting --testonly=true or --testonly=false.") |
| + .PrintToStdout(); |
| + return false; |
| + } |
| + |
| + // Filter into a copy of the vector, then swap to output. |
| + std::vector<const Target*> result; |
| + result.reserve(targets->size()); |
| + |
| + for (const Target* target : *targets) { |
| + if (target->testonly() == testonly) |
| + result.push_back(target); |
| + } |
| + |
| + targets->swap(result); |
| + return true; |
| +} |
| + |
| +// Applies any target type filtering specified on the command line to the given |
| +// target set. On failure, prints an error and returns false. |
| +bool ApplyTypeFilter(std::vector<const Target*>* targets) { |
| + Target::OutputType type = Target::UNKNOWN; |
| + if (!GetTargetTypeFilter(&type)) |
| + return false; |
| + if (targets->empty() || type == Target::UNKNOWN) |
| + return true; // Nothing to filter out. |
| + |
| + // Filter into a copy of the vector, then swap to output. |
| + std::vector<const Target*> result; |
| + result.reserve(targets->size()); |
| + |
| + for (const Target* target : *targets) { |
| + // Make "action" also apply to ACTION_FOREACH. |
| + if (target->output_type() == type || |
| + (type == Target::ACTION && |
| + target->output_type() == Target::ACTION_FOREACH)) |
| + result.push_back(target); |
| + } |
| + |
| + targets->swap(result); |
| + return true; |
| +} |
| + |
| +// Returns the file path generating this item. |
| +base::FilePath BuildFileForItem(const Item* item) { |
| + return item->defined_from()->GetRange().begin().file()->physical_name(); |
| +} |
| + |
| +void PrintTargetsAsBuildfiles(bool indent, |
| + const std::vector<const Target*>& targets) { |
| + // Output the set of unique source files. |
| + std::set<std::string> unique_files; |
| + for (const Target* target : targets) |
| + unique_files.insert(FilePathToUTF8(BuildFileForItem(target))); |
| + |
| + for (const std::string& file : unique_files) { |
| + if (indent) |
| + OutputString(" "); |
| + OutputString(file + "\n"); |
| + } |
| +} |
| + |
| +void PrintTargetsAsLabels(bool indent, |
| + const std::vector<const Target*>& targets) { |
| + // Putting the labels into a set automatically sorts them for us. |
| + std::set<Label> unique_labels; |
| + for (const auto& target : targets) |
| + unique_labels.insert(target->label()); |
| + |
| + // Grab the label of the default toolchain from the first target. |
| + Label default_tc_label = |
| + targets[0]->settings()->default_toolchain_label(); |
| + |
| + for (const Label& label : unique_labels) { |
| + // Print toolchain only for ones not in the default toolchain. |
| + if (indent) |
| + OutputString(" "); |
| + OutputString(label.GetUserVisibleName( |
| + label.GetToolchainLabel() != default_tc_label)); |
| + OutputString("\n"); |
| + } |
| +} |
| + |
| +void PrintTargetsAsOutputs(bool indent, |
| + const std::vector<const Target*>& targets) { |
| + if (targets.empty()) |
| + return; |
| + |
| + // Grab the build settings from a random target. |
| + const BuildSettings* build_settings = |
| + targets[0]->settings()->build_settings(); |
| + |
| + SourceDir current_dir = SourceDirForCurrentDirectory( |
| + build_settings->root_path()); |
| + for (const Target* target : targets) { |
| + // Use the link output file if there is one, otherwise fall back to the |
| + // dependency output file (for actions, for example). |
| + OutputFile output_file = target->link_output_file(); |
| + if (output_file.value().empty()) |
| + output_file = target->dependency_output_file(); |
| + |
| + SourceFile output_as_source = |
| + output_file.AsSourceFile(build_settings); |
| + std::string result = RebasePath(output_as_source.value(), current_dir, |
| + build_settings->root_path_utf8()); |
| + if (indent) |
| + OutputString(" "); |
| + OutputString(result); |
| + OutputString("\n"); |
| + } |
| +} |
| + |
| +} // namespace |
| + |
| CommandInfo::CommandInfo() |
| : help_short(nullptr), |
| help(nullptr), |
| @@ -84,38 +400,87 @@ const Target* ResolveTargetFromCommandLineString( |
| return target; |
| } |
| -bool ResolveTargetsFromCommandLinePattern( |
| +bool ResolveFromCommandLineInput( |
| Setup* setup, |
| - const std::string& label_pattern, |
| + const std::vector<std::string>& input, |
| bool all_toolchains, |
| - std::vector<const Target*>* matches) { |
| - Value pattern_value(nullptr, label_pattern); |
| - |
| - Err err; |
| - LabelPattern pattern = LabelPattern::GetPattern( |
| - SourceDirForCurrentDirectory(setup->build_settings().root_path()), |
| - pattern_value, |
| - &err); |
| - if (err.has_error()) { |
| - err.PrintToStdout(); |
| + UniqueVector<const Target*>* target_matches, |
| + UniqueVector<const Config*>* config_matches, |
| + UniqueVector<const Toolchain*>* toolchain_matches, |
| + UniqueVector<SourceFile>* file_matches) { |
| + if (input.empty()) { |
| + Err(Location(), "You need to specify a label, file, or pattern.") |
| + .PrintToStdout(); |
| return false; |
| } |
| - if (!all_toolchains) { |
| - // By default a pattern with an empty toolchain will match all toolchains. |
| - // IF the caller wants to default to the main toolchain only, set it |
| - // explicitly. |
| - if (pattern.toolchain().is_null()) { |
| - // No explicit toolchain set. |
| - pattern.set_toolchain(setup->loader()->default_toolchain_label()); |
| + SourceDir cur_dir = |
| + SourceDirForCurrentDirectory(setup->build_settings().root_path()); |
| + for (const auto& cur : input) { |
| + if (!ResolveStringFromCommandLineInput(setup, cur_dir, cur, |
| + all_toolchains, target_matches, |
| + config_matches, toolchain_matches, |
| + file_matches)) |
| + return false; |
| + } |
| + return true; |
| +} |
| + |
| +void FilterTargetsByPatterns(const std::vector<const Target*>& input, |
|
scottmg
2015/02/19 21:52:01
this could be template <class O> for output
brettw
2015/02/19 22:12:02
Yeah, but I didn't feel like I should implement th
|
| + const std::vector<LabelPattern>& filter, |
| + std::vector<const Target*>* output) { |
| + for (const auto& target : input) { |
| + for (const auto& pattern : filter) { |
| + if (pattern.Matches(target->label())) { |
| + output->push_back(target); |
| + break; |
| + } |
| } |
| } |
| +} |
| - std::vector<LabelPattern> pattern_vector; |
| - pattern_vector.push_back(pattern); |
| - FilterTargetsByPatterns(setup->builder()->GetAllResolvedTargets(), |
| - pattern_vector, matches); |
| - return true; |
| +void FilterTargetsByPatterns(const std::vector<const Target*>& input, |
| + const std::vector<LabelPattern>& filter, |
| + UniqueVector<const Target*>* output) { |
| + for (const auto& target : input) { |
| + for (const auto& pattern : filter) { |
| + if (pattern.Matches(target->label())) { |
| + output->push_back(target); |
| + break; |
| + } |
| + } |
| + } |
| +} |
| + |
| +void FilterAndPrintTargets(bool indent, std::vector<const Target*>* targets) { |
| + if (targets->empty()) |
| + return; |
| + |
| + if (!ApplyTestonlyFilter(targets)) |
| + return; |
| + if (!ApplyTypeFilter(targets)) |
| + return; |
| + |
| + TargetPrintingMode printing_mode = TARGET_PRINT_LABEL; |
| + if (targets->empty() || !GetTargetPrintingMode(&printing_mode)) |
| + return; |
| + switch (printing_mode) { |
| + case TARGET_PRINT_BUILDFILE: |
| + PrintTargetsAsBuildfiles(indent, *targets); |
| + break; |
| + case TARGET_PRINT_LABEL: |
| + PrintTargetsAsLabels(indent, *targets); |
| + break; |
| + case TARGET_PRINT_OUTPUT: |
| + PrintTargetsAsOutputs(indent, *targets); |
| + break; |
| + } |
| +} |
| + |
| +void FilterAndPrintTargetSet(bool indent, |
| + const std::set<const Target*>& targets) { |
| + std::vector<const Target*> target_vector(targets.begin(), targets.end()); |
| + FilterAndPrintTargets(indent, &target_vector); |
| } |
| } // namespace commands |