OLD | NEW |
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 |
OLD | NEW |