Home | History | Annotate | Download | only in gn
      1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include <set>
      6 
      7 #include "base/command_line.h"
      8 #include "tools/gn/commands.h"
      9 #include "tools/gn/filesystem_utils.h"
     10 #include "tools/gn/input_file.h"
     11 #include "tools/gn/item.h"
     12 #include "tools/gn/pattern.h"
     13 #include "tools/gn/setup.h"
     14 #include "tools/gn/standard_out.h"
     15 #include "tools/gn/target.h"
     16 
     17 namespace commands {
     18 
     19 namespace {
     20 
     21 // Returns the file path generating this record.
     22 base::FilePath FilePathForRecord(const BuilderRecord* record) {
     23   if (!record->item())
     24     return base::FilePath(FILE_PATH_LITERAL("=UNRESOLVED DEPENDENCY="));
     25   return record->item()->defined_from()->GetRange().begin().file()
     26       ->physical_name();
     27 }
     28 
     29 }  // namespace
     30 
     31 const char kRefs[] = "refs";
     32 const char kRefs_HelpShort[] =
     33     "refs: Find stuff referencing a target, directory, or config.";
     34 const char kRefs_Help[] =
     35     "gn refs <label_pattern> [--files]\n"
     36     "\n"
     37     "  Finds code referencing a given label. The label can be a\n"
     38     "  target or config name. Unlike most other commands, unresolved\n"
     39     "  dependencies will be tolerated. This allows you to use this command\n"
     40     "  to find references to targets you're in the process of moving.\n"
     41     "\n"
     42     "  By default, the mapping from source item to dest item (where the\n"
     43     "  pattern matches the dest item). See \"gn help pattern\" for\n"
     44     "  information on pattern-matching rules.\n"
     45     "\n"
     46     "Option:\n"
     47     "  --files\n"
     48     "      Output unique filenames referencing a matched target or config.\n"
     49     "\n"
     50     "Examples:\n"
     51     "  gn refs \"//tools/gn/*\"\n"
     52     "      Find all targets depending on any target or config in the\n"
     53     "      \"tools/gn\" directory.\n"
     54     "\n"
     55     "  gn refs //tools/gn:gn\n"
     56     "      Find all targets depending on the given exact target name.\n"
     57     "\n"
     58     "  gn refs \"*gtk*\" --files\n"
     59     "      Find all unique buildfiles with a dependency on a target that has\n"
     60     "      the substring \"gtk\" in the name.\n";
     61 
     62 int RunRefs(const std::vector<std::string>& args) {
     63   if (args.size() != 1 && args.size() != 2) {
     64     Err(Location(), "You're holding it wrong.",
     65         "Usage: \"gn refs <label_pattern>\"").PrintToStdout();
     66     return 1;
     67   }
     68 
     69   // Check for common errors on input.
     70   if (args[0].find('*') == std::string::npos) {
     71     // We need to begin with a "//" and have a colon if there's no "*" or it
     72     // will be impossible to match anything.
     73     if (args[0].size() < 2 ||
     74         (args[0][0] != '/' && args[0][1] != '/') ||
     75         args[0].find(':') == std::string::npos) {
     76       Err(Location(), "Patterns match the entire label. Since your pattern "
     77           "has no wildcard, it\nshould start with a \"//\" and have a colon "
     78           "or it can never match anything.\nTo match a substring, use "
     79           "\"*foo*\".").PrintToStdout();
     80       return 1;
     81     }
     82   }
     83 
     84   Pattern pattern(args[0]);
     85 
     86   Setup* setup = new Setup;
     87   setup->set_check_for_bad_items(false);
     88   if (!setup->DoSetup() || !setup->Run())
     89     return 1;
     90 
     91   std::vector<const BuilderRecord*> records = setup->builder()->GetAllRecords();
     92 
     93   const CommandLine* cmdline = CommandLine::ForCurrentProcess();
     94 
     95   bool file_output = cmdline->HasSwitch("files");
     96   std::set<std::string> unique_output;
     97 
     98   for (size_t record_index = 0; record_index < records.size(); record_index++) {
     99     const BuilderRecord* record = records[record_index];
    100     const BuilderRecord::BuilderRecordSet& deps = record->all_deps();
    101     for (BuilderRecord::BuilderRecordSet::const_iterator d = deps.begin();
    102          d != deps.end(); ++d) {
    103       std::string label = (*d)->label().GetUserVisibleName(false);
    104       if (pattern.MatchesString(label)) {
    105         // Got a match.
    106         if (file_output) {
    107           unique_output.insert(FilePathToUTF8(FilePathForRecord(record)));
    108           break;  // Found a match for this target's file, don't need more.
    109         } else {
    110           // We can get dupes when there are differnet toolchains involved,
    111           // so we want to send all output through the de-duper.
    112           unique_output.insert(
    113               record->item()->label().GetUserVisibleName(false) + " -> " +
    114               label);
    115         }
    116       }
    117     }
    118   }
    119 
    120   for (std::set<std::string>::iterator i = unique_output.begin();
    121        i != unique_output.end(); ++i)
    122     OutputString(*i + "\n");
    123 
    124   return 0;
    125 }
    126 
    127 }  // namespace commands
    128