4
4
import 'dart:async' ;
5
5
import 'dart:convert' ;
6
6
import 'dart:io' ;
7
+ import 'dart:mirrors' ;
7
8
8
9
import 'package:logging/logging.dart' ;
9
10
import 'package:path/path.dart' as path;
@@ -73,8 +74,18 @@ class BuildImpl {
73
74
74
75
/// Applies all [updates] to the [_assetGraph] as well as doing other
75
76
/// necessary cleanup.
77
+ _logger.info ('Updating dependency graph with changes since last build.' );
76
78
await _updateWithChanges (updates);
77
- _assetGraph.validAsOf = validAsOf;
79
+
80
+ /// Sometimes we need to fully invalidate the graph, such as when the
81
+ /// build script itself is updated.
82
+ _logger.info ('Checking if asset graph needs to be rebuilt' );
83
+ if (await _shouldInvalidateAssetGraph ()) {
84
+ _logger.info ('Invalidating asset graph due to build script update' );
85
+ _assetGraph.allNodes
86
+ .where ((node) => node is GeneratedAssetNode )
87
+ .forEach ((node) => (node as GeneratedAssetNode ).needsUpdate = true );
88
+ }
78
89
79
90
/// Wait while all inputs are collected.
80
91
_logger.info ('Initializing inputs' );
@@ -89,6 +100,8 @@ class BuildImpl {
89
100
var result = await _runPhases ();
90
101
91
102
/// Write out the dependency graph file.
103
+ _logger.info ('Caching finalized dependency graph' );
104
+ _assetGraph.validAsOf = validAsOf;
92
105
var assetGraphAsset =
93
106
new Asset (_assetGraphId, JSON .encode (_assetGraph.serialize ()));
94
107
await _writer.writeAsString (assetGraphAsset);
@@ -120,6 +133,55 @@ class BuildImpl {
120
133
}
121
134
}
122
135
136
+ /// Checks if the [_assetGraph] needs to be completely invalidated.
137
+ ///
138
+ /// For now this just means looking at the current running program, and seeing
139
+ /// if any of its sources are newer than the asset graph itself.
140
+ Future <bool > _shouldInvalidateAssetGraph () async {
141
+ var completer = new Completer <bool >();
142
+ Future .wait (currentMirrorSystem ().libraries.keys.map ((Uri uri) async {
143
+ /// Short-circuit
144
+ if (completer.isCompleted) return ;
145
+ var lastModified;
146
+ switch (uri.scheme) {
147
+ case 'dart' :
148
+ return ;
149
+ case 'package' :
150
+ var parts = uri.pathSegments;
151
+ var id = new AssetId (
152
+ parts[0 ],
153
+ path.url
154
+ .joinAll (['lib' ]..addAll (parts.getRange (1 , parts.length))));
155
+ lastModified = await _reader.lastModified (id);
156
+ break ;
157
+ case 'file' :
158
+
159
+ /// TODO(jakemac): Probably shouldn't use dart:io directly, but its
160
+ /// definitely the easiest solution and should be fine.
161
+ var file = new File .fromUri (uri);
162
+ lastModified = await file.lastModified ();
163
+ break ;
164
+ case 'data' :
165
+ /// Test runner uses a `data` scheme, don't invalidate for those.
166
+ if (uri.path.contains ('package:test' )) return ;
167
+ continue unknownUri;
168
+ unknownUri:
169
+ default :
170
+ _logger.info ('Unrecognized uri scheme `${uri .scheme }` found for '
171
+ 'library in build script, falling back on full rebuild.' );
172
+ if (! completer.isCompleted) completer.complete (true );
173
+ return ;
174
+ }
175
+ assert (lastModified != null );
176
+ if (lastModified.compareTo (_assetGraph.validAsOf) > 0 ) {
177
+ if (! completer.isCompleted) completer.complete (true );
178
+ }
179
+ })).then ((_) {
180
+ if (! completer.isCompleted) completer.complete (false );
181
+ });
182
+ return completer.future;
183
+ }
184
+
123
185
/// Creates and returns a map of updates to assets based on [_assetGraph] .
124
186
Future <Map <AssetId , ChangeType >> _getUpdates () async {
125
187
/// Collect updates to the graph based on any changed assets.
0 commit comments