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

Side by Side Diff: pkg/analyzer/tool/task_dependency_graph.dart

Issue 1408743006: Rework task model graphing to work on buildbots. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Created 5 years, 2 months 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 | pkg/analyzer/tool/task_dependency_graph/check_test.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2015, 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 /**
6 * This file contains code to output a description of tasks and their
7 * dependencies in ".dot" format. Prior to running, the user should run "pub
8 * get" in the analyzer directory to ensure that a "packages" folder exists.
9 *
10 * The ".dot" file is output to standard out. To convert it to a pdf, store it
11 * in a file (e.g. "tasks.dot"), and post-process it with
12 * "dot tasks.dot -Tpdf -O".
13 *
14 * TODO(paulberry):
15 * - Add general.dart and html.dart for completeness.
16 * - Use Graphviz's "record" feature to produce more compact output
17 * (http://www.graphviz.org/content/node-shapes#record)
18 * - Produce a warning if a result descriptor is found which isn't the output
19 * of exactly one task.
20 * - Convert this tool to use package_config to find the package map.
21 */
22 library task_dependency_graph;
23
24 import 'dart:io' hide File;
25
26 import 'package:analyzer/analyzer.dart';
27 import 'package:analyzer/file_system/file_system.dart';
28 import 'package:analyzer/file_system/physical_file_system.dart';
29 import 'package:analyzer/src/generated/constant.dart';
30 import 'package:analyzer/src/generated/element.dart';
31 import 'package:analyzer/src/generated/engine.dart';
32 import 'package:analyzer/src/generated/java_io.dart';
33 import 'package:analyzer/src/generated/sdk.dart';
34 import 'package:analyzer/src/generated/sdk_io.dart';
35 import 'package:analyzer/src/generated/source.dart';
36 import 'package:analyzer/src/generated/source_io.dart';
37 import 'package:path/path.dart' as path;
38
39 main() {
40 new Driver().run();
41 }
42
43 typedef void GetterFinderCallback(PropertyAccessorElement element);
44
45 class Driver {
46 PhysicalResourceProvider resourceProvider;
47 AnalysisContext context;
48 InterfaceType resultDescriptorType;
49 InterfaceType listOfResultDescriptorType;
50 ClassElement enginePluginClass;
51 CompilationUnitElement taskUnitElement;
52 InterfaceType extensionPointIdType;
53 String rootDir;
54
55 /**
56 * Starting at [node], find all calls to registerExtension() which refer to
57 * the given [extensionIdVariable], and execute [callback] for the associated
58 * result descriptors.
59 */
60 void findExtensions(AstNode node, TopLevelVariableElement extensionIdVariable,
61 void callback(descriptorName)) {
62 Set<PropertyAccessorElement> resultDescriptors =
63 new Set<PropertyAccessorElement>();
64 node.accept(new ExtensionFinder(
65 resultDescriptorType, extensionIdVariable, resultDescriptors.add));
66 for (PropertyAccessorElement resultDescriptor in resultDescriptors) {
67 callback(resultDescriptor.name);
68 }
69 }
70
71 /**
72 * Starting at [node], find all references to a getter of type
73 * `List<ResultDescriptor>`, and execute [callback] on the getter names.
74 */
75 void findResultDescriptorLists(
76 AstNode node, void callback(String descriptorListName)) {
77 Set<PropertyAccessorElement> resultDescriptorLists =
78 new Set<PropertyAccessorElement>();
79 node.accept(new GetterFinder(
80 listOfResultDescriptorType, resultDescriptorLists.add));
81 for (PropertyAccessorElement resultDescriptorList
82 in resultDescriptorLists) {
83 // We only care about result descriptor lists associated with getters in
84 // the engine plugin class.
85 if (resultDescriptorList.enclosingElement != enginePluginClass) {
86 continue;
87 }
88 callback(resultDescriptorList.name);
89 }
90 }
91
92 void findResultDescriptors(
93 AstNode node, void callback(String descriptorName)) {
94 Set<PropertyAccessorElement> resultDescriptors =
95 new Set<PropertyAccessorElement>();
96 node.accept(new GetterFinder(resultDescriptorType, resultDescriptors.add));
97 for (PropertyAccessorElement resultDescriptor in resultDescriptors) {
98 callback(resultDescriptor.name);
99 }
100 }
101
102 /**
103 * Find the root directory of the analyzer package by proceeding
104 * upward to the 'tool' dir, and then going up one more directory.
105 */
106 String findRoot(String pathname) {
107 while (path.basename(pathname) != 'tool') {
108 String parent = path.dirname(pathname);
109 if (parent.length >= pathname.length) {
110 throw new Exception("Can't find root directory");
111 }
112 pathname = parent;
113 }
114 return path.dirname(pathname);
115 }
116
117 CompilationUnit getUnit(Source source) =>
118 context.resolveCompilationUnit2(source, source);
119
120 void run() {
121 rootDir = findRoot(Platform.script.toFilePath(windows: Platform.isWindows));
122 resourceProvider = PhysicalResourceProvider.INSTANCE;
123 DartSdk sdk = DirectoryBasedDartSdk.defaultSdk;
124 context = AnalysisEngine.instance.createAnalysisContext();
125 JavaFile packagesDir = new JavaFile(path.join(rootDir, 'packages'));
126 List<UriResolver> uriResolvers = [
127 new DartUriResolver(sdk),
128 new PackageUriResolver(<JavaFile>[packagesDir]),
129 new FileUriResolver()
130 ];
131 context.sourceFactory = new SourceFactory(uriResolvers);
132 Source dartDartSource =
133 setupSource(path.join('lib', 'src', 'task', 'dart.dart'));
134 Source taskSource = setupSource(path.join('lib', 'plugin', 'task.dart'));
135 Source modelSource = setupSource(path.join('lib', 'task', 'model.dart'));
136 Source enginePluginSource =
137 setupSource(path.join('lib', 'src', 'plugin', 'engine_plugin.dart'));
138 CompilationUnitElement modelElement = getUnit(modelSource).element;
139 InterfaceType analysisTaskType = modelElement.getType('AnalysisTask').type;
140 DartType dynamicType = context.typeProvider.dynamicType;
141 resultDescriptorType = modelElement
142 .getType('ResultDescriptor')
143 .type
144 .substitute4([dynamicType]);
145 listOfResultDescriptorType =
146 context.typeProvider.listType.substitute4([resultDescriptorType]);
147 CompilationUnitElement enginePluginUnitElement =
148 getUnit(enginePluginSource).element;
149 enginePluginClass = enginePluginUnitElement.getType('EnginePlugin');
150 extensionPointIdType =
151 enginePluginUnitElement.getType('ExtensionPointId').type;
152 CompilationUnit dartDartUnit = getUnit(dartDartSource);
153 CompilationUnitElement dartDartUnitElement = dartDartUnit.element;
154 CompilationUnit taskUnit = getUnit(taskSource);
155 taskUnitElement = taskUnit.element;
156 print('digraph G {');
157 Set<String> results = new Set<String>();
158 Set<String> resultLists = new Set<String>();
159 for (ClassElement cls in dartDartUnitElement.types) {
160 if (!cls.isAbstract && cls.type.isSubtypeOf(analysisTaskType)) {
161 String task = cls.name;
162 // TODO(paulberry): node is deprecated. What am I supposed to do
163 // instead?
164 AstNode buildInputsAst = cls.getMethod('buildInputs').node;
165 findResultDescriptors(buildInputsAst, (String input) {
166 results.add(input);
167 print(' $input -> $task');
168 });
169 findResultDescriptorLists(buildInputsAst, (String input) {
170 resultLists.add(input);
171 print(' $input -> $task');
172 });
173 findResultDescriptors(cls.getField('DESCRIPTOR').node, (String output) {
174 results.add(output);
175 print(' $task -> $output');
176 });
177 }
178 }
179 AstNode enginePluginAst = enginePluginUnitElement.node;
180 for (String resultList in resultLists) {
181 print(' $resultList [shape=hexagon]');
182 TopLevelVariableElement extensionIdVariable = _getExtensionId(resultList);
183 findExtensions(enginePluginAst, extensionIdVariable, (String extension) {
184 results.add(extension);
185 print(' $extension -> $resultList');
186 });
187 }
188 for (String result in results) {
189 print(' $result [shape=box]');
190 }
191 print('}');
192 }
193
194 Source setupSource(String filename) {
195 String filePath = path.join(rootDir, filename);
196 File file = resourceProvider.getResource(filePath);
197 Source source = file.createSource();
198 Uri restoredUri = context.sourceFactory.restoreUri(source);
199 if (restoredUri != null) {
200 source = file.createSource(restoredUri);
201 }
202 ChangeSet changeSet = new ChangeSet();
203 changeSet.addedSource(source);
204 context.applyChanges(changeSet);
205 return source;
206 }
207
208 /**
209 * Find the result list getter having name [resultListGetterName] in the
210 * [EnginePlugin] class, and use the [ExtensionPointId] annotation on that
211 * getter to find the associated [TopLevelVariableElement] which can be used
212 * to register extensions for that getter.
213 */
214 TopLevelVariableElement _getExtensionId(String resultListGetterName) {
215 PropertyAccessorElement getter =
216 enginePluginClass.getGetter(resultListGetterName);
217 for (ElementAnnotation annotation in getter.metadata) {
218 // TODO(paulberry): we should be using part of the public API rather than
219 // just casting to ElementAnnotationImpl.
220 ElementAnnotationImpl annotationImpl = annotation;
221 DartObjectImpl annotationValue = annotationImpl.evaluationResult.value;
222 if (annotationValue.type.isSubtypeOf(extensionPointIdType)) {
223 String extensionPointId =
224 annotationValue.fields['extensionPointId'].value;
225 for (TopLevelVariableElement variable
226 in taskUnitElement.topLevelVariables) {
227 if (variable.name == extensionPointId) {
228 return variable;
229 }
230 }
231 }
232 }
233 throw new Exception(
234 'Could not find extension ID corresponding to $resultListGetterName');
235 }
236 }
237
238 /**
239 * Visitor that finds calls that register extension points. Specifically, we
240 * look for calls of the form `method(extensionIdVariable, resultDescriptor)`,
241 * where `resultDescriptor` has type [resultDescriptorType], and we pass the
242 * corresponding result descriptor names to [callback].
243 */
244 class ExtensionFinder extends GeneralizingAstVisitor {
245 final InterfaceType resultDescriptorType;
246 final TopLevelVariableElement extensionIdVariable;
247 final GetterFinderCallback callback;
248
249 ExtensionFinder(
250 this.resultDescriptorType, this.extensionIdVariable, this.callback);
251
252 @override
253 visitIdentifier(Identifier node) {
254 Element element = node.staticElement;
255 if (element is PropertyAccessorElement &&
256 element.isGetter &&
257 element.variable == extensionIdVariable) {
258 AstNode parent = node.parent;
259 if (parent is ArgumentList &&
260 parent.arguments.length == 2 &&
261 parent.arguments[0] == node) {
262 Expression extension = parent.arguments[1];
263 if (extension is Identifier) {
264 Element element = extension.staticElement;
265 if (element is PropertyAccessorElement &&
266 element.isGetter &&
267 element.returnType.isSubtypeOf(resultDescriptorType)) {
268 callback(element);
269 return;
270 }
271 }
272 }
273 throw new Exception('Could not decode extension setup: $parent');
274 }
275 }
276 }
277
278 /**
279 * Visitor that finds references to getters having a specific type (or a
280 * subtype of that type)
281 */
282 class GetterFinder extends GeneralizingAstVisitor {
283 final InterfaceType type;
284 final GetterFinderCallback callback;
285
286 GetterFinder(this.type, this.callback);
287
288 @override
289 visitIdentifier(Identifier node) {
290 Element element = node.staticElement;
291 if (element is PropertyAccessorElement &&
292 element.isGetter &&
293 element.returnType.isSubtypeOf(type)) {
294 callback(element);
295 }
296 }
297 }
OLDNEW
« no previous file with comments | « no previous file | pkg/analyzer/tool/task_dependency_graph/check_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698