forked from chromium/chromium
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcommand_path.cc
320 lines (279 loc) · 10.3 KB
/
command_path.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
// Copyright 2015 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 <stddef.h>
#include <algorithm>
#include "base/command_line.h"
#include "base/containers/hash_tables.h"
#include "base/strings/stringprintf.h"
#include "tools/gn/commands.h"
#include "tools/gn/setup.h"
#include "tools/gn/standard_out.h"
namespace commands {
namespace {
enum DepType {
DEP_NONE,
DEP_PUBLIC,
DEP_PRIVATE,
DEP_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.
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();
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;
}
OutputString("\n");
}
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 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();
return;
}
stack->push_back(TargetDep(current, DEP_PUBLIC));
for (const auto& pair : current->public_deps())
RecursiveFindPath(options, state, pair.ptr, desired, stack);
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->pop_back();
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
const char kPath[] = "path";
const char kPath_HelpShort[] =
"path: Find paths between two targets.";
const char kPath_Help[] =
"gn path <out_dir> <target_one> <target_two>\n"
"\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"
"\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. 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"
" gn path out/Default //base //tools/gn\n";
int RunPath(const std::vector<std::string>& args) {
if (args.size() != 3) {
Err(Location(), "You're holding it wrong.",
"Usage: \"gn path <out_dir> <target_one> <target_two>\"")
.PrintToStdout();
return 1;
}
Setup* setup = new Setup;
if (!setup->DoSetup(args[0], false))
return 1;
if (!setup->Run())
return 1;
const Target* target1 = ResolveTargetFromCommandLineString(setup, args[1]);
if (!target1)
return 1;
const Target* target2 = ResolveTargetFromCommandLineString(setup, args[2]);
if (!target2)
return 1;
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;
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);
}
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);
if (!options.public_only) {
if (state.found_public.empty())
OutputString(" It is not public.");
else
OutputString(" It is public.");
}
OutputString("\n");
} else {
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 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");
}
}
return 0;
}
} // namespace commands