diff --git a/build_runner/test/generate/watch_test.dart b/build_runner/test/generate/watch_test.dart index 3aa32d496..43f96da30 100644 --- a/build_runner/test/generate/watch_test.dart +++ b/build_runner/test/generate/watch_test.dart @@ -426,6 +426,10 @@ void main() { cachedGraph, equalsAssetGraph(expectedGraph, checkPreviousInputsDigest: false), ); + expect( + cachedGraph.allPostProcessBuildStepOutputs, + expectedGraph.allPostProcessBuildStepOutputs, + ); }); test('ignores events from nested packages', () async { diff --git a/build_runner/test/server/asset_handler_test.dart b/build_runner/test/server/asset_handler_test.dart index c5a8bae5a..1c092abba 100644 --- a/build_runner/test/server/asset_handler_test.dart +++ b/build_runner/test/server/asset_handler_test.dart @@ -10,6 +10,7 @@ import 'package:build_runner/src/server/server.dart'; import 'package:build_runner_core/build_runner_core.dart'; import 'package:build_runner_core/src/asset_graph/graph.dart'; import 'package:build_runner_core/src/asset_graph/node.dart'; +import 'package:build_runner_core/src/asset_graph/post_process_build_step_id.dart'; import 'package:build_runner_core/src/generate/build_phases.dart'; import 'package:build_runner_core/src/generate/options.dart'; import 'package:build_runner_core/src/package_graph/target_graph.dart'; @@ -48,9 +49,11 @@ void main() { void addAsset(String id, String content, {bool deleted = false}) { var node = makeAssetNode(id, [], computeDigest(AssetId.parse(id), 'a')); if (deleted) { - node = node.rebuild( - (b) => b..deletedBy.add(node.id.addExtension('.post_anchor.1')), - ); + node = node.rebuild((b) { + b.deletedBy.add( + PostProcessBuildStepId(input: node.id, actionNumber: 1), + ); + }); } graph.add(node); delegate.testing.writeString(node.id, content); diff --git a/build_runner/test/server/serve_handler_test.dart b/build_runner/test/server/serve_handler_test.dart index 5e1798dae..0de05ee44 100644 --- a/build_runner/test/server/serve_handler_test.dart +++ b/build_runner/test/server/serve_handler_test.dart @@ -15,6 +15,7 @@ import 'package:build_runner/src/server/server.dart'; import 'package:build_runner_core/build_runner_core.dart'; import 'package:build_runner_core/src/asset_graph/graph.dart'; import 'package:build_runner_core/src/asset_graph/node.dart'; +import 'package:build_runner_core/src/asset_graph/post_process_build_step_id.dart'; import 'package:build_runner_core/src/generate/build_phases.dart'; import 'package:build_runner_core/src/generate/options.dart'; import 'package:build_runner_core/src/generate/performance_tracker.dart'; @@ -139,9 +140,11 @@ void main() { void addSource(String id, String content, {bool deleted = false}) { var node = makeAssetNode(id, [], computeDigest(AssetId.parse(id), content)); if (deleted) { - node = node.rebuild( - (b) => b..deletedBy.add(node.id.addExtension('.post_anchor.1')), - ); + node = node.rebuild((b) { + b.deletedBy.add( + PostProcessBuildStepId(input: node.id, actionNumber: 1), + ); + }); } assetGraph.add(node); readerWriter.testing.writeString(node.id, content); diff --git a/build_runner_core/CHANGELOG.md b/build_runner_core/CHANGELOG.md index 01ee2ab90..f721bda37 100644 --- a/build_runner_core/CHANGELOG.md +++ b/build_runner_core/CHANGELOG.md @@ -27,6 +27,8 @@ - Add `reportUnusedAssetsForInput` to `BuildOptions`, to listen for when a builder notifies that an asset is unused. - Use `LibraryCycleGraphLoader` to load transitive deps for analysis. +- Track post process builder outputs separately from the main graph Instead of + in `postProcessAnchor` nodes. ## 8.0.0 diff --git a/build_runner_core/lib/src/asset_graph/graph.dart b/build_runner_core/lib/src/asset_graph/graph.dart index 258d0f1d1..bc4bf8e22 100644 --- a/build_runner_core/lib/src/asset_graph/graph.dart +++ b/build_runner_core/lib/src/asset_graph/graph.dart @@ -14,6 +14,7 @@ import 'package:build/src/internal.dart'; import 'package:built_collection/built_collection.dart'; import 'package:crypto/crypto.dart'; import 'package:glob/glob.dart'; +import 'package:meta/meta.dart'; import 'package:package_config/package_config.dart'; import 'package:watcher/watcher.dart'; @@ -23,6 +24,8 @@ import '../generate/phase.dart'; import '../util/constants.dart'; import 'exceptions.dart'; import 'node.dart'; +import 'post_process_build_step_id.dart'; +import 'serializers.dart'; part 'serialization.dart'; @@ -46,6 +49,14 @@ class AssetGraph implements GeneratedAssetHider { final BuiltMap packageLanguageVersions; + /// All post process build steps outputs, indexed by package then + /// [PostProcessBuildStepId]. + /// + /// Created with empty outputs at the start of the build if it's a new build + /// step; or deserialized with previous build outputs if it has run before. + final Map>> + _postProcessBuildStepOutputs = {}; + AssetGraph._( this.buildPhasesDigest, this.dartVersion, @@ -93,6 +104,10 @@ class AssetGraph implements GeneratedAssetHider { List serialize() => _AssetGraphSerializer(this).serialize(); + @visibleForTesting + Map>> + get allPostProcessBuildStepOutputs => _postProcessBuildStepOutputs; + /// Checks if [id] exists in the graph. bool contains(AssetId id) => _nodesByPackage[id.package]?.containsKey(id.path) ?? false; @@ -243,9 +258,6 @@ class AssetGraph implements GeneratedAssetHider { var node = get(id); if (node == null) return removedIds; removedIds.add(id); - for (var anchor in node.anchorOutputs.toList()) { - _removeRecursive(anchor, removedIds: removedIds); - } for (var output in node.primaryOutputs.toList()) { _removeRecursive(output, removedIds: removedIds); } @@ -290,6 +302,12 @@ class AssetGraph implements GeneratedAssetHider { if (node.type != NodeType.missingSource) { _nodesByPackage[id.package]!.remove(id.path); } + + // Remove post build action applications with removed assets as inputs. + for (final packageOutputs in _postProcessBuildStepOutputs.values) { + packageOutputs.removeWhere((id, _) => removedIds!.contains(id.input)); + } + return removedIds; } @@ -301,6 +319,31 @@ class AssetGraph implements GeneratedAssetHider { Iterable packageNodes(String package) => _nodesByPackage[package]?.values ?? []; + /// All the post process build steps for `package`. + Iterable postProcessBuildStepIds({ + required String package, + }) => _postProcessBuildStepOutputs[package]?.keys ?? const []; + + /// Creates or updates state for a [PostProcessBuildStepId]. + void updatePostProcessBuildStep( + PostProcessBuildStepId buildStepId, { + required Set outputs, + }) { + _postProcessBuildStepOutputs.putIfAbsent( + buildStepId.input.package, + () => {}, + )[buildStepId] = + outputs; + } + + /// Gets outputs of a [PostProcessBuildStepId]. + /// + /// These are set using [updatePostProcessBuildStep] during the build, then + /// used to clean up prior outputs in the next build. + Iterable postProcessBuildStepOutputs(PostProcessBuildStepId action) { + return _postProcessBuildStepOutputs[action.input.package]![action]!; + } + /// All the generated outputs in the graph. Iterable get outputs => allNodes.where((n) => n.type == NodeType.generated).map((n) => n.id); @@ -547,7 +590,7 @@ class AssetGraph implements GeneratedAssetHider { ), ); } else if (phase is PostBuildPhase) { - _addPostBuildPhaseAnchors(phase, allInputs); + _addPostBuildActionApplications(phase, allInputs); } else { throw StateError('Unrecognized phase type $phase'); } @@ -600,27 +643,21 @@ class AssetGraph implements GeneratedAssetHider { return phaseOutputs; } - /// Adds all [AssetNode.postProcessAnchor]s for [phase] given [allInputs]; - /// - /// Does not return anything because [AssetNode.postProcessAnchor]s are - /// synthetic and should not be treated as inputs. - void _addPostBuildPhaseAnchors(PostBuildPhase phase, Set allInputs) { - var actionNum = 0; + /// Adds all [PostProcessBuildStepId]s for [phase] given [allInputs]; + void _addPostBuildActionApplications( + PostBuildPhase phase, + Set allInputs, + ) { + var actionNumber = 0; for (var action in phase.builderActions) { var inputs = allInputs.where((input) => _actionMatches(action, input)); for (var input in inputs) { - var buildOptionsNodeId = builderOptionsIdForAction(action, actionNum); - var anchor = AssetNode.postProcessAnchorForInputAndAction( - input, - actionNum, - buildOptionsNodeId, + updatePostProcessBuildStep( + PostProcessBuildStepId(input: input, actionNumber: actionNumber), + outputs: {}, ); - add(anchor); - updateNode(input, (nodeBuilder) { - nodeBuilder.anchorOutputs.add(anchor.id); - }); } - actionNum++; + actionNumber++; } } @@ -762,11 +799,14 @@ class AssetGraph implements GeneratedAssetHider { Digest computeBuilderOptionsDigest(BuilderOptions options) => md5.convert(utf8.encode(json.encode(options.config))); -AssetId builderOptionsIdForAction(BuildAction action, int actionNum) { +AssetId builderOptionsIdForAction(BuildAction action, int actionNumber) { if (action is InBuildPhase) { - return AssetId(action.package, 'Phase$actionNum.builderOptions'); + return AssetId(action.package, 'Phase$actionNumber.builderOptions'); } else if (action is PostBuildAction) { - return AssetId(action.package, 'PostPhase$actionNum.builderOptions'); + return PostProcessBuildStepId.builderOptionsIdFor( + package: action.package, + actionNumber: actionNumber, + ); } else { throw StateError('Unsupported action type $action'); } diff --git a/build_runner_core/lib/src/asset_graph/node.dart b/build_runner_core/lib/src/asset_graph/node.dart index 8f8327701..615183fc0 100644 --- a/build_runner_core/lib/src/asset_graph/node.dart +++ b/build_runner_core/lib/src/asset_graph/node.dart @@ -10,7 +10,7 @@ import 'package:built_value/built_value.dart'; import 'package:built_value/serializer.dart'; import 'package:crypto/crypto.dart'; -import '../generate/phase.dart'; +import 'post_process_build_step_id.dart'; part 'node.g.dart'; @@ -23,7 +23,6 @@ class NodeType extends EnumClass { static const NodeType glob = _$glob; static const NodeType internal = _$internal; static const NodeType placeholder = _$placeholder; - static const NodeType postProcessAnchor = _$postProcessAnchor; static const NodeType source = _$source; static const NodeType missingSource = _$missingSource; @@ -54,10 +53,6 @@ abstract class AssetNode implements Built { /// [AssetNode.glob]. GlobNodeState? get globNodeState; - /// Additional node configuration for an - /// [AssetNode.postProcessAnchorNodeConfiguration]. - PostProcessAnchorNodeConfiguration? get postProcessAnchorNodeConfiguration; - /// The assets that any [Builder] in the build graph declares it may output /// when run on this asset. BuiltSet get primaryOutputs; @@ -66,19 +61,14 @@ abstract class AssetNode implements Built { /// which reads this asset. BuiltSet get outputs; - /// The [AssetId]s of all [AssetNode.postProcessAnchor] assets for which this - /// node is the primary input. - BuiltSet get anchorOutputs; - /// The [Digest] for this node in its last known state. /// /// May be `null` if this asset has no outputs, or if it doesn't actually /// exist. Digest? get lastKnownDigest; - /// The IDs of the [AssetNode.postProcessAnchor] for post process builder - /// which requested to delete this asset. - BuiltSet get deletedBy; + /// The `PostProcessBuildStep`s which requested to delete this asset. + BuiltSet get deletedBy; /// Whether this asset is a normal, readable file. /// @@ -223,35 +213,6 @@ abstract class AssetNode implements Built { static AssetId createGlobNodeId(String package, String glob, int phaseNum) => AssetId(package, 'glob.$phaseNum.${base64.encode(utf8.encode(glob))}'); - /// A [primaryInput] to a [PostBuildAction]. - /// - /// The [outputs] of this node are the individual outputs created for the - /// [primaryInput] during the [PostBuildAction] at index [actionNumber]. - factory AssetNode.postProcessAnchor( - AssetId id, { - required AssetId primaryInput, - required int actionNumber, - required AssetId builderOptionsId, - Digest? previousInputsDigest, - }) => AssetNode((b) { - b.id = id; - b.type = NodeType.postProcessAnchor; - b.postProcessAnchorNodeConfiguration.actionNumber = actionNumber; - b.postProcessAnchorNodeConfiguration.builderOptionsId = builderOptionsId; - b.postProcessAnchorNodeConfiguration.primaryInput = primaryInput; - }); - - factory AssetNode.postProcessAnchorForInputAndAction( - AssetId primaryInput, - int actionNumber, - AssetId builderOptionsId, - ) => AssetNode.postProcessAnchor( - primaryInput.addExtension('.post_anchor.$actionNumber'), - primaryInput: primaryInput, - actionNumber: actionNumber, - builderOptionsId: builderOptionsId, - ); - AssetNode._() { // Check that configuration and state fields are non-null exactly when the // node is of the corresponding type. @@ -277,10 +238,6 @@ abstract class AssetNode implements Built { globNodeConfiguration != null, globNodeState != null, ); - check( - type == NodeType.postProcessAnchor, - postProcessAnchorNodeConfiguration != null, - ); } } @@ -387,27 +344,6 @@ abstract class GlobNodeState GlobNodeState._(); } -// Additional configuration for an [AssetNode.postProcessAnchor]. -abstract class PostProcessAnchorNodeConfiguration - implements - Built< - PostProcessAnchorNodeConfiguration, - PostProcessAnchorNodeConfigurationBuilder - > { - static Serializer get serializer => - _$postProcessAnchorNodeConfigurationSerializer; - - int get actionNumber; - AssetId get builderOptionsId; - AssetId get primaryInput; - - PostProcessAnchorNodeConfiguration._(); - - factory PostProcessAnchorNodeConfiguration( - void Function(PostProcessAnchorNodeConfigurationBuilder) updates, - ) = _$PostProcessAnchorNodeConfiguration; -} - /// Work that needs doing for a node that tracks its inputs. class PendingBuildAction extends EnumClass { static Serializer get serializer => @@ -423,59 +359,3 @@ class PendingBuildAction extends EnumClass { static PendingBuildAction valueOf(String name) => _$pendingBuildActionValueOf(name); } - -@SerializersFor([AssetNode]) -final Serializers serializers = - (_$serializers.toBuilder() - ..add(AssetIdSerializer()) - ..add(DigestSerializer())) - .build(); - -/// Serializer for [AssetId]. -/// -/// It would also work to make `AssetId` a `built_value` class, but there's -/// little benefit and it's nicer to keep codegen local to this package. -class AssetIdSerializer implements PrimitiveSerializer { - @override - Iterable get types => [AssetId]; - - @override - String get wireName => 'AssetId'; - - @override - AssetId deserialize( - Serializers serializers, - Object serialized, { - FullType specifiedType = FullType.unspecified, - }) => AssetId.parse(serialized as String); - - @override - Object serialize( - Serializers serializers, - AssetId object, { - FullType specifiedType = FullType.unspecified, - }) => object.toString(); -} - -/// Serializer for [Digest]. -class DigestSerializer implements PrimitiveSerializer { - @override - Iterable get types => [Digest]; - - @override - String get wireName => 'Digest'; - - @override - Digest deserialize( - Serializers serializers, - Object serialized, { - FullType specifiedType = FullType.unspecified, - }) => Digest(base64.decode(serialized as String)); - - @override - Object serialize( - Serializers serializers, - Digest object, { - FullType specifiedType = FullType.unspecified, - }) => base64.encode(object.bytes); -} diff --git a/build_runner_core/lib/src/asset_graph/node.g.dart b/build_runner_core/lib/src/asset_graph/node.g.dart index d0769c757..e9a0ac844 100644 --- a/build_runner_core/lib/src/asset_graph/node.g.dart +++ b/build_runner_core/lib/src/asset_graph/node.g.dart @@ -11,7 +11,6 @@ const NodeType _$generated = const NodeType._('generated'); const NodeType _$glob = const NodeType._('glob'); const NodeType _$internal = const NodeType._('internal'); const NodeType _$placeholder = const NodeType._('placeholder'); -const NodeType _$postProcessAnchor = const NodeType._('postProcessAnchor'); const NodeType _$source = const NodeType._('source'); const NodeType _$missingSource = const NodeType._('missingSource'); @@ -27,8 +26,6 @@ NodeType _$nodeTypeValueOf(String name) { return _$internal; case 'placeholder': return _$placeholder; - case 'postProcessAnchor': - return _$postProcessAnchor; case 'source': return _$source; case 'missingSource': @@ -45,7 +42,6 @@ final BuiltSet _$nodeTypeValues = _$glob, _$internal, _$placeholder, - _$postProcessAnchor, _$source, _$missingSource, ]); @@ -76,45 +72,6 @@ final BuiltSet _$pendingBuildActionValues = _$build, ]); -Serializers _$serializers = - (new Serializers().toBuilder() - ..add(AssetNode.serializer) - ..add(GeneratedNodeConfiguration.serializer) - ..add(GeneratedNodeState.serializer) - ..add(GlobNodeConfiguration.serializer) - ..add(GlobNodeState.serializer) - ..add(NodeType.serializer) - ..add(PendingBuildAction.serializer) - ..add(PostProcessAnchorNodeConfiguration.serializer) - ..addBuilderFactory( - const FullType(BuiltSet, const [const FullType(AssetId)]), - () => new SetBuilder(), - ) - ..addBuilderFactory( - const FullType(BuiltSet, const [const FullType(AssetId)]), - () => new SetBuilder(), - ) - ..addBuilderFactory( - const FullType(BuiltList, const [const FullType(AssetId)]), - () => new ListBuilder(), - ) - ..addBuilderFactory( - const FullType(BuiltSet, const [const FullType(AssetId)]), - () => new SetBuilder(), - ) - ..addBuilderFactory( - const FullType(BuiltSet, const [const FullType(AssetId)]), - () => new SetBuilder(), - ) - ..addBuilderFactory( - const FullType(BuiltSet, const [const FullType(AssetId)]), - () => new SetBuilder(), - ) - ..addBuilderFactory( - const FullType(BuiltSet, const [const FullType(AssetId)]), - () => new SetBuilder(), - )) - .build(); Serializer _$nodeTypeSerializer = new _$NodeTypeSerializer(); Serializer _$assetNodeSerializer = new _$AssetNodeSerializer(); Serializer _$generatedNodeConfigurationSerializer = @@ -125,9 +82,6 @@ Serializer _$globNodeConfigurationSerializer = new _$GlobNodeConfigurationSerializer(); Serializer _$globNodeStateSerializer = new _$GlobNodeStateSerializer(); -Serializer -_$postProcessAnchorNodeConfigurationSerializer = - new _$PostProcessAnchorNodeConfigurationSerializer(); Serializer _$pendingBuildActionSerializer = new _$PendingBuildActionSerializer(); @@ -186,18 +140,11 @@ class _$AssetNodeSerializer implements StructuredSerializer { const FullType(AssetId), ]), ), - 'anchorOutputs', - serializers.serialize( - object.anchorOutputs, - specifiedType: const FullType(BuiltSet, const [ - const FullType(AssetId), - ]), - ), 'deletedBy', serializers.serialize( object.deletedBy, specifiedType: const FullType(BuiltSet, const [ - const FullType(AssetId), + const FullType(PostProcessBuildStepId), ]), ), ]; @@ -246,17 +193,6 @@ class _$AssetNodeSerializer implements StructuredSerializer { ), ); } - value = object.postProcessAnchorNodeConfiguration; - if (value != null) { - result - ..add('postProcessAnchorNodeConfiguration') - ..add( - serializers.serialize( - value, - specifiedType: const FullType(PostProcessAnchorNodeConfiguration), - ), - ); - } value = object.lastKnownDigest; if (value != null) { result @@ -334,17 +270,6 @@ class _$AssetNodeSerializer implements StructuredSerializer { as GlobNodeState, ); break; - case 'postProcessAnchorNodeConfiguration': - result.postProcessAnchorNodeConfiguration.replace( - serializers.deserialize( - value, - specifiedType: const FullType( - PostProcessAnchorNodeConfiguration, - ), - )! - as PostProcessAnchorNodeConfiguration, - ); - break; case 'primaryOutputs': result.primaryOutputs.replace( serializers.deserialize( @@ -367,17 +292,6 @@ class _$AssetNodeSerializer implements StructuredSerializer { as BuiltSet, ); break; - case 'anchorOutputs': - result.anchorOutputs.replace( - serializers.deserialize( - value, - specifiedType: const FullType(BuiltSet, const [ - const FullType(AssetId), - ]), - )! - as BuiltSet, - ); - break; case 'lastKnownDigest': result.lastKnownDigest = serializers.deserialize( @@ -391,7 +305,7 @@ class _$AssetNodeSerializer implements StructuredSerializer { serializers.deserialize( value, specifiedType: const FullType(BuiltSet, const [ - const FullType(AssetId), + const FullType(PostProcessBuildStepId), ]), )! as BuiltSet, @@ -750,88 +664,6 @@ class _$GlobNodeStateSerializer implements StructuredSerializer { } } -class _$PostProcessAnchorNodeConfigurationSerializer - implements StructuredSerializer { - @override - final Iterable types = const [ - PostProcessAnchorNodeConfiguration, - _$PostProcessAnchorNodeConfiguration, - ]; - @override - final String wireName = 'PostProcessAnchorNodeConfiguration'; - - @override - Iterable serialize( - Serializers serializers, - PostProcessAnchorNodeConfiguration object, { - FullType specifiedType = FullType.unspecified, - }) { - final result = [ - 'actionNumber', - serializers.serialize( - object.actionNumber, - specifiedType: const FullType(int), - ), - 'builderOptionsId', - serializers.serialize( - object.builderOptionsId, - specifiedType: const FullType(AssetId), - ), - 'primaryInput', - serializers.serialize( - object.primaryInput, - specifiedType: const FullType(AssetId), - ), - ]; - - return result; - } - - @override - PostProcessAnchorNodeConfiguration deserialize( - Serializers serializers, - Iterable serialized, { - FullType specifiedType = FullType.unspecified, - }) { - final result = new PostProcessAnchorNodeConfigurationBuilder(); - - final iterator = serialized.iterator; - while (iterator.moveNext()) { - final key = iterator.current! as String; - iterator.moveNext(); - final Object? value = iterator.current; - switch (key) { - case 'actionNumber': - result.actionNumber = - serializers.deserialize( - value, - specifiedType: const FullType(int), - )! - as int; - break; - case 'builderOptionsId': - result.builderOptionsId = - serializers.deserialize( - value, - specifiedType: const FullType(AssetId), - )! - as AssetId; - break; - case 'primaryInput': - result.primaryInput = - serializers.deserialize( - value, - specifiedType: const FullType(AssetId), - )! - as AssetId; - break; - } - } - - return result.build(); - } -} - class _$PendingBuildActionSerializer implements PrimitiveSerializer { @override @@ -868,17 +700,13 @@ class _$AssetNode extends AssetNode { @override final GlobNodeState? globNodeState; @override - final PostProcessAnchorNodeConfiguration? postProcessAnchorNodeConfiguration; - @override final BuiltSet primaryOutputs; @override final BuiltSet outputs; @override - final BuiltSet anchorOutputs; - @override final Digest? lastKnownDigest; @override - final BuiltSet deletedBy; + final BuiltSet deletedBy; factory _$AssetNode([void Function(AssetNodeBuilder)? updates]) => (new AssetNodeBuilder()..update(updates))._build(); @@ -890,10 +718,8 @@ class _$AssetNode extends AssetNode { this.generatedNodeState, this.globNodeConfiguration, this.globNodeState, - this.postProcessAnchorNodeConfiguration, required this.primaryOutputs, required this.outputs, - required this.anchorOutputs, this.lastKnownDigest, required this.deletedBy, }) : super._() { @@ -905,11 +731,6 @@ class _$AssetNode extends AssetNode { 'primaryOutputs', ); BuiltValueNullFieldError.checkNotNull(outputs, r'AssetNode', 'outputs'); - BuiltValueNullFieldError.checkNotNull( - anchorOutputs, - r'AssetNode', - 'anchorOutputs', - ); BuiltValueNullFieldError.checkNotNull(deletedBy, r'AssetNode', 'deletedBy'); } @@ -930,11 +751,8 @@ class _$AssetNode extends AssetNode { generatedNodeState == other.generatedNodeState && globNodeConfiguration == other.globNodeConfiguration && globNodeState == other.globNodeState && - postProcessAnchorNodeConfiguration == - other.postProcessAnchorNodeConfiguration && primaryOutputs == other.primaryOutputs && outputs == other.outputs && - anchorOutputs == other.anchorOutputs && lastKnownDigest == other.lastKnownDigest && deletedBy == other.deletedBy; } @@ -948,10 +766,8 @@ class _$AssetNode extends AssetNode { _$hash = $jc(_$hash, generatedNodeState.hashCode); _$hash = $jc(_$hash, globNodeConfiguration.hashCode); _$hash = $jc(_$hash, globNodeState.hashCode); - _$hash = $jc(_$hash, postProcessAnchorNodeConfiguration.hashCode); _$hash = $jc(_$hash, primaryOutputs.hashCode); _$hash = $jc(_$hash, outputs.hashCode); - _$hash = $jc(_$hash, anchorOutputs.hashCode); _$hash = $jc(_$hash, lastKnownDigest.hashCode); _$hash = $jc(_$hash, deletedBy.hashCode); _$hash = $jf(_$hash); @@ -967,13 +783,8 @@ class _$AssetNode extends AssetNode { ..add('generatedNodeState', generatedNodeState) ..add('globNodeConfiguration', globNodeConfiguration) ..add('globNodeState', globNodeState) - ..add( - 'postProcessAnchorNodeConfiguration', - postProcessAnchorNodeConfiguration, - ) ..add('primaryOutputs', primaryOutputs) ..add('outputs', outputs) - ..add('anchorOutputs', anchorOutputs) ..add('lastKnownDigest', lastKnownDigest) ..add('deletedBy', deletedBy)) .toString(); @@ -1018,19 +829,6 @@ class AssetNodeBuilder implements Builder { set globNodeState(GlobNodeStateBuilder? globNodeState) => _$this._globNodeState = globNodeState; - PostProcessAnchorNodeConfigurationBuilder? - _postProcessAnchorNodeConfiguration; - PostProcessAnchorNodeConfigurationBuilder - get postProcessAnchorNodeConfiguration => - _$this._postProcessAnchorNodeConfiguration ??= - new PostProcessAnchorNodeConfigurationBuilder(); - set postProcessAnchorNodeConfiguration( - PostProcessAnchorNodeConfigurationBuilder? - postProcessAnchorNodeConfiguration, - ) => - _$this._postProcessAnchorNodeConfiguration = - postProcessAnchorNodeConfiguration; - SetBuilder? _primaryOutputs; SetBuilder get primaryOutputs => _$this._primaryOutputs ??= new SetBuilder(); @@ -1042,21 +840,15 @@ class AssetNodeBuilder implements Builder { _$this._outputs ??= new SetBuilder(); set outputs(SetBuilder? outputs) => _$this._outputs = outputs; - SetBuilder? _anchorOutputs; - SetBuilder get anchorOutputs => - _$this._anchorOutputs ??= new SetBuilder(); - set anchorOutputs(SetBuilder? anchorOutputs) => - _$this._anchorOutputs = anchorOutputs; - Digest? _lastKnownDigest; Digest? get lastKnownDigest => _$this._lastKnownDigest; set lastKnownDigest(Digest? lastKnownDigest) => _$this._lastKnownDigest = lastKnownDigest; - SetBuilder? _deletedBy; - SetBuilder get deletedBy => - _$this._deletedBy ??= new SetBuilder(); - set deletedBy(SetBuilder? deletedBy) => + SetBuilder? _deletedBy; + SetBuilder get deletedBy => + _$this._deletedBy ??= new SetBuilder(); + set deletedBy(SetBuilder? deletedBy) => _$this._deletedBy = deletedBy; AssetNodeBuilder(); @@ -1070,11 +862,8 @@ class AssetNodeBuilder implements Builder { _generatedNodeState = $v.generatedNodeState?.toBuilder(); _globNodeConfiguration = $v.globNodeConfiguration?.toBuilder(); _globNodeState = $v.globNodeState?.toBuilder(); - _postProcessAnchorNodeConfiguration = - $v.postProcessAnchorNodeConfiguration?.toBuilder(); _primaryOutputs = $v.primaryOutputs.toBuilder(); _outputs = $v.outputs.toBuilder(); - _anchorOutputs = $v.anchorOutputs.toBuilder(); _lastKnownDigest = $v.lastKnownDigest; _deletedBy = $v.deletedBy.toBuilder(); _$v = null; @@ -1112,11 +901,8 @@ class AssetNodeBuilder implements Builder { generatedNodeState: _generatedNodeState?.build(), globNodeConfiguration: _globNodeConfiguration?.build(), globNodeState: _globNodeState?.build(), - postProcessAnchorNodeConfiguration: - _postProcessAnchorNodeConfiguration?.build(), primaryOutputs: primaryOutputs.build(), outputs: outputs.build(), - anchorOutputs: anchorOutputs.build(), lastKnownDigest: lastKnownDigest, deletedBy: deletedBy.build(), ); @@ -1131,14 +917,10 @@ class AssetNodeBuilder implements Builder { _globNodeConfiguration?.build(); _$failedField = 'globNodeState'; _globNodeState?.build(); - _$failedField = 'postProcessAnchorNodeConfiguration'; - _postProcessAnchorNodeConfiguration?.build(); _$failedField = 'primaryOutputs'; primaryOutputs.build(); _$failedField = 'outputs'; outputs.build(); - _$failedField = 'anchorOutputs'; - anchorOutputs.build(); _$failedField = 'deletedBy'; deletedBy.build(); @@ -1755,155 +1537,4 @@ class GlobNodeStateBuilder } } -class _$PostProcessAnchorNodeConfiguration - extends PostProcessAnchorNodeConfiguration { - @override - final int actionNumber; - @override - final AssetId builderOptionsId; - @override - final AssetId primaryInput; - - factory _$PostProcessAnchorNodeConfiguration([ - void Function(PostProcessAnchorNodeConfigurationBuilder)? updates, - ]) => - (new PostProcessAnchorNodeConfigurationBuilder()..update(updates)) - ._build(); - - _$PostProcessAnchorNodeConfiguration._({ - required this.actionNumber, - required this.builderOptionsId, - required this.primaryInput, - }) : super._() { - BuiltValueNullFieldError.checkNotNull( - actionNumber, - r'PostProcessAnchorNodeConfiguration', - 'actionNumber', - ); - BuiltValueNullFieldError.checkNotNull( - builderOptionsId, - r'PostProcessAnchorNodeConfiguration', - 'builderOptionsId', - ); - BuiltValueNullFieldError.checkNotNull( - primaryInput, - r'PostProcessAnchorNodeConfiguration', - 'primaryInput', - ); - } - - @override - PostProcessAnchorNodeConfiguration rebuild( - void Function(PostProcessAnchorNodeConfigurationBuilder) updates, - ) => (toBuilder()..update(updates)).build(); - - @override - PostProcessAnchorNodeConfigurationBuilder toBuilder() => - new PostProcessAnchorNodeConfigurationBuilder()..replace(this); - - @override - bool operator ==(Object other) { - if (identical(other, this)) return true; - return other is PostProcessAnchorNodeConfiguration && - actionNumber == other.actionNumber && - builderOptionsId == other.builderOptionsId && - primaryInput == other.primaryInput; - } - - @override - int get hashCode { - var _$hash = 0; - _$hash = $jc(_$hash, actionNumber.hashCode); - _$hash = $jc(_$hash, builderOptionsId.hashCode); - _$hash = $jc(_$hash, primaryInput.hashCode); - _$hash = $jf(_$hash); - return _$hash; - } - - @override - String toString() { - return (newBuiltValueToStringHelper(r'PostProcessAnchorNodeConfiguration') - ..add('actionNumber', actionNumber) - ..add('builderOptionsId', builderOptionsId) - ..add('primaryInput', primaryInput)) - .toString(); - } -} - -class PostProcessAnchorNodeConfigurationBuilder - implements - Builder< - PostProcessAnchorNodeConfiguration, - PostProcessAnchorNodeConfigurationBuilder - > { - _$PostProcessAnchorNodeConfiguration? _$v; - - int? _actionNumber; - int? get actionNumber => _$this._actionNumber; - set actionNumber(int? actionNumber) => _$this._actionNumber = actionNumber; - - AssetId? _builderOptionsId; - AssetId? get builderOptionsId => _$this._builderOptionsId; - set builderOptionsId(AssetId? builderOptionsId) => - _$this._builderOptionsId = builderOptionsId; - - AssetId? _primaryInput; - AssetId? get primaryInput => _$this._primaryInput; - set primaryInput(AssetId? primaryInput) => - _$this._primaryInput = primaryInput; - - PostProcessAnchorNodeConfigurationBuilder(); - - PostProcessAnchorNodeConfigurationBuilder get _$this { - final $v = _$v; - if ($v != null) { - _actionNumber = $v.actionNumber; - _builderOptionsId = $v.builderOptionsId; - _primaryInput = $v.primaryInput; - _$v = null; - } - return this; - } - - @override - void replace(PostProcessAnchorNodeConfiguration other) { - ArgumentError.checkNotNull(other, 'other'); - _$v = other as _$PostProcessAnchorNodeConfiguration; - } - - @override - void update( - void Function(PostProcessAnchorNodeConfigurationBuilder)? updates, - ) { - if (updates != null) updates(this); - } - - @override - PostProcessAnchorNodeConfiguration build() => _build(); - - _$PostProcessAnchorNodeConfiguration _build() { - final _$result = - _$v ?? - new _$PostProcessAnchorNodeConfiguration._( - actionNumber: BuiltValueNullFieldError.checkNotNull( - actionNumber, - r'PostProcessAnchorNodeConfiguration', - 'actionNumber', - ), - builderOptionsId: BuiltValueNullFieldError.checkNotNull( - builderOptionsId, - r'PostProcessAnchorNodeConfiguration', - 'builderOptionsId', - ), - primaryInput: BuiltValueNullFieldError.checkNotNull( - primaryInput, - r'PostProcessAnchorNodeConfiguration', - 'primaryInput', - ), - ); - replace(_$result); - return _$result; - } -} - // ignore_for_file: deprecated_member_use_from_same_package,type=lint diff --git a/build_runner_core/lib/src/asset_graph/post_process_build_step_id.dart b/build_runner_core/lib/src/asset_graph/post_process_build_step_id.dart new file mode 100644 index 000000000..eb7bc6f19 --- /dev/null +++ b/build_runner_core/lib/src/asset_graph/post_process_build_step_id.dart @@ -0,0 +1,38 @@ +// Copyright (c) 2016, 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 'package:build/build.dart' hide Builder; +import 'package:built_value/built_value.dart'; +import 'package:built_value/serializer.dart'; + +part 'post_process_build_step_id.g.dart'; + +/// Identifies a `PostProcessBuildStep` within a build: the application of a +/// `PostProcessBuilder` to one input. +abstract class PostProcessBuildStepId + implements Built { + static Serializer get serializer => + _$postProcessBuildStepIdSerializer; + + AssetId get input; + + /// The [actionNumber] identifying which `PostProcessBuilder` runs. + int get actionNumber; + + PostProcessBuildStepId._(); + + factory PostProcessBuildStepId({ + required AssetId input, + required int actionNumber, + }) = _$PostProcessBuildStepId._; + + /// The [AssetId] used to store the hash of the build options for the step. + AssetId get builderOptionsId => + builderOptionsIdFor(package: input.package, actionNumber: actionNumber); + + static AssetId builderOptionsIdFor({ + required String package, + required int actionNumber, + }) => AssetId(package, 'PostPhase$actionNumber.builderOptions'); +} diff --git a/build_runner_core/lib/src/asset_graph/post_process_build_step_id.g.dart b/build_runner_core/lib/src/asset_graph/post_process_build_step_id.g.dart new file mode 100644 index 000000000..5d99a4a52 --- /dev/null +++ b/build_runner_core/lib/src/asset_graph/post_process_build_step_id.g.dart @@ -0,0 +1,198 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'post_process_build_step_id.dart'; + +// ************************************************************************** +// BuiltValueGenerator +// ************************************************************************** + +Serializer _$postProcessBuildStepIdSerializer = + new _$PostProcessBuildStepIdSerializer(); + +class _$PostProcessBuildStepIdSerializer + implements StructuredSerializer { + @override + final Iterable types = const [ + PostProcessBuildStepId, + _$PostProcessBuildStepId, + ]; + @override + final String wireName = 'PostProcessBuildStepId'; + + @override + Iterable serialize( + Serializers serializers, + PostProcessBuildStepId object, { + FullType specifiedType = FullType.unspecified, + }) { + final result = [ + 'input', + serializers.serialize( + object.input, + specifiedType: const FullType(AssetId), + ), + 'actionNumber', + serializers.serialize( + object.actionNumber, + specifiedType: const FullType(int), + ), + ]; + + return result; + } + + @override + PostProcessBuildStepId deserialize( + Serializers serializers, + Iterable serialized, { + FullType specifiedType = FullType.unspecified, + }) { + final result = new PostProcessBuildStepIdBuilder(); + + final iterator = serialized.iterator; + while (iterator.moveNext()) { + final key = iterator.current! as String; + iterator.moveNext(); + final Object? value = iterator.current; + switch (key) { + case 'input': + result.input = + serializers.deserialize( + value, + specifiedType: const FullType(AssetId), + )! + as AssetId; + break; + case 'actionNumber': + result.actionNumber = + serializers.deserialize( + value, + specifiedType: const FullType(int), + )! + as int; + break; + } + } + + return result.build(); + } +} + +class _$PostProcessBuildStepId extends PostProcessBuildStepId { + @override + final AssetId input; + @override + final int actionNumber; + + factory _$PostProcessBuildStepId([ + void Function(PostProcessBuildStepIdBuilder)? updates, + ]) => (new PostProcessBuildStepIdBuilder()..update(updates))._build(); + + _$PostProcessBuildStepId._({required this.input, required this.actionNumber}) + : super._() { + BuiltValueNullFieldError.checkNotNull( + input, + r'PostProcessBuildStepId', + 'input', + ); + BuiltValueNullFieldError.checkNotNull( + actionNumber, + r'PostProcessBuildStepId', + 'actionNumber', + ); + } + + @override + PostProcessBuildStepId rebuild( + void Function(PostProcessBuildStepIdBuilder) updates, + ) => (toBuilder()..update(updates)).build(); + + @override + PostProcessBuildStepIdBuilder toBuilder() => + new PostProcessBuildStepIdBuilder()..replace(this); + + @override + bool operator ==(Object other) { + if (identical(other, this)) return true; + return other is PostProcessBuildStepId && + input == other.input && + actionNumber == other.actionNumber; + } + + @override + int get hashCode { + var _$hash = 0; + _$hash = $jc(_$hash, input.hashCode); + _$hash = $jc(_$hash, actionNumber.hashCode); + _$hash = $jf(_$hash); + return _$hash; + } + + @override + String toString() { + return (newBuiltValueToStringHelper(r'PostProcessBuildStepId') + ..add('input', input) + ..add('actionNumber', actionNumber)) + .toString(); + } +} + +class PostProcessBuildStepIdBuilder + implements Builder { + _$PostProcessBuildStepId? _$v; + + AssetId? _input; + AssetId? get input => _$this._input; + set input(AssetId? input) => _$this._input = input; + + int? _actionNumber; + int? get actionNumber => _$this._actionNumber; + set actionNumber(int? actionNumber) => _$this._actionNumber = actionNumber; + + PostProcessBuildStepIdBuilder(); + + PostProcessBuildStepIdBuilder get _$this { + final $v = _$v; + if ($v != null) { + _input = $v.input; + _actionNumber = $v.actionNumber; + _$v = null; + } + return this; + } + + @override + void replace(PostProcessBuildStepId other) { + ArgumentError.checkNotNull(other, 'other'); + _$v = other as _$PostProcessBuildStepId; + } + + @override + void update(void Function(PostProcessBuildStepIdBuilder)? updates) { + if (updates != null) updates(this); + } + + @override + PostProcessBuildStepId build() => _build(); + + _$PostProcessBuildStepId _build() { + final _$result = + _$v ?? + new _$PostProcessBuildStepId._( + input: BuiltValueNullFieldError.checkNotNull( + input, + r'PostProcessBuildStepId', + 'input', + ), + actionNumber: BuiltValueNullFieldError.checkNotNull( + actionNumber, + r'PostProcessBuildStepId', + 'actionNumber', + ), + ); + replace(_$result); + return _$result; + } +} + +// ignore_for_file: deprecated_member_use_from_same_package,type=lint diff --git a/build_runner_core/lib/src/asset_graph/serialization.dart b/build_runner_core/lib/src/asset_graph/serialization.dart index 6f2bac872..be6d45335 100644 --- a/build_runner_core/lib/src/asset_graph/serialization.dart +++ b/build_runner_core/lib/src/asset_graph/serialization.dart @@ -8,7 +8,7 @@ part of 'graph.dart'; /// /// This should be incremented any time the serialize/deserialize formats /// change. -const _version = 26; +const _version = 27; /// Deserializes an [AssetGraph] from a [Map]. class _AssetGraphDeserializer { @@ -67,6 +67,19 @@ class _AssetGraphDeserializer { graph._add(_deserializeAssetNode(serializedItem as List)); } + final postProcessOutputs = + serializers.deserialize( + _serializedGraph['postProcessOutputs'], + specifiedType: postProcessBuildStepOutputsFullType, + ) + as Map>>; + + for (final postProcessOutputsForPackage in postProcessOutputs.values) { + for (final entry in postProcessOutputsForPackage.entries) { + graph.updatePostProcessBuildStep(entry.key, outputs: entry.value); + } + } + return graph; } @@ -112,6 +125,10 @@ class _AssetGraphSerializer { .map((pkg, version) => MapEntry(pkg, version?.toString())) .toMap(), 'enabledExperiments': _graph.enabledExperiments.toList(), + 'postProcessOutputs': serializers.serialize( + _graph._postProcessBuildStepOutputs, + specifiedType: postProcessBuildStepOutputsFullType, + ), }; return utf8.encode(json.encode(result)); } diff --git a/build_runner_core/lib/src/asset_graph/serializers.dart b/build_runner_core/lib/src/asset_graph/serializers.dart new file mode 100644 index 000000000..40e8b316f --- /dev/null +++ b/build_runner_core/lib/src/asset_graph/serializers.dart @@ -0,0 +1,233 @@ +// 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 'dart:convert'; + +import 'package:build/build.dart' show AssetId, PostProcessBuildStep; +import 'package:built_collection/built_collection.dart'; +import 'package:built_value/serializer.dart'; +import 'package:crypto/crypto.dart'; + +import 'node.dart'; +import 'post_process_build_step_id.dart'; + +part 'serializers.g.dart'; + +final postProcessBuildStepOutputsInnerFullType = const FullType(Map, [ + FullType(PostProcessBuildStep), + FullType(Set, [FullType(AssetId)]), +]); + +final postProcessBuildStepOutputsFullType = FullType(Map, [ + const FullType(String), + postProcessBuildStepOutputsInnerFullType, +]); + +@SerializersFor([AssetNode]) +final Serializers serializers = + (_$serializers.toBuilder() + ..add(AssetIdSerializer()) + ..add(DigestSerializer()) + ..add(MapSerializer()) + ..add(SetSerializer()) + ..addBuilderFactory( + const FullType(Set, [FullType(AssetId)]), + () => {}, + ) + ..addBuilderFactory( + postProcessBuildStepOutputsInnerFullType, + () => >{}, + ) + ..addBuilderFactory( + postProcessBuildStepOutputsFullType, + () => >>{}, + )) + .build(); + +/// Serializer for [AssetId]. +/// +/// It would also work to make `AssetId` a `built_value` class, but there's +/// little benefit and it's nicer to keep codegen local to this package. +class AssetIdSerializer implements PrimitiveSerializer { + @override + Iterable get types => [AssetId]; + + @override + String get wireName => 'AssetId'; + + @override + AssetId deserialize( + Serializers serializers, + Object serialized, { + FullType specifiedType = FullType.unspecified, + }) => AssetId.parse(serialized as String); + + @override + Object serialize( + Serializers serializers, + AssetId object, { + FullType specifiedType = FullType.unspecified, + }) => object.toString(); +} + +/// Serializer for [Digest]. +class DigestSerializer implements PrimitiveSerializer { + @override + Iterable get types => [Digest]; + + @override + String get wireName => 'Digest'; + + @override + Digest deserialize( + Serializers serializers, + Object serialized, { + FullType specifiedType = FullType.unspecified, + }) => Digest(base64.decode(serialized as String)); + + @override + Object serialize( + Serializers serializers, + Digest object, { + FullType specifiedType = FullType.unspecified, + }) => base64.encode(object.bytes); +} + +// TODO(davidmorgan): this is a fork of `BuiltMapSerializer` from `built_value` +// adapted for SDK maps, upstream it. +class MapSerializer implements StructuredSerializer { + final bool structured = true; + @override + final Iterable types = BuiltList([ + Map, + {}.runtimeType, + ]); + @override + final String wireName = 'map'; + + @override + Iterable serialize( + Serializers serializers, + Map builtMap, { + FullType specifiedType = FullType.unspecified, + }) { + var isUnderspecified = + specifiedType.isUnspecified || specifiedType.parameters.isEmpty; + if (!isUnderspecified) serializers.expectBuilder(specifiedType); + + var keyType = + specifiedType.parameters.isEmpty + ? FullType.unspecified + : specifiedType.parameters[0]; + var valueType = + specifiedType.parameters.isEmpty + ? FullType.unspecified + : specifiedType.parameters[1]; + + var result = []; + for (var key in builtMap.keys) { + result.add(serializers.serialize(key, specifiedType: keyType)); + final value = builtMap[key]; + result.add(serializers.serialize(value, specifiedType: valueType)); + } + return result; + } + + @override + Map deserialize( + Serializers serializers, + Iterable serialized, { + FullType specifiedType = FullType.unspecified, + }) { + var isUnderspecified = + specifiedType.isUnspecified || specifiedType.parameters.isEmpty; + + var keyType = + specifiedType.parameters.isEmpty + ? FullType.unspecified + : specifiedType.parameters[0]; + var valueType = + specifiedType.parameters.isEmpty + ? FullType.unspecified + : specifiedType.parameters[1]; + + var result = + isUnderspecified + ? {} + : serializers.newBuilder(specifiedType) as Map; + + if (serialized.length.isOdd) { + throw ArgumentError('odd length'); + } + + for (var i = 0; i != serialized.length; i += 2) { + final key = serializers.deserialize( + serialized.elementAt(i), + specifiedType: keyType, + ); + final value = serializers.deserialize( + serialized.elementAt(i + 1), + specifiedType: valueType, + ); + result[key] = value; + } + + return result; + } +} + +// TODO(davidmorgan): this is a fork of `BuiltSetSerializer` from `built_value` +// adapted for SDK sets, upstream it. +class SetSerializer implements StructuredSerializer { + final bool structured = true; + @override + final Iterable types = BuiltList([Set, {}.runtimeType]); + @override + final String wireName = 'set'; + + @override + Iterable serialize( + Serializers serializers, + Set builtSet, { + FullType specifiedType = FullType.unspecified, + }) { + var isUnderspecified = + specifiedType.isUnspecified || specifiedType.parameters.isEmpty; + if (!isUnderspecified) serializers.expectBuilder(specifiedType); + + var elementType = + specifiedType.parameters.isEmpty + ? FullType.unspecified + : specifiedType.parameters[0]; + + return builtSet.map( + (item) => serializers.serialize(item, specifiedType: elementType), + ); + } + + @override + Set deserialize( + Serializers serializers, + Iterable serialized, { + FullType specifiedType = FullType.unspecified, + }) { + var isUnderspecified = + specifiedType.isUnspecified || specifiedType.parameters.isEmpty; + + var elementType = + specifiedType.parameters.isEmpty + ? FullType.unspecified + : specifiedType.parameters[0]; + var result = + isUnderspecified + ? {} + : serializers.newBuilder(specifiedType) as Set; + + for (final item in serialized) { + result.add(serializers.deserialize(item, specifiedType: elementType)); + } + + return result; + } +} diff --git a/build_runner_core/lib/src/asset_graph/serializers.g.dart b/build_runner_core/lib/src/asset_graph/serializers.g.dart new file mode 100644 index 000000000..3107ed284 --- /dev/null +++ b/build_runner_core/lib/src/asset_graph/serializers.g.dart @@ -0,0 +1,47 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'serializers.dart'; + +// ************************************************************************** +// BuiltValueGenerator +// ************************************************************************** + +Serializers _$serializers = + (new Serializers().toBuilder() + ..add(AssetNode.serializer) + ..add(GeneratedNodeConfiguration.serializer) + ..add(GeneratedNodeState.serializer) + ..add(GlobNodeConfiguration.serializer) + ..add(GlobNodeState.serializer) + ..add(NodeType.serializer) + ..add(PendingBuildAction.serializer) + ..add(PostProcessBuildStepId.serializer) + ..addBuilderFactory( + const FullType(BuiltSet, const [const FullType(AssetId)]), + () => new SetBuilder(), + ) + ..addBuilderFactory( + const FullType(BuiltSet, const [const FullType(AssetId)]), + () => new SetBuilder(), + ) + ..addBuilderFactory( + const FullType(BuiltList, const [const FullType(AssetId)]), + () => new ListBuilder(), + ) + ..addBuilderFactory( + const FullType(BuiltSet, const [const FullType(AssetId)]), + () => new SetBuilder(), + ) + ..addBuilderFactory( + const FullType(BuiltSet, const [const FullType(AssetId)]), + () => new SetBuilder(), + ) + ..addBuilderFactory( + const FullType(BuiltSet, const [ + const FullType(PostProcessBuildStepId), + ]), + () => new SetBuilder(), + )) + .build(); + +// ignore_for_file: deprecated_member_use_from_same_package,type=lint diff --git a/build_runner_core/lib/src/generate/build.dart b/build_runner_core/lib/src/generate/build.dart index 5948f921d..6d2a3c15a 100644 --- a/build_runner_core/lib/src/generate/build.dart +++ b/build_runner_core/lib/src/generate/build.dart @@ -19,6 +19,7 @@ import '../asset/writer.dart'; import '../asset_graph/graph.dart'; import '../asset_graph/node.dart'; import '../asset_graph/optional_output_tracker.dart'; +import '../asset_graph/post_process_build_step_id.dart'; import '../changes/asset_updates.dart'; import '../environment/build_environment.dart'; import '../logging/build_for_input_logger.dart'; @@ -275,7 +276,7 @@ class Build { ); return _runBuilder(phaseNum, phase, primaryInputs); } else if (phase is PostBuildPhase) { - return _runPostProcessPhase(phaseNum, phase); + return _runPostBuildPhase(phaseNum, phase); } else { throw StateError('Unrecognized BuildPhase type $phase'); } @@ -525,14 +526,14 @@ class Build { }); } - Future> _runPostProcessPhase( + Future> _runPostBuildPhase( int phaseNum, PostBuildPhase phase, ) async { var actionNum = 0; var outputLists = await Future.wait( phase.builderActions.map( - (action) => _runPostProcessAction(phaseNum, actionNum++, action), + (action) => _runPostBuildAction(phaseNum, actionNum++, action), ), ); return outputLists.fold>( @@ -541,42 +542,34 @@ class Build { ); } - Future> _runPostProcessAction( + Future> _runPostBuildAction( int phaseNum, int actionNum, PostBuildAction action, ) async { final outputs = []; - for (final node in assetGraph - .packageNodes(action.package) - .toList(growable: false)) { - if (node.type != NodeType.postProcessAnchor) continue; - final nodeConfiguration = node.postProcessAnchorNodeConfiguration!; - if (nodeConfiguration.actionNumber != actionNum) continue; - final inputNode = assetGraph.get(nodeConfiguration.primaryInput)!; + for (final buildStepId in assetGraph.postProcessBuildStepIds( + package: action.package, + )) { + if (buildStepId.actionNumber != actionNum) continue; + final inputNode = assetGraph.get(buildStepId.input)!; if (inputNode.type == NodeType.source || inputNode.type == NodeType.generated && inputNode.generatedNodeState!.isSuccessfulFreshOutput) { outputs.addAll( - await _runPostProcessBuilderForAnchor( - phaseNum, - actionNum, - action.builder, - node, - ), + await _runPostProcessBuildStep(phaseNum, action.builder, buildStepId), ); } } return outputs; } - Future> _runPostProcessBuilderForAnchor( + Future> _runPostProcessBuildStep( int phaseNumber, - int actionNum, PostProcessBuilder builder, - AssetNode anchorNode, + PostProcessBuildStepId postProcessBuildStepId, ) async { - var input = anchorNode.postProcessAnchorNodeConfiguration!.primaryInput; + var input = postProcessBuildStepId.input; var inputNode = assetGraph.get(input)!; var readerWriter = SingleStepReaderWriter( runningBuild: RunningBuild( @@ -596,25 +589,26 @@ class Build { assetsWritten: {}, ); - if (!await _postProcessBuildShouldRun(anchorNode, readerWriter)) { + if (!await _postProcessBuildStepShouldRun( + postProcessBuildStepId, + readerWriter, + )) { return []; } // Clear input tracking accumulated during `_buildShouldRun`. readerWriter.inputTracker.clear(); - // The anchor node is an input. - readerWriter.inputTracker.add(anchorNode.id); // Clean out the impacts of the previous run. await FailureReporter.clean(phaseNumber, input); - await _cleanUpStaleOutputs(anchorNode.outputs); - for (final output in anchorNode.outputs.toList(growable: false)) { + final existingOutputs = assetGraph.postProcessBuildStepOutputs( + postProcessBuildStepId, + ); + await _cleanUpStaleOutputs(existingOutputs); + for (final output in existingOutputs) { assetGraph.remove(output); } - assetGraph.updateNode(anchorNode.id, (nodeBuilder) { - nodeBuilder.outputs.clear(); - }); assetGraph.updateNode(inputNode.id, (nodeBuilder) { - nodeBuilder.deletedBy.remove(anchorNode.id); + nodeBuilder.deletedBy.remove(postProcessBuildStepId); }); var actionDescription = '$builder on $input'; @@ -625,6 +619,7 @@ class Build { .putIfAbsent(phaseNumber, () => {}) .add(actionDescription); + final outputs = {}; await runPostProcessBuilder( builder, input, @@ -638,8 +633,7 @@ class Build { var node = AssetNode.generated( assetId, primaryInput: input, - builderOptionsId: - anchorNode.postProcessAnchorNodeConfiguration!.builderOptionsId, + builderOptionsId: postProcessBuildStepId.builderOptionsId, isHidden: true, phaseNumber: phaseNumber, wasOutput: true, @@ -647,9 +641,7 @@ class Build { pendingBuildAction: PendingBuildAction.none, ); assetGraph.add(node); - assetGraph.updateNode(anchorNode.id, (nodeBuilder) { - nodeBuilder.outputs.add(assetId); - }); + outputs.add(assetId); }, deleteAsset: (assetId) { if (!assetGraph.contains(assetId)) { @@ -662,12 +654,18 @@ class Build { ); } assetGraph.updateNode(assetId, (nodeBuilder) { - nodeBuilder.deletedBy.add(anchorNode.id); + nodeBuilder.deletedBy.add(postProcessBuildStepId); }); }, ).catchError((void _) { // Errors tracked through the logger }); + + assetGraph.updatePostProcessBuildStep( + postProcessBuildStepId, + outputs: outputs, + ); + actionsCompletedCount++; hungActionsHeartbeat.ping(); pendingActions[phaseNumber]!.remove(actionDescription); @@ -834,21 +832,21 @@ class Build { return false; } - /// Checks if a post process build should run based on [anchorNode]. - Future _postProcessBuildShouldRun( - AssetNode anchorNode, + /// Whether the post process build step [buildStepId] should run. + /// + /// It should run if its builder options changed or its input changed. + Future _postProcessBuildStepShouldRun( + PostProcessBuildStepId buildStepId, AssetReader reader, ) async { - final nodeConfiguration = anchorNode.postProcessAnchorNodeConfiguration!; - - final input = nodeConfiguration.primaryInput; + final input = buildStepId.input; final node = assetGraph.get(input)!; if (cleanBuild) { return true; } - final builderOptionsId = nodeConfiguration.builderOptionsId; + final builderOptionsId = buildStepId.builderOptionsId; if (changedInputs.contains(builderOptionsId)) { return true; } diff --git a/build_runner_core/test/asset/finalized_reader_test.dart b/build_runner_core/test/asset/finalized_reader_test.dart index 92b3d3402..7de27d50b 100644 --- a/build_runner_core/test/asset/finalized_reader_test.dart +++ b/build_runner_core/test/asset/finalized_reader_test.dart @@ -10,6 +10,7 @@ import 'package:build/build.dart'; import 'package:build_runner_core/build_runner_core.dart'; import 'package:build_runner_core/src/asset_graph/graph.dart'; import 'package:build_runner_core/src/asset_graph/node.dart'; +import 'package:build_runner_core/src/asset_graph/post_process_build_step_id.dart'; import 'package:build_runner_core/src/generate/build_phases.dart'; import 'package:build_runner_core/src/generate/options.dart'; import 'package:build_runner_core/src/generate/phase.dart'; @@ -52,7 +53,11 @@ void main() { ); deleted = deleted.rebuild( - (b) => b..deletedBy.add(deleted.id.addExtension('.post_anchor.1')), + (b) => + b + ..deletedBy.add( + PostProcessBuildStepId(input: notDeleted.id, actionNumber: 0), + ), ); graph diff --git a/build_runner_core/test/asset_graph/graph_test.dart b/build_runner_core/test/asset_graph/graph_test.dart index 6aec4eb2f..a7f2ae316 100644 --- a/build_runner_core/test/asset_graph/graph_test.dart +++ b/build_runner_core/test/asset_graph/graph_test.dart @@ -11,6 +11,7 @@ import 'package:build/build.dart'; import 'package:build_config/build_config.dart'; import 'package:build_runner_core/src/asset_graph/graph.dart'; import 'package:build_runner_core/src/asset_graph/node.dart'; +import 'package:build_runner_core/src/asset_graph/post_process_build_step_id.dart'; import 'package:build_runner_core/src/generate/build_phases.dart'; import 'package:build_runner_core/src/generate/phase.dart'; import 'package:crypto/crypto.dart'; @@ -112,13 +113,11 @@ void main() { lastKnownDigest: Digest([n]), ); graph.add(builderOptionsNode); - final anchorNode = AssetNode.postProcessAnchorForInputAndAction( - node.id, - n, - builderOptionsNode.id, + final postProcessBuildStep = PostProcessBuildStepId( + input: node.id, + actionNumber: n, ); - graph.add(anchorNode); - node = node.rebuild((b) => b..anchorOutputs.add(anchorNode.id)); + graph.updatePostProcessBuildStep(postProcessBuildStep, outputs: {}); for (var g = 0; g < 5 - n; g++) { var builderOptionsNode = AssetNode.builderOptions( makeAssetId(), @@ -150,7 +149,9 @@ void main() { (b) => b..outputs.add(generatedNode.id), ); if (g.isEven) { - node = node.rebuild((b) => b..deletedBy.add(anchorNode.id)); + node = node.rebuild( + (b) => b..deletedBy.add(postProcessBuildStep), + ); } var syntheticNode = AssetNode.missingSource( @@ -176,7 +177,11 @@ void main() { var encoded = graph.serialize(); var decoded = AssetGraph.deserialize(encoded); expect(decoded.failedOutputs, isNotEmpty); - expect(graph, equalsAssetGraph(decoded)); + expect(decoded, equalsAssetGraph(graph)); + expect( + decoded.allPostProcessBuildStepOutputs, + graph.allPostProcessBuildStepOutputs, + ); }); test( @@ -229,10 +234,9 @@ void main() { final builderOptionsId = makeAssetId('foo|Phase0.builderOptions'); final postBuilderOptionsId = makeAssetId('foo|PostPhase0.builderOptions'); final placeholders = placeholderIdsFor(fooPackageGraph); - final expectedAnchorNode = AssetNode.postProcessAnchorForInputAndAction( - primaryInputId, - 0, - postBuilderOptionsId, + final expectedBuildStepId = PostProcessBuildStepId( + input: primaryInputId, + actionNumber: 0, ); setUp(() async { @@ -258,11 +262,13 @@ void main() { primaryOutputId, internalId, builderOptionsId, - expectedAnchorNode.id, postBuilderOptionsId, ...placeholders, ]), ); + expect(graph.postProcessBuildStepIds(package: 'foo'), { + expectedBuildStepId, + }); var node = graph.get(primaryInputId)!; expect(node.primaryOutputs, [primaryOutputId]); expect(node.outputs, isEmpty); @@ -302,8 +308,9 @@ void main() { expect(postBuilderOptionsNode.type, NodeType.builderOptions); expect(postBuilderOptionsNode, isNotNull); expect(postBuilderOptionsNode.outputs, isEmpty); - var anchorNode = graph.get(expectedAnchorNode.id)!; - expect(anchorNode.type, NodeType.postProcessAnchor); + expect(graph.postProcessBuildStepIds(package: 'foo'), { + expectedBuildStepId, + }); }); group('updateAndInvalidate', () { @@ -317,12 +324,14 @@ void main() { digestReader, ); expect(graph.contains(AssetId('foo', 'new.txt.copy')), isTrue); - var newAnchor = AssetNode.postProcessAnchorForInputAndAction( - primaryInputId, - 0, - AssetId('foo', '\$builder_options'), + var newBuildStepId = PostProcessBuildStepId( + input: primaryInputId, + actionNumber: 0, + ); + expect( + graph.postProcessBuildStepIds(package: 'foo'), + contains(newBuildStepId), ); - expect(graph.contains(newAnchor.id), isTrue); }); test('delete old primary input', () async { @@ -339,7 +348,7 @@ void main() { expect(graph.contains(primaryInputId), isFalse); expect(graph.contains(primaryOutputId), isFalse); expect(deletes, equals([primaryOutputId])); - expect(graph.contains(expectedAnchorNode.id), isFalse); + expect(graph.postProcessBuildStepIds(package: 'foo'), isEmpty); }); test('modify primary input', () async { @@ -393,13 +402,14 @@ void main() { expect(graph.contains(syntheticOutputId), isTrue); expect(graph.get(syntheticOutputId)!.type, NodeType.generated); - var newAnchor = AssetNode.postProcessAnchorForInputAndAction( - syntheticId, - 0, - AssetId('foo', '\$builder_options'), + var newAnchor = PostProcessBuildStepId( + input: syntheticId, + actionNumber: 0, + ); + expect( + graph.postProcessBuildStepIds(package: 'foo'), + contains(newAnchor), ); - expect(graph.contains(newAnchor.id), isTrue); - expect(graph.get(newAnchor.id)!.type, NodeType.postProcessAnchor); }); test( diff --git a/build_runner_core/test/environment/create_merged_dir_test.dart b/build_runner_core/test/environment/create_merged_dir_test.dart index c019f768b..2a95c2f53 100644 --- a/build_runner_core/test/environment/create_merged_dir_test.dart +++ b/build_runner_core/test/environment/create_merged_dir_test.dart @@ -12,6 +12,7 @@ import 'package:build_runner_core/build_runner_core.dart'; import 'package:build_runner_core/src/asset_graph/graph.dart'; import 'package:build_runner_core/src/asset_graph/node.dart'; import 'package:build_runner_core/src/asset_graph/optional_output_tracker.dart'; +import 'package:build_runner_core/src/asset_graph/post_process_build_step_id.dart'; import 'package:build_runner_core/src/environment/create_merged_dir.dart'; import 'package:build_runner_core/src/generate/build_phases.dart'; import 'package:build_runner_core/src/generate/options.dart'; @@ -134,7 +135,9 @@ void main() { test('doesnt write deleted files', () async { var node = graph.get(AssetId('b', 'lib/c.txt.copy'))!; graph.updateNode(node.id, (nodeBuilder) { - nodeBuilder.deletedBy.add(node.id.addExtension('.post_anchor.1')); + nodeBuilder.deletedBy.add( + PostProcessBuildStepId(input: node.id, actionNumber: 1), + ); }); var success = await createMergedOutputDirectories( @@ -536,7 +539,10 @@ void main() { for (var remove in removes) { graph.updateNode(makeAssetId(remove), (nodeBuilder) { nodeBuilder.deletedBy.add( - makeAssetId(remove).addExtension('.post_anchor.1'), + PostProcessBuildStepId( + input: makeAssetId(remove), + actionNumber: 1, + ), ); }); } diff --git a/build_runner_core/test/generate/build_test.dart b/build_runner_core/test/generate/build_test.dart index 22462ba29..4eb959667 100644 --- a/build_runner_core/test/generate/build_test.dart +++ b/build_runner_core/test/generate/build_test.dart @@ -14,6 +14,7 @@ import 'package:build_config/build_config.dart'; import 'package:build_runner_core/build_runner_core.dart'; import 'package:build_runner_core/src/asset_graph/graph.dart'; import 'package:build_runner_core/src/asset_graph/node.dart'; +import 'package:build_runner_core/src/asset_graph/post_process_build_step_id.dart'; import 'package:build_runner_core/src/generate/build_phases.dart'; import 'package:build_runner_core/src/generate/options.dart' show defaultNonRootVisibleAssets; @@ -1326,15 +1327,13 @@ void main() { lastKnownDigest: computeBuilderOptionsDigest(defaultBuilderOptions), ); - var aAnchorNode = AssetNode.postProcessAnchorForInputAndAction( - aSourceNode.id, - 0, - postBuilderOptionsId, + var aPostProcessBuildStepId = PostProcessBuildStepId( + input: aSourceNode.id, + actionNumber: 0, ); - var bAnchorNode = AssetNode.postProcessAnchorForInputAndAction( - bSourceNode.id, - 0, - postBuilderOptionsId, + var bPostProcessBuildStepId = PostProcessBuildStepId( + input: bSourceNode.id, + actionNumber: 0, ); var aPostCopyNode = AssetNode.generated( @@ -1346,18 +1345,12 @@ void main() { isFailure: false, builderOptionsId: postBuilderOptionsId, lastKnownDigest: computeDigest(makeAssetId(r'$$a|web/a.txt.post'), 'a'), - inputs: [makeAssetId('a|web/a.txt'), aAnchorNode.id], + inputs: [makeAssetId('a|web/a.txt')], isHidden: true, ); // Note we don't expect this node to get added to the builder options node // outputs. - aSourceNode = aSourceNode.rebuild( - (b) => - b - ..outputs.add(aPostCopyNode.id) - ..anchorOutputs.add(aAnchorNode.id), - ); - aAnchorNode = aAnchorNode.rebuild((b) => b..outputs.add(aPostCopyNode.id)); + aSourceNode = aSourceNode.rebuild((b) => b..outputs.add(aPostCopyNode.id)); aSourceNode = aSourceNode.rebuild( (b) => b..primaryOutputs.add(aPostCopyNode.id), ); @@ -1371,18 +1364,12 @@ void main() { isFailure: false, builderOptionsId: postBuilderOptionsId, lastKnownDigest: computeDigest(makeAssetId(r'$$a|lib/b.txt.post'), 'b'), - inputs: [makeAssetId('a|lib/b.txt'), bAnchorNode.id], + inputs: [makeAssetId('a|lib/b.txt')], isHidden: true, ); // Note we don't expect this node to get added to the builder options node // outputs. - bSourceNode = bSourceNode.rebuild( - (b) => - b - ..outputs.add(bPostCopyNode.id) - ..anchorOutputs.add(bAnchorNode.id), - ); - bAnchorNode = bAnchorNode.rebuild((b) => b..outputs.add(bPostCopyNode.id)); + bSourceNode = bSourceNode.rebuild((b) => b..outputs.add(bPostCopyNode.id)); bSourceNode = bSourceNode.rebuild( (b) => b..primaryOutputs.add(bPostCopyNode.id), ); @@ -1396,8 +1383,14 @@ void main() { ..add(postBuilderOptionsNode) ..add(aPostCopyNode) ..add(bPostCopyNode) - ..add(aAnchorNode) - ..add(bAnchorNode); + ..updatePostProcessBuildStep( + aPostProcessBuildStepId, + outputs: {aPostCopyNode.id}, + ) + ..updatePostProcessBuildStep( + bPostProcessBuildStepId, + outputs: {bPostCopyNode.id}, + ); // TODO: We dont have a shared way of computing the combined input hashes // today, but eventually we should test those here too. @@ -1405,6 +1398,10 @@ void main() { cachedGraph, equalsAssetGraph(expectedGraph, checkPreviousInputsDigest: false), ); + expect( + cachedGraph.allPostProcessBuildStepOutputs, + expectedGraph.allPostProcessBuildStepOutputs, + ); }); test(