summaryrefslogtreecommitdiffstats
path: root/tools/gn/command_refs.cc
blob: 86e12adffda821c81f6b51ae0ccc2d120ef67f04 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
// 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