-
Notifications
You must be signed in to change notification settings - Fork 210
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Use batches for builds * Add changelog entry to build runner * Review feedback aAnother attempt for #3321, my [previous PR](#3418) for this got stale, this one is a bit simpler. This attempts to solve the issue of `build_runner` and external tools watching for file system events (like the analysis server) not playing too well together. At the moment, a common issue is that, as a user runs `build_runner build` and the generated assets are written as they are generated, the analysis server will see many file changes and keep re-analyzing sources in an incomplete state. By making `build_runner` cache pending writes in memory and then flushing them after a completed build, we'll likely get all changes to disk before they're picked up by external tools, reducing friction. I've implemented this at a fairly low level (the raw file readers and writers making up the default `IOEnvironment`). They have to opt-in to batches by checking for the presence of a zone key when reading or writing assets. We support batches by default when not in low-resources mode. A batch runs in a zone wrapping a whole build run. A todo is that I need to write integration tests for this, but it would be good to get a review for the overall approach before completing this. Closes #3321. * Bump version of build_runner_core * Make batch private, fix dependency resolution * Start fixing tests * Add lints dependency for out-of-workspace pkg
- Loading branch information
Showing
19 changed files
with
348 additions
and
21 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
// Copyright (c) 2024, 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 'dart:async'; | ||
import 'dart:convert'; | ||
|
||
import 'package:build/build.dart'; | ||
import 'package:glob/glob.dart'; | ||
import 'package:meta/meta.dart'; | ||
|
||
import '../environment/io_environment.dart'; | ||
import 'reader.dart'; | ||
import 'writer.dart'; | ||
|
||
/// A batch of file system writes that should be committed at once instead of | ||
/// when [AssetWriter.writeAsBytes] or [AssetWriter.writeAsString] is called. | ||
/// | ||
/// During a typical build run emitting generated files one-by-one, it's | ||
/// possible that other running tools such as an analysis server will have to | ||
/// re-analyze incomplete states multiple times. | ||
/// By storing pending outputs in memory first and then committing them at the | ||
/// end of the build, we have a better view over that needs to happen. | ||
/// | ||
/// The default [IOEnvironment] uses readers and writes that are batch-aware | ||
/// outside of low-memory mode. | ||
final class _FileSystemWriteBatch { | ||
final Map<AssetId, _PendingFileState> _pendingWrites = {}; | ||
|
||
_FileSystemWriteBatch._(); | ||
|
||
Future<void> completeWrites(RunnerAssetWriter writer) async { | ||
await Future.wait(_pendingWrites.keys.map((id) async { | ||
final pending = _pendingWrites[id]!; | ||
|
||
if (pending.content case final content?) { | ||
await writer.writeAsBytes(id, content); | ||
} else { | ||
await writer.delete(id); | ||
} | ||
})); | ||
|
||
_pendingWrites.clear(); | ||
} | ||
} | ||
|
||
/// Wraps a pair of a [RunnerAssetReader] with path-prividing capabilities and | ||
/// a [RunnerAssetWriter] into a pair of readers and writers that will | ||
/// internally buffer writes and only flush them in | ||
/// [RunnerAssetWriter.completeBuild]. | ||
/// | ||
/// The returned reader will see pending writes by the returned writer before | ||
/// 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), | ||
BatchWriter(writer, batch), | ||
); | ||
} | ||
|
||
final class _PendingFileState { | ||
final List<int>? content; | ||
|
||
const _PendingFileState(this.content); | ||
|
||
bool get isDeleted => content == null; | ||
} | ||
|
||
@internal | ||
final class BatchReader extends AssetReader | ||
implements RunnerAssetReader, PathProvidingAssetReader { | ||
final RunnerAssetReader _inner; | ||
final PathProvidingAssetReader _innerPathProviding; | ||
final _FileSystemWriteBatch _batch; | ||
|
||
BatchReader(this._inner, this._innerPathProviding, this._batch); | ||
|
||
_PendingFileState? _stateFor(AssetId id) { | ||
return _batch._pendingWrites[id]; | ||
} | ||
|
||
@override | ||
Future<bool> canRead(AssetId id) async { | ||
if (_stateFor(id) case final state?) { | ||
return !state.isDeleted; | ||
} else { | ||
return await _inner.canRead(id); | ||
} | ||
} | ||
|
||
@override | ||
Stream<AssetId> findAssets(Glob glob, {String? package}) { | ||
return _inner | ||
.findAssets(glob, package: package) | ||
.where((asset) => _stateFor(asset)?.isDeleted != true); | ||
} | ||
|
||
@override | ||
String pathTo(AssetId id) { | ||
return _innerPathProviding.pathTo(id); | ||
} | ||
|
||
@override | ||
Future<List<int>> readAsBytes(AssetId id) async { | ||
if (_stateFor(id) case final state?) { | ||
if (state.isDeleted) { | ||
throw AssetNotFoundException(id); | ||
} else { | ||
return state.content!; | ||
} | ||
} else { | ||
return await _inner.readAsBytes(id); | ||
} | ||
} | ||
|
||
@override | ||
Future<String> readAsString(AssetId id, {Encoding encoding = utf8}) async { | ||
if (_stateFor(id) case final state?) { | ||
if (state.isDeleted) { | ||
throw AssetNotFoundException(id); | ||
} else { | ||
return encoding.decode(state.content!); | ||
} | ||
} else { | ||
return await _inner.readAsString(id, encoding: encoding); | ||
} | ||
} | ||
} | ||
|
||
@internal | ||
final class BatchWriter extends RunnerAssetWriter { | ||
final RunnerAssetWriter _inner; | ||
final _FileSystemWriteBatch _batch; | ||
|
||
BatchWriter(this._inner, this._batch); | ||
|
||
@override | ||
Future delete(AssetId id) async { | ||
_batch._pendingWrites[id] = const _PendingFileState(null); | ||
} | ||
|
||
@override | ||
Future<void> writeAsBytes(AssetId id, List<int> bytes) async { | ||
_batch._pendingWrites[id] = _PendingFileState(bytes); | ||
} | ||
|
||
@override | ||
Future<void> writeAsString(AssetId id, String contents, | ||
{Encoding encoding = utf8}) async { | ||
_batch._pendingWrites[id] = _PendingFileState(encoding.encode(contents)); | ||
} | ||
|
||
@override | ||
Future<void> completeBuild() async { | ||
await _batch.completeWrites(_inner); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.