| 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 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/file_system/file_system.dart'; | 24 import 'package:analyzer/file_system/file_system.dart'; |
| 25 import 'package:analyzer/file_system/physical_file_system.dart'; | 25 import 'package:analyzer/file_system/physical_file_system.dart'; |
| 26 import 'package:analyzer/src/codegen/tools.dart'; |
| 26 import 'package:analyzer/src/generated/constant.dart'; | 27 import 'package:analyzer/src/generated/constant.dart'; |
| 27 import 'package:analyzer/src/generated/element.dart'; | 28 import 'package:analyzer/src/generated/element.dart'; |
| 28 import 'package:analyzer/src/generated/engine.dart'; | 29 import 'package:analyzer/src/generated/engine.dart'; |
| 29 import 'package:analyzer/src/generated/java_io.dart'; | 30 import 'package:analyzer/src/generated/java_io.dart'; |
| 30 import 'package:analyzer/src/generated/sdk.dart'; | 31 import 'package:analyzer/src/generated/sdk.dart'; |
| 31 import 'package:analyzer/src/generated/sdk_io.dart'; | 32 import 'package:analyzer/src/generated/sdk_io.dart'; |
| 32 import 'package:analyzer/src/generated/source.dart'; | 33 import 'package:analyzer/src/generated/source.dart'; |
| 33 import 'package:analyzer/src/generated/source_io.dart'; | 34 import 'package:analyzer/src/generated/source_io.dart'; |
| 34 import 'package:path/path.dart' as path; | 35 import 'package:path/path.dart' as path; |
| 36 import 'package:path/path.dart'; |
| 35 | 37 |
| 36 /** | 38 /** |
| 37 * Generate the target .dot file. | 39 * Generate the target .dot file. |
| 38 */ | 40 */ |
| 39 main() { | 41 main() { |
| 40 new Driver().generateFile(); | 42 String script = Platform.script.toFilePath(windows: Platform.isWindows); |
| 43 String pkgPath = normalize(join(dirname(script), '..', '..')); |
| 44 target.generate(pkgPath); |
| 41 } | 45 } |
| 42 | 46 |
| 47 final GeneratedFile target = new GeneratedFile( |
| 48 'tool/task_dependency_graph/tasks.dot', |
| 49 (String pkgPath) => new Driver(pkgPath).generateFileContents()); |
| 50 |
| 43 typedef void GetterFinderCallback(PropertyAccessorElement element); | 51 typedef void GetterFinderCallback(PropertyAccessorElement element); |
| 44 | 52 |
| 45 class Driver { | 53 class Driver { |
| 46 PhysicalResourceProvider resourceProvider; | 54 PhysicalResourceProvider resourceProvider; |
| 47 AnalysisContext context; | 55 AnalysisContext context; |
| 48 InterfaceType resultDescriptorType; | 56 InterfaceType resultDescriptorType; |
| 49 InterfaceType listOfResultDescriptorType; | 57 InterfaceType listOfResultDescriptorType; |
| 50 ClassElement enginePluginClass; | 58 ClassElement enginePluginClass; |
| 51 CompilationUnitElement taskUnitElement; | 59 CompilationUnitElement taskUnitElement; |
| 52 InterfaceType extensionPointIdType; | 60 InterfaceType extensionPointIdType; |
| 53 final String rootDir; | 61 final String rootDir; |
| 54 | 62 |
| 55 Driver() | 63 Driver(String pkgPath) : rootDir = new Directory(pkgPath).absolute.path; |
| 56 : rootDir = | |
| 57 findRoot(Platform.script.toFilePath(windows: Platform.isWindows)); | |
| 58 | 64 |
| 59 /** | 65 /** |
| 60 * Get an [io.File] object corresponding to the file in which the generated | 66 * Get an [io.File] object corresponding to the file in which the generated |
| 61 * graph should be output. | 67 * graph should be output. |
| 62 */ | 68 */ |
| 63 io.File get file => new io.File( | 69 io.File get file => new io.File( |
| 64 path.join(rootDir, 'tool', 'task_dependency_graph', 'tasks.dot')); | 70 path.join(rootDir, 'tool', 'task_dependency_graph', 'tasks.dot')); |
| 65 | 71 |
| 66 /** | 72 /** |
| 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 | 73 * Starting at [node], find all calls to registerExtension() which refer to |
| 80 * the given [extensionIdVariable], and execute [callback] for the associated | 74 * the given [extensionIdVariable], and execute [callback] for the associated |
| 81 * result descriptors. | 75 * result descriptors. |
| 82 */ | 76 */ |
| 83 void findExtensions(AstNode node, TopLevelVariableElement extensionIdVariable, | 77 void findExtensions(AstNode node, TopLevelVariableElement extensionIdVariable, |
| 84 void callback(descriptorName)) { | 78 void callback(descriptorName)) { |
| 85 Set<PropertyAccessorElement> resultDescriptors = | 79 Set<PropertyAccessorElement> resultDescriptors = |
| 86 new Set<PropertyAccessorElement>(); | 80 new Set<PropertyAccessorElement>(); |
| 87 node.accept(new ExtensionFinder( | 81 node.accept(new ExtensionFinder( |
| 88 resultDescriptorType, extensionIdVariable, resultDescriptors.add)); | 82 resultDescriptorType, extensionIdVariable, resultDescriptors.add)); |
| (...skipping 27 matching lines...) Expand all Loading... |
| 116 AstNode node, void callback(String descriptorName)) { | 110 AstNode node, void callback(String descriptorName)) { |
| 117 Set<PropertyAccessorElement> resultDescriptors = | 111 Set<PropertyAccessorElement> resultDescriptors = |
| 118 new Set<PropertyAccessorElement>(); | 112 new Set<PropertyAccessorElement>(); |
| 119 node.accept(new GetterFinder(resultDescriptorType, resultDescriptors.add)); | 113 node.accept(new GetterFinder(resultDescriptorType, resultDescriptors.add)); |
| 120 for (PropertyAccessorElement resultDescriptor in resultDescriptors) { | 114 for (PropertyAccessorElement resultDescriptor in resultDescriptors) { |
| 121 callback(resultDescriptor.name); | 115 callback(resultDescriptor.name); |
| 122 } | 116 } |
| 123 } | 117 } |
| 124 | 118 |
| 125 /** | 119 /** |
| 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]. | 120 * Generate the task dependency graph and return it as a [String]. |
| 135 */ | 121 */ |
| 136 String generateFileContents() { | 122 String generateFileContents() { |
| 137 List<String> lines = <String>[]; | 123 List<String> lines = <String>[]; |
| 138 resourceProvider = PhysicalResourceProvider.INSTANCE; | 124 resourceProvider = PhysicalResourceProvider.INSTANCE; |
| 139 DartSdk sdk = DirectoryBasedDartSdk.defaultSdk; | 125 DartSdk sdk = DirectoryBasedDartSdk.defaultSdk; |
| 140 context = AnalysisEngine.instance.createAnalysisContext(); | 126 context = AnalysisEngine.instance.createAnalysisContext(); |
| 141 String packageRootPath; | 127 String packageRootPath; |
| 142 if (Platform.packageRoot.isNotEmpty) { | 128 if (Platform.packageRoot.isNotEmpty) { |
| 143 packageRootPath = Platform.packageRoot; | 129 packageRootPath = Platform.packageRoot; |
| (...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 261 in taskUnitElement.topLevelVariables) { | 247 in taskUnitElement.topLevelVariables) { |
| 262 if (variable.name == extensionPointId) { | 248 if (variable.name == extensionPointId) { |
| 263 return variable; | 249 return variable; |
| 264 } | 250 } |
| 265 } | 251 } |
| 266 } | 252 } |
| 267 } | 253 } |
| 268 throw new Exception( | 254 throw new Exception( |
| 269 'Could not find extension ID corresponding to $resultListGetterName'); | 255 'Could not find extension ID corresponding to $resultListGetterName'); |
| 270 } | 256 } |
| 271 | |
| 272 /** | |
| 273 * Find the root directory of the analyzer package by proceeding | |
| 274 * upward to the 'tool' dir, and then going up one more directory. | |
| 275 */ | |
| 276 static String findRoot(String pathname) { | |
| 277 while (path.basename(pathname) != 'tool') { | |
| 278 String parent = path.dirname(pathname); | |
| 279 if (parent.length >= pathname.length) { | |
| 280 throw new Exception("Can't find root directory"); | |
| 281 } | |
| 282 pathname = parent; | |
| 283 } | |
| 284 return path.dirname(pathname); | |
| 285 } | |
| 286 } | 257 } |
| 287 | 258 |
| 288 /** | 259 /** |
| 289 * Visitor that finds calls that register extension points. Specifically, we | 260 * Visitor that finds calls that register extension points. Specifically, we |
| 290 * look for calls of the form `method(extensionIdVariable, resultDescriptor)`, | 261 * look for calls of the form `method(extensionIdVariable, resultDescriptor)`, |
| 291 * where `resultDescriptor` has type [resultDescriptorType], and we pass the | 262 * where `resultDescriptor` has type [resultDescriptorType], and we pass the |
| 292 * corresponding result descriptor names to [callback]. | 263 * corresponding result descriptor names to [callback]. |
| 293 */ | 264 */ |
| 294 class ExtensionFinder extends GeneralizingAstVisitor { | 265 class ExtensionFinder extends GeneralizingAstVisitor { |
| 295 final InterfaceType resultDescriptorType; | 266 final InterfaceType resultDescriptorType; |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 338 @override | 309 @override |
| 339 visitIdentifier(Identifier node) { | 310 visitIdentifier(Identifier node) { |
| 340 Element element = node.staticElement; | 311 Element element = node.staticElement; |
| 341 if (element is PropertyAccessorElement && | 312 if (element is PropertyAccessorElement && |
| 342 element.isGetter && | 313 element.isGetter && |
| 343 element.returnType.isSubtypeOf(type)) { | 314 element.returnType.isSubtypeOf(type)) { |
| 344 callback(element); | 315 callback(element); |
| 345 } | 316 } |
| 346 } | 317 } |
| 347 } | 318 } |
| OLD | NEW |