Skip to content

Commit 974ee94

Browse files
committed
Refactor: split BuildAssetUriResolver into AnalysisDriverModel and AnalysisDriverModelUriResolver.
`AnalysisDriverModel` is `build_runner`'s state related to build steps that use analysis, including an in-memory filesystem for use by the analyzer. `UriResolver` is the analyzer's view of that state.
1 parent 32e3816 commit 974ee94

6 files changed

+164
-67
lines changed

build_resolvers/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
## 2.4.4-wip
22

3+
- Refactor `BuildAssetUriResolver` into `AnalysisDriverModel` and
4+
`AnalysisDriverModelUriResolver`.
5+
36
## 2.4.3
47

58
- Require the latest analyzer, and stop passing the `withNullability`

build_resolvers/lib/src/analysis_driver.dart

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,15 @@ import 'package:package_config/package_config.dart' show PackageConfig;
1212
import 'package:path/path.dart' as p;
1313
import 'package:pub_semver/pub_semver.dart';
1414

15+
import 'analysis_driver_model.dart';
16+
import 'analysis_driver_model_uri_resolver.dart';
1517
import 'build_asset_uri_resolver.dart';
1618

1719
/// Builds an [AnalysisDriverForPackageBuild] backed by a summary SDK.
1820
///
19-
/// Any code must be resolvable through [buildAssetUriResolver].
21+
/// Any code must be resolvable through [analysisDriverModel].
2022
Future<AnalysisDriverForPackageBuild> analysisDriver(
21-
BuildAssetUriResolver buildAssetUriResolver,
23+
AnalysisDriverModel analysisDriverModel,
2224
AnalysisOptions analysisOptions,
2325
String sdkSummaryPath,
2426
PackageConfig packageConfig,
@@ -27,12 +29,12 @@ Future<AnalysisDriverForPackageBuild> analysisDriver(
2729
analysisOptions: analysisOptions,
2830
packages: _buildAnalyzerPackages(
2931
packageConfig,
30-
buildAssetUriResolver.resourceProvider,
32+
analysisDriverModel.resourceProvider,
3133
),
32-
resourceProvider: buildAssetUriResolver.resourceProvider,
34+
resourceProvider: analysisDriverModel.resourceProvider,
3335
sdkSummaryBytes: File(sdkSummaryPath).readAsBytesSync(),
3436
uriResolvers: [
35-
buildAssetUriResolver,
37+
AnalysisDriverModelUriResolver(analysisDriverModel),
3638
],
3739
);
3840
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'dart:async';
6+
7+
import 'package:analyzer/file_system/memory_file_system.dart';
8+
// ignore: implementation_imports
9+
import 'package:analyzer/src/clients/build_resolvers/build_resolvers.dart';
10+
import 'package:build/build.dart';
11+
12+
/// Manages analysis driver and related build state.
13+
///
14+
/// - Tracks the import graph of all sources needed for analysis.
15+
/// - Given a set of entrypoints, adds them to known sources, optionally with
16+
/// their transitive imports.
17+
/// - Given a set of entrypoints, informs a `BuildStep` which inputs it now
18+
/// depends on because of analysis.
19+
/// - Maintains an in-memory filesystem that is the analyzer's view of the
20+
/// build.
21+
/// - Notifies the analyzer of changes to that in-memory filesystem.
22+
abstract class AnalysisDriverModel {
23+
/// In-memory filesystem for the analyzer.
24+
abstract final MemoryResourceProvider resourceProvider;
25+
26+
/// Notifies that [step] has completed.
27+
///
28+
/// All build steps must complete before [reset] is called.
29+
void notifyComplete(BuildStep step);
30+
31+
/// Clear cached information specific to an individual build.
32+
void reset();
33+
34+
/// Attempts to parse [uri] into an [AssetId] and returns it if it is cached.
35+
///
36+
/// Handles 'package:' or 'asset:' URIs, as well as 'file:' URIs of the form
37+
/// `/$packageName/$assetPath`.
38+
///
39+
/// Returns null if the Uri cannot be parsed or is not cached.
40+
AssetId? lookupCachedAsset(Uri uri);
41+
42+
/// Updates [resourceProvider] and the analysis driver given by
43+
/// `withDriverResource` with updated versions of [entryPoints].
44+
///
45+
/// If [transitive], then all the transitive imports from [entryPoints] are
46+
/// also updated.
47+
Future<void> performResolve(
48+
BuildStep buildStep,
49+
List<AssetId> entryPoints,
50+
Future<void> Function(
51+
FutureOr<void> Function(AnalysisDriverForPackageBuild))
52+
withDriverResource,
53+
{required bool transitive});
54+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'package:analyzer/source/file_source.dart';
6+
// ignore: implementation_imports
7+
import 'package:analyzer/src/clients/build_resolvers/build_resolvers.dart';
8+
import 'package:build/build.dart';
9+
import 'package:path/path.dart' as p;
10+
11+
import 'analysis_driver_model.dart';
12+
13+
const _ignoredSchemes = ['dart', 'dart-ext'];
14+
15+
/// A [UriResolver] on top of [AnalysisDriverModel]'s in-memory filesystem.
16+
class AnalysisDriverModelUriResolver implements UriResolver {
17+
final AnalysisDriverModel analysisDriverModel;
18+
AnalysisDriverModelUriResolver(this.analysisDriverModel);
19+
20+
@override
21+
Source? resolveAbsolute(Uri uri, [Uri? actualUri]) {
22+
final assetId = parseAsset(uri);
23+
if (assetId == null) return null;
24+
25+
var file = analysisDriverModel.resourceProvider.getFile(assetPath(assetId));
26+
return FileSource(file, assetId.uri);
27+
}
28+
29+
@override
30+
Uri pathToUri(String path) {
31+
var pathSegments = p.posix.split(path);
32+
var packageName = pathSegments[1];
33+
if (pathSegments[2] == 'lib') {
34+
return Uri(
35+
scheme: 'package',
36+
pathSegments: [packageName].followedBy(pathSegments.skip(3)),
37+
);
38+
} else {
39+
return Uri(
40+
scheme: 'asset',
41+
pathSegments: [packageName].followedBy(pathSegments.skip(2)),
42+
);
43+
}
44+
}
45+
46+
/// Attempts to parse [uri] into an [AssetId].
47+
///
48+
/// Handles 'package:' or 'asset:' URIs, as well as 'file:' URIs that have the
49+
/// same pattern used by [assetPath].
50+
///
51+
/// Returns null if the Uri cannot be parsed.
52+
static AssetId? parseAsset(Uri uri) {
53+
if (_ignoredSchemes.any(uri.isScheme)) return null;
54+
if (uri.isScheme('package') || uri.isScheme('asset')) {
55+
return AssetId.resolve(uri);
56+
}
57+
if (uri.isScheme('file')) {
58+
final parts = p.split(uri.path);
59+
return AssetId(parts[1], p.posix.joinAll(parts.skip(2)));
60+
}
61+
return null;
62+
}
63+
64+
static String assetPath(AssetId assetId) =>
65+
p.posix.join('/${assetId.package}', assetId.path);
66+
}

build_resolvers/lib/src/build_asset_uri_resolver.dart

Lines changed: 8 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import 'dart:isolate';
99
import 'package:analyzer/dart/analysis/utilities.dart';
1010
import 'package:analyzer/dart/ast/ast.dart';
1111
import 'package:analyzer/file_system/memory_file_system.dart';
12-
import 'package:analyzer/source/file_source.dart';
1312
// ignore: implementation_imports
1413
import 'package:analyzer/src/clients/build_resolvers/build_resolvers.dart';
1514
import 'package:build/build.dart' show AssetId, BuildStep;
@@ -19,11 +18,13 @@ import 'package:graphs/graphs.dart';
1918
import 'package:path/path.dart' as p;
2019
import 'package:stream_transform/stream_transform.dart';
2120

21+
import 'analysis_driver_model.dart';
22+
2223
const _ignoredSchemes = ['dart', 'dart-ext'];
2324

2425
const transitiveDigestExtension = '.transitive_digest';
2526

26-
class BuildAssetUriResolver extends UriResolver {
27+
class BuildAssetUriResolver implements AnalysisDriverModel {
2728
/// A cache of the directives for each Dart library.
2829
///
2930
/// This is stored across builds and is only invalidated if we read a file and
@@ -41,6 +42,7 @@ class BuildAssetUriResolver extends UriResolver {
4142
/// updated in the analysis driver.
4243
final _needsChangeFile = HashSet<String>();
4344

45+
@override
4446
final resourceProvider = MemoryResourceProvider(context: p.posix);
4547

4648
/// The assets which are known to be readable at some point during the current
@@ -66,11 +68,7 @@ class BuildAssetUriResolver extends UriResolver {
6668
/// This is not used within testing contexts or similar custom contexts.
6769
static final BuildAssetUriResolver sharedInstance = BuildAssetUriResolver();
6870

69-
/// Updates [resourceProvider] and the analysis driver given by
70-
/// `withDriverResource` with updated versions of [entryPoints].
71-
///
72-
/// If [transitive], then all the transitive imports from [entryPoints] are
73-
/// also updated.
71+
@override
7472
Future<void> performResolve(
7573
BuildStep buildStep,
7674
List<AssetId> entryPoints,
@@ -186,12 +184,7 @@ class BuildAssetUriResolver extends UriResolver {
186184
return null;
187185
}
188186

189-
/// Attempts to parse [uri] into an [AssetId] and returns it if it is cached.
190-
///
191-
/// Handles 'package:' or 'asset:' URIs, as well as 'file:' URIs that have the
192-
/// same pattern used by [assetPath].
193-
///
194-
/// Returns null if the Uri cannot be parsed or is not cached.
187+
@override
195188
AssetId? lookupCachedAsset(Uri uri) {
196189
final assetId = parseAsset(uri);
197190
if (assetId == null || !_cachedAssetDigests.containsKey(assetId)) {
@@ -201,43 +194,18 @@ class BuildAssetUriResolver extends UriResolver {
201194
return assetId;
202195
}
203196

197+
@override
204198
void notifyComplete(BuildStep step) {
205199
_buildStepTransitivelyResolvedAssets.remove(step);
206200
}
207201

208-
/// Clear cached information specific to an individual build.
202+
@override
209203
void reset() {
210204
assert(_buildStepTransitivelyResolvedAssets.isEmpty,
211205
'Reset was called before all build steps completed');
212206
globallySeenAssets.clear();
213207
_needsChangeFile.clear();
214208
}
215-
216-
@override
217-
Source? resolveAbsolute(Uri uri, [Uri? actualUri]) {
218-
final assetId = parseAsset(uri);
219-
if (assetId == null) return null;
220-
221-
var file = resourceProvider.getFile(assetPath(assetId));
222-
return FileSource(file, assetId.uri);
223-
}
224-
225-
@override
226-
Uri pathToUri(String path) {
227-
var pathSegments = p.posix.split(path);
228-
var packageName = pathSegments[1];
229-
if (pathSegments[2] == 'lib') {
230-
return Uri(
231-
scheme: 'package',
232-
pathSegments: [packageName].followedBy(pathSegments.skip(3)),
233-
);
234-
} else {
235-
return Uri(
236-
scheme: 'asset',
237-
pathSegments: [packageName].followedBy(pathSegments.skip(2)),
238-
);
239-
}
240-
}
241209
}
242210

243211
String assetPath(AssetId assetId) =>

0 commit comments

Comments
 (0)