| Index: tools/gn/commands.cc
 | 
| diff --git a/tools/gn/commands.cc b/tools/gn/commands.cc
 | 
| index ace78a7dff9c2a713e2f92383da72fb711638334..b4f58e3ff85888ef26f50607367075360ef5f60b 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
 | 
| 
 |