// 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 #include #include "base/command_line.h" #include "base/files/file_util.h" #include "base/strings/string_split.h" #include "base/strings/string_util.h" #include "tools/gn/commands.h" #include "tools/gn/config_values_extractors.h" #include "tools/gn/deps_iterator.h" #include "tools/gn/filesystem_utils.h" #include "tools/gn/input_file.h" #include "tools/gn/item.h" #include "tools/gn/setup.h" #include "tools/gn/standard_out.h" #include "tools/gn/switches.h" #include "tools/gn/target.h" namespace commands { namespace { typedef std::set TargetSet; typedef std::vector TargetVector; // Maps targets to the list of targets that depend on them. typedef std::multimap DepMap; // Populates the reverse dependency map for the targets in the Setup. void FillDepMap(Setup* setup, DepMap* dep_map) { for (auto* target : setup->builder().GetAllResolvedTargets()) { for (const auto& dep_pair : target->GetDeps(Target::DEPS_ALL)) dep_map->insert(std::make_pair(dep_pair.ptr, target)); } } // Forward declaration for function below. size_t RecursivePrintTargetDeps(const DepMap& dep_map, const Target* target, TargetSet* seen_targets, int indent_level); // Prints the target and its dependencies in tree form. If the set is non-null, // new targets encountered will be added to the set, and if a ref is in the set // already, it will not be recused into. When the set is null, all refs will be // printed. // // Returns the number of items printed. size_t RecursivePrintTarget(const DepMap& dep_map, const Target* target, TargetSet* seen_targets, int indent_level) { std::string indent(indent_level * 2, ' '); size_t count = 1; // Only print the toolchain for non-default-toolchain targets. OutputString(indent + target->label().GetUserVisibleName( !target->settings()->is_default())); bool print_children = true; if (seen_targets) { if (seen_targets->find(target) == seen_targets->end()) { // New target, mark it visited. seen_targets->insert(target); } else { // Already seen. print_children = false; // Only print "..." if something is actually elided, which means that // the current target has children. if (dep_map.lower_bound(target) != dep_map.upper_bound(target)) OutputString("..."); } } OutputString("\n"); if (print_children) { count += RecursivePrintTargetDeps(dep_map, target, seen_targets, indent_level + 1); } return count; } // Prints refs of the given target (not the target itself). See // RecursivePrintTarget. size_t RecursivePrintTargetDeps(const DepMap& dep_map, const Target* target, TargetSet* seen_targets, int indent_level) { DepMap::const_iterator dep_begin = dep_map.lower_bound(target); DepMap::const_iterator dep_end = dep_map.upper_bound(target); size_t count = 0; for (DepMap::const_iterator cur_dep = dep_begin; cur_dep != dep_end; cur_dep++) { count += RecursivePrintTarget(dep_map, cur_dep->second, seen_targets, indent_level); } return count; } void RecursiveCollectChildRefs(const DepMap& dep_map, const Target* target, TargetSet* results); // Recursively finds all targets that reference the given one, and additionally // adds the current one to the list. void RecursiveCollectRefs(const DepMap& dep_map, const Target* target, TargetSet* results) { if (results->find(target) != results->end()) return; // Already found this target. results->insert(target); RecursiveCollectChildRefs(dep_map, target, results); } // Recursively finds all targets that reference the given one. void RecursiveCollectChildRefs(const DepMap& dep_map, const Target* target, TargetSet* results) { DepMap::const_iterator dep_begin = dep_map.lower_bound(target); DepMap::const_iterator dep_end = dep_map.upper_bound(target); for (DepMap::const_iterator cur_dep = dep_begin; cur_dep != dep_end; cur_dep++) RecursiveCollectRefs(dep_map, cur_dep->second, results); } bool TargetContainsFile(const Target* target, const SourceFile& file) { for (const auto& cur_file : target->sources()) { if (cur_file == file) return true; } for (const auto& cur_file : target->public_headers()) { if (cur_file == file) return true; } for (ConfigValuesIterator iter(target); !iter.done(); iter.Next()) { for (const auto& cur_file : iter.cur().inputs()) { if (cur_file == file) return true; } } for (const auto& cur_file : target->data()) { if (cur_file == file.value()) return true; if (cur_file.back() == '/' && base::StartsWith(file.value(), cur_file, base::CompareCase::SENSITIVE)) return true; } if (target->action_values().script().value() == file.value()) return true; std::vector output_sources; target->action_values().GetOutputsAsSourceFiles(target, &output_sources); for (const auto& cur_file : output_sources) { if (cur_file == file) return true; } for (const auto& cur_file : target->computed_outputs()) { if (cur_file.AsSourceFile(target->settings()->build_settings()) == file) return true; } return false; } void GetTargetsContainingFile(Setup* setup, const std::vector& all_targets, const SourceFile& file, bool all_toolchains, UniqueVector* matches) { Label default_toolchain = setup->loader()->default_toolchain_label(); for (auto* target : all_targets) { if (!all_toolchains) { // Only check targets in the default toolchain. if (target->label().GetToolchainLabel() != default_toolchain) continue; } if (TargetContainsFile(target, file)) matches->push_back(target); } } bool TargetReferencesConfig(const Target* target, const Config* config) { for (const LabelConfigPair& cur : target->configs()) { if (cur.ptr == config) return true; } for (const LabelConfigPair& cur : target->public_configs()) { if (cur.ptr == config) return true; } return false; } void GetTargetsReferencingConfig(Setup* setup, const std::vector& all_targets, const Config* config, bool all_toolchains, UniqueVector* matches) { Label default_toolchain = setup->loader()->default_toolchain_label(); for (auto* target : all_targets) { if (!all_toolchains) { // Only check targets in the default toolchain. if (target->label().GetToolchainLabel() != default_toolchain) continue; } if (TargetReferencesConfig(target, config)) matches->push_back(target); } } // Returns the number of matches printed. size_t DoTreeOutput(const DepMap& dep_map, const UniqueVector& implicit_target_matches, const UniqueVector& explicit_target_matches, bool all) { TargetSet seen_targets; size_t count = 0; // Implicit targets don't get printed themselves. for (const Target* target : implicit_target_matches) { if (all) count += RecursivePrintTargetDeps(dep_map, target, nullptr, 0); else count += RecursivePrintTargetDeps(dep_map, target, &seen_targets, 0); } // Explicit targets appear in the output. for (const Target* target : implicit_target_matches) { if (all) count += RecursivePrintTarget(dep_map, target, nullptr, 0); else count += RecursivePrintTarget(dep_map, target, &seen_targets, 0); } return count; } // Returns the number of matches printed. size_t DoAllListOutput( const DepMap& dep_map, const UniqueVector& implicit_target_matches, const UniqueVector& explicit_target_matches) { // Output recursive dependencies, uniquified and flattened. TargetSet results; for (const Target* target : implicit_target_matches) RecursiveCollectChildRefs(dep_map, target, &results); for (const Target* target : explicit_target_matches) { // Explicit targets also get added to the output themselves. results.insert(target); RecursiveCollectChildRefs(dep_map, target, &results); } FilterAndPrintTargetSet(false, results); return results.size(); } // Returns the number of matches printed. size_t DoDirectListOutput( const DepMap& dep_map, const UniqueVector& implicit_target_matches, const UniqueVector& explicit_target_matches) { TargetSet results; // Output everything that refers to the implicit ones. for (const Target* target : implicit_target_matches) { DepMap::const_iterator dep_begin = dep_map.lower_bound(target); DepMap::const_iterator dep_end = dep_map.upper_bound(target); for (DepMap::const_iterator cur_dep = dep_begin; cur_dep != dep_end; cur_dep++) results.insert(cur_dep->second); } // And just output the explicit ones directly (these are the target matches // when referring to what references a file or config). for (const Target* target : explicit_target_matches) results.insert(target); FilterAndPrintTargetSet(false, results); return results.size(); } } // namespace const char kRefs[] = "refs"; const char kRefs_HelpShort[] = "refs: Find stuff referencing a target or file."; const char kRefs_Help[] = R"(gn refs (|