@@ -40,6 +40,8 @@ class BuildImpl {
40
40
bool _buildRunning = false ;
41
41
final _logger = new Logger ('Build' );
42
42
43
+ bool _isFirstBuild = true ;
44
+
43
45
BuildImpl (this ._reader, this ._writer, this ._packageGraph, this ._phaseGroups);
44
46
45
47
/// Runs a build
@@ -57,6 +59,9 @@ class BuildImpl {
57
59
{DateTime validAsOf, Map <AssetId , ChangeType > updates}) async {
58
60
validAsOf ?? = new DateTime .now ();
59
61
updates ?? = < AssetId , ChangeType > {};
62
+
63
+ /// Assume incremental, change if necessary.
64
+ var buildType = BuildType .Incremental ;
60
65
try {
61
66
if (_buildRunning) throw const ConcurrentBuildException ();
62
67
_buildRunning = true ;
@@ -72,21 +77,41 @@ class BuildImpl {
72
77
updates.addAll (await _getUpdates ());
73
78
}
74
79
80
+ /// If the build script gets updated, we need to either fully invalidate
81
+ /// the graph (if the script current running is up to date), or we need to
82
+ /// terminate and ask the user to restart the script (if the currently
83
+ /// running script is out of date).
84
+ ///
85
+ /// The [_isFirstBuild] flag is used as a proxy for "has this script
86
+ /// been updated since it started running".
87
+ ///
88
+ /// TODO(jakemac): Come up with a better way of telling if the script
89
+ /// has been updated since it started running.
90
+ _logger.info ('Checking if the build script has been updated' );
91
+ if (await _buildScriptUpdated ()) {
92
+ buildType = BuildType .Full ;
93
+ if (_isFirstBuild) {
94
+ _logger.info ('Invalidating asset graph due to build script update' );
95
+ _assetGraph.allNodes
96
+ .where ((node) => node is GeneratedAssetNode )
97
+ .forEach (
98
+ (node) => (node as GeneratedAssetNode ).needsUpdate = true );
99
+ } else {
100
+ var message = 'Build abandoned due to change to the build script or '
101
+ 'one of its dependencies. This could be caused by a pub get or '
102
+ 'any other change. Please terminate the build script and restart '
103
+ 'it.' ;
104
+ _logger.warning (message);
105
+ return new BuildResult (BuildStatus .Failure , buildType, [],
106
+ exception: message);
107
+ }
108
+ }
109
+
75
110
/// Applies all [updates] to the [_assetGraph] as well as doing other
76
111
/// necessary cleanup.
77
112
_logger.info ('Updating dependency graph with changes since last build.' );
78
113
await _updateWithChanges (updates);
79
114
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
- }
89
-
90
115
/// Wait while all inputs are collected.
91
116
_logger.info ('Initializing inputs' );
92
117
await _initializeInputsByPackage ();
@@ -108,10 +133,11 @@ class BuildImpl {
108
133
109
134
return result;
110
135
} catch (e, s) {
111
- return new BuildResult (BuildStatus .Failure , BuildType . Full , [],
136
+ return new BuildResult (BuildStatus .Failure , buildType , [],
112
137
exception: e, stackTrace: s);
113
138
} finally {
114
139
_buildRunning = false ;
140
+ _isFirstBuild = false ;
115
141
}
116
142
}
117
143
@@ -133,13 +159,12 @@ class BuildImpl {
133
159
}
134
160
}
135
161
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 {
162
+ /// Checks if the current running program has been updated since the asset
163
+ /// graph was last built.
164
+ Future <bool > _buildScriptUpdated () async {
141
165
var completer = new Completer <bool >();
142
- Future .wait (currentMirrorSystem ().libraries.keys.map ((Uri uri) async {
166
+ Future
167
+ .wait (currentMirrorSystem ().libraries.keys.map ((Uri uri) async {
143
168
/// Short-circuit
144
169
if (completer.isCompleted) return ;
145
170
var lastModified;
@@ -162,11 +187,11 @@ class BuildImpl {
162
187
lastModified = await file.lastModified ();
163
188
break ;
164
189
case 'data' :
190
+
165
191
/// Test runner uses a `data` scheme, don't invalidate for those.
166
192
if (uri.path.contains ('package:test' )) return ;
167
193
continue unknownUri;
168
- unknownUri:
169
- default :
194
+ unknownUri: default :
170
195
_logger.info ('Unrecognized uri scheme `${uri .scheme }` found for '
171
196
'library in build script, falling back on full rebuild.' );
172
197
if (! completer.isCompleted) completer.complete (true );
@@ -176,7 +201,8 @@ class BuildImpl {
176
201
if (lastModified.compareTo (_assetGraph.validAsOf) > 0 ) {
177
202
if (! completer.isCompleted) completer.complete (true );
178
203
}
179
- })).then ((_) {
204
+ }))
205
+ .then ((_) {
180
206
if (! completer.isCompleted) completer.complete (false );
181
207
});
182
208
return completer.future;
0 commit comments