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

Side by Side Diff: lib/devc.dart

Issue 973433003: Initial cut for a development server (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: Created 5 years, 9 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 | « bin/devc.dart ('k') | lib/src/codegen/code_generator.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 /// Command line tool to run the checker on a Dart program. 5 /// Command line tool to run the checker on a Dart program.
6 library ddc.devc; 6 library ddc.devc;
7 7
8 import 'dart:async'; 8 import 'dart:async';
9 import 'dart:convert'; 9 import 'dart:convert';
10 import 'dart:io'; 10 import 'dart:io';
11 11
12 import 'package:analyzer/src/generated/engine.dart' show ChangeSet;
12 import 'package:logging/logging.dart' show Level, Logger, LogRecord; 13 import 'package:logging/logging.dart' show Level, Logger, LogRecord;
13 import 'package:path/path.dart' as path; 14 import 'package:path/path.dart' as path;
14 import 'package:html5lib/parser.dart' show parse; 15 import 'package:shelf/shelf.dart' as shelf;
16 import 'package:shelf/shelf_io.dart' as shelf;
17 import 'package:shelf_static/shelf_static.dart' as shelf_static;
15 18
19 import 'src/checker/checker.dart';
20 import 'src/checker/dart_sdk.dart' show mockSdkSources;
16 import 'src/checker/resolver.dart'; 21 import 'src/checker/resolver.dart';
17 import 'src/checker/checker.dart';
18 import 'src/checker/rules.dart'; 22 import 'src/checker/rules.dart';
19 import 'src/codegen/code_generator.dart' show CodeGenerator; 23 import 'src/codegen/code_generator.dart' show CodeGenerator;
20 import 'src/codegen/dart_codegen.dart'; 24 import 'src/codegen/dart_codegen.dart';
25 import 'src/codegen/html_codegen.dart';
21 import 'src/codegen/js_codegen.dart'; 26 import 'src/codegen/js_codegen.dart';
22 import 'src/codegen/html_codegen.dart'; 27 import 'src/dependency_graph.dart';
28 import 'src/info.dart' show LibraryInfo, CheckerResults;
23 import 'src/options.dart'; 29 import 'src/options.dart';
24 import 'src/report.dart'; 30 import 'src/report.dart';
25 import 'src/info.dart' show LibraryInfo, CheckerResults;
26 import 'src/utils.dart' show reachableSources, partsOf, colorOf;
27 31
28 /// Sets up the type checker logger to print a span that highlights error 32 /// Sets up the type checker logger to print a span that highlights error
29 /// messages. 33 /// messages.
30 StreamSubscription setupLogger(Level level, printFn) { 34 StreamSubscription setupLogger(Level level, printFn) {
31 Logger.root.level = level; 35 Logger.root.level = level;
32 return Logger.root.onRecord.listen((LogRecord rec) { 36 return Logger.root.onRecord.listen((LogRecord rec) {
33 printFn('${rec.level.name.toLowerCase()}: ${rec.message}'); 37 printFn('${rec.level.name.toLowerCase()}: ${rec.message}');
34 }); 38 });
35 } 39 }
36 40
37 /// Compiles [inputFile] writing output as specified by the arguments. 41 /// Encapsulates the logic to do a one-off compilation or a partial compilation
38 CheckerResults compile( 42 /// when the compiler is run as a development server.
39 String inputFile, TypeResolver resolver, CompilerOptions options, 43 class Compiler {
40 [CheckerReporter reporter]) { 44 final CompilerOptions _options;
41 if (inputFile.endsWith('.html')) { 45 final TypeResolver _resolver;
42 return _compileHtml(inputFile, resolver, options, reporter); 46 final CheckerReporter _reporter;
43 } else { 47 final TypeRules _rules;
44 return _compileDart(inputFile, resolver, options, reporter); 48 final CodeChecker _checker;
45 } 49 final SourceGraph _graph;
46 } 50 final SourceNode _entryNode;
47 51 List<LibraryInfo> _libraries = <LibraryInfo>[];
48 CheckerResults _compileHtml( 52 final List<CodeGenerator> _generators;
49 String inputFile, TypeResolver resolver, CompilerOptions options, 53 bool _failure = false;
50 [CheckerReporter reporter]) { 54 bool _devCompilerRuntimeCopied = false;
51 var doc = parse(new File(inputFile).readAsStringSync(), generateSpans: true); 55
52 var scripts = doc.querySelectorAll('script[type="application/dart"]'); 56 factory Compiler(CompilerOptions options,
53 if (scripts.isEmpty) { 57 [TypeResolver resolver, CheckerReporter reporter]) {
54 _log.severe('No <script type="application/dart"> found in $inputFile'); 58 if (resolver == null) {
55 return _earlyErrorResult; 59 resolver = options.useMockSdk
56 } 60 ? new TypeResolver.fromMock(mockSdkSources, options)
57 var mainScriptTag = scripts[0]; 61 : new TypeResolver.fromDir(options.dartSdkPath, options);
58 scripts.skip(1).forEach((s) { 62 }
59 _log.warning(s.sourceSpan.message( 63
60 'unexpected script. Only one Dart script tag allowed ' 64 if (reporter == null) {
61 '(see https://github.com/dart-lang/dart-dev-compiler/issues/53).', 65 reporter = options.dumpInfo
62 color: options.useColors ? colorOf('warning') : false)); 66 ? new SummaryReporter()
63 }); 67 : new LogReporter(options.useColors);
64 68 }
65 var url = mainScriptTag.attributes['src']; 69 var graph = new SourceGraph(resolver.context, options);
66 if (url == null) { 70 var rules = new RestrictedRules(resolver.context.typeProvider, reporter,
67 _log.severe(mainScriptTag.sourceSpan.message( 71 options: options);
68 'inlined script tags not supported at this time ' 72 var checker = new CodeChecker(rules, reporter, options);
69 '(see https://github.com/dart-lang/dart-dev-compiler/issues/54).', 73 var inputFile = options.entryPointFile;
70 color: options.useColors ? colorOf('error') : false)); 74 var uri = inputFile.startsWith('dart:') || inputFile.startsWith('package:')
71 return _earlyErrorResult; 75 ? Uri.parse(inputFile)
72 } 76 : new Uri.file(path.absolute(inputFile));
73 77 var entryNode = graph.nodeFromUri(uri);
74 var dartInputFile = path.join(path.dirname(inputFile), url); 78
75 79 var outputDir = options.outputDir;
76 if (!new File(dartInputFile).existsSync()) { 80 var generators = <CodeGenerator>[];
77 _log.severe(mainScriptTag.sourceSpan.message( 81 if (options.dumpSrcDir != null) {
78 'Script file $dartInputFile not found', 82 generators.add(new EmptyDartGenerator(
79 color: options.useColors ? colorOf('error') : false)); 83 options.dumpSrcDir, entryNode.uri, rules, options));
80 return _earlyErrorResult; 84 }
81 } 85 if (outputDir != null) {
82 86 generators.add(options.outputDart
83 var results = _compileDart(dartInputFile, resolver, options, reporter, 87 ? new DartGenerator(outputDir, entryNode.uri, rules, options)
84 new Uri.file(path.absolute(inputFile))); 88 : new JSGenerator(outputDir, entryNode.uri, rules, options));
85 if (results.failure && !options.forceCompile) return results; 89 }
86 90 return new Compiler._(options, resolver, reporter, rules, checker, graph,
87 if (options.outputDir != null) { 91 entryNode, generators);
88 generateEntryHtml(inputFile, options, results, doc); 92 }
89 } 93
90 return results; 94 Compiler._(this._options, this._resolver, this._reporter, this._rules,
91 } 95 this._checker, this._graph, this._entryNode, this._generators);
92 96
93 CheckerResults _compileDart( 97 bool _buildSource(SourceNode node) {
94 String inputFile, TypeResolver resolver, CompilerOptions options, 98 if (node is HtmlSourceNode) {
95 [CheckerReporter reporter, Uri htmlUri]) { 99 _buildHtmlFile(node);
96 Uri uri; 100 } else if (node is LibrarySourceNode) {
97 if (inputFile.startsWith('dart:') || inputFile.startsWith('package:')) { 101 _buildDartLibrary(node);
98 uri = Uri.parse(inputFile); 102 } else {
99 } else { 103 assert(false); // should not get a build request on PartSourceNode
100 uri = new Uri.file(path.absolute(inputFile)); 104 }
101 } 105
102 var codegenRoot = htmlUri != null ? htmlUri : uri; 106 // TODO(sigmund): don't always return true. Use summarization to better
103 if (reporter == null) { 107 // determine when rebuilding is needed.
104 reporter = options.dumpInfo 108 return true;
105 ? new SummaryReporter() 109 }
106 : new LogReporter(options.useColors); 110
107 } 111 void _buildHtmlFile(HtmlSourceNode node) {
108 112 if (_options.outputDir == null) return;
109 var libraries = <LibraryInfo>[]; 113 var output = generateEntryHtml(node, _options);
110 var rules = new RestrictedRules(resolver.context.typeProvider, reporter, 114 if (output == null) {
111 options: options); 115 _failure = true;
112 var codeChecker = new CodeChecker(rules, reporter, options); 116 return;
113 var generators = <CodeGenerator>[]; 117 }
114 if (options.dumpSrcDir != null) { 118 var filename = path.basename(node.uri.path);
115 generators.add(new EmptyDartGenerator( 119 String outputFile = path.join(_options.outputDir, filename);
116 options.dumpSrcDir, codegenRoot, rules, options)); 120 new File(outputFile).writeAsStringSync(output);
117 } 121
118 var outputDir = options.outputDir; 122 if (_options.outputDart || _devCompilerRuntimeCopied) return;
119 if (outputDir != null) { 123 // Copy the dev_compiler runtime (implicit dependency for js codegen)
120 var cg = options.outputDart 124 // TODO(sigmund): split this out as a separate node in our dependency graph
121 ? new DartGenerator(outputDir, codegenRoot, rules, options) 125 // (https://github.com/dart-lang/dev_compiler/issues/85).
122 : new JSGenerator(outputDir, codegenRoot, rules, options); 126 var runtimeDir = path.join(
123 generators.add(cg); 127 path.dirname(path.dirname(Platform.script.path)), 'lib/runtime/');
124 } 128 var runtimeOutput = path.join(_options.outputDir, 'dev_compiler/runtime/');
125 129 new Directory(runtimeOutput).createSync(recursive: true);
126 bool failure = false; 130 new File(path.join(runtimeDir, 'harmony_feature_check.js'))
127 var rootSource = resolver.findSource(uri); 131 .copy(path.join(runtimeOutput, 'harmony_feature_check.js'));
128 // TODO(sigmund): switch to use synchronous codegen? 132 new File(path.join(runtimeDir, 'dart_runtime.js'))
129 for (var source in reachableSources(rootSource, resolver.context)) { 133 .copy(path.join(runtimeOutput, 'dart_runtime.js'));
130 var entryUnit = resolver.context.resolveCompilationUnit2(source, source); 134 _devCompilerRuntimeCopied = true;
135 }
136
137 bool _isEntry(LibrarySourceNode node) {
138 if (_entryNode is LibrarySourceNode) return _entryNode == node;
139 return (_entryNode as HtmlSourceNode).scripts.contains(node);
140 }
141
142 void _buildDartLibrary(LibrarySourceNode node) {
143 var source = node.source;
144 // TODO(sigmund): find out from analyzer team if there is a better way
145 _resolver.context.applyChanges(new ChangeSet()..changedSource(source));
146 var entryUnit = _resolver.context.resolveCompilationUnit2(source, source);
131 var lib = entryUnit.element.enclosingElement; 147 var lib = entryUnit.element.enclosingElement;
132 if (!options.checkSdk && lib.isInSdk) continue; 148 if (!_options.checkSdk && lib.isInSdk) return;
133 var current = new LibraryInfo(lib, source.uri == uri); 149 var current = node.info;
134 reporter.enterLibrary(current); 150 if (current != null) {
135 libraries.add(current); 151 assert(current.library == lib);
136 rules.currentLibraryInfo = current; 152 } else {
153 node.info = current = new LibraryInfo(lib, _isEntry(node));
154 }
155 _reporter.enterLibrary(current);
156 _libraries.add(current);
157 _rules.currentLibraryInfo = current;
137 158
138 var units = [entryUnit] 159 var units = [entryUnit]
139 ..addAll(partsOf(entryUnit, resolver.context) 160 ..addAll(node.parts.map(
140 .map((p) => resolver.context.resolveCompilationUnit2(p, source))); 161 (p) => _resolver.context.resolveCompilationUnit2(p.source, source)));
141 bool failureInLib = false; 162 bool failureInLib = false;
142 for (var unit in units) { 163 for (var unit in units) {
143 var unitSource = unit.element.source; 164 var unitSource = unit.element.source;
144 reporter.enterSource(unitSource); 165 _reporter.enterSource(unitSource);
145 // TODO(sigmund): integrate analyzer errors with static-info (issue #6). 166 // TODO(sigmund): integrate analyzer errors with static-info (issue #6).
146 failureInLib = resolver.logErrors(unitSource, reporter) || failureInLib; 167 failureInLib = _resolver.logErrors(unitSource, _reporter) || failureInLib;
147 unit.visitChildren(codeChecker); 168 unit.visitChildren(_checker);
148 if (codeChecker.failure) failureInLib = true; 169 if (_checker.failure) failureInLib = true;
149 reporter.leaveSource(); 170 _reporter.leaveSource();
150 } 171 }
151 reporter.leaveLibrary();
152
153 if (failureInLib) { 172 if (failureInLib) {
154 failure = true; 173 _failure = true;
155 if (!options.forceCompile) continue; 174 if (!_options.forceCompile) return;
156 } 175 }
157 for (var cg in generators) { 176 for (var cg in _generators) {
158 cg.generateLibrary(units, current, reporter); 177 cg.generateLibrary(units, current, _reporter);
159 } 178 }
160 } 179 _reporter.leaveLibrary();
161 180 }
162 if (options.dumpInfo && reporter is SummaryReporter) { 181
163 print(summaryToString(reporter.result)); 182 CheckerResults run() {
164 if (options.dumpInfoFile != null) { 183 var clock = new Stopwatch()..start();
165 new File(options.dumpInfoFile) 184
166 .writeAsStringSync(JSON.encode(reporter.result.toJsonMap())); 185 // TODO(sigmund): we are missing a couple failures here. The
167 } 186 // dependendency_graph now detects broken imports or unsupported features
168 } 187 // like more than one script tag (see .severe messages in
169 return new CheckerResults(libraries, rules, failure || options.forceCompile); 188 // dependency_graph.dart). Such failures should be reported back
189 // here so we can mark failure=true in the CheckerResutls.
190 rebuild(_entryNode, _graph, _buildSource);
191 if (_options.dumpInfo && _reporter is SummaryReporter) {
192 var result = (_reporter as SummaryReporter).result;
193 print(summaryToString(result));
194 if (_options.dumpInfoFile != null) {
195 new File(_options.dumpInfoFile)
196 .writeAsStringSync(JSON.encode(result.toJsonMap()));
197 }
198 }
199 clock.stop();
200 if (_options.serverMode) {
201 var time = (clock.elapsedMilliseconds / 1000).toStringAsFixed(2);
202 print('Compiled ${_libraries.length} libraries in ${time} s\n');
203 }
204 return new CheckerResults(
205 _libraries, _rules, _failure || _options.forceCompile);
206 }
207
208 void _runAgain() {
209 var clock = new Stopwatch()..start();
210 if (_reporter is SummaryReporter) (_reporter as SummaryReporter).clear();
211 _libraries = <LibraryInfo>[];
212 int changed = 0;
213
214 // TODO(sigmund): propagate failures here (see TODO in run).
215 rebuild(_entryNode, _graph, (n) {
216 changed++;
217 return _buildSource(n);
218 });
219 if (_reporter is SummaryReporter) {
220 print(summaryToString((_reporter as SummaryReporter).result));
221 }
222 clock.stop();
223 var time = (clock.elapsedMilliseconds / 1000).toStringAsFixed(2);
224 print("Compiled ${changed} libraries in ${time} s\n");
225 }
170 } 226 }
171 227
228 class CompilerServer {
229 final Compiler compiler;
230 final String outDir;
231 final int port;
232 final String _entryPath;
233
234 factory CompilerServer(CompilerOptions options) {
235 var entryPath = path.basename(options.entryPointFile);
236 if (path.extension(entryPath) != '.html') {
237 print('error: devc in server mode requires an HTML entry point.');
238 exit(1);
239 }
240
241 // TODO(sigmund): allow running without a dir, but keep output in memory?
242 var outDir = options.outputDir;
243 if (outDir == null) {
244 print('error: devc in server mode also requires specifying and '
245 'output location for generated code.');
246 exit(1);
247 }
248 var port = options.port;
249 print('[dev_compiler]: Serving $entryPath at http://0.0.0.0:$port/');
250 var compiler = new Compiler(options);
251 return new CompilerServer._(compiler, outDir, port, entryPath);
252 }
253
254 CompilerServer._(this.compiler, this.outDir, this.port, this._entryPath);
255
256 Future start() async {
257 var handler = const shelf.Pipeline()
258 .addMiddleware(shelf.createMiddleware(requestHandler: rebuildIfNeeded))
259 .addHandler(shelf_static.createStaticHandler(outDir,
260 defaultDocument: _entryPath));
261 await shelf.serve(handler, '0.0.0.0', port);
262 compiler.run();
263 }
264
265 rebuildIfNeeded(shelf.Request request) {
266 var filepath = request.url.path;
267 if (filepath == '/$_entryPath' || filepath == '/') compiler._runAgain();
268 }
269 }
270
172 final _log = new Logger('ddc'); 271 final _log = new Logger('ddc');
173 final _earlyErrorResult = new CheckerResults(const [], null, true); 272 final _earlyErrorResult = new CheckerResults(const [], null, true);
OLDNEW
« no previous file with comments | « bin/devc.dart ('k') | lib/src/codegen/code_generator.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698