@@ -6,8 +6,44 @@ import 'dart:async';
66import 'dart:convert' show JSON;
77
88import 'package:crypto/crypto.dart' show md5;
9+ import 'package:meta/meta.dart' ;
910
11+ import '../artifacts.dart' ;
12+ import '../build_info.dart' ;
13+ import '../globals.dart' ;
14+ import 'context.dart' ;
1015import 'file_system.dart' ;
16+ import 'process.dart' ;
17+
18+ GenSnapshot get genSnapshot => context.putIfAbsent (GenSnapshot , () => const GenSnapshot ());
19+
20+ class GenSnapshot {
21+ const GenSnapshot ();
22+
23+ Future <int > run ({
24+ @required TargetPlatform targetPlatform,
25+ @required BuildMode buildMode,
26+ @required String packagesPath,
27+ @required String depfilePath,
28+ Iterable <String > additionalArgs: const < String > [],
29+ }) {
30+ final String vmSnapshotData = artifacts.getArtifactPath (Artifact .vmSnapshotData);
31+ final String isolateSnapshotData = artifacts.getArtifactPath (Artifact .isolateSnapshotData);
32+ final List <String > args = < String > [
33+ '--assert_initializer' ,
34+ '--await_is_keyword' ,
35+ '--causal_async_stacks' ,
36+ '--vm_snapshot_data=$vmSnapshotData ' ,
37+ '--isolate_snapshot_data=$isolateSnapshotData ' ,
38+ '--packages=$packagesPath ' ,
39+ '--dependencies=$depfilePath ' ,
40+ '--print_snapshot_sizes' ,
41+ ]..addAll (additionalArgs);
42+
43+ final String snapshotterPath = artifacts.getArtifactPath (Artifact .genSnapshot, targetPlatform, buildMode);
44+ return runCommandAndStreamOutput (< String > [snapshotterPath]..addAll (args));
45+ }
46+ }
1147
1248/// A collection of checksums for a set of input files.
1349///
@@ -68,3 +104,109 @@ Future<Set<String>> readDepfile(String depfilePath) async {
68104 .where ((String path) => path.isNotEmpty)
69105 .toSet ();
70106}
107+
108+ /// Dart snapshot builder.
109+ ///
110+ /// Builds Dart snapshots in one of three modes:
111+ /// * Script snapshot: architecture-independent snapshot of a Dart script core
112+ /// libraries.
113+ /// * AOT snapshot: architecture-specific ahead-of-time compiled snapshot
114+ /// suitable for loading with `mmap`.
115+ /// * Assembly AOT snapshot: architecture-specific ahead-of-time compile to
116+ /// assembly suitable for compilation as a static or dynamic library.
117+ class Snapshotter {
118+ /// Builds an architecture-independent snapshot of the specified script.
119+ Future <int > buildScriptSnapshot ({
120+ @required String mainPath,
121+ @required String snapshotPath,
122+ @required String depfilePath,
123+ @required String packagesPath
124+ }) async {
125+ final List <String > args = < String > [
126+ '--snapshot_kind=script' ,
127+ '--script_snapshot=$snapshotPath ' ,
128+ mainPath,
129+ ];
130+
131+ final String checksumsPath = '$depfilePath .checksums' ;
132+ final int exitCode = await _build (
133+ outputSnapshotPath: snapshotPath,
134+ packagesPath: packagesPath,
135+ snapshotArgs: args,
136+ depfilePath: depfilePath,
137+ mainPath: mainPath,
138+ checksumsPath: checksumsPath,
139+ );
140+ if (exitCode != 0 )
141+ return exitCode;
142+ await _writeChecksum (snapshotPath, depfilePath, mainPath, checksumsPath);
143+ return exitCode;
144+ }
145+
146+ /// Builds an architecture-specific ahead-of-time compiled snapshot of the specified script.
147+ Future <Null > buildAotSnapshot () async {
148+ throw new UnimplementedError ('AOT snapshotting not yet implemented' );
149+ }
150+
151+ Future <int > _build ({
152+ @required List <String > snapshotArgs,
153+ @required String outputSnapshotPath,
154+ @required String packagesPath,
155+ @required String depfilePath,
156+ @required String mainPath,
157+ @required String checksumsPath,
158+ }) async {
159+ if (! await _isBuildRequired (outputSnapshotPath, depfilePath, mainPath, checksumsPath)) {
160+ printTrace ('Skipping snapshot build. Checksums match.' );
161+ return 0 ;
162+ }
163+
164+ // Build the snapshot.
165+ final int exitCode = await genSnapshot.run (
166+ targetPlatform: null ,
167+ buildMode: BuildMode .debug,
168+ packagesPath: packagesPath,
169+ depfilePath: depfilePath,
170+ additionalArgs: snapshotArgs,
171+ );
172+ if (exitCode != 0 )
173+ return exitCode;
174+
175+ _writeChecksum (outputSnapshotPath, depfilePath, mainPath, checksumsPath);
176+ return 0 ;
177+ }
178+
179+ Future <bool > _isBuildRequired (String outputSnapshotPath, String depfilePath, String mainPath, String checksumsPath) async {
180+ final File checksumFile = fs.file (checksumsPath);
181+ final File outputSnapshotFile = fs.file (outputSnapshotPath);
182+ final File depfile = fs.file (depfilePath);
183+ if (! outputSnapshotFile.existsSync () || ! depfile.existsSync () || ! checksumFile.existsSync ())
184+ return true ;
185+
186+ try {
187+ if (checksumFile.existsSync ()) {
188+ final Checksum oldChecksum = new Checksum .fromJson (await checksumFile.readAsString ());
189+ final Set <String > checksumPaths = await readDepfile (depfilePath)
190+ ..addAll (< String > [outputSnapshotPath, mainPath]);
191+ final Checksum newChecksum = new Checksum .fromFiles (checksumPaths);
192+ return oldChecksum != newChecksum;
193+ }
194+ } catch (e, s) {
195+ // Log exception and continue, this step is a performance improvement only.
196+ printTrace ('Error during snapshot checksum output: $e \n $s ' );
197+ }
198+ return true ;
199+ }
200+
201+ Future <Null > _writeChecksum (String outputSnapshotPath, String depfilePath, String mainPath, String checksumsPath) async {
202+ try {
203+ final Set <String > checksumPaths = await readDepfile (depfilePath)
204+ ..addAll (< String > [outputSnapshotPath, mainPath]);
205+ final Checksum checksum = new Checksum .fromFiles (checksumPaths);
206+ await fs.file (checksumsPath).writeAsString (checksum.toJson ());
207+ } catch (e, s) {
208+ // Log exception and continue, this step is a performance improvement only.
209+ print ('Error during snapshot checksum output: $e \n $s ' );
210+ }
211+ }
212+ }
0 commit comments