diff --git a/build/CHANGELOG.md b/build/CHANGELOG.md index 4e802e719..e41b9ed1b 100644 --- a/build/CHANGELOG.md +++ b/build/CHANGELOG.md @@ -3,6 +3,7 @@ - Add `package:build/src/internal.dart` for use by `build_resolvers`, `build_runner_core` and `build_test`. - Use `build_test` 3.0.0. +- Refactor `PathProvidingAssetReader` to `AssetPathProvider`. ## 2.4.2 diff --git a/build/lib/src/builder/build_step_impl.dart b/build/lib/src/builder/build_step_impl.dart index 80023f670..bed0efbff 100644 --- a/build/lib/src/builder/build_step_impl.dart +++ b/build/lib/src/builder/build_step_impl.dart @@ -18,6 +18,7 @@ import '../asset/id.dart'; import '../asset/reader.dart'; import '../asset/writer.dart'; import '../resource/resource.dart'; +import '../state/asset_path_provider.dart'; import '../state/input_tracker.dart'; import '../state/reader_state.dart'; import 'build_step.dart'; @@ -80,6 +81,9 @@ class BuildStepImpl implements BuildStep, AssetReaderState { _stageTracker = stageTracker ?? NoOpStageTracker.instance, _reportUnusedAssets = reportUnusedAssets; + @override + AssetPathProvider? get assetPathProvider => _reader.assetPathProvider; + @override InputTracker? get inputTracker => _reader.inputTracker; diff --git a/build/lib/src/internal.dart b/build/lib/src/internal.dart index 199be0a49..895606231 100644 --- a/build/lib/src/internal.dart +++ b/build/lib/src/internal.dart @@ -6,5 +6,6 @@ /// `build_test` only. library; +export 'state/asset_path_provider.dart'; export 'state/input_tracker.dart'; export 'state/reader_state.dart'; diff --git a/build/lib/src/state/asset_path_provider.dart b/build/lib/src/state/asset_path_provider.dart new file mode 100644 index 000000000..f31451f51 --- /dev/null +++ b/build/lib/src/state/asset_path_provider.dart @@ -0,0 +1,21 @@ +// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import '../asset/id.dart'; + +/// Converts [AssetId] to paths. +abstract interface class AssetPathProvider { + String pathFor(AssetId id); +} + +/// Applies a function to an existing [AssetPathProvider]. +class OverlayAssetPathProvider implements AssetPathProvider { + AssetPathProvider delegate; + AssetId Function(AssetId) overlay; + + OverlayAssetPathProvider({required this.delegate, required this.overlay}); + + @override + String pathFor(AssetId id) => delegate.pathFor(overlay(id)); +} diff --git a/build/lib/src/state/reader_state.dart b/build/lib/src/state/reader_state.dart index 52341e0d7..7cd668966 100644 --- a/build/lib/src/state/reader_state.dart +++ b/build/lib/src/state/reader_state.dart @@ -3,6 +3,7 @@ // BSD-style license that can be found in the LICENSE file. import '../asset/reader.dart'; +import 'asset_path_provider.dart'; import 'input_tracker.dart'; /// Provides access to the state backing an [AssetReader]. @@ -21,6 +22,10 @@ extension AssetReaderStateExtension on AssetReader { return result; } + AssetPathProvider? get assetPathProvider => this is AssetReaderState + ? (this as AssetReaderState).assetPathProvider + : null; + /// Throws if `this` is not an [AssetReaderState]. void _requireIsAssetReaderState() { if (this is! AssetReaderState) { @@ -35,4 +40,8 @@ abstract interface class AssetReaderState { /// The [InputTracker] that this reader records reads to; or `null` if it does /// not have one. InputTracker? get inputTracker; + + /// The [AssetPathProvider] associated with this reader, or `null` if it does + /// not have one. + AssetPathProvider? get assetPathProvider; } diff --git a/build_runner_core/CHANGELOG.md b/build_runner_core/CHANGELOG.md index 132711b87..acddb6da6 100644 --- a/build_runner_core/CHANGELOG.md +++ b/build_runner_core/CHANGELOG.md @@ -3,6 +3,7 @@ - Fix crash when running on assets ending in a dot. - Start using `package:build/src/internal.dart'. - Use `build_test` 3.0.0. +- Refactor `PathProvidingAssetReader` to `AssetPathProvider`. ## 8.0.0 diff --git a/build_runner_core/lib/src/asset/batch.dart b/build_runner_core/lib/src/asset/batch.dart index 7e904f37f..2863ddd01 100644 --- a/build_runner_core/lib/src/asset/batch.dart +++ b/build_runner_core/lib/src/asset/batch.dart @@ -6,6 +6,8 @@ import 'dart:async'; import 'dart:convert'; import 'package:build/build.dart'; +// ignore: implementation_imports +import 'package:build/src/internal.dart'; import 'package:glob/glob.dart'; import 'package:meta/meta.dart'; @@ -53,13 +55,12 @@ final class _FileSystemWriteBatch { /// they are flushed to the file system. (RunnerAssetReader, RunnerAssetWriter) wrapInBatch({ required RunnerAssetReader reader, - required PathProvidingAssetReader pathProvidingReader, required RunnerAssetWriter writer, }) { final batch = _FileSystemWriteBatch._(); return ( - BatchReader(reader, pathProvidingReader, batch), + BatchReader(reader, batch), BatchWriter(writer, batch), ); } @@ -74,12 +75,17 @@ final class _PendingFileState { @internal final class BatchReader extends AssetReader - implements RunnerAssetReader, PathProvidingAssetReader { + implements AssetReaderState, RunnerAssetReader { final RunnerAssetReader _inner; - final PathProvidingAssetReader _innerPathProviding; final _FileSystemWriteBatch _batch; - BatchReader(this._inner, this._innerPathProviding, this._batch); + BatchReader(this._inner, this._batch); + + @override + AssetPathProvider? get assetPathProvider => _inner.assetPathProvider; + + @override + InputTracker? get inputTracker => _inner.inputTracker; _PendingFileState? _stateFor(AssetId id) { return _batch._pendingWrites[id]; @@ -101,11 +107,6 @@ final class BatchReader extends AssetReader .where((asset) => _stateFor(asset)?.isDeleted != true); } - @override - String pathTo(AssetId id) { - return _innerPathProviding.pathTo(id); - } - @override Future> readAsBytes(AssetId id) async { if (_stateFor(id) case final state?) { diff --git a/build_runner_core/lib/src/asset/build_cache.dart b/build_runner_core/lib/src/asset/build_cache.dart index c9156fc1b..aaa7783b4 100644 --- a/build_runner_core/lib/src/asset/build_cache.dart +++ b/build_runner_core/lib/src/asset/build_cache.dart @@ -2,29 +2,39 @@ import 'dart:async'; import 'dart:convert'; import 'package:build/build.dart'; +// ignore: implementation_imports +import 'package:build/src/internal.dart'; import 'package:crypto/crypto.dart'; import 'package:glob/glob.dart'; import '../asset_graph/graph.dart'; import '../asset_graph/node.dart'; import '../util/constants.dart'; -import 'reader.dart'; import 'writer.dart'; /// Wraps an [AssetReader] and translates reads for generated files into reads /// from the build cache directory -class BuildCacheReader implements AssetReader { - final AssetGraph _assetGraph; +class BuildCacheReader implements AssetReader, AssetReaderState { + @override + final AssetPathProvider? assetPathProvider; + final AssetReader _delegate; + final AssetGraph _assetGraph; final String _rootPackage; - BuildCacheReader._(this._delegate, this._assetGraph, this._rootPackage); + BuildCacheReader( + AssetReader delegate, AssetGraph assetGraph, String rootPackage) + : _delegate = delegate, + _assetGraph = assetGraph, + _rootPackage = rootPackage, + assetPathProvider = delegate.assetPathProvider == null + ? null + : OverlayAssetPathProvider( + delegate: delegate.assetPathProvider!, + overlay: (id) => _cacheLocation(id, assetGraph, rootPackage)); - factory BuildCacheReader( - AssetReader delegate, AssetGraph assetGraph, String rootPackage) => - delegate is PathProvidingAssetReader - ? _PathProvidingBuildCacheReader._(delegate, assetGraph, rootPackage) - : BuildCacheReader._(delegate, assetGraph, rootPackage); + @override + InputTracker? get inputTracker => _delegate.inputTracker; @override Future canRead(AssetId id) => @@ -48,21 +58,6 @@ class BuildCacheReader implements AssetReader { 'Asset globbing should be done per phase with the AssetGraph'); } -class _PathProvidingBuildCacheReader extends BuildCacheReader - implements PathProvidingAssetReader { - @override - PathProvidingAssetReader get _delegate => - super._delegate as PathProvidingAssetReader; - - _PathProvidingBuildCacheReader._(PathProvidingAssetReader super.delegate, - super.assetGraph, super.rootPackage) - : super._(); - - @override - String pathTo(AssetId id) => - _delegate.pathTo(_cacheLocation(id, _assetGraph, _rootPackage)); -} - class BuildCacheWriter implements RunnerAssetWriter { final AssetGraph _assetGraph; final RunnerAssetWriter _delegate; diff --git a/build_runner_core/lib/src/asset/cache.dart b/build_runner_core/lib/src/asset/cache.dart index 363bec1cd..c6a083a18 100644 --- a/build_runner_core/lib/src/asset/cache.dart +++ b/build_runner_core/lib/src/asset/cache.dart @@ -7,18 +7,19 @@ import 'dart:convert'; import 'dart:typed_data'; import 'package:build/build.dart'; +// ignore: implementation_imports +import 'package:build/src/internal.dart'; import 'package:crypto/crypto.dart'; import 'package:glob/glob.dart'; import 'lru_cache.dart'; -import 'reader.dart'; /// An [AssetReader] that caches all results from the delegate. /// /// Assets are cached until [invalidate] is invoked. /// /// Does not implement [findAssets]. -class CachingAssetReader implements AssetReader { +class CachingAssetReader implements AssetReader, AssetReaderState { /// Cached results of [readAsBytes]. final _bytesContentCache = LruCache>( 1024 * 1024, @@ -46,12 +47,13 @@ class CachingAssetReader implements AssetReader { final AssetReader _delegate; - CachingAssetReader._(this._delegate); + CachingAssetReader(this._delegate); - factory CachingAssetReader(AssetReader delegate) => - delegate is PathProvidingAssetReader - ? _PathProvidingCachingAssetReader._(delegate) - : CachingAssetReader._(delegate); + @override + AssetPathProvider? get assetPathProvider => _delegate.assetPathProvider; + + @override + InputTracker? get inputTracker => _delegate.inputTracker; @override Future canRead(AssetId id) => @@ -111,17 +113,3 @@ class CachingAssetReader implements AssetReader { } } } - -/// A version of a [CachingAssetReader] that implements -/// [PathProvidingAssetReader]. -class _PathProvidingCachingAssetReader extends CachingAssetReader - implements PathProvidingAssetReader { - @override - PathProvidingAssetReader get _delegate => - super._delegate as PathProvidingAssetReader; - - _PathProvidingCachingAssetReader._(super.delegate) : super._(); - - @override - String pathTo(AssetId id) => _delegate.pathTo(id); -} diff --git a/build_runner_core/lib/src/asset/file_based.dart b/build_runner_core/lib/src/asset/file_based.dart index e3e239dee..a57a0f85b 100644 --- a/build_runner_core/lib/src/asset/file_based.dart +++ b/build_runner_core/lib/src/asset/file_based.dart @@ -6,6 +6,8 @@ import 'dart:convert'; import 'dart:io'; import 'package:build/build.dart'; +// ignore: implementation_imports +import 'package:build/src/internal.dart'; import 'package:glob/glob.dart'; import 'package:glob/list_local_fs.dart'; import 'package:path/path.dart' as path; @@ -21,11 +23,17 @@ final _descriptorPool = Pool(32); /// Basic [AssetReader] which uses a [PackageGraph] to look up where to read /// files from disk. class FileBasedAssetReader extends AssetReader - implements RunnerAssetReader, PathProvidingAssetReader { + implements AssetReaderState, RunnerAssetReader { final PackageGraph packageGraph; FileBasedAssetReader(this.packageGraph); + @override + AssetPathProvider? get assetPathProvider => packageGraph; + + @override + InputTracker? get inputTracker => null; + @override Future canRead(AssetId id) => _descriptorPool.withResource(() => _fileFor(id, packageGraph).exists()); @@ -55,9 +63,6 @@ class FileBasedAssetReader extends AssetReader .cast() .map((file) => _fileToAssetId(file, packageNode)); } - - @override - String pathTo(AssetId id) => _filePathFor(id, packageGraph); } /// Creates an [AssetId] for [file], which is a part of [packageNode]. @@ -112,18 +117,9 @@ class FileBasedAssetWriter implements RunnerAssetWriter { Future completeBuild() async {} } -/// Returns the path to [id] for a given [packageGraph]. -String _filePathFor(AssetId id, PackageGraph packageGraph) { - var package = packageGraph[id.package]; - if (package == null) { - throw PackageNotFoundException(id.package); - } - return path.join(package.path, id.path); -} - /// Returns a [File] for [id] given [packageGraph]. File _fileFor(AssetId id, PackageGraph packageGraph) { - return File(_filePathFor(id, packageGraph)); + return File(packageGraph.pathFor(id)); } /// Returns a `File` for the asset reference by [id] given [packageGraph]. diff --git a/build_runner_core/lib/src/asset/reader.dart b/build_runner_core/lib/src/asset/reader.dart index dd7dbd9c8..54018ba4d 100644 --- a/build_runner_core/lib/src/asset/reader.dart +++ b/build_runner_core/lib/src/asset/reader.dart @@ -19,11 +19,6 @@ import '../util/async.dart'; /// A [RunnerAssetReader] must implement [MultiPackageAssetReader]. abstract class RunnerAssetReader implements MultiPackageAssetReader {} -/// An [AssetReader] that can provide actual paths to assets on disk. -abstract class PathProvidingAssetReader implements AssetReader { - String pathTo(AssetId id); -} - /// Describes if and how a [SingleStepReader] should read an [AssetId]. class Readability { final bool canRead; @@ -80,6 +75,9 @@ class SingleStepReader implements AssetReader, AssetReaderState { this._primaryPackage, this._isReadableNode, this._checkInvalidInput, [this._getGlobNode, this._writtenAssets]); + @override + AssetPathProvider? get assetPathProvider => _delegate.assetPathProvider; + /// Checks whether [id] can be read by this step - attempting to build the /// asset if necessary. /// diff --git a/build_runner_core/lib/src/environment/create_merged_dir.dart b/build_runner_core/lib/src/environment/create_merged_dir.dart index 60b25b98d..76f434c0a 100644 --- a/build_runner_core/lib/src/environment/create_merged_dir.dart +++ b/build_runner_core/lib/src/environment/create_merged_dir.dart @@ -7,11 +7,12 @@ import 'dart:convert'; import 'dart:io'; import 'package:build/build.dart'; +// ignore: implementation_imports +import 'package:build/src/internal.dart'; import 'package:logging/logging.dart'; import 'package:path/path.dart' as p; import 'package:pool/pool.dart'; -import '../asset/reader.dart'; import '../environment/build_environment.dart'; import '../generate/build_directory.dart'; import '../generate/finalized_assets_view.dart'; @@ -35,7 +36,7 @@ Future createMergedOutputDirectories( AssetReader reader, FinalizedAssetsView finalizedAssetsView, bool outputSymlinksOnly) async { - if (outputSymlinksOnly && reader is! PathProvidingAssetReader) { + if (outputSymlinksOnly && reader.assetPathProvider == null) { _logger.severe( 'The current environment does not support symlinks, but symlinks were ' 'requested.'); @@ -229,7 +230,7 @@ Future _writeAsset( await Link(_filePathFor(outputDir, outputId)).create( // We assert at the top of `createMergedOutputDirectories` that the // reader implements this type when requesting symlinks. - (reader as PathProvidingAssetReader).pathTo(id), + reader.assetPathProvider!.pathFor(id), recursive: true); } else { await _writeAsBytes(outputDir, outputId, await reader.readAsBytes(id)); diff --git a/build_runner_core/lib/src/environment/io_environment.dart b/build_runner_core/lib/src/environment/io_environment.dart index 4ca30ab5a..9ad50e749 100644 --- a/build_runner_core/lib/src/environment/io_environment.dart +++ b/build_runner_core/lib/src/environment/io_environment.dart @@ -55,10 +55,7 @@ class IOEnvironment implements BuildEnvironment { var (reader, writer) = lowResourcesMode ? (fileReader, fileWriter) - : wrapInBatch( - reader: fileReader, - pathProvidingReader: fileReader, - writer: fileWriter); + : wrapInBatch(reader: fileReader, writer: fileWriter); return IOEnvironment._(reader, writer, assumeTty == true || _canPrompt(), outputSymlinksOnly, packageGraph); diff --git a/build_runner_core/lib/src/package_graph/package_graph.dart b/build_runner_core/lib/src/package_graph/package_graph.dart index 1f4cd474a..e930ac7eb 100644 --- a/build_runner_core/lib/src/package_graph/package_graph.dart +++ b/build_runner_core/lib/src/package_graph/package_graph.dart @@ -4,6 +4,9 @@ import 'dart:io'; +import 'package:build/build.dart'; +// ignore: implementation_imports +import 'package:build/src/internal.dart'; import 'package:package_config/package_config.dart'; import 'package:path/path.dart' as p; import 'package:yaml/yaml.dart'; @@ -16,7 +19,7 @@ final _sdkPackageNode = PackageNode(r'$sdk', sdkPath, DependencyType.hosted, null); /// A graph of the package dependencies for an application. -class PackageGraph { +class PackageGraph implements AssetPathProvider { /// The root application package. final PackageNode root; @@ -167,6 +170,15 @@ class PackageGraph { /// Shorthand to get a package by name. PackageNode? operator [](String packageName) => allPackages[packageName]; + @override + String pathFor(AssetId id) { + var package = this[id.package]; + if (package == null) { + throw PackageNotFoundException(id.package); + } + return p.join(package.path, id.path); + } + @override String toString() { var buffer = StringBuffer(); diff --git a/build_runner_core/test/asset/batch_test.dart b/build_runner_core/test/asset/batch_test.dart index 04a5f797b..7bdf4caaa 100644 --- a/build_runner_core/test/asset/batch_test.dart +++ b/build_runner_core/test/asset/batch_test.dart @@ -8,7 +8,6 @@ import 'dart:io'; import 'package:build/build.dart'; import 'package:build_runner_core/build_runner_core.dart'; - import 'package:glob/glob.dart'; import 'package:package_config/package_config_types.dart'; import 'package:test/test.dart'; @@ -24,10 +23,7 @@ void main() { final fileReader = FileBasedAssetReader(packageGraph); final fileWriter = FileBasedAssetWriter(packageGraph); - (reader, writer) = wrapInBatch( - reader: fileReader, - pathProvidingReader: fileReader, - writer: fileWriter); + (reader, writer) = wrapInBatch(reader: fileReader, writer: fileWriter); }); test('delays writes until end', () async { diff --git a/build_test/CHANGELOG.md b/build_test/CHANGELOG.md index 44143ebfb..5df465f44 100644 --- a/build_test/CHANGELOG.md +++ b/build_test/CHANGELOG.md @@ -13,6 +13,7 @@ in-memory reader instead of providing multiple readers. - Support checks on reader state after a build action in `resolveSources`. - Start using `package:build/src/internal.dart`. +- Refactor `PathProvidingAssetReader` to `AssetPathProvider` ## 2.2.3 diff --git a/build_test/lib/src/in_memory_reader.dart b/build_test/lib/src/in_memory_reader.dart index 19c5cbbde..8bd5a7513 100644 --- a/build_test/lib/src/in_memory_reader.dart +++ b/build_test/lib/src/in_memory_reader.dart @@ -42,8 +42,8 @@ class InMemoryAssetReaderWriter extends AssetReader {Map? sourceAssets, this.rootPackage}) : assets = _assetsAsBytes(sourceAssets); - /// Create a new asset reader backed by [assets]. - InMemoryAssetReaderWriter.shareAssetCache(this.assets, {this.rootPackage}); + @override + AssetPathProvider? get assetPathProvider => null; static Map> _assetsAsBytes(Map? assets) { if (assets == null || assets.isEmpty) {