@@ -29,6 +29,9 @@ import 'analysis_driver_filesystem.dart';
29
29
/// anywhere; finish it. See `build_asset_uri_resolver.dart` for the current
30
30
/// implementation.
31
31
class AnalysisDriverModel {
32
+ /// The instance used by the shared `AnalyzerResolvers` instance.
33
+ static AnalysisDriverModel sharedInstance = AnalysisDriverModel ();
34
+
32
35
/// In-memory filesystem for the analyzer.
33
36
final AnalysisDriverFilesystem filesystem = AnalysisDriverFilesystem ();
34
37
@@ -37,7 +40,7 @@ class AnalysisDriverModel {
37
40
38
41
/// Assets that have been synced into the in-memory filesystem
39
42
/// [filesystem] .
40
- final _syncedOntoFilesystem = < AssetId > {};
43
+ final _syncedOntoFilesystemAtPhase = < AssetId , int > {};
41
44
42
45
/// Notifies that [step] has completed.
43
46
///
@@ -49,7 +52,7 @@ class AnalysisDriverModel {
49
52
/// Clear cached information specific to an individual build.
50
53
void reset () {
51
54
_graphLoader.clear ();
52
- _syncedOntoFilesystem .clear ();
55
+ _syncedOntoFilesystemAtPhase .clear ();
53
56
}
54
57
55
58
/// Attempts to parse [uri] into an [AssetId] and returns it if it is cached.
@@ -86,25 +89,17 @@ class AnalysisDriverModel {
86
89
withDriverResource, {
87
90
required bool transitive,
88
91
}) async {
89
- // Immediately take the lock on `driver` so that the whole class state,
90
- // is only mutated by one build step at a time.
91
- await withDriverResource ((driver) async {
92
- // TODO(davidmorgan): it looks like this is only ever called with a single
93
- // entrypoint, consider doing a breaking release to simplify the API.
94
- for (final entrypoint in entrypoints) {
95
- await _performResolve (
96
- driver,
97
- buildStep as BuildStepImpl ,
98
- entrypoint,
99
- withDriverResource,
100
- transitive: transitive,
101
- );
102
- }
103
- });
92
+ for (final entrypoint in entrypoints) {
93
+ await _performResolve (
94
+ buildStep as BuildStepImpl ,
95
+ entrypoint,
96
+ withDriverResource,
97
+ transitive: transitive,
98
+ );
99
+ }
104
100
}
105
101
106
102
Future <void > _performResolve (
107
- AnalysisDriverForPackageBuild driver,
108
103
BuildStepImpl buildStep,
109
104
AssetId entrypoint,
110
105
Future <void > Function (
@@ -113,17 +108,18 @@ class AnalysisDriverModel {
113
108
withDriverResource, {
114
109
required bool transitive,
115
110
}) async {
116
- var idsToSyncOntoFilesystem = [entrypoint];
111
+ Iterable < AssetId > idsToSyncOntoFilesystem = [entrypoint];
117
112
Iterable <AssetId > inputIds = [entrypoint];
118
113
119
114
// If requested, find transitive imports.
120
115
if (transitive) {
116
+ // Note: `transitiveDepsOf` can cause loads that cause builds that cause a
117
+ // recursive `_performResolve` on this same `AnalysisDriver` instance.
121
118
final nodeLoader = AssetDepsLoader (buildStep.phasedReader);
122
- idsToSyncOntoFilesystem =
123
- (await _graphLoader.transitiveDepsOf (
124
- nodeLoader,
125
- entrypoint,
126
- )).toList ();
119
+ idsToSyncOntoFilesystem = await _graphLoader.transitiveDepsOf (
120
+ nodeLoader,
121
+ entrypoint,
122
+ );
127
123
inputIds = idsToSyncOntoFilesystem;
128
124
}
129
125
@@ -133,27 +129,45 @@ class AnalysisDriverModel {
133
129
inputs: inputIds,
134
130
);
135
131
136
- // Sync changes onto the "URI resolver", the in-memory filesystem.
137
- for (final id in idsToSyncOntoFilesystem) {
138
- if (! _syncedOntoFilesystem.add (id)) {
139
- continue ;
140
- }
141
- final content =
142
- await buildStep.canRead (id) ? await buildStep.readAsString (id) : null ;
143
- if (content == null ) {
144
- filesystem.deleteFile (id.asPath);
145
- } else {
146
- filesystem.writeFile (id.asPath, content);
132
+ await withDriverResource ((driver) async {
133
+ // Sync changes onto the "URI resolver", the in-memory filesystem.
134
+ final phase = buildStep.phasedReader.phase;
135
+ for (final id in idsToSyncOntoFilesystem) {
136
+ final wasSyncedAt = _syncedOntoFilesystemAtPhase[id];
137
+ if (wasSyncedAt != null ) {
138
+ // Skip if already synced at this phase.
139
+ if (wasSyncedAt == phase) {
140
+ continue ;
141
+ }
142
+ // Skip if synced at an equivalent other phase.
143
+ if (! buildStep.phasedReader.hasChanged (
144
+ id,
145
+ comparedToPhase: wasSyncedAt,
146
+ )) {
147
+ continue ;
148
+ }
149
+ }
150
+
151
+ _syncedOntoFilesystemAtPhase[id] = phase;
152
+ final content =
153
+ await buildStep.canRead (id)
154
+ ? await buildStep.readAsString (id)
155
+ : null ;
156
+ if (content == null ) {
157
+ filesystem.deleteFile (id.asPath);
158
+ } else {
159
+ filesystem.writeFile (id.asPath, content);
160
+ }
147
161
}
148
- }
149
162
150
- // Notify the analyzer of changes and wait for it to update its internal
151
- // state.
152
- for (final path in filesystem.changedPaths) {
153
- driver.changeFile (path);
154
- }
155
- filesystem.clearChangedPaths ();
156
- await driver.applyPendingFileChanges ();
163
+ // Notify the analyzer of changes and wait for it to update its internal
164
+ // state.
165
+ for (final path in filesystem.changedPaths) {
166
+ driver.changeFile (path);
167
+ }
168
+ filesystem.clearChangedPaths ();
169
+ await driver.applyPendingFileChanges ();
170
+ });
157
171
}
158
172
}
159
173
0 commit comments