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 <map>
      6 #include <set>
      7 
      8 #include "base/command_line.h"
      9 #include "tools/gn/commands.h"
     10 #include "tools/gn/deps_iterator.h"
     11 #include "tools/gn/filesystem_utils.h"
     12 #include "tools/gn/input_file.h"
     13 #include "tools/gn/item.h"
     14 #include "tools/gn/setup.h"
     15 #include "tools/gn/standard_out.h"
     16 #include "tools/gn/target.h"
     17 
     18 namespace commands {
     19 
     20 namespace {
     21 
     22 typedef std::set<const Target*> TargetSet;
     23 typedef std::vector<const Target*> TargetVector;
     24 
     25 // Maps targets to the list of targets that depend on them.
     26 typedef std::multimap<const Target*, const Target*> DepMap;
     27 
     28 // Populates the reverse dependency map for the targets in the Setup.
     29 void FillDepMap(Setup* setup, DepMap* dep_map) {
     30   std::vector<const Target*> targets =
     31       setup->builder()->GetAllResolvedTargets();
     32 
     33   for (size_t target_i = 0; target_i < targets.size(); target_i++) {
     34     for (DepsIterator iter(targets[target_i]); !iter.done(); iter.Advance())
     35       dep_map->insert(std::make_pair(iter.target(), targets[target_i]));
     36   }
     37 }
     38 
     39 // Returns the file path generating this item.
     40 base::FilePath FilePathForItem(const Item* item) {
     41   return item->defined_from()->GetRange().begin().file()->physical_name();
     42 }
     43 
     44 // Prints the targets which are the result of a query. This list is sorted
     45 // and, if as_files is set, the unique filenames matching those targets will
     46 // be used.
     47 void OutputResultSet(const TargetSet& results, bool as_files) {
     48   if (results.empty())
     49     return;
     50 
     51   if (as_files) {
     52     // Output the set of unique source files.
     53     std::set<std::string> unique_files;
     54     for (TargetSet::const_iterator iter = results.begin();
     55          iter != results.end(); ++iter)
     56       unique_files.insert(FilePathToUTF8(FilePathForItem(*iter)));
     57 
     58     for (std::set<std::string>::const_iterator iter = unique_files.begin();
     59          iter != unique_files.end(); ++iter) {
     60       OutputString(*iter + "\n");
     61     }
     62   } else {
     63     // Output sorted and uniquified list of labels. The set will sort the
     64     // labels.
     65     std::set<Label> unique_labels;
     66     for (TargetSet::const_iterator iter = results.begin();
     67          iter != results.end(); ++iter)
     68       unique_labels.insert((*iter)->label());
     69 
     70     // Grab the label of the default toolchain from a random target.
     71     Label default_tc_label =
     72         (*results.begin())->settings()->default_toolchain_label();
     73 
     74     for (std::set<Label>::const_iterator iter = unique_labels.begin();
     75          iter != unique_labels.end(); ++iter) {
     76       // Print toolchain only for ones not in the default toolchain.
     77       OutputString(iter->GetUserVisibleName(
     78           iter->GetToolchainLabel() != default_tc_label));
     79       OutputString("\n");
     80     }
     81   }
     82 }
     83 
     84 // Prints refs of the given target (not the target itself). If the set is
     85 // non-null, new targets encountered will be added to the set, and if a ref is
     86 // in the set already, it will not be recused into. When the set is null, all
     87 // refs will be printed.
     88 void RecursivePrintTree(const DepMap& dep_map,
     89                         const Target* target,
     90                         TargetSet* seen_targets,
     91                         int indent_level) {
     92   std::string indent(indent_level * 2, ' ');
     93 
     94   DepMap::const_iterator dep_begin = dep_map.lower_bound(target);
     95   DepMap::const_iterator dep_end = dep_map.upper_bound(target);
     96   for (DepMap::const_iterator cur_dep = dep_begin;
     97        cur_dep != dep_end; cur_dep++) {
     98     const Target* cur_target = cur_dep->second;
     99 
    100     // Only print the toolchain for non-default-toolchain targets.
    101     OutputString(indent + cur_target->label().GetUserVisibleName(
    102         !cur_target->settings()->is_default()));
    103 
    104     bool print_children = true;
    105     if (seen_targets) {
    106       if (seen_targets->find(cur_target) == seen_targets->end()) {
    107         // New target, mark it visited.
    108         seen_targets->insert(cur_target);
    109       } else {
    110         // Already seen.
    111         print_children = false;
    112         // Only print "..." if something is actually elided, which means that
    113         // the current target has children.
    114         if (dep_map.lower_bound(cur_target) != dep_map.upper_bound(cur_target))
    115           OutputString("...");
    116       }
    117     }
    118 
    119     OutputString("\n");
    120     if (print_children)
    121       RecursivePrintTree(dep_map, cur_target, seen_targets, indent_level + 1);
    122   }
    123 }
    124 
    125 void RecursiveCollectChildRefs(const DepMap& dep_map,
    126                                const Target* target,
    127                                TargetSet* results);
    128 
    129 // Recursively finds all targets that reference the given one, and additionally
    130 // adds the current one to the list.
    131 void RecursiveCollectRefs(const DepMap& dep_map,
    132                           const Target* target,
    133                           TargetSet* results) {
    134   if (results->find(target) != results->end())
    135     return;  // Already found this target.
    136   results->insert(target);
    137   RecursiveCollectChildRefs(dep_map, target, results);
    138 }
    139 
    140 // Recursively finds all targets that reference the given one.
    141 void RecursiveCollectChildRefs(const DepMap& dep_map,
    142                                const Target* target,
    143                                TargetSet* results) {
    144   DepMap::const_iterator dep_begin = dep_map.lower_bound(target);
    145   DepMap::const_iterator dep_end = dep_map.upper_bound(target);
    146   for (DepMap::const_iterator cur_dep = dep_begin;
    147        cur_dep != dep_end; cur_dep++)
    148     RecursiveCollectRefs(dep_map, cur_dep->second, results);
    149 }
    150 
    151 }  // namespace
    152 
    153 const char kRefs[] = "refs";
    154 const char kRefs_HelpShort[] =
    155     "refs: Find stuff referencing a target, directory, or config.";
    156 const char kRefs_Help[] =
    157     "gn refs <build_dir> <label_pattern> [--files] [--tree] [--all]\n"
    158     "        [--all-toolchains]\n"
    159     "\n"
    160     "  Finds which targets reference a given target or targets (reverse\n"
    161     "  dependencies).\n"
    162     "\n"
    163     "  The <label_pattern> can take exact labels or patterns that match more\n"
    164     "  than one (although not general regular expressions).\n"
    165     "  See \"gn help label_pattern\" for details.\n"
    166     "\n"
    167     "  --all\n"
    168     "      When used without --tree, will recurse and display all unique\n"
    169     "      dependencies of the given targets. When used with --tree, turns\n"
    170     "      off eliding to show a complete tree.\n"
    171     "\n"
    172     "  --all-toolchains\n"
    173     "      Make the label pattern matche all toolchains. If the label pattern\n"
    174     "      does not specify an explicit toolchain, labels from all toolchains\n"
    175     "      will be matched (normally only the default toolchain is matched\n"
    176     "      when no toolchain is specified).\n"
    177     "\n"
    178     "  --files\n"
    179     "      Output unique filenames referencing a matched target or config.\n"
    180     "      These will be relative to the source root directory such that they\n"
    181     "      are suitable for piping to other commands.\n"
    182     "\n"
    183     "  --tree\n"
    184     "      Outputs a reverse dependency tree from the given target. The label\n"
    185     "      pattern must match one target exactly. Duplicates will be elided.\n"
    186     "      Combine with --all to see a full dependency tree.\n"
    187     "\n"
    188     "Examples\n"
    189     "\n"
    190     "  gn refs out/Debug //tools/gn:gn\n"
    191     "      Find all targets depending on the given exact target name.\n"
    192     "\n"
    193     "  gn refs out/Debug //base:i18n --files | xargs gvim\n"
    194     "      Edit all files containing references to //base:i18n\n"
    195     "\n"
    196     "  gn refs out/Debug //base --all\n"
    197     "      List all targets depending directly or indirectly on //base:base.\n"
    198     "\n"
    199     "  gn refs out/Debug \"//base/*\"\n"
    200     "      List all targets depending directly on any target in //base or\n"
    201     "      its subdirectories.\n"
    202     "\n"
    203     "  gn refs out/Debug \"//base:*\"\n"
    204     "      List all targets depending directly on any target in\n"
    205     "      //base/BUILD.gn.\n"
    206     "\n"
    207     "  gn refs out/Debug //base --tree\n"
    208     "      Print a reverse dependency tree of //base:base\n";
    209 
    210 int RunRefs(const std::vector<std::string>& args) {
    211   if (args.size() != 2) {
    212     Err(Location(), "You're holding it wrong.",
    213         "Usage: \"gn refs <build_dir> <label_pattern>\"").PrintToStdout();
    214     return 1;
    215   }
    216 
    217   const CommandLine* cmdline = CommandLine::ForCurrentProcess();
    218   bool tree = cmdline->HasSwitch("tree");
    219   bool all = cmdline->HasSwitch("all");
    220   bool all_toolchains = cmdline->HasSwitch("all-toolchains");
    221   bool files = cmdline->HasSwitch("files");
    222 
    223   Setup* setup = new Setup;
    224   setup->set_check_for_bad_items(false);
    225   if (!setup->DoSetup(args[0], false) || !setup->Run())
    226     return 1;
    227 
    228   // Figure out the target or targets that the user is querying.
    229   std::vector<const Target*> query;
    230   if (!ResolveTargetsFromCommandLinePattern(setup, args[1], all_toolchains,
    231                                             &query))
    232     return 1;
    233   if (query.empty()) {
    234     OutputString("\"" + args[1] + "\" matches no targets.\n");
    235     return 0;
    236   }
    237 
    238   // Construct the reverse dependency tree.
    239   DepMap dep_map;
    240   FillDepMap(setup, &dep_map);
    241 
    242   if (tree) {
    243     // Output dependency tree.
    244     if (files) {
    245       Err(NULL, "--files option can't be used with --tree option.")
    246           .PrintToStdout();
    247       return 1;
    248     }
    249     if (query.size() != 1) {
    250       Err(NULL, "Query matches more than one target.",
    251           "--tree only supports a single target as input.").PrintToStdout();
    252       return 1;
    253     }
    254     if (all) {
    255       // Recursively print all targets.
    256       RecursivePrintTree(dep_map, query[0], NULL, 0);
    257     } else {
    258       // Recursively print unique targets.
    259       TargetSet seen_targets;
    260       RecursivePrintTree(dep_map, query[0], &seen_targets, 0);
    261     }
    262   } else if (all) {
    263     // Output recursive dependencies, uniquified and flattened.
    264     TargetSet results;
    265     for (size_t query_i = 0; query_i < query.size(); query_i++)
    266       RecursiveCollectChildRefs(dep_map, query[query_i], &results);
    267     OutputResultSet(results, files);
    268   } else {
    269     // Output direct references of everything in the query.
    270     TargetSet results;
    271     for (size_t query_i = 0; query_i < query.size(); query_i++) {
    272       DepMap::const_iterator dep_begin = dep_map.lower_bound(query[query_i]);
    273       DepMap::const_iterator dep_end = dep_map.upper_bound(query[query_i]);
    274       for (DepMap::const_iterator cur_dep = dep_begin;
    275            cur_dep != dep_end; cur_dep++)
    276         results.insert(cur_dep->second);
    277     }
    278     OutputResultSet(results, files);
    279   }
    280 
    281   return 0;
    282 }
    283 
    284 }  // namespace commands
    285