OLD | NEW |
| (Empty) |
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | |
2 // for details. All rights reserved. Use of this source code is governed by a | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 library pub.command.list; | |
6 | |
7 import 'dart:async'; | |
8 import 'dart:collection'; | |
9 | |
10 import '../ascii_tree.dart' as tree; | |
11 import '../command.dart'; | |
12 import '../log.dart' as log; | |
13 import '../package.dart'; | |
14 import '../package_graph.dart'; | |
15 import '../utils.dart'; | |
16 | |
17 /// Handles the `deps` pub command. | |
18 class DepsCommand extends PubCommand { | |
19 String get name => "deps"; | |
20 String get description => "Print package dependencies."; | |
21 List<String> get aliases => const ["dependencies", "tab"]; | |
22 String get invocation => "pub deps"; | |
23 String get docUrl => "http://dartlang.org/tools/pub/cmd/pub-deps.html"; | |
24 bool get takesArguments => false; | |
25 | |
26 /// The loaded package graph. | |
27 PackageGraph _graph; | |
28 | |
29 /// The [StringBuffer] used to accumulate the output. | |
30 StringBuffer _buffer; | |
31 | |
32 DepsCommand() { | |
33 argParser.addOption( | |
34 "style", | |
35 abbr: "s", | |
36 help: "How output should be displayed.", | |
37 allowed: ["compact", "tree", "list"], | |
38 defaultsTo: "tree"); | |
39 } | |
40 | |
41 Future run() { | |
42 return entrypoint.loadPackageGraph().then((graph) { | |
43 _graph = graph; | |
44 _buffer = new StringBuffer(); | |
45 | |
46 _buffer.writeln(_labelPackage(entrypoint.root)); | |
47 | |
48 switch (argResults["style"]) { | |
49 case "compact": | |
50 _outputCompact(); | |
51 break; | |
52 case "list": | |
53 _outputList(); | |
54 break; | |
55 case "tree": | |
56 _outputTree(); | |
57 break; | |
58 } | |
59 | |
60 log.message(_buffer); | |
61 }); | |
62 } | |
63 | |
64 /// Outputs a list of all of the package's immediate, dev, override, and | |
65 /// transitive dependencies. | |
66 /// | |
67 /// For each dependency listed, *that* package's immediate dependencies are | |
68 /// shown. Unlike [_outputList], this prints all of these dependencies on one | |
69 /// line. | |
70 void _outputCompact() { | |
71 var root = entrypoint.root; | |
72 _outputCompactPackages( | |
73 "dependencies", | |
74 root.dependencies.map((dep) => dep.name)); | |
75 _outputCompactPackages( | |
76 "dev dependencies", | |
77 root.devDependencies.map((dep) => dep.name)); | |
78 _outputCompactPackages( | |
79 "dependency overrides", | |
80 root.dependencyOverrides.map((dep) => dep.name)); | |
81 | |
82 var transitive = _getTransitiveDependencies(); | |
83 _outputCompactPackages("transitive dependencies", transitive); | |
84 } | |
85 | |
86 /// Outputs one section of packages in the compact output. | |
87 _outputCompactPackages(String section, Iterable<String> names) { | |
88 if (names.isEmpty) return; | |
89 | |
90 _buffer.writeln(); | |
91 _buffer.writeln("$section:"); | |
92 for (var name in ordered(names)) { | |
93 var package = _graph.packages[name]; | |
94 | |
95 _buffer.write("- ${_labelPackage(package)}"); | |
96 if (package.dependencies.isEmpty) { | |
97 _buffer.writeln(); | |
98 } else { | |
99 var depNames = package.dependencies.map((dep) => dep.name); | |
100 var depsList = "[${depNames.join(' ')}]"; | |
101 _buffer.writeln(" ${log.gray(depsList)}"); | |
102 } | |
103 } | |
104 } | |
105 | |
106 /// Outputs a list of all of the package's immediate, dev, override, and | |
107 /// transitive dependencies. | |
108 /// | |
109 /// For each dependency listed, *that* package's immediate dependencies are | |
110 /// shown. | |
111 void _outputList() { | |
112 var root = entrypoint.root; | |
113 _outputListSection( | |
114 "dependencies", | |
115 root.dependencies.map((dep) => dep.name)); | |
116 _outputListSection( | |
117 "dev dependencies", | |
118 root.devDependencies.map((dep) => dep.name)); | |
119 _outputListSection( | |
120 "dependency overrides", | |
121 root.dependencyOverrides.map((dep) => dep.name)); | |
122 | |
123 var transitive = _getTransitiveDependencies(); | |
124 if (transitive.isEmpty) return; | |
125 | |
126 _outputListSection("transitive dependencies", ordered(transitive)); | |
127 } | |
128 | |
129 /// Outputs one section of packages in the list output. | |
130 _outputListSection(String name, Iterable<String> deps) { | |
131 if (deps.isEmpty) return; | |
132 | |
133 _buffer.writeln(); | |
134 _buffer.writeln("$name:"); | |
135 | |
136 for (var name in deps) { | |
137 var package = _graph.packages[name]; | |
138 _buffer.writeln("- ${_labelPackage(package)}"); | |
139 | |
140 for (var dep in package.dependencies) { | |
141 _buffer.writeln( | |
142 " - ${log.bold(dep.name)} ${log.gray(dep.constraint)}"); | |
143 } | |
144 } | |
145 } | |
146 | |
147 /// Generates a dependency tree for the root package. | |
148 /// | |
149 /// If a package is encountered more than once (i.e. a shared or circular | |
150 /// dependency), later ones are not traversed. This is done in breadth-first | |
151 /// fashion so that a package will always be expanded at the shallowest | |
152 /// depth that it appears at. | |
153 void _outputTree() { | |
154 // The work list for the breadth-first traversal. It contains the package | |
155 // being added to the tree, and the parent map that will receive that | |
156 // package. | |
157 var toWalk = new Queue<Pair<Package, Map>>(); | |
158 var visited = new Set<String>(); | |
159 | |
160 // Start with the root dependencies. | |
161 var packageTree = {}; | |
162 for (var dep in entrypoint.root.immediateDependencies) { | |
163 toWalk.add(new Pair(_graph.packages[dep.name], packageTree)); | |
164 } | |
165 | |
166 // Do a breadth-first walk to the dependency graph. | |
167 while (toWalk.isNotEmpty) { | |
168 var pair = toWalk.removeFirst(); | |
169 var package = pair.first; | |
170 var map = pair.last; | |
171 | |
172 if (visited.contains(package.name)) { | |
173 map[log.gray('${package.name}...')] = {}; | |
174 continue; | |
175 } | |
176 | |
177 visited.add(package.name); | |
178 | |
179 // Populate the map with this package's dependencies. | |
180 var childMap = {}; | |
181 map[_labelPackage(package)] = childMap; | |
182 | |
183 for (var dep in package.dependencies) { | |
184 toWalk.add(new Pair(_graph.packages[dep.name], childMap)); | |
185 } | |
186 } | |
187 | |
188 _buffer.write(tree.fromMap(packageTree, showAllChildren: true)); | |
189 } | |
190 | |
191 String _labelPackage(Package package) => | |
192 "${log.bold(package.name)} ${package.version}"; | |
193 | |
194 /// Gets the names of the non-immediate dependencies of the root package. | |
195 Set<String> _getTransitiveDependencies() { | |
196 var transitive = _graph.packages.keys.toSet(); | |
197 var root = entrypoint.root; | |
198 transitive.remove(root.name); | |
199 transitive.removeAll(root.dependencies.map((dep) => dep.name)); | |
200 transitive.removeAll(root.devDependencies.map((dep) => dep.name)); | |
201 transitive.removeAll(root.dependencyOverrides.map((dep) => dep.name)); | |
202 return transitive; | |
203 } | |
204 } | |
OLD | NEW |