@@ -8,13 +8,17 @@ import 'package:build/build.dart';
8
8
import 'package:built_collection/built_collection.dart' ;
9
9
import 'package:watcher/watcher.dart' ;
10
10
11
- import '../bootstrap/build_script_updates.dart' ;
12
11
import '../build_plan/build_directory.dart' ;
13
12
import '../build_plan/build_filter.dart' ;
14
13
import '../build_plan/build_plan.dart' ;
14
+ import '../commands/watch/asset_change.dart' ;
15
+ import '../constants.dart' ;
16
+ import '../io/asset_tracker.dart' ;
17
+ import '../io/build_output_reader.dart' ;
15
18
import '../io/filesystem_cache.dart' ;
16
19
import '../io/reader_writer.dart' ;
17
20
import 'asset_graph/graph.dart' ;
21
+ import 'asset_graph/node.dart' ;
18
22
import 'build.dart' ;
19
23
import 'build_result.dart' ;
20
24
@@ -31,46 +35,126 @@ import 'build_result.dart';
31
35
/// this serialized state is not actually used: the `AssetGraph` instance
32
36
/// already in memory is used directly.
33
37
class BuildSeries {
34
- final BuildPlan buildPlan ;
38
+ final BuildPlan _buildPlan ;
35
39
36
- final AssetGraph assetGraph;
37
- final BuildScriptUpdates ? buildScriptUpdates;
40
+ final AssetGraph _assetGraph;
38
41
39
- final ReaderWriter readerWriter ;
40
- final ResourceManager resourceManager = ResourceManager ();
42
+ final ReaderWriter _readerWriter ;
43
+ final ResourceManager _resourceManager = ResourceManager ();
41
44
42
45
/// For the first build only, updates from the previous serialized build
43
46
/// state.
44
47
///
45
48
/// Null after the first build, or if there was no serialized build state, or
46
49
/// if the serialized build state was discarded.
47
- BuiltMap <AssetId , ChangeType >? updatesFromLoad ;
50
+ BuiltMap <AssetId , ChangeType >? _updatesFromLoad ;
48
51
49
52
final StreamController <BuildResult > _buildResultsController =
50
53
StreamController .broadcast ();
51
54
52
55
/// Whether the next build is the first build.
53
56
bool firstBuild = true ;
54
57
55
- Future <void > beforeExit () => resourceManager.beforeExit ();
56
-
57
- BuildSeries ._(
58
- this .buildPlan,
59
- this .assetGraph,
60
- this .buildScriptUpdates,
61
- this .updatesFromLoad,
62
- ) : readerWriter = buildPlan.readerWriter.copyWith (
63
- generatedAssetHider: assetGraph,
64
- cache:
65
- buildPlan.buildOptions.enableLowResourcesMode
66
- ? const PassthroughFilesystemCache ()
67
- : InMemoryFilesystemCache (),
68
- );
58
+ BuildSeries ._({
59
+ required BuildPlan buildPlan,
60
+ required AssetGraph assetGraph,
61
+ required ReaderWriter readerWriter,
62
+ required BuiltMap <AssetId , ChangeType >? updatesFromLoad,
63
+ }) : _buildPlan = buildPlan,
64
+ _assetGraph = assetGraph,
65
+ _readerWriter = readerWriter,
66
+ _updatesFromLoad = updatesFromLoad;
67
+
68
+ factory BuildSeries (BuildPlan buildPlan) {
69
+ final assetGraph = buildPlan.takeAssetGraph ();
70
+ final readerWriter = buildPlan.readerWriter.copyWith (
71
+ generatedAssetHider: assetGraph,
72
+ cache:
73
+ buildPlan.buildOptions.enableLowResourcesMode
74
+ ? const PassthroughFilesystemCache ()
75
+ : InMemoryFilesystemCache (),
76
+ );
77
+ return BuildSeries ._(
78
+ buildPlan: buildPlan,
79
+ assetGraph: assetGraph,
80
+ readerWriter: readerWriter,
81
+ updatesFromLoad: buildPlan.updates,
82
+ );
83
+ }
69
84
70
85
/// Broadcast stream of build results.
71
86
Stream <BuildResult > get buildResults => _buildResultsController.stream;
72
87
Future <BuildResult >? _currentBuildResult;
73
88
89
+ bool _hasBuildScriptChanged (Set <AssetId > changes) {
90
+ if (_buildPlan.buildOptions.skipBuildScriptCheck) return false ;
91
+ if (_buildPlan.buildScriptUpdates == null ) return true ;
92
+ return _buildPlan.buildScriptUpdates! .hasBeenUpdated (changes);
93
+ }
94
+
95
+ /// Returns whether [change] might trigger a build.
96
+ ///
97
+ /// Pass expected deletes in [expectedDeletes] . Expected deletes do not
98
+ /// trigger a build. A delete that matches is removed from the set.
99
+ Future <bool > shouldProcess (
100
+ AssetChange change,
101
+ Set <AssetId > expectedDeletes,
102
+ ) async {
103
+ // Ignore any expected delete once.
104
+ if (change.type == ChangeType .REMOVE && expectedDeletes.remove (change.id)) {
105
+ return false ;
106
+ }
107
+
108
+ final node =
109
+ _assetGraph.contains (change.id) ? _assetGraph.get (change.id) : null ;
110
+
111
+ // Changes to files that are not currently part of the build.
112
+ if (node == null ) {
113
+ // Ignore under `.dart_tool/build`.
114
+ if (change.id.path.startsWith (cacheDir)) return false ;
115
+
116
+ // Ignore modifications and deletes.
117
+ if (change.type != ChangeType .ADD ) return false ;
118
+
119
+ // It's an add: return whether it's a new input.
120
+ return _buildPlan.targetGraph.anyMatchesAsset (change.id);
121
+ }
122
+
123
+ // Changes to files that are part of the build.
124
+
125
+ // If not copying to a merged output directory, ignore changes to files with
126
+ // no outputs.
127
+ if (! _buildPlan.buildOptions.anyMergedOutputDirectory &&
128
+ ! node.changesRequireRebuild) {
129
+ return false ;
130
+ }
131
+
132
+ // Ignore creation or modification of outputs.
133
+ if (node.type == NodeType .generated && change.type != ChangeType .REMOVE ) {
134
+ return false ;
135
+ }
136
+
137
+ // For modifications, confirm that the content actually changed.
138
+ if (change.type == ChangeType .MODIFY ) {
139
+ _readerWriter.cache.invalidate ([change.id]);
140
+ final newDigest = await _readerWriter.digest (change.id);
141
+ return node.digest != newDigest;
142
+ }
143
+
144
+ // It's an add of "missing source" node or a deletion of an input.
145
+ return true ;
146
+ }
147
+
148
+ Future <List <WatchEvent >> checkForChanges () async {
149
+ final updates = await AssetTracker (
150
+ _buildPlan.readerWriter,
151
+ _buildPlan.targetGraph,
152
+ ).collectChanges (_assetGraph);
153
+ return List .of (
154
+ updates.entries.map ((entry) => WatchEvent (entry.value, '${entry .key }' )),
155
+ );
156
+ }
157
+
74
158
/// If a build is running, the build result when it's done.
75
159
///
76
160
/// If no build has ever run, returns the first build result when it's
@@ -93,27 +177,39 @@ class BuildSeries {
93
177
BuiltSet <BuildDirectory >? buildDirs,
94
178
BuiltSet <BuildFilter >? buildFilters,
95
179
}) async {
96
- buildDirs ?? = buildPlan.buildOptions.buildDirs;
97
- buildFilters ?? = buildPlan.buildOptions.buildFilters;
180
+ if (_hasBuildScriptChanged (updates.keys.toSet ())) {
181
+ return BuildResult (
182
+ status: BuildStatus .failure,
183
+ failureType: FailureType .buildScriptChanged,
184
+ buildOutputReader: BuildOutputReader (
185
+ buildPlan: _buildPlan,
186
+ readerWriter: _readerWriter,
187
+ assetGraph: _assetGraph,
188
+ ),
189
+ );
190
+ }
191
+
192
+ buildDirs ?? = _buildPlan.buildOptions.buildDirs;
193
+ buildFilters ?? = _buildPlan.buildOptions.buildFilters;
98
194
if (firstBuild) {
99
- if (updatesFromLoad != null ) {
100
- updates = updatesFromLoad ! .toMap ()..addAll (updates);
101
- updatesFromLoad = null ;
195
+ if (_updatesFromLoad != null ) {
196
+ updates = _updatesFromLoad ! .toMap ()..addAll (updates);
197
+ _updatesFromLoad = null ;
102
198
}
103
199
} else {
104
- if (updatesFromLoad != null ) {
200
+ if (_updatesFromLoad != null ) {
105
201
throw StateError ('Only first build can have updates from load.' );
106
202
}
107
203
}
108
204
109
205
final build = Build (
110
- buildPlan: buildPlan .copyWith (
206
+ buildPlan: _buildPlan .copyWith (
111
207
buildDirs: buildDirs,
112
208
buildFilters: buildFilters,
113
209
),
114
- assetGraph: assetGraph ,
115
- readerWriter: readerWriter ,
116
- resourceManager: resourceManager ,
210
+ assetGraph: _assetGraph ,
211
+ readerWriter: _readerWriter ,
212
+ resourceManager: _resourceManager ,
117
213
);
118
214
if (firstBuild) firstBuild = false ;
119
215
@@ -123,14 +219,5 @@ class BuildSeries {
123
219
return result;
124
220
}
125
221
126
- static Future <BuildSeries > create ({required BuildPlan buildPlan}) async {
127
- final assetGraph = buildPlan.takeAssetGraph ();
128
- final build = BuildSeries ._(
129
- buildPlan,
130
- assetGraph,
131
- buildPlan.buildScriptUpdates,
132
- buildPlan.updates,
133
- );
134
- return build;
135
- }
222
+ Future <void > beforeExit () => _resourceManager.beforeExit ();
136
223
}
0 commit comments