Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(421)

Unified Diff: tools/gn/command_path.cc

Issue 2037303002: Improve the "gn path" command. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: comment fix Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: tools/gn/command_path.cc
diff --git a/tools/gn/command_path.cc b/tools/gn/command_path.cc
index 2368788d581b698e9287510e529297e63f579da8..a17001dd6c684491755dd19f5f38de344cc11a23 100644
--- a/tools/gn/command_path.cc
+++ b/tools/gn/command_path.cc
@@ -17,166 +17,259 @@ namespace commands {
namespace {
-enum DepType {
- DEP_NONE,
- DEP_PUBLIC,
- DEP_PRIVATE,
- DEP_DATA
+enum class DepType {
+ NONE,
+ PUBLIC,
+ PRIVATE,
+ DATA
};
-// As we do a depth-first search, this vector will store the current path
-// the current target for printing when a match is found.
+// The dependency paths are stored in a vector. Assuming the chain:
+// A --[public]--> B --[private]--> C
+// The stack will look like:
+// [0] = A, NONE (this has no dep type since nobody depends on it)
+// [1] = B, PUBLIC
+// [2] = C, PRIVATE
using TargetDep = std::pair<const Target*, DepType>;
-using DepStack = std::vector<TargetDep>;
+using PathVector = std::vector<TargetDep>;
-// Note that this uses raw pointers. These need to be manually deleted (which
-// we won't normally bother with). This allows the vector to be resized
-// more quickly.
-using DepStackVector = std::vector<DepStack*>;
-
-using DepSet = base::hash_set<const Target*>;
+// How to search.
+enum class PrivateDeps { INCLUDE, EXCLUDE };
+enum class DataDeps { INCLUDE, EXCLUDE };
+enum class PrintWhat { ONE, ALL };
struct Options {
Options()
- : all(false),
+ : print_what(PrintWhat::ONE),
public_only(false),
with_data(false) {
}
- bool all;
+ PrintWhat print_what;
bool public_only;
bool with_data;
};
-struct State {
- State() : found_count(0) {
- // Reserve fairly large buffers for the found vectors.
- const size_t kReserveSize = 32768;
- found_public.reserve(kReserveSize);
- found_other.reserve(kReserveSize);
+typedef std::list<PathVector> WorkQueue;
+
+struct Stats {
+ Stats() : public_paths(0), other_paths(0) {
}
- // Stores targets that do not have any paths to the destination. This is
- // an optimization to avoid revisiting useless paths.
- DepSet rejected;
+ int total_paths() const { return public_paths + other_paths; }
- // Total number of paths found.
- int found_count;
+ int public_paths;
+ int other_paths;
- // The pointers in these vectors are owned by this object, but are
- // deliberately leaked. There can be a lot of them which can take a long time
- // to free, and GN will just exit after this is used anyway.
- DepStackVector found_public;
- DepStackVector found_other;
+ // Stores targets that have a path to the destination, and whether that
+ // path is public, private, or data.
+ std::map<const Target*, DepType> found_paths;
};
-void PrintDepStack(const DepStack& stack) {
- // Don't print toolchains unless they differ from the first target.
- const Label& default_toolchain = stack[0].first->label().GetToolchainLabel();
-
- for (const auto& pair : stack) {
- OutputString(pair.first->label().GetUserVisibleName(default_toolchain));
- switch (pair.second) {
- case DEP_NONE:
- break;
- case DEP_PUBLIC:
- OutputString(" --[public]-->", DECORATION_DIM);
- break;
- case DEP_PRIVATE:
- OutputString(" --[private]-->", DECORATION_DIM);
- break;
- case DEP_DATA:
- OutputString(" --[data]-->", DECORATION_DIM);
- break;
+// If the implicit_last_dep is not "none", this type indicates the
+// classification of the elided last part of path.
+DepType ClassifyPath(const PathVector& path, DepType implicit_last_dep) {
+ DepType result;
+ if (implicit_last_dep != DepType::NONE)
+ result = implicit_last_dep;
+ else
+ result = DepType::PUBLIC;
+
+ // Skip the 0th one since that is always NONE.
+ for (size_t i = 1; i < path.size(); i++) {
+ // PRIVATE overrides PUBLIC, and DATA overrides everything (the idea is
+ // to find the worst link in the path).
+ if (path[i].second == DepType::PRIVATE) {
+ if (result == DepType::PUBLIC)
+ result = DepType::PRIVATE;
+ } else if (path[i].second == DepType::DATA) {
+ result = DepType::DATA;
}
- OutputString("\n");
}
- OutputString("\n");
+ return result;
}
-bool AreAllPublic(const DepStack& stack) {
- // Don't check the type of the last one since that doesn't point to anything.
- for (size_t i = 0; i < stack.size() - 1; i++) {
- if (stack[i].second != DEP_PUBLIC)
- return false;
+const char* StringForDepType(DepType type) {
+ switch(type) {
+ case DepType::PUBLIC:
+ return "public";
+ case DepType::PRIVATE:
+ return "private";
+ case DepType::DATA:
+ return "data";
+ break;
+ case DepType::NONE:
+ default:
+ return "";
}
- return true;
}
-// Increments *found_count to reflect how many results are found. If print_all
-// is not set, only the first result will be printed.
-//
-// As an optimization, targets that do not have any paths are added to
-// *reject so this function doesn't waste time revisiting them.
-void RecursiveFindPath(const Options& options,
- State* state,
- const Target* current,
- const Target* desired,
- DepStack* stack) {
- if (state->rejected.find(current) != state->rejected.end())
- return;
- int initial_found_count = state->found_count;
-
- if (current == desired) {
- // Found a path.
- state->found_count++;
- stack->push_back(TargetDep(current, DEP_NONE));
- if (AreAllPublic(*stack))
- state->found_public.push_back(new DepStack(*stack));
- else
- state->found_other.push_back(new DepStack(*stack));
- stack->pop_back();
+// Prints the given path. If the implicit_last_dep is not "none", the last
+// dependency will show an elided dependency with the given annotation.
+void PrintPath(const PathVector& path, DepType implicit_last_dep) {
+ if (path.empty())
return;
+
+ // Don't print toolchains unless they differ from the first target.
+ const Label& default_toolchain = path[0].first->label().GetToolchainLabel();
+
+ for (size_t i = 0; i < path.size(); i++) {
+ OutputString(path[i].first->label().GetUserVisibleName(default_toolchain));
+
+ // Output dependency type.
+ if (i == path.size() - 1) {
+ // Last one either gets the implicit last dep type or nothing.
+ if (implicit_last_dep != DepType::NONE) {
+ OutputString(std::string(" --> see ") +
+ StringForDepType(implicit_last_dep) +
+ " chain printed above...", DECORATION_DIM);
+ }
+ } else {
+ // Take type from the next entry.
+ OutputString(std::string(" --[") + StringForDepType(path[i + 1].second) +
+ "]-->", DECORATION_DIM);
+ }
+ OutputString("\n");
}
- stack->push_back(TargetDep(current, DEP_PUBLIC));
- for (const auto& pair : current->public_deps())
- RecursiveFindPath(options, state, pair.ptr, desired, stack);
+ OutputString("\n");
+}
- if (!options.public_only) {
- stack->back().second = DEP_PRIVATE;
- for (const auto& pair : current->private_deps())
- RecursiveFindPath(options, state, pair.ptr, desired, stack);
+void InsertTargetsIntoFoundPaths(const PathVector& path,
+ DepType implicit_last_dep,
+ Stats* stats) {
+ DepType type = ClassifyPath(path, implicit_last_dep);
+
+ bool inserted = false;
+
+ // Don't try to insert the 0th item in the list which is the "from" target.
+ // The search will be run more than once (for the different path types) and
+ // if the "from" target was in the list, subsequent passes could never run
+ // the starting point is alredy in the list of targets considered).
+ //
+ // One might imagine an alternate implementation where all items are counted
+ // here but the "from" item is erased at the beginning of each search, but
+ // that will mess up the metrics (the private search pass will find the
+ // same public paths as the previous public pass, "inserted" will be true
+ // here since the item wasn't found, and the public path will be
+ // double-counted in the stats.
+ for (size_t i = 1; i < path.size(); i++) {
+ const auto& pair = path[i];
+
+ // Don't overwrite an existing one. The algorithm works by first doing
+ // public, then private, then data, so anything already there is guaranteed
+ // at least as good as our addition.
+ if (stats->found_paths.find(pair.first) == stats->found_paths.end()) {
+ stats->found_paths.insert(std::make_pair(pair.first, type));
+ inserted = true;
+ }
}
- if (options.with_data) {
- stack->back().second = DEP_DATA;
- for (const auto& pair : current->data_deps())
- RecursiveFindPath(options, state, pair.ptr, desired, stack);
+ if (inserted) {
+ // Only count this path in the stats if any part of it was actually new.
+ if (type == DepType::PUBLIC)
+ stats->public_paths++;
+ else
+ stats->other_paths++;
}
+}
+
+void BreadthFirstSearch(const Target* from, const Target* to,
+ PrivateDeps private_deps, DataDeps data_deps,
+ PrintWhat print_what,
+ Stats* stats) {
+ // Seed the initial stack with just the "from" target.
+ PathVector initial_stack;
+ initial_stack.emplace_back(from, DepType::NONE);
+ WorkQueue work_queue;
+ work_queue.push_back(initial_stack);
+
+ // Track checked targets to avoid checking the same once more than once.
+ std::set<const Target*> visited;
+
+ while (!work_queue.empty()) {
+ PathVector current_path = work_queue.front();
+ work_queue.pop_front();
+
+ const Target* current_target = current_path.back().first;
+
+ if (current_target == to) {
+ // Found a new path.
+ if (stats->total_paths() == 0 || print_what == PrintWhat::ALL)
+ PrintPath(current_path, DepType::NONE);
+
+ // Insert all nodes on the path into the found paths list. Since we're
+ // doing search breadth first, we know that the current path is the best
+ // path for all nodes on it.
+ InsertTargetsIntoFoundPaths(current_path, DepType::NONE, stats);
+ } else {
+ // Check for a path that connects to an already known-good one. Printing
+ // this here will mean the results aren't strictly in depth-first order
+ // since there could be many items on the found path this connects to.
+ // Doing this here will mean that the output is sorted by length of items
+ // printed (with the redundant parts of the path omitted) rather than
+ // complete path length.
+ const auto& found_current_target =
+ stats->found_paths.find(current_target);
+ if (found_current_target != stats->found_paths.end()) {
+ if (stats->total_paths() == 0 || print_what == PrintWhat::ALL)
+ PrintPath(current_path, found_current_target->second);
+
+ // Insert all nodes on the path into the found paths list since we know
+ // everything along this path also leads to the destination.
+ InsertTargetsIntoFoundPaths(current_path, found_current_target->second,
+ stats);
+ continue;
+ }
+ }
- stack->pop_back();
+ // If we've already checked this one, stop. This should be after the above
+ // check for a known-good check, because known-good ones will always have
+ // been previously visited.
+ if (visited.find(current_target) == visited.end())
+ visited.insert(current_target);
+ else
+ continue;
- if (state->found_count == initial_found_count)
- state->rejected.insert(current); // Eliminated this target.
-}
+ // Add public deps for this target to the queue.
+ for (const auto& pair : current_target->public_deps()) {
+ work_queue.push_back(current_path);
+ work_queue.back().push_back(TargetDep(pair.ptr, DepType::PUBLIC));
+ }
-bool StackLengthLess(const DepStack* a, const DepStack* b) {
- return a->size() < b->size();
-}
+ if (private_deps == PrivateDeps::INCLUDE) {
+ // Add private deps.
+ for (const auto& pair : current_target->private_deps()) {
+ work_queue.push_back(current_path);
+ work_queue.back().push_back(
+ TargetDep(pair.ptr, DepType::PRIVATE));
+ }
+ }
-// Prints one result vector. The vector will be modified.
-void PrintResultVector(const Options& options, DepStackVector* result) {
- if (!options.all && !result->empty()) {
- // Just print the smallest one.
- PrintDepStack(**std::min_element(result->begin(), result->end(),
- &StackLengthLess));
- return;
+ if (data_deps == DataDeps::INCLUDE) {
+ // Add data deps.
+ for (const auto& pair : current_target->data_deps()) {
+ work_queue.push_back(current_path);
+ work_queue.back().push_back(TargetDep(pair.ptr, DepType::DATA));
+ }
+ }
}
-
- // Print all in order of increasing length.
- std::sort(result->begin(), result->end(), &StackLengthLess);
- for (const auto& stack : *result)
- PrintDepStack(*stack);
}
-void PrintResults(const Options& options, State* state) {
- PrintResultVector(options, &state->found_public);
-
- // Consider non-public paths only if all paths are requested or there were
- // no public paths.
- if (state->found_public.empty() || options.all)
- PrintResultVector(options, &state->found_other);
+void DoSearch(const Target* from, const Target* to, const Options& options,
+ Stats* stats) {
+ BreadthFirstSearch(from, to, PrivateDeps::EXCLUDE, DataDeps::EXCLUDE,
+ options.print_what, stats);
+ if (!options.public_only) {
+ // Check private deps.
+ BreadthFirstSearch(from, to, PrivateDeps::INCLUDE,
+ DataDeps::EXCLUDE, options.print_what, stats);
+ if (options.with_data) {
+ // Check data deps.
+ BreadthFirstSearch(from, to, PrivateDeps::INCLUDE,
+ DataDeps::INCLUDE, options.print_what, stats);
+ }
+ }
}
} // namespace
@@ -189,8 +282,8 @@ const char kPath_Help[] =
"\n"
" Finds paths of dependencies between two targets. Each unique path\n"
" will be printed in one group, and groups will be separate by newlines.\n"
- " The two targets can appear in either order: paths will be found going\n"
- " in either direction.\n"
+ " The two targets can appear in either order (paths will be found going\n"
+ " in either direction).\n"
"\n"
" By default, a single path will be printed. If there is a path with\n"
" only public dependencies, the shortest public path will be printed.\n"
@@ -199,12 +292,19 @@ const char kPath_Help[] =
" will also be considered. If there are multiple shortest paths, an\n"
" arbitrary one will be selected.\n"
"\n"
+ "Interesting paths\n"
+ "\n"
+ " In a large project, there can be 100's of millions of unique paths\n"
+ " between a very high level and a common low-level target. To make the\n"
+ " output more useful (and terminate in a reasonable time), GN will not\n"
+ " revisit sub-paths previously known to lead to the target.\n"
+ "\n"
"Options\n"
"\n"
" --all\n"
- " Prints all paths found rather than just the first one. Public paths\n"
- " will be printed first in order of increasing length, followed by\n"
- " non-public paths in order of increasing length.\n"
+ " Prints all \"interesting\" paths found rather than just the first\n"
+ " one. Public paths will be printed first in order of increasing\n"
+ " length, followed by non-public paths in order of increasing length.\n"
"\n"
" --public\n"
" Considers only public paths. Can't be used with --with-data.\n"
@@ -239,7 +339,8 @@ int RunPath(const std::vector<std::string>& args) {
return 1;
Options options;
- options.all = base::CommandLine::ForCurrentProcess()->HasSwitch("all");
+ options.print_what = base::CommandLine::ForCurrentProcess()->HasSwitch("all")
+ ? PrintWhat::ALL : PrintWhat::ONE;
options.public_only =
base::CommandLine::ForCurrentProcess()->HasSwitch("public");
options.with_data =
@@ -251,21 +352,15 @@ int RunPath(const std::vector<std::string>& args) {
return 1;
}
- // If we don't find a path going "forwards", try the reverse direction. Deps
- // can only go in one direction without having a cycle, which will have
- // caused a run failure above.
- State state;
- DepStack stack;
- RecursiveFindPath(options, &state, target1, target2, &stack);
- if (state.found_count == 0) {
- // Need to reset the rejected set for a new invocation since the reverse
- // search will revisit the same targets looking for something else.
- state.rejected.clear();
- RecursiveFindPath(options, &state, target2, target1, &stack);
+ Stats stats;
+ DoSearch(target1, target2, options, &stats);
+ if (stats.total_paths() == 0) {
+ // If we don't find a path going "forwards", try the reverse direction.
+ // Deps can only go in one direction without having a cycle, which will
+ // have caused a run failure above.
+ DoSearch(target2, target1, options, &stats);
}
- PrintResults(options, &state);
-
// This string is inserted in the results to annotate whether the result
// is only public or includes data deps or not.
const char* path_annotation = "";
@@ -274,42 +369,42 @@ int RunPath(const std::vector<std::string>& args) {
else if (!options.with_data)
path_annotation = "non-data ";
- if (state.found_count == 0) {
+ if (stats.total_paths() == 0) {
// No results.
OutputString(base::StringPrintf(
"No %spaths found between these two targets.\n", path_annotation),
DECORATION_YELLOW);
- } else if (state.found_count == 1) {
+ } else if (stats.total_paths() == 1) {
// Exactly one result.
OutputString(base::StringPrintf("1 %spath found.", path_annotation),
DECORATION_YELLOW);
if (!options.public_only) {
- if (state.found_public.empty())
- OutputString(" It is not public.");
- else
+ if (stats.public_paths)
OutputString(" It is public.");
+ else
+ OutputString(" It is not public.");
}
OutputString("\n");
} else {
- if (options.all) {
+ if (options.print_what == PrintWhat::ALL) {
// Showing all paths when there are many.
- OutputString(base::StringPrintf("%d unique %spaths found.",
- state.found_count, path_annotation),
+ OutputString(base::StringPrintf("%d \"interesting\" %spaths found.",
+ stats.total_paths(), path_annotation),
DECORATION_YELLOW);
if (!options.public_only) {
OutputString(base::StringPrintf(" %d of them are public.",
- static_cast<int>(state.found_public.size())));
+ stats.public_paths));
}
OutputString("\n");
} else {
// Showing one path when there are many.
OutputString(
- base::StringPrintf("Showing one of %d unique %spaths.",
- state.found_count, path_annotation),
+ base::StringPrintf("Showing one of %d \"interesting\" %spaths.",
+ stats.total_paths(), path_annotation),
DECORATION_YELLOW);
if (!options.public_only) {
OutputString(base::StringPrintf(" %d of them are public.\n",
- static_cast<int>(state.found_public.size())));
+ stats.public_paths));
}
OutputString("Use --all to print all paths.\n");
}
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698