152 lines
5.5 KiB
Dart
152 lines
5.5 KiB
Dart
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
/**
|
|
* This file contains code to output a description of tasks and their
|
|
* dependencies in ".dot" format. Prior to running, the user should run "pub
|
|
* get" in the analyzer directory to ensure that a "packages" folder exists.
|
|
*
|
|
* The ".dot" file is output to standard out. To convert it to a pdf, store it
|
|
* in a file (e.g. "tasks.dot"), and post-process it with
|
|
* "dot tasks.dart -Tpdf -O".
|
|
*
|
|
* TODO(paulberry):
|
|
* - Add general.dart and html.dart for completeness.
|
|
* - Use Graphviz's "record" feature to produce more compact output
|
|
* (http://www.graphviz.org/content/node-shapes#record)
|
|
* - Produce a warning if a result descriptor is found which isn't the output
|
|
* of exactly one task.
|
|
* - Convert this tool to use package_config to find the package map.
|
|
*/
|
|
library task_dependency_graph;
|
|
|
|
import 'dart:io' hide File;
|
|
|
|
import 'package:analyzer/analyzer.dart';
|
|
import 'package:analyzer/file_system/file_system.dart';
|
|
import 'package:analyzer/file_system/physical_file_system.dart';
|
|
import 'package:analyzer/src/generated/element.dart';
|
|
import 'package:analyzer/src/generated/engine.dart';
|
|
import 'package:analyzer/src/generated/java_io.dart';
|
|
import 'package:analyzer/src/generated/sdk.dart';
|
|
import 'package:analyzer/src/generated/sdk_io.dart';
|
|
import 'package:analyzer/src/generated/source.dart';
|
|
import 'package:analyzer/src/generated/source_io.dart';
|
|
import 'package:path/path.dart' as path;
|
|
|
|
main() {
|
|
new Driver().run();
|
|
}
|
|
|
|
typedef void ResultDescriptorFinderCallback(PropertyAccessorElement element);
|
|
|
|
class Driver {
|
|
PhysicalResourceProvider resourceProvider;
|
|
AnalysisContext context;
|
|
InterfaceType resultDescriptorType;
|
|
String rootDir;
|
|
|
|
void findResultDescriptors(
|
|
AstNode node, void callback(String descriptorName)) {
|
|
Set<PropertyAccessorElement> resultDescriptors =
|
|
new Set<PropertyAccessorElement>();
|
|
node.accept(new ResultDescriptorFinder(
|
|
resultDescriptorType, resultDescriptors.add));
|
|
for (PropertyAccessorElement resultDescriptor in resultDescriptors) {
|
|
callback(resultDescriptor.name);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Find the root directory of the analyzer package by proceeding
|
|
* upward to the 'tool' dir, and then going up one more directory.
|
|
*/
|
|
String findRoot(String pathname) {
|
|
while (path.basename(pathname) != 'tool') {
|
|
String parent = path.dirname(pathname);
|
|
if (parent.length >= pathname.length) {
|
|
throw new Exception("Can't find root directory");
|
|
}
|
|
pathname = parent;
|
|
}
|
|
return path.dirname(pathname);
|
|
}
|
|
|
|
CompilationUnit getUnit(Source source) =>
|
|
context.resolveCompilationUnit2(source, source);
|
|
|
|
void run() {
|
|
rootDir = findRoot(Platform.script.toFilePath(windows: Platform.isWindows));
|
|
resourceProvider = PhysicalResourceProvider.INSTANCE;
|
|
DartSdk sdk = DirectoryBasedDartSdk.defaultSdk;
|
|
context = AnalysisEngine.instance.createAnalysisContext();
|
|
JavaFile packagesDir = new JavaFile(path.join(rootDir, 'packages'));
|
|
List<UriResolver> uriResolvers = [
|
|
new FileUriResolver(),
|
|
new DartUriResolver(sdk),
|
|
new PackageUriResolver(<JavaFile>[packagesDir])
|
|
];
|
|
context.sourceFactory = new SourceFactory(uriResolvers);
|
|
Source taskSource =
|
|
setupSource(path.join('lib', 'src', 'task', 'dart.dart'));
|
|
Source modelSource = setupSource(path.join('lib', 'task', 'model.dart'));
|
|
CompilationUnitElement modelElement = getUnit(modelSource).element;
|
|
InterfaceType analysisTaskType = modelElement.getType('AnalysisTask').type;
|
|
DartType dynamicType = context.typeProvider.dynamicType;
|
|
resultDescriptorType = modelElement.getType('ResultDescriptor').type
|
|
.substitute4([dynamicType]);
|
|
CompilationUnit taskUnit = getUnit(taskSource);
|
|
CompilationUnitElement taskUnitElement = taskUnit.element;
|
|
print('digraph G {');
|
|
Set<String> results = new Set<String>();
|
|
for (ClassElement cls in taskUnitElement.types) {
|
|
if (!cls.isAbstract && cls.type.isSubtypeOf(analysisTaskType)) {
|
|
String task = cls.name;
|
|
// TODO(paulberry): node is deprecated. What am I supposed to do
|
|
// instead?
|
|
findResultDescriptors(cls.getMethod('buildInputs').node,
|
|
(String input) {
|
|
results.add(input);
|
|
print(' $input -> $task');
|
|
});
|
|
findResultDescriptors(cls.getField('DESCRIPTOR').node, (String output) {
|
|
results.add(output);
|
|
print(' $task -> $output');
|
|
});
|
|
}
|
|
}
|
|
for (String result in results) {
|
|
print(' $result [shape=box]');
|
|
}
|
|
print('}');
|
|
}
|
|
|
|
Source setupSource(String filename) {
|
|
String filePath = path.join(rootDir, filename);
|
|
File file = resourceProvider.getResource(filePath);
|
|
Source source = file.createSource();
|
|
ChangeSet changeSet = new ChangeSet();
|
|
changeSet.addedSource(source);
|
|
context.applyChanges(changeSet);
|
|
return source;
|
|
}
|
|
}
|
|
|
|
class ResultDescriptorFinder extends GeneralizingAstVisitor {
|
|
final InterfaceType resultDescriptorType;
|
|
final ResultDescriptorFinderCallback callback;
|
|
|
|
ResultDescriptorFinder(this.resultDescriptorType, this.callback);
|
|
|
|
@override
|
|
visitIdentifier(Identifier node) {
|
|
Element element = node.staticElement;
|
|
if (element is PropertyAccessorElement &&
|
|
element.isGetter &&
|
|
element.returnType.isSubtypeOf(resultDescriptorType)) {
|
|
callback(element);
|
|
}
|
|
}
|
|
}
|