diff options
author | brettw <brettw@chromium.org> | 2015-02-19 15:02:52 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-02-19 23:03:23 +0000 |
commit | e604e508f8b3b08d89b5e98ef1d376c74d471a38 (patch) | |
tree | 7ccd08c8294c318d7d021bf399e205c0d0ace143 /tools/gn/commands.cc | |
parent | da52b2f3ea91e809f640373b7cad609725ea7877 (diff) | |
download | chromium_src-e604e508f8b3b08d89b5e98ef1d376c74d471a38.zip chromium_src-e604e508f8b3b08d89b5e98ef1d376c74d471a38.tar.gz chromium_src-e604e508f8b3b08d89b5e98ef1d376c74d471a38.tar.bz2 |
Enhance GN introspection
This adds additional matching and output capabilities to the various GN introspection commands (desc, ls, refs). It adds --as for controlling output, and --testonly and --type for controlling which ones to show.
It adds support for matching multiple input arguments for the refs, ls, and check commands.
It adds support for finding which targets reference a config in the refs command.
Review URL: https://codereview.chromium.org/937003002
Cr-Commit-Position: refs/heads/master@{#317159}
Diffstat (limited to 'tools/gn/commands.cc')
-rw-r--r-- | tools/gn/commands.cc | 413 |
1 files changed, 389 insertions, 24 deletions
diff --git a/tools/gn/commands.cc b/tools/gn/commands.cc index ace78a7..b4f58e3 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_toolchains is false, a pattern with an unspecified toolchain will +// 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, + 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 |