| // 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. |
| |
| import 'dart:io'; |
| |
| import 'dart:async' show |
| EventSink, |
| Future, |
| Stream, |
| StreamController, |
| StreamSubscription; |
| |
| import 'package:dart2js_incremental/dart2js_incremental.dart' show |
| IncrementalCompilationFailed, |
| IncrementalCompiler; |
| |
| import 'package:compiler/compiler_new.dart' show |
| CompilerOutput; |
| |
| import 'package:compiler/src/old_to_new_api.dart' show |
| LegacyCompilerDiagnostics, |
| LegacyCompilerInput; |
| |
| import 'package:compiler/src/source_file_provider.dart' show |
| FormattingDiagnosticHandler; |
| |
| import 'watcher.dart'; |
| |
| main(List<String> arguments) { |
| int updateCount = 0; |
| StreamSubscription<CompilerEvent> subscription = |
| compile(Uri.base.resolve(arguments.first)).listen(null); |
| subscription.onData((CompilerEvent event) { |
| switch (event.kind) { |
| case IncrementalKind.FULL: |
| updateCount = 0; |
| print('// Compiled JavaScript:'); |
| print(event['.js']); |
| break; |
| |
| case IncrementalKind.INCREMENTAL: |
| Stopwatch sw = event.stopwatch..start(); |
| String updates = '${event.compiler.allUpdates()}'; |
| sw.stop(); |
| |
| print('// Patch after ${++updateCount} updates,'); |
| print('// computed in ${sw.elapsedMicroseconds/1000000} seconds:'); |
| print(updates); |
| break; |
| |
| case IncrementalKind.ERROR: |
| updateCount = 0; |
| print("Compilation failed"); |
| break; |
| |
| default: |
| throw "Unknown kind: ${event.kind}"; |
| } |
| }); |
| subscription.onError((error, StackTrace trace) { |
| if (error is IncrementalCompilationFailed) { |
| print("Incremental compilation failed due to:\n${error.reason}"); |
| } else { |
| throw error; |
| } |
| }); |
| } |
| |
| Stream<CompilerEvent> compile(Uri originalInput) { |
| StreamController<CompilerEvent> controller = |
| new StreamController<CompilerEvent>(); |
| compileToStream(originalInput, controller); |
| return controller.stream; |
| } |
| |
| compileToStream( |
| Uri originalInput, |
| StreamController<CompilerEvent> controller) async { |
| var watcher = new Watcher(); |
| |
| Uri libraryRoot = Uri.base.resolve('sdk/'); |
| Uri packageRoot = Uri.base.resolve('packages/'); |
| |
| FormattingDiagnosticHandler diagnosticHandler = |
| new FormattingDiagnosticHandler(); |
| |
| OutputProvider outputProvider = new OutputProvider(); |
| |
| void resilientDiagnosticHandler( |
| Uri uri, int begin, int end, String message, kind) { |
| try { |
| diagnosticHandler(uri, begin, end, message, kind); |
| } catch (e) { |
| String name = diagnosticHandler.provider.relativizeUri(uri); |
| print('$name@$begin+${end - begin}: [$kind] $message}'); |
| } |
| } |
| |
| Future inputProvider(Uri uri) { |
| if (uri.scheme == "file") { |
| if (!'$uri'.startsWith('$libraryRoot')) { |
| watcher.watchFile(uri); |
| } |
| } |
| return diagnosticHandler.provider(uri); |
| } |
| |
| while (true) { |
| Stopwatch sw = new Stopwatch()..start(); |
| IncrementalCompiler compiler = new IncrementalCompiler( |
| libraryRoot: libraryRoot, |
| packageRoot: packageRoot, |
| inputProvider: new LegacyCompilerInput(inputProvider), |
| diagnosticHandler: |
| new LegacyCompilerDiagnostics(resilientDiagnosticHandler), |
| outputProvider: outputProvider); |
| |
| bool success = await compiler.compile(originalInput); |
| sw.stop(); |
| if (success) { |
| controller.add( |
| new CompilerEvent( |
| IncrementalKind.FULL, compiler, outputProvider.output, sw)); |
| } else { |
| controller.add( |
| new CompilerEvent( |
| IncrementalKind.ERROR, compiler, outputProvider.output, sw)); |
| } |
| |
| while (await watcher.hasChanges()) { |
| try { |
| Map<Uri, Uri> changes = watcher.readChanges(); |
| |
| sw = new Stopwatch()..start(); |
| String updates = await compiler.compileUpdates(changes); |
| sw.stop(); |
| |
| controller.add( |
| new CompilerEvent( |
| IncrementalKind.INCREMENTAL, compiler, outputProvider.output, |
| sw, updates: updates)); |
| |
| } on IncrementalCompilationFailed catch (error, trace) { |
| controller.addError(error, trace); |
| break; |
| } |
| } |
| } |
| } |
| |
| /// Output provider which collects output in [output]. |
| class OutputProvider implements CompilerOutput { |
| final Map<String, String> output = new Map<String, String>(); |
| |
| EventSink<String> createEventSink(String name, String extension) { |
| return new StringEventSink((String data) { |
| output['$name.$extension'] = data; |
| }); |
| } |
| |
| String operator[](String key) => output[key]; |
| } |
| |
| /// Helper class to collect sources. |
| class StringEventSink implements EventSink<String> { |
| List<String> data = <String>[]; |
| |
| final Function onClose; |
| |
| StringEventSink(this.onClose); |
| |
| void add(String event) { |
| if (data == null) throw 'StringEventSink is closed.'; |
| data.add(event); |
| } |
| |
| void addError(errorEvent, [StackTrace stackTrace]) { |
| throw 'addError($errorEvent, $stackTrace)'; |
| } |
| |
| void close() { |
| if (data != null) { |
| onClose(data.join()); |
| data = null; |
| } |
| } |
| } |
| |
| enum IncrementalKind { |
| FULL, |
| INCREMENTAL, |
| ERROR, |
| } |
| |
| class CompilerEvent { |
| final IncrementalKind kind; |
| |
| final IncrementalCompiler compiler; |
| |
| final Map<String, String> _output; |
| |
| final Stopwatch stopwatch; |
| |
| final String updates; |
| |
| CompilerEvent( |
| this.kind, this.compiler, this._output, this.stopwatch, {this.updates}); |
| |
| String operator[](String key) => _output[key]; |
| } |