| OLD | NEW |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 /** | 5 /** |
| 6 * This file contains code to output a description of tasks and their | 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 | 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. | 8 * get" in the analyzer directory to ensure that a "packages" folder exists. |
| 9 * | 9 * |
| 10 * TODO(paulberry): | 10 * TODO(paulberry): |
| 11 * - Add general.dart and html.dart for completeness. | 11 * - Add general.dart and html.dart for completeness. |
| 12 * - Use Graphviz's "record" feature to produce more compact output | 12 * - Use Graphviz's "record" feature to produce more compact output |
| 13 * (http://www.graphviz.org/content/node-shapes#record) | 13 * (http://www.graphviz.org/content/node-shapes#record) |
| 14 * - Produce a warning if a result descriptor is found which isn't the output | 14 * - Produce a warning if a result descriptor is found which isn't the output |
| 15 * of exactly one task. | 15 * of exactly one task. |
| 16 * - Convert this tool to use package_config to find the package map. | 16 * - Convert this tool to use package_config to find the package map. |
| 17 */ | 17 */ |
| 18 library task_dependency_graph.generate; | 18 library analyzer.tool.task_dependency_graph.generate; |
| 19 | 19 |
| 20 import 'dart:io' hide File; | 20 import 'dart:io' hide File; |
| 21 import 'dart:io' as io; | 21 import 'dart:io' as io; |
| 22 | 22 |
| 23 import 'package:analyzer/analyzer.dart'; | 23 import 'package:analyzer/analyzer.dart'; |
| 24 import 'package:analyzer/dart/element/element.dart'; |
| 25 import 'package:analyzer/dart/element/type.dart'; |
| 24 import 'package:analyzer/file_system/file_system.dart'; | 26 import 'package:analyzer/file_system/file_system.dart'; |
| 25 import 'package:analyzer/file_system/physical_file_system.dart'; | 27 import 'package:analyzer/file_system/physical_file_system.dart'; |
| 28 import 'package:analyzer/source/package_map_resolver.dart'; |
| 29 import 'package:analyzer/src/codegen/tools.dart'; |
| 30 import 'package:analyzer/src/context/builder.dart'; |
| 31 import 'package:analyzer/src/dart/sdk/sdk.dart'; |
| 26 import 'package:analyzer/src/generated/constant.dart'; | 32 import 'package:analyzer/src/generated/constant.dart'; |
| 27 import 'package:analyzer/src/generated/element.dart'; | |
| 28 import 'package:analyzer/src/generated/engine.dart'; | 33 import 'package:analyzer/src/generated/engine.dart'; |
| 29 import 'package:analyzer/src/generated/java_io.dart'; | |
| 30 import 'package:analyzer/src/generated/sdk.dart'; | 34 import 'package:analyzer/src/generated/sdk.dart'; |
| 31 import 'package:analyzer/src/generated/sdk_io.dart'; | |
| 32 import 'package:analyzer/src/generated/source.dart'; | 35 import 'package:analyzer/src/generated/source.dart'; |
| 33 import 'package:analyzer/src/generated/source_io.dart'; | 36 import 'package:analyzer/src/generated/source_io.dart'; |
| 34 import 'package:path/path.dart' as path; | 37 import 'package:path/path.dart' as path; |
| 38 import 'package:path/path.dart'; |
| 35 | 39 |
| 36 /** | 40 /** |
| 37 * Generate the target .dot file. | 41 * Generate the target .dot file. |
| 38 */ | 42 */ |
| 39 main() { | 43 main() { |
| 40 new Driver().generateFile(); | 44 String script = Platform.script.toFilePath(windows: Platform.isWindows); |
| 45 String pkgPath = normalize(join(dirname(script), '..', '..')); |
| 46 GeneratedContent.generateAll(pkgPath, <GeneratedContent>[target, htmlTarget]); |
| 41 } | 47 } |
| 42 | 48 |
| 49 final GeneratedFile htmlTarget = new GeneratedFile( |
| 50 'doc/tasks.html', (String pkgPath) => new Driver(pkgPath).generateHtml()); |
| 51 |
| 52 final GeneratedFile target = new GeneratedFile( |
| 53 'tool/task_dependency_graph/tasks.dot', |
| 54 (String pkgPath) => new Driver(pkgPath).generateFileContents()); |
| 55 |
| 43 typedef void GetterFinderCallback(PropertyAccessorElement element); | 56 typedef void GetterFinderCallback(PropertyAccessorElement element); |
| 44 | 57 |
| 45 class Driver { | 58 class Driver { |
| 59 static bool hasInitializedPlugins = false; |
| 46 PhysicalResourceProvider resourceProvider; | 60 PhysicalResourceProvider resourceProvider; |
| 47 AnalysisContext context; | 61 AnalysisContext context; |
| 48 InterfaceType resultDescriptorType; | 62 InterfaceType resultDescriptorType; |
| 49 InterfaceType listOfResultDescriptorType; | 63 InterfaceType listOfResultDescriptorType; |
| 50 ClassElement enginePluginClass; | 64 ClassElement enginePluginClass; |
| 51 CompilationUnitElement taskUnitElement; | 65 CompilationUnitElement taskUnitElement; |
| 52 InterfaceType extensionPointIdType; | 66 InterfaceType extensionPointIdType; |
| 67 |
| 53 final String rootDir; | 68 final String rootDir; |
| 54 | 69 |
| 55 Driver() | 70 Driver(String pkgPath) : rootDir = new Directory(pkgPath).absolute.path; |
| 56 : rootDir = | |
| 57 findRoot(Platform.script.toFilePath(windows: Platform.isWindows)); | |
| 58 | 71 |
| 59 /** | 72 /** |
| 60 * Get an [io.File] object corresponding to the file in which the generated | 73 * Get an [io.File] object corresponding to the file in which the generated |
| 61 * graph should be output. | 74 * graph should be output. |
| 62 */ | 75 */ |
| 63 io.File get file => new io.File( | 76 io.File get file => new io.File( |
| 64 path.join(rootDir, 'tool', 'task_dependency_graph', 'tasks.dot')); | 77 path.join(rootDir, 'tool', 'task_dependency_graph', 'tasks.dot')); |
| 65 | 78 |
| 66 /** | 79 /** |
| 67 * Determine if the output [file] contains the expected contents. | |
| 68 */ | |
| 69 bool checkFile() { | |
| 70 String expectedContents = generateFileContents(); | |
| 71 String actualContents = file.readAsStringSync(); | |
| 72 // Normalize Windows line endings to Unix line endings so that the | |
| 73 // comparison doesn't fail on Windows. | |
| 74 actualContents = actualContents.replaceAll('\r\n', '\n'); | |
| 75 return expectedContents == actualContents; | |
| 76 } | |
| 77 | |
| 78 /** | |
| 79 * Starting at [node], find all calls to registerExtension() which refer to | 80 * Starting at [node], find all calls to registerExtension() which refer to |
| 80 * the given [extensionIdVariable], and execute [callback] for the associated | 81 * the given [extensionIdVariable], and execute [callback] for the associated |
| 81 * result descriptors. | 82 * result descriptors. |
| 82 */ | 83 */ |
| 83 void findExtensions(AstNode node, TopLevelVariableElement extensionIdVariable, | 84 void findExtensions(AstNode node, TopLevelVariableElement extensionIdVariable, |
| 84 void callback(descriptorName)) { | 85 void callback(descriptorName)) { |
| 85 Set<PropertyAccessorElement> resultDescriptors = | 86 Set<PropertyAccessorElement> resultDescriptors = |
| 86 new Set<PropertyAccessorElement>(); | 87 new Set<PropertyAccessorElement>(); |
| 87 node.accept(new ExtensionFinder( | 88 node.accept(new ExtensionFinder( |
| 88 resultDescriptorType, extensionIdVariable, resultDescriptors.add)); | 89 resultDescriptorType, extensionIdVariable, resultDescriptors.add)); |
| (...skipping 27 matching lines...) Expand all Loading... |
| 116 AstNode node, void callback(String descriptorName)) { | 117 AstNode node, void callback(String descriptorName)) { |
| 117 Set<PropertyAccessorElement> resultDescriptors = | 118 Set<PropertyAccessorElement> resultDescriptors = |
| 118 new Set<PropertyAccessorElement>(); | 119 new Set<PropertyAccessorElement>(); |
| 119 node.accept(new GetterFinder(resultDescriptorType, resultDescriptors.add)); | 120 node.accept(new GetterFinder(resultDescriptorType, resultDescriptors.add)); |
| 120 for (PropertyAccessorElement resultDescriptor in resultDescriptors) { | 121 for (PropertyAccessorElement resultDescriptor in resultDescriptors) { |
| 121 callback(resultDescriptor.name); | 122 callback(resultDescriptor.name); |
| 122 } | 123 } |
| 123 } | 124 } |
| 124 | 125 |
| 125 /** | 126 /** |
| 126 * Generate the task dependency graph and write it to the output [file]. | |
| 127 */ | |
| 128 void generateFile() { | |
| 129 String fileContents = generateFileContents(); | |
| 130 file.writeAsStringSync(fileContents); | |
| 131 } | |
| 132 | |
| 133 /** | |
| 134 * Generate the task dependency graph and return it as a [String]. | 127 * Generate the task dependency graph and return it as a [String]. |
| 135 */ | 128 */ |
| 136 String generateFileContents() { | 129 String generateFileContents() { |
| 130 return ''' |
| 131 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| 132 // for details. All rights reserved. Use of this source code is governed by a |
| 133 // BSD-style license that can be found in the LICENSE file. |
| 134 // |
| 135 // This file has been automatically generated. Please do not edit it manually. |
| 136 // To regenerate the file, use the script |
| 137 // "pkg/analyzer/tool/task_dependency_graph/generate.dart". |
| 138 // |
| 139 // To render this graph using Graphviz (www.graphviz.org) use the command: |
| 140 // "dot tasks.dot -Tpdf -O". |
| 141 digraph G { |
| 142 ${generateGraphData()} |
| 143 } |
| 144 '''; |
| 145 } |
| 146 |
| 147 String generateGraphData() { |
| 148 if (!hasInitializedPlugins) { |
| 149 AnalysisEngine.instance.processRequiredPlugins(); |
| 150 hasInitializedPlugins = true; |
| 151 } |
| 137 List<String> lines = <String>[]; | 152 List<String> lines = <String>[]; |
| 138 resourceProvider = PhysicalResourceProvider.INSTANCE; | 153 resourceProvider = PhysicalResourceProvider.INSTANCE; |
| 139 DartSdk sdk = DirectoryBasedDartSdk.defaultSdk; | 154 DartSdk sdk = new FolderBasedDartSdk(resourceProvider, |
| 155 FolderBasedDartSdk.defaultSdkDirectory(resourceProvider)); |
| 140 context = AnalysisEngine.instance.createAnalysisContext(); | 156 context = AnalysisEngine.instance.createAnalysisContext(); |
| 141 String packageRootPath; | 157 ContextBuilder builder = new ContextBuilder(resourceProvider, null, null); |
| 142 if (Platform.packageRoot.isNotEmpty) { | 158 if (Platform.packageRoot != null) { |
| 143 packageRootPath = Platform.packageRoot; | 159 builder.defaultPackagesDirectoryPath = |
| 160 Uri.parse(Platform.packageRoot).toFilePath(); |
| 161 } else if (Platform.packageConfig != null) { |
| 162 builder.defaultPackageFilePath = |
| 163 Uri.parse(Platform.packageConfig).toFilePath(); |
| 144 } else { | 164 } else { |
| 145 packageRootPath = path.join(rootDir, 'packages'); | 165 // Let the context builder use the default algorithm for package |
| 166 // resolution. |
| 146 } | 167 } |
| 147 JavaFile packagesDir = new JavaFile(packageRootPath); | |
| 148 List<UriResolver> uriResolvers = [ | 168 List<UriResolver> uriResolvers = [ |
| 149 new DartUriResolver(sdk), | 169 new DartUriResolver(sdk), |
| 150 new PackageUriResolver(<JavaFile>[packagesDir]), | 170 new PackageMapUriResolver(resourceProvider, |
| 151 new FileUriResolver() | 171 builder.convertPackagesToMap(builder.createPackageMap(''))), |
| 172 new ResourceUriResolver(PhysicalResourceProvider.INSTANCE) |
| 152 ]; | 173 ]; |
| 153 context.sourceFactory = new SourceFactory(uriResolvers); | 174 context.sourceFactory = new SourceFactory(uriResolvers); |
| 154 Source dartDartSource = | 175 Source dartDartSource = |
| 155 setupSource(path.join('lib', 'src', 'task', 'dart.dart')); | 176 setupSource(path.join('lib', 'src', 'task', 'dart.dart')); |
| 156 Source taskSource = setupSource(path.join('lib', 'plugin', 'task.dart')); | 177 Source taskSource = setupSource(path.join('lib', 'plugin', 'task.dart')); |
| 157 Source modelSource = setupSource(path.join('lib', 'task', 'model.dart')); | 178 Source modelSource = setupSource(path.join('lib', 'task', 'model.dart')); |
| 158 Source enginePluginSource = | 179 Source enginePluginSource = |
| 159 setupSource(path.join('lib', 'src', 'plugin', 'engine_plugin.dart')); | 180 setupSource(path.join('lib', 'src', 'plugin', 'engine_plugin.dart')); |
| 160 CompilationUnitElement modelElement = getUnit(modelSource).element; | 181 CompilationUnitElement modelElement = getUnit(modelSource).element; |
| 161 InterfaceType analysisTaskType = modelElement.getType('AnalysisTask').type; | 182 InterfaceType analysisTaskType = modelElement.getType('AnalysisTask').type; |
| 162 DartType dynamicType = context.typeProvider.dynamicType; | 183 DartType dynamicType = context.typeProvider.dynamicType; |
| 163 resultDescriptorType = modelElement | 184 resultDescriptorType = modelElement |
| 164 .getType('ResultDescriptor') | 185 .getType('ResultDescriptor') |
| 165 .type | 186 .type |
| 166 .substitute4([dynamicType]); | 187 .instantiate([dynamicType]); |
| 167 listOfResultDescriptorType = | 188 listOfResultDescriptorType = |
| 168 context.typeProvider.listType.substitute4([resultDescriptorType]); | 189 context.typeProvider.listType.instantiate([resultDescriptorType]); |
| 169 CompilationUnitElement enginePluginUnitElement = | 190 CompilationUnitElement enginePluginUnitElement = |
| 170 getUnit(enginePluginSource).element; | 191 getUnit(enginePluginSource).element; |
| 171 enginePluginClass = enginePluginUnitElement.getType('EnginePlugin'); | 192 enginePluginClass = enginePluginUnitElement.getType('EnginePlugin'); |
| 172 extensionPointIdType = | 193 extensionPointIdType = |
| 173 enginePluginUnitElement.getType('ExtensionPointId').type; | 194 enginePluginUnitElement.getType('ExtensionPointId').type; |
| 174 CompilationUnit dartDartUnit = getUnit(dartDartSource); | 195 CompilationUnit dartDartUnit = getUnit(dartDartSource); |
| 175 CompilationUnitElement dartDartUnitElement = dartDartUnit.element; | 196 CompilationUnitElement dartDartUnitElement = dartDartUnit.element; |
| 176 CompilationUnit taskUnit = getUnit(taskSource); | 197 CompilationUnit taskUnit = getUnit(taskSource); |
| 177 taskUnitElement = taskUnit.element; | 198 taskUnitElement = taskUnit.element; |
| 178 Set<String> results = new Set<String>(); | 199 Set<String> results = new Set<String>(); |
| 179 Set<String> resultLists = new Set<String>(); | 200 Set<String> resultLists = new Set<String>(); |
| 180 for (ClassElement cls in dartDartUnitElement.types) { | 201 for (ClassElement cls in dartDartUnitElement.types) { |
| 181 if (!cls.isAbstract && cls.type.isSubtypeOf(analysisTaskType)) { | 202 if (!cls.isAbstract && cls.type.isSubtypeOf(analysisTaskType)) { |
| 182 String task = cls.name; | 203 String task = cls.name; |
| 183 AstNode buildInputsAst = cls.getMethod('buildInputs').computeNode(); | 204 AstNode buildInputsAst = cls.getMethod('buildInputs').computeNode(); |
| 184 findResultDescriptors(buildInputsAst, (String input) { | 205 findResultDescriptors(buildInputsAst, (String input) { |
| 185 results.add(input); | 206 results.add(input); |
| 186 lines.add(' $input -> $task'); | 207 lines.add(' $input -> $task'); |
| 187 }); | 208 }); |
| 188 findResultDescriptorLists(buildInputsAst, (String input) { | 209 findResultDescriptorLists(buildInputsAst, (String input) { |
| 189 resultLists.add(input); | 210 resultLists.add(input); |
| 190 lines.add(' $input -> $task'); | 211 lines.add(' $input -> $task'); |
| 191 }); | 212 }); |
| 192 findResultDescriptors(cls.getField('DESCRIPTOR').computeNode(), (String
out) { | 213 findResultDescriptors(cls.getField('DESCRIPTOR').computeNode(), |
| 214 (String out) { |
| 193 results.add(out); | 215 results.add(out); |
| 194 lines.add(' $task -> $out'); | 216 lines.add(' $task -> $out'); |
| 195 }); | 217 }); |
| 196 } | 218 } |
| 197 } | 219 } |
| 198 AstNode enginePluginAst = enginePluginUnitElement.computeNode(); | 220 AstNode enginePluginAst = enginePluginUnitElement.computeNode(); |
| 199 for (String resultList in resultLists) { | 221 for (String resultList in resultLists) { |
| 200 lines.add(' $resultList [shape=hexagon]'); | 222 lines.add(' $resultList [shape=hexagon]'); |
| 201 TopLevelVariableElement extensionIdVariable = _getExtensionId(resultList); | 223 TopLevelVariableElement extensionIdVariable = _getExtensionId(resultList); |
| 202 findExtensions(enginePluginAst, extensionIdVariable, (String extension) { | 224 findExtensions(enginePluginAst, extensionIdVariable, (String extension) { |
| 203 results.add(extension); | 225 results.add(extension); |
| 204 lines.add(' $extension -> $resultList'); | 226 lines.add(' $extension -> $resultList'); |
| 205 }); | 227 }); |
| 206 } | 228 } |
| 207 for (String result in results) { | 229 for (String result in results) { |
| 208 lines.add(' $result [shape=box]'); | 230 lines.add(' $result [shape=box]'); |
| 209 } | 231 } |
| 210 lines.sort(); | 232 lines.sort(); |
| 233 return lines.join('\n'); |
| 234 } |
| 235 |
| 236 String generateHtml() { |
| 211 return ''' | 237 return ''' |
| 212 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 238 <!DOCTYPE html> |
| 213 // for details. All rights reserved. Use of this source code is governed by a | 239 <html> |
| 214 // BSD-style license that can be found in the LICENSE file. | 240 <head> |
| 215 // | 241 <title>Analysis Task Dependency Graph</title> |
| 216 // This file has been automatically generated. Please do not edit it manually. | 242 <link rel="stylesheet" href="support/style.css"> |
| 217 // To regenerate the file, use the script | 243 <script src="support/viz.js"></script> |
| 218 // "pkg/analyzer/tool/task_dependency_graph/generate.dart". | 244 <script type="application/dart" src="support/web_app.dart.js"></script> |
| 219 // | 245 <script src="support/dart.js"></script> |
| 220 // To render this graph using Graphviz (www.graphviz.org) use the command: | 246 </head> |
| 221 // "dot tasks.dot -Tpdf -O". | 247 <body> |
| 248 <button id="zoomBtn">Zoom</button> |
| 249 <script type="text/vnd.graphviz" id="dot"> |
| 222 digraph G { | 250 digraph G { |
| 223 ${lines.join('\n')} | 251 tooltip="Analysis Task Dependency Graph"; |
| 252 node [fontname=Helvetica]; |
| 253 edge [fontname=Helvetica, fontcolor=gray]; |
| 254 ${generateGraphData()} |
| 224 } | 255 } |
| 256 </script> |
| 257 </body> |
| 258 </html> |
| 225 '''; | 259 '''; |
| 226 } | 260 } |
| 227 | 261 |
| 228 CompilationUnit getUnit(Source source) => | 262 CompilationUnit getUnit(Source source) => |
| 229 context.resolveCompilationUnit2(source, source); | 263 context.resolveCompilationUnit2(source, source); |
| 230 | 264 |
| 231 Source setupSource(String filename) { | 265 Source setupSource(String filename) { |
| 232 String filePath = path.join(rootDir, filename); | 266 String filePath = path.join(rootDir, filename); |
| 233 File file = resourceProvider.getResource(filePath); | 267 File file = resourceProvider.getResource(filePath); |
| 234 Source source = file.createSource(); | 268 Source source = file.createSource(); |
| (...skipping 25 matching lines...) Expand all Loading... |
| 260 in taskUnitElement.topLevelVariables) { | 294 in taskUnitElement.topLevelVariables) { |
| 261 if (variable.name == extensionPointId) { | 295 if (variable.name == extensionPointId) { |
| 262 return variable; | 296 return variable; |
| 263 } | 297 } |
| 264 } | 298 } |
| 265 } | 299 } |
| 266 } | 300 } |
| 267 throw new Exception( | 301 throw new Exception( |
| 268 'Could not find extension ID corresponding to $resultListGetterName'); | 302 'Could not find extension ID corresponding to $resultListGetterName'); |
| 269 } | 303 } |
| 270 | |
| 271 /** | |
| 272 * Find the root directory of the analyzer package by proceeding | |
| 273 * upward to the 'tool' dir, and then going up one more directory. | |
| 274 */ | |
| 275 static String findRoot(String pathname) { | |
| 276 while (path.basename(pathname) != 'tool') { | |
| 277 String parent = path.dirname(pathname); | |
| 278 if (parent.length >= pathname.length) { | |
| 279 throw new Exception("Can't find root directory"); | |
| 280 } | |
| 281 pathname = parent; | |
| 282 } | |
| 283 return path.dirname(pathname); | |
| 284 } | |
| 285 } | 304 } |
| 286 | 305 |
| 287 /** | 306 /** |
| 288 * Visitor that finds calls that register extension points. Specifically, we | 307 * Visitor that finds calls that register extension points. Specifically, we |
| 289 * look for calls of the form `method(extensionIdVariable, resultDescriptor)`, | 308 * look for calls of the form `method(extensionIdVariable, resultDescriptor)`, |
| 290 * where `resultDescriptor` has type [resultDescriptorType], and we pass the | 309 * where `resultDescriptor` has type [resultDescriptorType], and we pass the |
| 291 * corresponding result descriptor names to [callback]. | 310 * corresponding result descriptor names to [callback]. |
| 292 */ | 311 */ |
| 293 class ExtensionFinder extends GeneralizingAstVisitor { | 312 class ExtensionFinder extends GeneralizingAstVisitor { |
| 294 final InterfaceType resultDescriptorType; | 313 final InterfaceType resultDescriptorType; |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 337 @override | 356 @override |
| 338 visitIdentifier(Identifier node) { | 357 visitIdentifier(Identifier node) { |
| 339 Element element = node.staticElement; | 358 Element element = node.staticElement; |
| 340 if (element is PropertyAccessorElement && | 359 if (element is PropertyAccessorElement && |
| 341 element.isGetter && | 360 element.isGetter && |
| 342 element.returnType.isSubtypeOf(type)) { | 361 element.returnType.isSubtypeOf(type)) { |
| 343 callback(element); | 362 callback(element); |
| 344 } | 363 } |
| 345 } | 364 } |
| 346 } | 365 } |
| OLD | NEW |