root/tools/gn/command_refs.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. FilePathForRecord
  2. RunRefs

// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <set>

#include "base/command_line.h"
#include "tools/gn/commands.h"
#include "tools/gn/filesystem_utils.h"
#include "tools/gn/input_file.h"
#include "tools/gn/item.h"
#include "tools/gn/pattern.h"
#include "tools/gn/setup.h"
#include "tools/gn/standard_out.h"
#include "tools/gn/target.h"

namespace commands {

namespace {

// Returns the file path generating this record.
base::FilePath FilePathForRecord(const BuilderRecord* record) {
  if (!record->item())
    return base::FilePath(FILE_PATH_LITERAL("=UNRESOLVED DEPENDENCY="));
  return record->item()->defined_from()->GetRange().begin().file()
      ->physical_name();
}

}  // namespace

const char kRefs[] = "refs";
const char kRefs_HelpShort[] =
    "refs: Find stuff referencing a target, directory, or config.";
const char kRefs_Help[] =
    "gn refs <label_pattern> [--files]\n"
    "\n"
    "  Finds code referencing a given label. The label can be a\n"
    "  target or config name. Unlike most other commands, unresolved\n"
    "  dependencies will be tolerated. This allows you to use this command\n"
    "  to find references to targets you're in the process of moving.\n"
    "\n"
    "  By default, the mapping from source item to dest item (where the\n"
    "  pattern matches the dest item). See \"gn help pattern\" for\n"
    "  information on pattern-matching rules.\n"
    "\n"
    "Option:\n"
    "  --files\n"
    "      Output unique filenames referencing a matched target or config.\n"
    "\n"
    "Examples:\n"
    "  gn refs \"//tools/gn/*\"\n"
    "      Find all targets depending on any target or config in the\n"
    "      \"tools/gn\" directory.\n"
    "\n"
    "  gn refs //tools/gn:gn\n"
    "      Find all targets depending on the given exact target name.\n"
    "\n"
    "  gn refs \"*gtk*\" --files\n"
    "      Find all unique buildfiles with a dependency on a target that has\n"
    "      the substring \"gtk\" in the name.\n";

int RunRefs(const std::vector<std::string>& args) {
  if (args.size() != 1 && args.size() != 2) {
    Err(Location(), "You're holding it wrong.",
        "Usage: \"gn refs <label_pattern>\"").PrintToStdout();
    return 1;
  }

  // Check for common errors on input.
  if (args[0].find('*') == std::string::npos) {
    // We need to begin with a "//" and have a colon if there's no "*" or it
    // will be impossible to match anything.
    if (args[0].size() < 2 ||
        (args[0][0] != '/' && args[0][1] != '/') ||
        args[0].find(':') == std::string::npos) {
      Err(Location(), "Patterns match the entire label. Since your pattern "
          "has no wildcard, it\nshould start with a \"//\" and have a colon "
          "or it can never match anything.\nTo match a substring, use "
          "\"*foo*\".").PrintToStdout();
      return 1;
    }
  }

  Pattern pattern(args[0]);

  Setup* setup = new Setup;
  setup->set_check_for_bad_items(false);
  // TODO(brettw) bug 343726: Use a temporary directory instead of this
  // default one to avoid messing up any build that's in there.
  if (!setup->DoSetup("//out/Default/") || !setup->Run())
    return 1;

  std::vector<const BuilderRecord*> records = setup->builder()->GetAllRecords();

  const CommandLine* cmdline = CommandLine::ForCurrentProcess();

  bool file_output = cmdline->HasSwitch("files");
  std::set<std::string> unique_output;

  for (size_t record_index = 0; record_index < records.size(); record_index++) {
    const BuilderRecord* record = records[record_index];
    const BuilderRecord::BuilderRecordSet& deps = record->all_deps();
    for (BuilderRecord::BuilderRecordSet::const_iterator d = deps.begin();
         d != deps.end(); ++d) {
      std::string label = (*d)->label().GetUserVisibleName(false);
      if (pattern.MatchesString(label)) {
        // Got a match.
        if (file_output) {
          unique_output.insert(FilePathToUTF8(FilePathForRecord(record)));
          break;  // Found a match for this target's file, don't need more.
        } else {
          // We can get dupes when there are differnet toolchains involved,
          // so we want to send all output through the de-duper.
          unique_output.insert(
              record->item()->label().GetUserVisibleName(false) + " -> " +
              label);
        }
      }
    }
  }

  for (std::set<std::string>::iterator i = unique_output.begin();
       i != unique_output.end(); ++i)
    OutputString(*i + "\n");

  return 0;
}

}  // namespace commands

/* [<][>][^][v][top][bottom][index][help] */