blob: f0a195e689361ea7bfbdaf7c1d7bc066d6843e3f [file] [log] [blame]
// 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];
}