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 // TODO(brettw) bug 343726: Use a temporary directory instead of this 89 // default one to avoid messing up any build that's in there. 90 if (!setup->DoSetup("//out/Default/") || !setup->Run()) 91 return 1; 92 93 std::vector<const BuilderRecord*> records = setup->builder()->GetAllRecords(); 94 95 const CommandLine* cmdline = CommandLine::ForCurrentProcess(); 96 97 bool file_output = cmdline->HasSwitch("files"); 98 std::set<std::string> unique_output; 99 100 for (size_t record_index = 0; record_index < records.size(); record_index++) { 101 const BuilderRecord* record = records[record_index]; 102 const BuilderRecord::BuilderRecordSet& deps = record->all_deps(); 103 for (BuilderRecord::BuilderRecordSet::const_iterator d = deps.begin(); 104 d != deps.end(); ++d) { 105 std::string label = (*d)->label().GetUserVisibleName(false); 106 if (pattern.MatchesString(label)) { 107 // Got a match. 108 if (file_output) { 109 unique_output.insert(FilePathToUTF8(FilePathForRecord(record))); 110 break; // Found a match for this target's file, don't need more. 111 } else { 112 // We can get dupes when there are differnet toolchains involved, 113 // so we want to send all output through the de-duper. 114 unique_output.insert( 115 record->item()->label().GetUserVisibleName(false) + " -> " + 116 label); 117 } 118 } 119 } 120 } 121 122 for (std::set<std::string>::iterator i = unique_output.begin(); 123 i != unique_output.end(); ++i) 124 OutputString(*i + "\n"); 125 126 return 0; 127 } 128 129 } // namespace commands 130