// 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 #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 [--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& args) { if (args.size() != 1 && args.size() != 2) { Err(Location(), "You're holding it wrong.", "Usage: \"gn refs \"").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 records = setup->builder()->GetAllRecords(); const CommandLine* cmdline = CommandLine::ForCurrentProcess(); bool file_output = cmdline->HasSwitch("files"); std::set 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::iterator i = unique_output.begin(); i != unique_output.end(); ++i) OutputString(*i + "\n"); return 0; } } // namespace commands