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

Side by Side Diff: tools/gn/command_path.cc

Issue 1424313009: Improve gn path command. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 1 month 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 unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include <algorithm>
6
5 #include "base/command_line.h" 7 #include "base/command_line.h"
6 #include "base/containers/hash_tables.h" 8 #include "base/containers/hash_tables.h"
7 #include "base/strings/stringprintf.h" 9 #include "base/strings/stringprintf.h"
8 #include "tools/gn/commands.h" 10 #include "tools/gn/commands.h"
9 #include "tools/gn/setup.h" 11 #include "tools/gn/setup.h"
10 #include "tools/gn/standard_out.h" 12 #include "tools/gn/standard_out.h"
11 13
12 namespace commands { 14 namespace commands {
13 15
14 namespace { 16 namespace {
15 17
16 enum DepType { 18 enum DepType {
17 DEP_NONE, 19 DEP_NONE,
18 DEP_PUBLIC, 20 DEP_PUBLIC,
19 DEP_PRIVATE, 21 DEP_PRIVATE,
20 DEP_DATA 22 DEP_DATA
21 }; 23 };
22 24
23 // As we do a depth-first search, this vector will store the current path 25 // As we do a depth-first search, this vector will store the current path
24 // the current target for printing when a match is found. 26 // the current target for printing when a match is found.
25 using TargetDep = std::pair<const Target*, DepType>; 27 using TargetDep = std::pair<const Target*, DepType>;
26 using DepStack = std::vector<TargetDep>; 28 using DepStack = std::vector<TargetDep>;
27 29
30 // Note that this uses raw pointers. These need to be manually deleted (which
31 // we won't normally bother with). This allows the vector to be resized
32 // more quickly.
33 using DepStackVector = std::vector<DepStack*>;
34
28 using DepSet = base::hash_set<const Target*>; 35 using DepSet = base::hash_set<const Target*>;
29 36
37 struct Options {
38 Options()
39 : all(false),
40 public_only(false),
41 with_data(false) {
42 }
43
44 bool all;
45 bool public_only;
46 bool with_data;
47 };
48
49 struct State {
50 State() : found_count(0) {
51 // Reserve fairly large buffers for the found vectors.
52 const size_t kReserveSize = 32768;
53 found_public.reserve(kReserveSize);
54 found_other.reserve(kReserveSize);
55 }
56
57 // Stores targets that do not have any paths to the destination. This is
58 // an optimization to avoid revisiting useless paths.
59 DepSet rejected;
60
61 // Total number of paths found.
62 int found_count;
63
64 // The pointers in these vectors are owned by this object, but are
65 // deliberately leaked. There can be a lot of them which can take a long time
66 // to free, and GN will just exit after this is used anyway.
67 DepStackVector found_public;
68 DepStackVector found_other;
69 };
70
30 void PrintDepStack(const DepStack& stack) { 71 void PrintDepStack(const DepStack& stack) {
31 // Don't print toolchains unless they differ from the first target. 72 // Don't print toolchains unless they differ from the first target.
32 const Label& default_toolchain = stack[0].first->label().GetToolchainLabel(); 73 const Label& default_toolchain = stack[0].first->label().GetToolchainLabel();
33 74
34 for (const auto& pair : stack) { 75 for (const auto& pair : stack) {
35 OutputString(pair.first->label().GetUserVisibleName(default_toolchain)); 76 OutputString(pair.first->label().GetUserVisibleName(default_toolchain));
36 switch (pair.second) { 77 switch (pair.second) {
37 case DEP_NONE: 78 case DEP_NONE:
38 break; 79 break;
39 case DEP_PUBLIC: 80 case DEP_PUBLIC:
40 OutputString(" --[public]-->", DECORATION_DIM); 81 OutputString(" --[public]-->", DECORATION_DIM);
41 break; 82 break;
42 case DEP_PRIVATE: 83 case DEP_PRIVATE:
43 OutputString(" --[private]-->", DECORATION_DIM); 84 OutputString(" --[private]-->", DECORATION_DIM);
44 break; 85 break;
45 case DEP_DATA: 86 case DEP_DATA:
46 OutputString(" --[data]-->", DECORATION_DIM); 87 OutputString(" --[data]-->", DECORATION_DIM);
47 break; 88 break;
48 } 89 }
49 OutputString("\n"); 90 OutputString("\n");
50 } 91 }
51 OutputString("\n"); 92 OutputString("\n");
52 } 93 }
53 94
95 bool AreAllPublic(const DepStack& stack) {
96 // Don't check the type of the last one since that doesn't point to anything.
97 for (size_t i = 0; i < stack.size() - 1; i++) {
98 if (stack[i].second != DEP_PUBLIC)
99 return false;
100 }
101 return true;
102 }
103
54 // Increments *found_count to reflect how many results are found. If print_all 104 // Increments *found_count to reflect how many results are found. If print_all
55 // is not set, only the first result will be printed. 105 // is not set, only the first result will be printed.
56 // 106 //
57 // As an optimization, targets that do not have any paths are added to 107 // As an optimization, targets that do not have any paths are added to
58 // *reject so this function doesn't waste time revisiting them. 108 // *reject so this function doesn't waste time revisiting them.
59 void RecursiveFindPath(const Target* current, 109 void RecursiveFindPath(const Options& options,
110 State* state,
111 const Target* current,
60 const Target* desired, 112 const Target* desired,
61 DepStack* stack, 113 DepStack* stack) {
62 DepSet* reject, 114 if (state->rejected.find(current) != state->rejected.end())
63 int* found_count,
64 bool print_all) {
65 if (reject->find(current) != reject->end())
66 return; 115 return;
67 int initial_found_count = *found_count; 116 int initial_found_count = state->found_count;
68 117
69 if (current == desired) { 118 if (current == desired) {
70 (*found_count)++; 119 // Found a path.
71 if (print_all || *found_count == 1) { 120 state->found_count++;
72 stack->push_back(TargetDep(current, DEP_NONE)); 121 stack->push_back(TargetDep(current, DEP_NONE));
73 PrintDepStack(*stack); 122 if (AreAllPublic(*stack))
74 stack->pop_back(); 123 state->found_public.push_back(new DepStack(*stack));
75 } 124 else
125 state->found_other.push_back(new DepStack(*stack));
126 stack->pop_back();
76 return; 127 return;
77 } 128 }
78 129
79 stack->push_back(TargetDep(current, DEP_PUBLIC)); 130 stack->push_back(TargetDep(current, DEP_PUBLIC));
80 for (const auto& pair : current->public_deps()) 131 for (const auto& pair : current->public_deps())
81 RecursiveFindPath(pair.ptr, desired, stack, reject, found_count, print_all); 132 RecursiveFindPath(options, state, pair.ptr, desired, stack);
82 133
83 stack->back().second = DEP_PRIVATE; 134 if (!options.public_only) {
84 for (const auto& pair : current->private_deps()) 135 stack->back().second = DEP_PRIVATE;
85 RecursiveFindPath(pair.ptr, desired, stack, reject, found_count, print_all); 136 for (const auto& pair : current->private_deps())
137 RecursiveFindPath(options, state, pair.ptr, desired, stack);
138 }
86 139
87 stack->back().second = DEP_DATA; 140 if (options.with_data) {
88 for (const auto& pair : current->data_deps()) 141 stack->back().second = DEP_DATA;
89 RecursiveFindPath(pair.ptr, desired, stack, reject, found_count, print_all); 142 for (const auto& pair : current->data_deps())
143 RecursiveFindPath(options, state, pair.ptr, desired, stack);
144 }
145
90 stack->pop_back(); 146 stack->pop_back();
91 147
92 if (*found_count == initial_found_count) 148 if (state->found_count == initial_found_count)
93 reject->insert(current); // Eliminated this target. 149 state->rejected.insert(current); // Eliminated this target.
150 }
151
152 bool StackLengthLess(const DepStack* a, const DepStack* b) {
153 return a->size() < b->size();
154 }
155
156 // Prints one result vector. The vector will be modified.
157 void PrintResultVector(const Options& options, DepStackVector* result) {
158 if (!options.all && !result->empty()) {
159 // Just print the smallest one.
160 PrintDepStack(**std::min_element(result->begin(), result->end(),
161 &StackLengthLess));
162 return;
163 }
164
165 // Print all in order of increasing length.
166 std::sort(result->begin(), result->end(), &StackLengthLess);
167 for (const auto& stack : *result)
168 PrintDepStack(*stack);
169 }
170
171 void PrintResults(const Options& options, State* state) {
172 PrintResultVector(options, &state->found_public);
173
174 // Consider non-public paths only if all paths are requested or there were
175 // no public paths.
176 if (state->found_public.empty() || options.all)
177 PrintResultVector(options, &state->found_other);
94 } 178 }
95 179
96 } // namespace 180 } // namespace
97 181
98 const char kPath[] = "path"; 182 const char kPath[] = "path";
99 const char kPath_HelpShort[] = 183 const char kPath_HelpShort[] =
100 "path: Find paths between two targets."; 184 "path: Find paths between two targets.";
101 const char kPath_Help[] = 185 const char kPath_Help[] =
102 "gn path <out_dir> <target_one> <target_two>\n" 186 "gn path <out_dir> <target_one> <target_two>\n"
103 "\n" 187 "\n"
104 " Finds paths of dependencies between two targets. Each unique path\n" 188 " Finds paths of dependencies between two targets. Each unique path\n"
105 " will be printed in one group, and groups will be separate by newlines.\n" 189 " will be printed in one group, and groups will be separate by newlines.\n"
106 " The two targets can appear in either order: paths will be found going\n" 190 " The two targets can appear in either order: paths will be found going\n"
107 " in either direction.\n" 191 " in either direction.\n"
108 "\n" 192 "\n"
109 " Each dependency will be annotated with its type. By default, only the\n" 193 " By default, a single path will be printed. If there is a path with\n"
110 " first path encountered will be printed, which is not necessarily the\n" 194 " only public dependencies, the shortest public path will be printed.\n"
111 " shortest path.\n" 195 " Otherwise, the shortest path using either public or private\n"
196 " dependencies will be printed. If --with-data is specified, data deps\n"
197 " will also be considered. If there are multiple shortest paths, an\n"
198 " arbitrary one will be selected.\n"
112 "\n" 199 "\n"
113 "Options\n" 200 "Options\n"
114 "\n" 201 "\n"
115 " --all\n" 202 " --all\n"
116 " Prints all paths found rather than just the first one.\n" 203 " Prints all paths found rather than just the first one. Public paths\n"
204 " will be printed first in order of increasing length, followed by\n"
205 " non-public paths in order of increasing length.\n"
206 "\n"
207 " --public\n"
208 " Considers only public paths. Can't be used with --with-data.\n"
209 "\n"
210 " --with-data\n"
211 " Additionally follows data deps. Without this flag, only public and\n"
212 " private linked deps will be followed. Can't be used with --public.\n"
117 "\n" 213 "\n"
118 "Example\n" 214 "Example\n"
119 "\n" 215 "\n"
120 " gn path out/Default //base //tools/gn\n"; 216 " gn path out/Default //base //tools/gn\n";
121 217
122 int RunPath(const std::vector<std::string>& args) { 218 int RunPath(const std::vector<std::string>& args) {
123 if (args.size() != 3) { 219 if (args.size() != 3) {
124 Err(Location(), "You're holding it wrong.", 220 Err(Location(), "You're holding it wrong.",
125 "Usage: \"gn path <out_dir> <target_one> <target_two>\"") 221 "Usage: \"gn path <out_dir> <target_one> <target_two>\"")
126 .PrintToStdout(); 222 .PrintToStdout();
127 return 1; 223 return 1;
128 } 224 }
129 225
130 Setup* setup = new Setup; 226 Setup* setup = new Setup;
131 if (!setup->DoSetup(args[0], false)) 227 if (!setup->DoSetup(args[0], false))
132 return 1; 228 return 1;
133 if (!setup->Run()) 229 if (!setup->Run())
134 return 1; 230 return 1;
135 231
136 const Target* target1 = ResolveTargetFromCommandLineString(setup, args[1]); 232 const Target* target1 = ResolveTargetFromCommandLineString(setup, args[1]);
137 if (!target1) 233 if (!target1)
138 return 1; 234 return 1;
139 const Target* target2 = ResolveTargetFromCommandLineString(setup, args[2]); 235 const Target* target2 = ResolveTargetFromCommandLineString(setup, args[2]);
140 if (!target2) 236 if (!target2)
141 return 1; 237 return 1;
142 238
143 bool print_all = base::CommandLine::ForCurrentProcess()->HasSwitch("all"); 239 Options options;
240 options.all = base::CommandLine::ForCurrentProcess()->HasSwitch("all");
241 options.public_only =
242 base::CommandLine::ForCurrentProcess()->HasSwitch("public");
243 options.with_data =
244 base::CommandLine::ForCurrentProcess()->HasSwitch("with-data");
245 if (options.public_only && options.with_data) {
246 Err(Location(), "Can't use --public with --with-data for 'gn path'.",
247 "Your zealous over-use of arguments has inevitably resulted in an "
248 "invalid\ncombination of flags.").PrintToStdout();
249 return 1;
250 }
144 251
145 // If we don't find a path going "forwards", try the reverse direction. Deps 252 // If we don't find a path going "forwards", try the reverse direction. Deps
146 // can only go in one direction without having a cycle, which will have 253 // can only go in one direction without having a cycle, which will have
147 // caused a run failure above. 254 // caused a run failure above.
255 State state;
148 DepStack stack; 256 DepStack stack;
149 DepSet rejected; 257 RecursiveFindPath(options, &state, target1, target2, &stack);
150 int found = 0; 258 if (state.found_count == 0) {
151 RecursiveFindPath(target1, target2, &stack, &rejected, &found, print_all);
152 if (found == 0) {
153 // Need to reset the rejected set for a new invocation since the reverse 259 // Need to reset the rejected set for a new invocation since the reverse
154 // search will revisit the same targets looking for something else. 260 // search will revisit the same targets looking for something else.
155 rejected.clear(); 261 state.rejected.clear();
156 RecursiveFindPath(target2, target1, &stack, &rejected, &found, print_all); 262 RecursiveFindPath(options, &state, target2, target1, &stack);
157 } 263 }
158 264
159 if (found == 0) { 265 PrintResults(options, &state);
160 OutputString("No paths found between these two targets.\n", 266
267 // This string is inserted in the results to annotate whether the result
268 // is only public or includes data deps or not.
269 const char* path_annotation = "";
270 if (options.public_only)
271 path_annotation = "public ";
272 else if (!options.with_data)
273 path_annotation = "non-data ";
274
275 if (state.found_count == 0) {
276 // No results.
277 OutputString(base::StringPrintf(
278 "No %spaths found between these two targets.\n", path_annotation),
279 DECORATION_YELLOW);
280 } else if (state.found_count == 1) {
281 // Exactly one result.
282 OutputString(base::StringPrintf("1 %spath found.", path_annotation),
161 DECORATION_YELLOW); 283 DECORATION_YELLOW);
162 } else if (found == 1) { 284 if (!options.public_only) {
163 OutputString("1 path found.\n", DECORATION_YELLOW); 285 if (state.found_public.empty())
286 OutputString(" It is not public.");
287 else
288 OutputString(" It is public.");
289 }
290 OutputString("\n");
164 } else { 291 } else {
165 if (print_all) { 292 if (options.all) {
166 OutputString(base::StringPrintf("%d unique paths found.\n", found), 293 // Showing all paths when there are many.
294 OutputString(base::StringPrintf("%d unique %spaths found.",
295 state.found_count, path_annotation),
167 DECORATION_YELLOW); 296 DECORATION_YELLOW);
297 if (!options.public_only) {
298 OutputString(base::StringPrintf(" %d of them are public.",
299 static_cast<int>(state.found_public.size())));
300 }
301 OutputString("\n");
168 } else { 302 } else {
303 // Showing one path when there are many.
169 OutputString( 304 OutputString(
170 base::StringPrintf("Showing the first of %d unique paths. ", found), 305 base::StringPrintf("Showing one of %d unique %spaths.",
306 state.found_count, path_annotation),
171 DECORATION_YELLOW); 307 DECORATION_YELLOW);
308 if (!options.public_only) {
309 OutputString(base::StringPrintf(" %d of them are public.\n",
310 static_cast<int>(state.found_public.size())));
311 }
172 OutputString("Use --all to print all paths.\n"); 312 OutputString("Use --all to print all paths.\n");
173 } 313 }
174 } 314 }
175 return 0; 315 return 0;
176 } 316 }
177 317
178 } // namespace commands 318 } // namespace commands
OLDNEW
« 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