mirror of
https://github.com/dart-lang/sdk
synced 2024-09-19 15:21:31 +00:00
b71bd543a9
Original CL had a bug that wasn't visible unless you delete your
out/ReleaseX64/patched_sdk folder.
Patchset #1 is the original CL, patchset #2 shows the fix.
This reverts commit 4aadfe09df
.
BUG=
Review-Url: https://codereview.chromium.org/2976543002 .
182 lines
6.4 KiB
Dart
182 lines
6.4 KiB
Dart
// Copyright (c) 2017, 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.
|
|
|
|
/// A wrapper on top of the [IncrementalKernelGenerator] that tracks
|
|
/// file modifications between subsequent compilation requests and only
|
|
/// invalidates those files that appear to be modified.
|
|
library front_end.example.incremental_reload.compiler_with_invalidation;
|
|
|
|
import 'dart:io';
|
|
import 'dart:async';
|
|
import 'dart:convert' show JSON;
|
|
|
|
import 'package:front_end/compiler_options.dart';
|
|
import 'package:front_end/incremental_kernel_generator.dart';
|
|
import 'package:front_end/src/incremental/file_byte_store.dart';
|
|
import 'package:front_end/src/incremental/byte_store.dart';
|
|
import 'package:kernel/ast.dart';
|
|
import 'package:kernel/binary/limited_ast_to_binary.dart';
|
|
|
|
/// Create an instance of an [IncrementalCompiler] to compile a program whose
|
|
/// main entry point file is [entry]. This uses some default options
|
|
/// for the location of the sdk and temporary folder to save intermediate
|
|
/// results.
|
|
// TODO(sigmund): make this example work outside of the SDK repo.
|
|
Future<IncrementalCompiler> createIncrementalCompiler(String entry,
|
|
{bool persistent: true}) {
|
|
var entryUri = Uri.base.resolve(entry);
|
|
var dartVm = Uri.base.resolve(Platform.resolvedExecutable);
|
|
var sdkRoot = dartVm.resolve("patched_sdk/");
|
|
var tmpDir = Directory.systemTemp.createTempSync('ikg_cache');
|
|
var options = new CompilerOptions()
|
|
..sdkRoot = sdkRoot
|
|
..packagesFileUri = Uri.base.resolve('.packages')
|
|
..strongMode = false
|
|
..dartLibraries = loadDartLibraries(sdkRoot)
|
|
..byteStore =
|
|
persistent ? new FileByteStore(tmpDir.path) : new MemoryByteStore();
|
|
return IncrementalCompiler.create(options, entryUri);
|
|
}
|
|
|
|
/// Reads the `libraries.json` file for an SDK to provide the location of the
|
|
/// SDK files.
|
|
// TODO(sigmund): this should be handled by package:front_end internally.
|
|
Map<String, Uri> loadDartLibraries(Uri sdkRoot) {
|
|
var libraries = sdkRoot.resolve('lib/libraries.json');
|
|
var map =
|
|
JSON.decode(new File.fromUri(libraries).readAsStringSync())['libraries'];
|
|
var dartLibraries = <String, Uri>{};
|
|
map.forEach((k, v) => dartLibraries[k] = libraries.resolve(v));
|
|
return dartLibraries;
|
|
}
|
|
|
|
/// An incremental compiler that monitors file modifications on disk and
|
|
/// invalidates only files that have been modified since the previous time the
|
|
/// compiler was invoked.
|
|
class IncrementalCompiler {
|
|
/// Underlying incremental compiler implementation.
|
|
IncrementalKernelGenerator _generator;
|
|
|
|
/// Last modification for each tracked input file.
|
|
Map<Uri, DateTime> lastModified = {};
|
|
|
|
/// Create an instance of [IncrementalCompiler].
|
|
static Future<IncrementalCompiler> create(
|
|
CompilerOptions options, Uri entryUri) async {
|
|
var compiler = new IncrementalCompiler._internal();
|
|
compiler._generator = await IncrementalKernelGenerator
|
|
.newInstance(options, entryUri, watch: compiler._watch);
|
|
return compiler;
|
|
}
|
|
|
|
IncrementalCompiler._internal();
|
|
|
|
/// Callback for the [IncrementalKernelGenerator] to keep track of relevant
|
|
/// files.
|
|
Future _watch(Uri uri, bool used) {
|
|
if (used) {
|
|
lastModified[uri] ??= new File.fromUri(uri).lastModifiedSync();
|
|
} else {
|
|
lastModified.remove(uri);
|
|
}
|
|
return new Future.value(null);
|
|
}
|
|
|
|
/// How many files changed during the last call to [recompile].
|
|
int changed;
|
|
|
|
/// Time spent updating time-stamps from disk during the last call to
|
|
/// [recompile].
|
|
int invalidateTime;
|
|
|
|
/// Time actually spent compiling the code in the incremental compiler during
|
|
/// the last call to [recompile].
|
|
int compileTime;
|
|
|
|
/// Determine which files have been modified, and recompile the program
|
|
/// incrementally based on that information.
|
|
Future<Program> recompile() async {
|
|
changed = 0;
|
|
invalidateTime = 0;
|
|
compileTime = 0;
|
|
|
|
var invalidateTimer = new Stopwatch()..start();
|
|
for (var uri in lastModified.keys.toList()) {
|
|
var last = lastModified[uri];
|
|
var current = new File.fromUri(uri).lastModifiedSync();
|
|
if (last != current) {
|
|
lastModified[uri] = current;
|
|
_generator.invalidate(uri);
|
|
changed++;
|
|
}
|
|
}
|
|
invalidateTimer.stop();
|
|
invalidateTime = invalidateTimer.elapsedMilliseconds;
|
|
if (changed == 0 && lastModified.isNotEmpty) return null;
|
|
|
|
var compileTimer = new Stopwatch()..start();
|
|
var delta = await _generator.computeDelta();
|
|
compileTimer.stop();
|
|
compileTime = compileTimer.elapsedMilliseconds;
|
|
var program = delta.newProgram;
|
|
return program;
|
|
}
|
|
}
|
|
|
|
/// The result of an incremental compile and metrics collected during the
|
|
/// the compilation.
|
|
class CompilationResult {
|
|
/// How many files were modified by the time we invoked the compiler again.
|
|
int changed = 0;
|
|
|
|
/// How many files are currently being tracked for modifications.
|
|
int totalFiles = 0;
|
|
|
|
/// How long it took to invalidate files that have been modified.
|
|
int invalidateTime = 0;
|
|
|
|
/// How long it took to build the incremental program.
|
|
int compileTime = 0;
|
|
|
|
/// How long it took to do the hot-reload in the VM.
|
|
int reloadTime = 0;
|
|
|
|
/// Whether we saw errors during compilation or reload.
|
|
bool errorSeen = false;
|
|
|
|
/// Error message when [errorSeen] is true.
|
|
String errorDetails;
|
|
|
|
/// The program that was generated by the incremental compiler.
|
|
Program program;
|
|
}
|
|
|
|
/// Request a recompile and possibly a reload, and gather timing metrics.
|
|
Future<CompilationResult> rebuild(
|
|
IncrementalCompiler compiler, Uri outputUri) async {
|
|
var result = new CompilationResult();
|
|
try {
|
|
var program = result.program = await compiler.recompile();
|
|
if (program != null && !program.libraries.isEmpty) {
|
|
var sink = new File.fromUri(outputUri).openWrite();
|
|
// TODO(sigmund): should the incremental generator always filter these
|
|
// libraries instead?
|
|
new LimitedBinaryPrinter(
|
|
sink, (library) => library.importUri.scheme != 'dart', false)
|
|
.writeProgramFile(program);
|
|
await sink.close();
|
|
}
|
|
} catch (e, t) {
|
|
result.errorDetails = 'compilation error: $e, $t';
|
|
result.errorSeen = true;
|
|
}
|
|
|
|
result.changed = compiler.changed;
|
|
result.totalFiles = compiler.lastModified.length;
|
|
result.invalidateTime = compiler.invalidateTime;
|
|
result.compileTime = compiler.compileTime;
|
|
result.reloadTime = 0;
|
|
return result;
|
|
}
|