Skip to content

Commit 292a623

Browse files
authored
Fix an issue where multiple AnalyzerResolvers instances lead to incorrect behavior (#3580)
Deprecate the unnamed AnalyzerResolvers constructor, replace it with a static instance getter and `AnalyzerResolvers.custom` factory constructor. These new APIs guarantee a 1:1 relationship between the BuildAssetUriResolver and an analysis engine, which is necessary to ensure `changeFile` is always called on each analysis engine when needed. Note that the deprecated constructor does not uphold this invariant, it remains backwards compatible with its old implementation, and uses the same BuildAssetUriResolver as the shared resolver.
1 parent a161297 commit 292a623

File tree

14 files changed

+97
-38
lines changed

14 files changed

+97
-38
lines changed

build/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
## 2.4.2-wip
2+
13
## 2.4.1
24

35
- Allow the latest analyzer (6.x.x).

build/pubspec.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: build
2-
version: 2.4.1
2+
version: 2.4.2-wip
33
description: A package for authoring build_runner compatible code generators.
44
repository: https://github.com/dart-lang/build/tree/master/build
55

@@ -18,7 +18,7 @@ dependencies:
1818
path: ^1.8.0
1919

2020
dev_dependencies:
21-
build_resolvers: ^2.0.0
21+
build_resolvers: ^2.4.0
2222
build_test: ^2.0.0
2323
dart_flutter_team_lints: ^1.0.0
2424
test: ^1.16.0

build/test/builder/build_step_impl_test.dart

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ void main() {
3535
primary = makeAssetId();
3636
outputs = List.generate(5, (index) => makeAssetId());
3737
buildStep = BuildStepImpl(primary, outputs, reader, writer,
38-
AnalyzerResolvers(), resourceManager, _unsupported);
38+
AnalyzerResolvers.custom(), resourceManager, _unsupported);
3939
});
4040

4141
test('doesnt allow non-expected outputs', () {
@@ -93,7 +93,7 @@ void main() {
9393
[outputId],
9494
reader,
9595
writer,
96-
AnalyzerResolvers(),
96+
AnalyzerResolvers.custom(),
9797
resourceManager,
9898
_unsupported,
9999
);
@@ -121,7 +121,7 @@ void main() {
121121

122122
var primary = makeAssetId('a|web/a.dart');
123123
var buildStep = BuildStepImpl(primary, [], reader, writer,
124-
AnalyzerResolvers(), resourceManager, _unsupported);
124+
AnalyzerResolvers.custom(), resourceManager, _unsupported);
125125
var resolver = buildStep.resolver;
126126

127127
var aLib = await resolver.libraryFor(primary);
@@ -150,8 +150,14 @@ void main() {
150150
assetWriter = SlowAssetWriter();
151151
outputId = makeAssetId('a|test.txt');
152152
outputContent = '$outputId';
153-
buildStep = BuildStepImpl(primary, [outputId], StubAssetReader(),
154-
assetWriter, AnalyzerResolvers(), resourceManager, _unsupported);
153+
buildStep = BuildStepImpl(
154+
primary,
155+
[outputId],
156+
StubAssetReader(),
157+
assetWriter,
158+
AnalyzerResolvers.custom(),
159+
resourceManager,
160+
_unsupported);
155161
});
156162

157163
test('Completes only after writes finish', () async {
@@ -199,7 +205,7 @@ void main() {
199205
primary = makeAssetId();
200206
output = makeAssetId();
201207
buildStep = BuildStepImpl(primary, [output], reader, writer,
202-
AnalyzerResolvers(), resourceManager, _unsupported,
208+
AnalyzerResolvers.custom(), resourceManager, _unsupported,
203209
stageTracker: NoOpStageTracker.instance);
204210
});
205211

@@ -214,7 +220,7 @@ void main() {
214220
var writer = StubAssetWriter();
215221
var unused = <AssetId>{};
216222
var buildStep = BuildStepImpl(makeAssetId(), [], reader, writer,
217-
AnalyzerResolvers(), resourceManager, _unsupported,
223+
AnalyzerResolvers.custom(), resourceManager, _unsupported,
218224
reportUnusedAssets: unused.addAll);
219225
var reported = [
220226
makeAssetId(),

build_resolvers/CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
## 2.4.0-wip
2+
3+
- Deprecate the unnamed `AnalyzerResolvers` constructor, an replace it with the
4+
`AnalyzerResolvers.sharedInstance` getter and `AnalyzerResolvers.custom`
5+
constructor. These new apis enforce a 1:1 relationship between the
6+
`BuildAssetUriResolver` instances and `AnalyzerResolvers` instances, which is
7+
necessary to ensure correct builds in the presence of multiple resolvers.
8+
19
## 2.3.2
210

311
- Skip file delete for SDK summary and deps file. This will only impact behavior

build_resolvers/lib/src/build_asset_uri_resolver.dart

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,16 @@ class BuildAssetUriResolver extends UriResolver {
5454
/// input, subsequent calls to a resolver, or a transitive import thereof.
5555
final _buildStepTransitivelyResolvedAssets = <BuildStep, HashSet<AssetId>>{};
5656

57-
/// Use the [instance] getter to get an instance.
58-
BuildAssetUriResolver._();
57+
/// Use the [sharedInstance] getter to get the shared instance, which is what
58+
/// real builds should do. There should always be a 1:1 relationship between
59+
/// [BuildAssetUriResolver]s and `AnalyzerResolvers`.
60+
BuildAssetUriResolver();
5961

60-
static final BuildAssetUriResolver instance = BuildAssetUriResolver._();
62+
/// The instance used by the shared `AnalyzerResolvers` instance, which is
63+
/// what should be used by normal builds.
64+
///
65+
/// This is not used within testing contexts or similar custom contexts.
66+
static final BuildAssetUriResolver sharedInstance = BuildAssetUriResolver();
6167

6268
/// Updates [resourceProvider] and the analysis driver given by
6369
/// `withDriverResource` with updated versions of [entryPoints].
@@ -261,7 +267,7 @@ Set<AssetId> _parseDependencies(String content, AssetId from) => HashSet.of(
261267
/// directives, and cache the results if they weren't already cached.
262268
Future<Iterable<AssetId>?> dependenciesOf(
263269
AssetId id, BuildStep buildStep) async =>
264-
(await BuildAssetUriResolver.instance
270+
(await BuildAssetUriResolver.sharedInstance
265271
._updateCachedAssetState(id, buildStep))
266272
?.dependencies;
267273

build_resolvers/lib/src/resolver.dart

Lines changed: 48 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,8 @@ class AnalyzerResolvers implements Resolvers {
381381

382382
// Lazy, all access must be preceded by a call to `_ensureInitialized`.
383383
late final AnalyzerResolver _resolver;
384-
BuildAssetUriResolver? _uriResolver;
384+
385+
final BuildAssetUriResolver _uriResolver;
385386

386387
/// Nullable, should not be accessed outside of [_ensureInitialized].
387388
Future<Result<void>>? _initialized;
@@ -402,30 +403,65 @@ class AnalyzerResolvers implements Resolvers {
402403
/// **NOTE**: The [_packageConfig] is not used for path resolution, it is
403404
/// primarily used to get the language versions. Any other data (including
404405
/// extra data), may be passed to the analyzer on an as needed basis.
405-
AnalyzerResolvers(
406-
[AnalysisOptions? analysisOptions,
407-
Future<String> Function()? sdkSummaryGenerator,
408-
this._packageConfig])
409-
: _analysisOptions = analysisOptions ??
406+
factory AnalyzerResolvers.custom({
407+
AnalysisOptions? analysisOptions,
408+
Future<String> Function()? sdkSummaryGenerator,
409+
PackageConfig? packageConfig,
410+
}) =>
411+
AnalyzerResolvers._(
412+
analysisOptions: analysisOptions,
413+
sdkSummaryGenerator: sdkSummaryGenerator,
414+
packageConfig: packageConfig,
415+
// Custom resolvers get their own asset uri resolver, as there should
416+
// always be a 1:1 relationship between them.
417+
uriResolver: BuildAssetUriResolver());
418+
419+
/// See [AnalyzerResolvers.custom] for docs.
420+
@Deprecated('Use either the AnalyzerResolvers.custom constructor or the '
421+
'AnalyzerResolvers.sharedInstance static getter to get an instance.')
422+
factory AnalyzerResolvers([
423+
AnalysisOptions? analysisOptions,
424+
Future<String> Function()? sdkSummaryGenerator,
425+
PackageConfig? packageConfig,
426+
]) =>
427+
AnalyzerResolvers._(
428+
analysisOptions: analysisOptions,
429+
sdkSummaryGenerator: sdkSummaryGenerator,
430+
packageConfig: packageConfig,
431+
// For backwards compatibility we use the shared instance here.
432+
uriResolver: BuildAssetUriResolver.sharedInstance);
433+
434+
/// See [AnalyzerResolvers.custom] for docs.
435+
AnalyzerResolvers._({
436+
AnalysisOptions? analysisOptions,
437+
PackageConfig? packageConfig,
438+
Future<String> Function()? sdkSummaryGenerator,
439+
required BuildAssetUriResolver uriResolver,
440+
}) : _analysisOptions = analysisOptions ??
410441
(AnalysisOptionsImpl()
411442
..contextFeatures =
412443
_featureSet(enableExperiments: enabledExperiments)),
444+
_packageConfig = packageConfig,
413445
_sdkSummaryGenerator =
414-
sdkSummaryGenerator ?? defaultSdkSummaryGenerator;
446+
sdkSummaryGenerator ?? defaultSdkSummaryGenerator,
447+
_uriResolver = uriResolver;
448+
449+
/// The instance that most real build systems should use.
450+
static final AnalyzerResolvers sharedInstance =
451+
AnalyzerResolvers._(uriResolver: BuildAssetUriResolver.sharedInstance);
415452

416453
/// Create a Resolvers backed by an `AnalysisContext` using options
417454
/// [_analysisOptions].
418455
Future<void> _ensureInitialized() {
419456
return Result.release(_initialized ??= Result.capture(() async {
420457
_warnOnLanguageVersionMismatch();
421-
final uriResolver = _uriResolver = BuildAssetUriResolver.instance;
422458
final loadedConfig = _packageConfig ??=
423459
await loadPackageConfigUri((await Isolate.packageConfig)!);
424-
var driver = await analysisDriver(uriResolver, _analysisOptions,
460+
var driver = await analysisDriver(_uriResolver, _analysisOptions,
425461
await _sdkSummaryGenerator(), loadedConfig);
426462

427-
_resolver =
428-
AnalyzerResolver(driver, _driverPool, _readAndWritePool, uriResolver);
463+
_resolver = AnalyzerResolver(
464+
driver, _driverPool, _readAndWritePool, _uriResolver);
429465
}()));
430466
}
431467

@@ -439,7 +475,7 @@ class AnalyzerResolvers implements Resolvers {
439475
/// Must be called between each build.
440476
@override
441477
void reset() {
442-
_uriResolver?.reset();
478+
_uriResolver.reset();
443479
}
444480
}
445481

build_resolvers/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: build_resolvers
2-
version: 2.3.2
2+
version: 2.4.0-wip
33
description: Resolve Dart code in a Builder
44
repository: https://github.com/dart-lang/build/tree/master/build_resolvers
55

build_runner_core/CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
## 7.2.11-dev
1+
## 7.2.11-wip
22

33
- Add better error messages for incomplete package configs when creating a
44
package graph.
5+
- Update to build_resolvers 2.4.0 and use the shared analyzer resolvers
6+
instance.
57

68
## 7.2.10
79

build_runner_core/lib/src/generate/options.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ feature, you may need to run `dart run build_runner clean` and then rebuild.
208208
}
209209
trackPerformance = true;
210210
}
211-
resolvers ??= AnalyzerResolvers();
211+
resolvers ??= AnalyzerResolvers.sharedInstance;
212212

213213
return BuildOptions._(
214214
debounceDelay: debounceDelay,

build_runner_core/pubspec.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: build_runner_core
2-
version: 7.2.11-dev
2+
version: 7.2.11-wip
33
description: Core tools to organize the structure of a build and run Builders.
44
repository: https://github.com/dart-lang/build/tree/master/build_runner_core
55

@@ -15,7 +15,7 @@ dependencies:
1515
async: ^2.5.0
1616
build: ^2.4.0
1717
build_config: ^1.0.0
18-
build_resolvers: ^2.0.0
18+
build_resolvers: ^2.4.0
1919
collection: ^1.15.0
2020
convert: ^3.0.0
2121
crypto: ^3.0.0

build_test/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
## 2.2.1-dev
22

33
- Avoid passing a nullable value to `Future.value`.
4+
- Update to build_resolvers 2.4.0, which resolves some race conditions when
5+
using multiple resolvers instances.
46

57
## 2.2.0
68

build_test/lib/src/resolve_source.dart

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,6 @@ import 'in_memory_writer.dart';
1515
import 'multi_asset_reader.dart';
1616
import 'package_reader.dart';
1717

18-
final defaultResolvers = AnalyzerResolvers();
19-
2018
/// Marker constant that may be used in combination with [resolveSources].
2119
///
2220
/// Use of this string means instead of using the contents of the string as the
@@ -199,8 +197,8 @@ Future<T> _resolveAssets<T>(
199197
// Use the default resolver if no experiments are enabled. This is much
200198
// faster.
201199
resolvers ??= packageConfig == null && enabledExperiments.isEmpty
202-
? defaultResolvers
203-
: AnalyzerResolvers(null, null, resolvedConfig);
200+
? AnalyzerResolvers.sharedInstance
201+
: AnalyzerResolvers.custom(packageConfig: resolvedConfig);
204202

205203
// We don't care about the results of this build, but we also can't await
206204
// it because that would block on the `tearDown` of the `resolveBuilder`.

build_test/lib/src/test_builder.dart

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import 'assets.dart';
1414
import 'in_memory_reader.dart';
1515
import 'in_memory_writer.dart';
1616
import 'multi_asset_reader.dart';
17-
import 'resolve_source.dart';
1817
import 'written_asset_reader.dart';
1918

2019
AssetId _passThrough(AssetId id) => id;
@@ -171,8 +170,8 @@ Future testBuilder(
171170
var logger = Logger('testBuilder');
172171
var logSubscription = logger.onRecord.listen(onLog);
173172
var resolvers = packageConfig == null && enabledExperiments.isEmpty
174-
? defaultResolvers
175-
: AnalyzerResolvers(null, null, packageConfig);
173+
? AnalyzerResolvers.sharedInstance
174+
: AnalyzerResolvers.custom(packageConfig: packageConfig);
176175

177176
for (var input in inputIds) {
178177
// create another writer spy and reader for each input. This prevents writes

build_test/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ dependencies:
1010
async: ^2.5.0
1111
build: ^2.1.0
1212
build_config: ^1.0.0
13-
build_resolvers: ^2.0.0
13+
build_resolvers: ^2.4.0
1414
crypto: ^3.0.0
1515
glob: ^2.0.0
1616
html: ^0.15.0

0 commit comments

Comments
 (0)