summaryrefslogtreecommitdiffstats
path: root/tools/gn/commands.cc
diff options
context:
space:
mode:
authorbrettw <brettw@chromium.org>2015-02-19 15:02:52 -0800
committerCommit bot <commit-bot@chromium.org>2015-02-19 23:03:23 +0000
commite604e508f8b3b08d89b5e98ef1d376c74d471a38 (patch)
tree7ccd08c8294c318d7d021bf399e205c0d0ace143 /tools/gn/commands.cc
parentda52b2f3ea91e809f640373b7cad609725ea7877 (diff)
downloadchromium_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.cc413
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