Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file | |
| 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. | |
| 4 | |
| 5 /// A wrapper on top of the [IncrementalKernelGenerator] that tracks | |
| 6 /// file modifications between subsequent compilation requests and only | |
| 7 /// invalidates those files that appear to be modified. | |
| 8 library front_end.example.incremental_reload.compiler_with_invalidation; | |
| 9 | |
| 10 import 'dart:io'; | |
| 11 import 'dart:async'; | |
| 12 import 'dart:convert' show JSON; | |
| 13 | |
| 14 import 'package:front_end/compiler_options.dart'; | |
| 15 import 'package:front_end/incremental_kernel_generator.dart'; | |
| 16 import 'package:front_end/src/incremental/file_byte_store.dart'; | |
| 17 import 'package:front_end/src/incremental/byte_store.dart'; | |
| 18 import 'package:kernel/ast.dart'; | |
| 19 import 'package:kernel/binary/limited_ast_to_binary.dart'; | |
| 20 | |
| 21 /// Create an instance of an [IncrementalCompiler] to compile a program whose | |
| 22 /// main entry point file is [entry]. This uses some default options | |
| 23 /// for the location of the sdk and temporary folder to save intermediate | |
| 24 /// results. | |
| 25 // TODO(sigmund): make this example work outside of the SDK repo. | |
| 26 Future<IncrementalCompiler> createIncrementalCompiler(String entry, | |
| 27 {bool persistent: true}) { | |
| 28 var entryUri = Uri.base.resolve(entry); | |
| 29 var dartVm = Uri.base.resolve(Platform.resolvedExecutable); | |
| 30 var sdkRoot = dartVm.resolve("patched_sdk/"); | |
| 31 var options = new CompilerOptions() | |
| 32 ..sdkRoot = sdkRoot | |
| 33 ..packagesFileUri = Uri.base.resolve('.packages') | |
| 34 ..strongMode = false | |
| 35 ..dartLibraries = loadDartLibraries(sdkRoot) | |
| 36 ..byteStore = persistent | |
| 37 ? new FileByteStore('/tmp/ikg_cache') | |
|
scheglov
2017/06/07 01:04:42
This will not work on Windows.
Siggi Cherem (dart-lang)
2017/06/07 20:17:11
Done.
| |
| 38 : new MemoryByteStore(); | |
| 39 return IncrementalCompiler.create(options, entryUri); | |
| 40 } | |
| 41 | |
| 42 /// Reads the `libraries.json` file for an SDK to provide the location of the | |
| 43 /// SDK files. | |
| 44 // TODO(sigmund): this should be handled by package:front_end internally. | |
| 45 Map<String, Uri> loadDartLibraries(Uri sdkRoot) { | |
| 46 var libraries = sdkRoot.resolve('lib/libraries.json'); | |
| 47 var map = | |
| 48 JSON.decode(new File.fromUri(libraries).readAsStringSync())['libraries']; | |
| 49 var dartLibraries = <String, Uri>{}; | |
| 50 map.forEach((k, v) => dartLibraries[k] = libraries.resolve(v)); | |
| 51 return dartLibraries; | |
| 52 } | |
| 53 | |
| 54 /// An incremental compiler that monitors file modifications on disk and | |
| 55 /// invalidates only files that have been modified since the previous time the | |
| 56 /// compiler was invoked. | |
| 57 class IncrementalCompiler { | |
| 58 /// Underlying incremental compiler implementation. | |
| 59 IncrementalKernelGenerator _generator; | |
| 60 | |
| 61 /// Last modification for each tracked input file. | |
| 62 Map<Uri, DateTime> lastModified = {}; | |
| 63 | |
| 64 /// Create an instance of [IncrementalCompiler]. | |
| 65 static Future<IncrementalCompiler> create( | |
| 66 CompilerOptions options, Uri entryUri) async { | |
| 67 return new IncrementalCompiler._internal( | |
| 68 await IncrementalKernelGenerator.newInstance(options, entryUri)); | |
| 69 } | |
| 70 | |
| 71 IncrementalCompiler._internal(this._generator); | |
| 72 | |
| 73 /// Callback for the [IncrementalKernelGenerator] to keep track of relevant | |
| 74 /// files. | |
| 75 Future _watch(Uri uri, bool used) { | |
| 76 if (used) { | |
| 77 if (lastModified[uri] == null) { | |
| 78 lastModified[uri] = new File.fromUri(uri).lastModifiedSync(); | |
|
scheglov
2017/06/07 01:04:42
??=
Siggi Cherem (dart-lang)
2017/06/07 20:17:11
Done.
| |
| 79 } | |
| 80 } else { | |
| 81 lastModified.remove(uri); | |
| 82 } | |
| 83 return new Future.value(null); | |
| 84 } | |
| 85 | |
| 86 /// How many files changed during the last call to [recompile]. | |
| 87 int changed; | |
| 88 | |
| 89 /// Time spent updating time-stamps from disk during the last call to | |
| 90 /// [recompile]. | |
| 91 int invalidateTime; | |
| 92 | |
| 93 /// Time actually spent compiling the code in the incremental compiler during | |
| 94 /// the last call to [recompile]. | |
| 95 int compileTime; | |
| 96 | |
| 97 /// Determine which files have been modified, and recompile the program | |
| 98 /// incrementally based on that information. | |
| 99 Future<Program> recompile() async { | |
| 100 changed = 0; | |
| 101 invalidateTime = 0; | |
| 102 compileTime = 0; | |
| 103 | |
| 104 var invalidateTimer = new Stopwatch()..start(); | |
| 105 for (var uri in lastModified.keys.toList()) { | |
| 106 var last = lastModified[uri]; | |
| 107 var current = new File.fromUri(uri).lastModifiedSync(); | |
| 108 if (last != current) { | |
| 109 lastModified[uri] = current; | |
| 110 _generator.invalidate(uri); | |
| 111 changed++; | |
| 112 } | |
| 113 } | |
| 114 invalidateTimer.stop(); | |
| 115 invalidateTime = invalidateTimer.elapsedMilliseconds; | |
| 116 if (changed == 0 && lastModified.isNotEmpty) return null; | |
| 117 | |
| 118 var compileTimer = new Stopwatch()..start(); | |
| 119 var delta = await _generator.computeDelta(watch: _watch); | |
| 120 compileTimer.stop(); | |
| 121 compileTime = compileTimer.elapsedMilliseconds; | |
| 122 var program = delta.newProgram; | |
| 123 return program; | |
| 124 } | |
| 125 } | |
| 126 | |
| 127 /// The result of an incremental compile and metrics collected during the | |
| 128 /// the compilation. | |
| 129 class CompilationResult { | |
| 130 /// How many files were modified by the time we invoked the compiler again. | |
| 131 int changed = 0; | |
| 132 | |
| 133 /// How many files are currently being tracked for modifications. | |
| 134 int totalFiles = 0; | |
| 135 | |
| 136 /// How long it took to invalidate files that have been modified. | |
| 137 int invalidateTime = 0; | |
| 138 | |
| 139 /// How long it took to build the incremental program. | |
| 140 int compileTime = 0; | |
| 141 | |
| 142 /// How long it took to do the hot-reload in the VM. | |
| 143 int reloadTime = 0; | |
| 144 | |
| 145 /// Whether we saw errors during compilation or reload. | |
| 146 bool errorSeen = false; | |
| 147 | |
| 148 /// Error message when [errorSeen] is true. | |
| 149 String errorDetails; | |
| 150 | |
| 151 /// The program that was generated by the incremental compiler. | |
| 152 Program program; | |
| 153 } | |
| 154 | |
| 155 /// Request a recompile and possibly a reload, and gather timing metrics. | |
| 156 Future<CompilationResult> rebuild( | |
| 157 IncrementalCompiler compiler, Uri outputUri) async { | |
| 158 var result = new CompilationResult(); | |
| 159 try { | |
| 160 var program = result.program = await compiler.recompile(); | |
| 161 if (program != null && !program.libraries.isEmpty) { | |
| 162 // TODO(sigmund): the incremental generator should set the main method | |
| 163 // directly. | |
| 164 var mainLib = program.libraries.last; | |
| 165 program.mainMethod = | |
| 166 mainLib.procedures.firstWhere((p) => p.name.name == 'main'); | |
| 167 var sink = new File.fromUri(outputUri).openWrite(); | |
| 168 | |
| 169 // TODO(sigmund): should the incremental generator always filter these | |
| 170 // libraries instead? | |
| 171 new LimitedBinaryPrinter( | |
| 172 sink, (library) => library.importUri.scheme != 'dart') | |
| 173 .writeProgramFile(program); | |
| 174 await sink.close(); | |
| 175 } | |
| 176 } catch (e, t) { | |
| 177 result.errorDetails = 'compilation error: $e, $t'; | |
| 178 result.errorSeen = true; | |
| 179 } | |
| 180 | |
| 181 result.changed = compiler.changed; | |
| 182 result.totalFiles = compiler.lastModified.length; | |
| 183 result.invalidateTime = compiler.invalidateTime; | |
| 184 result.compileTime = compiler.compileTime; | |
| 185 result.reloadTime = 0; | |
| 186 return result; | |
| 187 } | |
| OLD | NEW |