| Index: tools/gn/command_path.cc
|
| diff --git a/tools/gn/command_path.cc b/tools/gn/command_path.cc
|
| index ae39c8c557191789a2ac2b358fb640ccb8413b95..021ffe32bca215b78f64d1531addee6ad89371db 100644
|
| --- a/tools/gn/command_path.cc
|
| +++ b/tools/gn/command_path.cc
|
| @@ -2,6 +2,8 @@
|
| // Use of this source code is governed by a BSD-style license that can be
|
| // found in the LICENSE file.
|
|
|
| +#include <algorithm>
|
| +
|
| #include "base/command_line.h"
|
| #include "base/containers/hash_tables.h"
|
| #include "base/strings/stringprintf.h"
|
| @@ -25,8 +27,47 @@ enum DepType {
|
| using TargetDep = std::pair<const Target*, DepType>;
|
| using DepStack = 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*>;
|
|
|
| +struct Options {
|
| + Options()
|
| + : all(false),
|
| + public_only(false),
|
| + with_data(false) {
|
| + }
|
| +
|
| + bool all;
|
| + 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);
|
| + }
|
| +
|
| + // Stores targets that do not have any paths to the destination. This is
|
| + // an optimization to avoid revisiting useless paths.
|
| + DepSet rejected;
|
| +
|
| + // Total number of paths found.
|
| + int found_count;
|
| +
|
| + // 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;
|
| +};
|
| +
|
| 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();
|
| @@ -51,46 +92,89 @@ void PrintDepStack(const DepStack& stack) {
|
| OutputString("\n");
|
| }
|
|
|
| +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;
|
| + }
|
| + 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 Target* current,
|
| +void RecursiveFindPath(const Options& options,
|
| + State* state,
|
| + const Target* current,
|
| const Target* desired,
|
| - DepStack* stack,
|
| - DepSet* reject,
|
| - int* found_count,
|
| - bool print_all) {
|
| - if (reject->find(current) != reject->end())
|
| + DepStack* stack) {
|
| + if (state->rejected.find(current) != state->rejected.end())
|
| return;
|
| - int initial_found_count = *found_count;
|
| + int initial_found_count = state->found_count;
|
|
|
| if (current == desired) {
|
| - (*found_count)++;
|
| - if (print_all || *found_count == 1) {
|
| - stack->push_back(TargetDep(current, DEP_NONE));
|
| - PrintDepStack(*stack);
|
| - stack->pop_back();
|
| - }
|
| + // 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();
|
| return;
|
| }
|
|
|
| stack->push_back(TargetDep(current, DEP_PUBLIC));
|
| for (const auto& pair : current->public_deps())
|
| - RecursiveFindPath(pair.ptr, desired, stack, reject, found_count, print_all);
|
| + RecursiveFindPath(options, state, pair.ptr, desired, stack);
|
|
|
| - stack->back().second = DEP_PRIVATE;
|
| - for (const auto& pair : current->private_deps())
|
| - RecursiveFindPath(pair.ptr, desired, stack, reject, found_count, print_all);
|
| + if (!options.public_only) {
|
| + stack->back().second = DEP_PRIVATE;
|
| + for (const auto& pair : current->private_deps())
|
| + RecursiveFindPath(options, state, pair.ptr, desired, stack);
|
| + }
|
| +
|
| + if (options.with_data) {
|
| + stack->back().second = DEP_DATA;
|
| + for (const auto& pair : current->data_deps())
|
| + RecursiveFindPath(options, state, pair.ptr, desired, stack);
|
| + }
|
|
|
| - stack->back().second = DEP_DATA;
|
| - for (const auto& pair : current->data_deps())
|
| - RecursiveFindPath(pair.ptr, desired, stack, reject, found_count, print_all);
|
| stack->pop_back();
|
|
|
| - if (*found_count == initial_found_count)
|
| - reject->insert(current); // Eliminated this target.
|
| + if (state->found_count == initial_found_count)
|
| + state->rejected.insert(current); // Eliminated this target.
|
| +}
|
| +
|
| +bool StackLengthLess(const DepStack* a, const DepStack* b) {
|
| + return a->size() < b->size();
|
| +}
|
| +
|
| +// 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;
|
| + }
|
| +
|
| + // 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);
|
| }
|
|
|
| } // namespace
|
| @@ -106,14 +190,26 @@ const char kPath_Help[] =
|
| " The two targets can appear in either order: paths will be found going\n"
|
| " in either direction.\n"
|
| "\n"
|
| - " Each dependency will be annotated with its type. By default, only the\n"
|
| - " first path encountered will be printed, which is not necessarily the\n"
|
| - " shortest path.\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"
|
| + " Otherwise, the shortest path using either public or private\n"
|
| + " dependencies will be printed. If --with-data is specified, data deps\n"
|
| + " will also be considered. If there are multiple shortest paths, an\n"
|
| + " arbitrary one will be selected.\n"
|
| "\n"
|
| "Options\n"
|
| "\n"
|
| " --all\n"
|
| - " Prints all paths found rather than just the first one.\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"
|
| + "\n"
|
| + " --public\n"
|
| + " Considers only public paths. Can't be used with --with-data.\n"
|
| + "\n"
|
| + " --with-data\n"
|
| + " Additionally follows data deps. Without this flag, only public and\n"
|
| + " private linked deps will be followed. Can't be used with --public.\n"
|
| "\n"
|
| "Example\n"
|
| "\n"
|
| @@ -140,35 +236,79 @@ int RunPath(const std::vector<std::string>& args) {
|
| if (!target2)
|
| return 1;
|
|
|
| - bool print_all = base::CommandLine::ForCurrentProcess()->HasSwitch("all");
|
| + Options options;
|
| + options.all = base::CommandLine::ForCurrentProcess()->HasSwitch("all");
|
| + options.public_only =
|
| + base::CommandLine::ForCurrentProcess()->HasSwitch("public");
|
| + options.with_data =
|
| + base::CommandLine::ForCurrentProcess()->HasSwitch("with-data");
|
| + if (options.public_only && options.with_data) {
|
| + Err(Location(), "Can't use --public with --with-data for 'gn path'.",
|
| + "Your zealous over-use of arguments has inevitably resulted in an "
|
| + "invalid\ncombination of flags.").PrintToStdout();
|
| + 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;
|
| - DepSet rejected;
|
| - int found = 0;
|
| - RecursiveFindPath(target1, target2, &stack, &rejected, &found, print_all);
|
| - if (found == 0) {
|
| + 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.
|
| - rejected.clear();
|
| - RecursiveFindPath(target2, target1, &stack, &rejected, &found, print_all);
|
| + state.rejected.clear();
|
| + RecursiveFindPath(options, &state, target2, target1, &stack);
|
| }
|
|
|
| - if (found == 0) {
|
| - OutputString("No paths found between these two targets.\n",
|
| + 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 = "";
|
| + if (options.public_only)
|
| + path_annotation = "public ";
|
| + else if (!options.with_data)
|
| + path_annotation = "non-data ";
|
| +
|
| + if (state.found_count == 0) {
|
| + // No results.
|
| + OutputString(base::StringPrintf(
|
| + "No %spaths found between these two targets.\n", path_annotation),
|
| + DECORATION_YELLOW);
|
| + } else if (state.found_count == 1) {
|
| + // Exactly one result.
|
| + OutputString(base::StringPrintf("1 %spath found.", path_annotation),
|
| DECORATION_YELLOW);
|
| - } else if (found == 1) {
|
| - OutputString("1 path found.\n", DECORATION_YELLOW);
|
| + if (!options.public_only) {
|
| + if (state.found_public.empty())
|
| + OutputString(" It is not public.");
|
| + else
|
| + OutputString(" It is public.");
|
| + }
|
| + OutputString("\n");
|
| } else {
|
| - if (print_all) {
|
| - OutputString(base::StringPrintf("%d unique paths found.\n", found),
|
| + if (options.all) {
|
| + // Showing all paths when there are many.
|
| + OutputString(base::StringPrintf("%d unique %spaths found.",
|
| + state.found_count, path_annotation),
|
| DECORATION_YELLOW);
|
| + if (!options.public_only) {
|
| + OutputString(base::StringPrintf(" %d of them are public.",
|
| + static_cast<int>(state.found_public.size())));
|
| + }
|
| + OutputString("\n");
|
| } else {
|
| + // Showing one path when there are many.
|
| OutputString(
|
| - base::StringPrintf("Showing the first of %d unique paths. ", found),
|
| + base::StringPrintf("Showing one of %d unique %spaths.",
|
| + state.found_count, path_annotation),
|
| DECORATION_YELLOW);
|
| + if (!options.public_only) {
|
| + OutputString(base::StringPrintf(" %d of them are public.\n",
|
| + static_cast<int>(state.found_public.size())));
|
| + }
|
| OutputString("Use --all to print all paths.\n");
|
| }
|
| }
|
|
|