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

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