diff --git a/_test_common/lib/matchers.dart b/_test_common/lib/matchers.dart index 75705415c..5a3a17448 100644 --- a/_test_common/lib/matchers.dart +++ b/_test_common/lib/matchers.dart @@ -178,11 +178,10 @@ class _AssetGraphMatcher extends Matcher { final configuration = node.globNodeConfiguration!; final expectedConfiguration = expectedNode.globNodeConfiguration!; - if (configuration.glob.pattern != - expectedConfiguration.glob.pattern) { + if (configuration.glob != expectedConfiguration.glob) { matchState['glob of ${node.id}'] = [ - configuration.glob.pattern, - expectedConfiguration.glob.pattern, + configuration.glob, + expectedConfiguration.glob, ]; matches = false; } diff --git a/build_runner/lib/src/server/asset_graph_handler.dart b/build_runner/lib/src/server/asset_graph_handler.dart index 13cdca6e7..81923e814 100644 --- a/build_runner/lib/src/server/asset_graph_handler.dart +++ b/build_runner/lib/src/server/asset_graph_handler.dart @@ -173,7 +173,7 @@ class AssetGraphHandler { 'type': node.runtimeType.toString(), 'glob': node.type == NodeType.glob - ? node.globNodeConfiguration!.glob.pattern + ? node.globNodeConfiguration!.glob : null, 'lastKnownDigest': node.lastKnownDigest.toString(), }, diff --git a/build_runner_core/CHANGELOG.md b/build_runner_core/CHANGELOG.md index 1b580edab..43dd47e3c 100644 --- a/build_runner_core/CHANGELOG.md +++ b/build_runner_core/CHANGELOG.md @@ -17,7 +17,7 @@ - Refactor `SingleStepReader` to `SingleStepReaderWriter`, incorporating `AssetWriterSpy` functionality. - Add `NodeType` to `AssetNode`, remove subtypes. Make mutations explicit. -- Use `built_value` for `AssetNode` and related types. +- Use `built_value` for `AssetNode` and related types, and for serialization. ## 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 9fa92be3b..3fc1ca378 100644 --- a/build_runner_core/lib/src/asset_graph/graph.dart +++ b/build_runner_core/lib/src/asset_graph/graph.dart @@ -469,7 +469,8 @@ class AssetGraph { ); for (final node in samePackageGlobNodes) { final nodeConfiguration = node.globNodeConfiguration!; - if (nodeConfiguration.glob.matches(id.path)) { + final glob = Glob(nodeConfiguration.glob); + if (glob.matches(id.path)) { invalidateNodeAndDeps(node.id); updateNode(node.id, (nodeBuilder) { nodeBuilder.globNodeState.pendingBuildAction = diff --git a/build_runner_core/lib/src/asset_graph/node.dart b/build_runner_core/lib/src/asset_graph/node.dart index ff2d9df2f..6cfc47f86 100644 --- a/build_runner_core/lib/src/asset_graph/node.dart +++ b/build_runner_core/lib/src/asset_graph/node.dart @@ -7,8 +7,8 @@ import 'dart:convert'; import 'package:build/build.dart' hide Builder; import 'package:built_collection/built_collection.dart'; import 'package:built_value/built_value.dart'; +import 'package:built_value/serializer.dart'; import 'package:crypto/crypto.dart'; -import 'package:glob/glob.dart'; import '../generate/phase.dart'; @@ -16,6 +16,8 @@ part 'node.g.dart'; /// Types of [AssetNode]. class NodeType extends EnumClass { + static Serializer get serializer => _$nodeTypeSerializer; + static const NodeType builderOptions = _$builderOptions; static const NodeType generated = _$generated; static const NodeType glob = _$glob; @@ -33,6 +35,8 @@ class NodeType extends EnumClass { /// A node in the asset graph which may be an input to other assets. abstract class AssetNode implements Built { + static Serializer get serializer => _$assetNodeSerializer; + AssetId get id; NodeType get type; @@ -219,7 +223,7 @@ abstract class AssetNode implements Built { factory AssetNode.glob( AssetId id, { Digest? lastKnownDigest, - required Glob glob, + required String glob, required int phaseNumber, Iterable? inputs, required PendingBuildAction pendingBuildAction, @@ -236,11 +240,8 @@ abstract class AssetNode implements Built { ..lastKnownDigest = lastKnownDigest, ); - static AssetId createGlobNodeId(String package, Glob glob, int phaseNum) => - AssetId( - package, - 'glob.$phaseNum.${base64.encode(utf8.encode(glob.pattern))}', - ); + static AssetId createGlobNodeId(String package, String glob, int phaseNum) => + AssetId(package, 'glob.$phaseNum.${base64.encode(utf8.encode(glob))}'); /// A [primaryInput] to a [PostBuildAction]. /// @@ -313,6 +314,9 @@ abstract class AssetNode implements Built { abstract class GeneratedNodeConfiguration implements Built { + static Serializer get serializer => + _$generatedNodeConfigurationSerializer; + /// The primary input which generated this node. AssetId get primaryInput; @@ -342,6 +346,9 @@ abstract class GeneratedNodeConfiguration /// State for an [AssetNode.generated] that changes during the build. abstract class GeneratedNodeState implements Built { + static Serializer get serializer => + _$generatedNodeStateSerializer; + /// All the inputs that were read when generating this asset, or deciding not /// to generate it. BuiltSet get inputs; @@ -373,7 +380,10 @@ abstract class GeneratedNodeState /// Additional configuration for an [AssetNode.glob]. abstract class GlobNodeConfiguration implements Built { - Glob get glob; + static Serializer get serializer => + _$globNodeConfigurationSerializer; + + String get glob; int get phaseNumber; factory GlobNodeConfiguration( @@ -386,6 +396,10 @@ abstract class GlobNodeConfiguration /// State for an [AssetNode.glob] that changes during the build. abstract class GlobNodeState implements Built { + static Serializer get serializer => _$globNodeStateSerializer; + + /// The next work that needs doing on this node. + /// All the potential inputs matching this glob. /// /// This field differs from [results] in that [AssetNode.generated] which may @@ -412,6 +426,9 @@ abstract class PostProcessAnchorNodeConfiguration PostProcessAnchorNodeConfiguration, PostProcessAnchorNodeConfigurationBuilder > { + static Serializer get serializer => + _$postProcessAnchorNodeConfigurationSerializer; + int get actionNumber; AssetId get builderOptionsId; AssetId get primaryInput; @@ -427,6 +444,9 @@ abstract class PostProcessAnchorNodeConfiguration abstract class PostProcessAnchorNodeState implements Built { + static Serializer get serializer => + _$postProcessAnchorNodeStateSerializer; + Digest? get previousInputsDigest; factory PostProcessAnchorNodeState( @@ -438,6 +458,9 @@ abstract class PostProcessAnchorNodeState /// Work that needs doing for a node that tracks its inputs. class PendingBuildAction extends EnumClass { + static Serializer get serializer => + _$pendingBuildActionSerializer; + static const PendingBuildAction none = _$none; static const PendingBuildAction buildIfInputsChanged = _$buildIfInputsChanged; static const PendingBuildAction build = _$build; @@ -448,3 +471,59 @@ 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 c76715245..1925a4666 100644 --- a/build_runner_core/lib/src/asset_graph/node.g.dart +++ b/build_runner_core/lib/src/asset_graph/node.g.dart @@ -76,6 +76,881 @@ 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) + ..add(PostProcessAnchorNodeState.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 = + new _$GeneratedNodeConfigurationSerializer(); +Serializer _$generatedNodeStateSerializer = + new _$GeneratedNodeStateSerializer(); +Serializer _$globNodeConfigurationSerializer = + new _$GlobNodeConfigurationSerializer(); +Serializer _$globNodeStateSerializer = + new _$GlobNodeStateSerializer(); +Serializer +_$postProcessAnchorNodeConfigurationSerializer = + new _$PostProcessAnchorNodeConfigurationSerializer(); +Serializer _$postProcessAnchorNodeStateSerializer = + new _$PostProcessAnchorNodeStateSerializer(); +Serializer _$pendingBuildActionSerializer = + new _$PendingBuildActionSerializer(); + +class _$NodeTypeSerializer implements PrimitiveSerializer { + @override + final Iterable types = const [NodeType]; + @override + final String wireName = 'NodeType'; + + @override + Object serialize( + Serializers serializers, + NodeType object, { + FullType specifiedType = FullType.unspecified, + }) => object.name; + + @override + NodeType deserialize( + Serializers serializers, + Object serialized, { + FullType specifiedType = FullType.unspecified, + }) => NodeType.valueOf(serialized as String); +} + +class _$AssetNodeSerializer implements StructuredSerializer { + @override + final Iterable types = const [AssetNode, _$AssetNode]; + @override + final String wireName = 'AssetNode'; + + @override + Iterable serialize( + Serializers serializers, + AssetNode object, { + FullType specifiedType = FullType.unspecified, + }) { + final result = [ + 'id', + serializers.serialize(object.id, specifiedType: const FullType(AssetId)), + 'type', + serializers.serialize( + object.type, + specifiedType: const FullType(NodeType), + ), + 'primaryOutputs', + serializers.serialize( + object.primaryOutputs, + specifiedType: const FullType(BuiltSet, const [ + const FullType(AssetId), + ]), + ), + 'outputs', + serializers.serialize( + object.outputs, + specifiedType: const FullType(BuiltSet, const [ + 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), + ]), + ), + ]; + Object? value; + value = object.generatedNodeConfiguration; + if (value != null) { + result + ..add('generatedNodeConfiguration') + ..add( + serializers.serialize( + value, + specifiedType: const FullType(GeneratedNodeConfiguration), + ), + ); + } + value = object.generatedNodeState; + if (value != null) { + result + ..add('generatedNodeState') + ..add( + serializers.serialize( + value, + specifiedType: const FullType(GeneratedNodeState), + ), + ); + } + value = object.globNodeConfiguration; + if (value != null) { + result + ..add('globNodeConfiguration') + ..add( + serializers.serialize( + value, + specifiedType: const FullType(GlobNodeConfiguration), + ), + ); + } + value = object.globNodeState; + if (value != null) { + result + ..add('globNodeState') + ..add( + serializers.serialize( + value, + specifiedType: const FullType(GlobNodeState), + ), + ); + } + value = object.postProcessAnchorNodeConfiguration; + if (value != null) { + result + ..add('postProcessAnchorNodeConfiguration') + ..add( + serializers.serialize( + value, + specifiedType: const FullType(PostProcessAnchorNodeConfiguration), + ), + ); + } + value = object.postProcessAnchorNodeState; + if (value != null) { + result + ..add('postProcessAnchorNodeState') + ..add( + serializers.serialize( + value, + specifiedType: const FullType(PostProcessAnchorNodeState), + ), + ); + } + value = object.lastKnownDigest; + if (value != null) { + result + ..add('lastKnownDigest') + ..add( + serializers.serialize(value, specifiedType: const FullType(Digest)), + ); + } + return result; + } + + @override + AssetNode deserialize( + Serializers serializers, + Iterable serialized, { + FullType specifiedType = FullType.unspecified, + }) { + final result = new AssetNodeBuilder(); + + final iterator = serialized.iterator; + while (iterator.moveNext()) { + final key = iterator.current! as String; + iterator.moveNext(); + final Object? value = iterator.current; + switch (key) { + case 'id': + result.id = + serializers.deserialize( + value, + specifiedType: const FullType(AssetId), + )! + as AssetId; + break; + case 'type': + result.type = + serializers.deserialize( + value, + specifiedType: const FullType(NodeType), + )! + as NodeType; + break; + case 'generatedNodeConfiguration': + result.generatedNodeConfiguration.replace( + serializers.deserialize( + value, + specifiedType: const FullType(GeneratedNodeConfiguration), + )! + as GeneratedNodeConfiguration, + ); + break; + case 'generatedNodeState': + result.generatedNodeState.replace( + serializers.deserialize( + value, + specifiedType: const FullType(GeneratedNodeState), + )! + as GeneratedNodeState, + ); + break; + case 'globNodeConfiguration': + result.globNodeConfiguration.replace( + serializers.deserialize( + value, + specifiedType: const FullType(GlobNodeConfiguration), + )! + as GlobNodeConfiguration, + ); + break; + case 'globNodeState': + result.globNodeState.replace( + serializers.deserialize( + value, + specifiedType: const FullType(GlobNodeState), + )! + as GlobNodeState, + ); + break; + case 'postProcessAnchorNodeConfiguration': + result.postProcessAnchorNodeConfiguration.replace( + serializers.deserialize( + value, + specifiedType: const FullType( + PostProcessAnchorNodeConfiguration, + ), + )! + as PostProcessAnchorNodeConfiguration, + ); + break; + case 'postProcessAnchorNodeState': + result.postProcessAnchorNodeState.replace( + serializers.deserialize( + value, + specifiedType: const FullType(PostProcessAnchorNodeState), + )! + as PostProcessAnchorNodeState, + ); + break; + case 'primaryOutputs': + result.primaryOutputs.replace( + serializers.deserialize( + value, + specifiedType: const FullType(BuiltSet, const [ + const FullType(AssetId), + ]), + )! + as BuiltSet, + ); + break; + case 'outputs': + result.outputs.replace( + serializers.deserialize( + value, + specifiedType: const FullType(BuiltSet, const [ + const FullType(AssetId), + ]), + )! + 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( + value, + specifiedType: const FullType(Digest), + ) + as Digest?; + break; + case 'deletedBy': + result.deletedBy.replace( + serializers.deserialize( + value, + specifiedType: const FullType(BuiltSet, const [ + const FullType(AssetId), + ]), + )! + as BuiltSet, + ); + break; + } + } + + return result.build(); + } +} + +class _$GeneratedNodeConfigurationSerializer + implements StructuredSerializer { + @override + final Iterable types = const [ + GeneratedNodeConfiguration, + _$GeneratedNodeConfiguration, + ]; + @override + final String wireName = 'GeneratedNodeConfiguration'; + + @override + Iterable serialize( + Serializers serializers, + GeneratedNodeConfiguration object, { + FullType specifiedType = FullType.unspecified, + }) { + final result = [ + 'primaryInput', + serializers.serialize( + object.primaryInput, + specifiedType: const FullType(AssetId), + ), + 'builderOptionsId', + serializers.serialize( + object.builderOptionsId, + specifiedType: const FullType(AssetId), + ), + 'phaseNumber', + serializers.serialize( + object.phaseNumber, + specifiedType: const FullType(int), + ), + 'isHidden', + serializers.serialize( + object.isHidden, + specifiedType: const FullType(bool), + ), + ]; + + return result; + } + + @override + GeneratedNodeConfiguration deserialize( + Serializers serializers, + Iterable serialized, { + FullType specifiedType = FullType.unspecified, + }) { + final result = new GeneratedNodeConfigurationBuilder(); + + final iterator = serialized.iterator; + while (iterator.moveNext()) { + final key = iterator.current! as String; + iterator.moveNext(); + final Object? value = iterator.current; + switch (key) { + case 'primaryInput': + result.primaryInput = + serializers.deserialize( + value, + specifiedType: const FullType(AssetId), + )! + as AssetId; + break; + case 'builderOptionsId': + result.builderOptionsId = + serializers.deserialize( + value, + specifiedType: const FullType(AssetId), + )! + as AssetId; + break; + case 'phaseNumber': + result.phaseNumber = + serializers.deserialize( + value, + specifiedType: const FullType(int), + )! + as int; + break; + case 'isHidden': + result.isHidden = + serializers.deserialize( + value, + specifiedType: const FullType(bool), + )! + as bool; + break; + } + } + + return result.build(); + } +} + +class _$GeneratedNodeStateSerializer + implements StructuredSerializer { + @override + final Iterable types = const [GeneratedNodeState, _$GeneratedNodeState]; + @override + final String wireName = 'GeneratedNodeState'; + + @override + Iterable serialize( + Serializers serializers, + GeneratedNodeState object, { + FullType specifiedType = FullType.unspecified, + }) { + final result = [ + 'inputs', + serializers.serialize( + object.inputs, + specifiedType: const FullType(BuiltSet, const [ + const FullType(AssetId), + ]), + ), + 'pendingBuildAction', + serializers.serialize( + object.pendingBuildAction, + specifiedType: const FullType(PendingBuildAction), + ), + 'wasOutput', + serializers.serialize( + object.wasOutput, + specifiedType: const FullType(bool), + ), + 'isFailure', + serializers.serialize( + object.isFailure, + specifiedType: const FullType(bool), + ), + ]; + Object? value; + value = object.previousInputsDigest; + if (value != null) { + result + ..add('previousInputsDigest') + ..add( + serializers.serialize(value, specifiedType: const FullType(Digest)), + ); + } + return result; + } + + @override + GeneratedNodeState deserialize( + Serializers serializers, + Iterable serialized, { + FullType specifiedType = FullType.unspecified, + }) { + final result = new GeneratedNodeStateBuilder(); + + final iterator = serialized.iterator; + while (iterator.moveNext()) { + final key = iterator.current! as String; + iterator.moveNext(); + final Object? value = iterator.current; + switch (key) { + case 'inputs': + result.inputs.replace( + serializers.deserialize( + value, + specifiedType: const FullType(BuiltSet, const [ + const FullType(AssetId), + ]), + )! + as BuiltSet, + ); + break; + case 'pendingBuildAction': + result.pendingBuildAction = + serializers.deserialize( + value, + specifiedType: const FullType(PendingBuildAction), + )! + as PendingBuildAction; + break; + case 'wasOutput': + result.wasOutput = + serializers.deserialize( + value, + specifiedType: const FullType(bool), + )! + as bool; + break; + case 'isFailure': + result.isFailure = + serializers.deserialize( + value, + specifiedType: const FullType(bool), + )! + as bool; + break; + case 'previousInputsDigest': + result.previousInputsDigest = + serializers.deserialize( + value, + specifiedType: const FullType(Digest), + ) + as Digest?; + break; + } + } + + return result.build(); + } +} + +class _$GlobNodeConfigurationSerializer + implements StructuredSerializer { + @override + final Iterable types = const [ + GlobNodeConfiguration, + _$GlobNodeConfiguration, + ]; + @override + final String wireName = 'GlobNodeConfiguration'; + + @override + Iterable serialize( + Serializers serializers, + GlobNodeConfiguration object, { + FullType specifiedType = FullType.unspecified, + }) { + final result = [ + 'glob', + serializers.serialize(object.glob, specifiedType: const FullType(String)), + 'phaseNumber', + serializers.serialize( + object.phaseNumber, + specifiedType: const FullType(int), + ), + ]; + + return result; + } + + @override + GlobNodeConfiguration deserialize( + Serializers serializers, + Iterable serialized, { + FullType specifiedType = FullType.unspecified, + }) { + final result = new GlobNodeConfigurationBuilder(); + + final iterator = serialized.iterator; + while (iterator.moveNext()) { + final key = iterator.current! as String; + iterator.moveNext(); + final Object? value = iterator.current; + switch (key) { + case 'glob': + result.glob = + serializers.deserialize( + value, + specifiedType: const FullType(String), + )! + as String; + break; + case 'phaseNumber': + result.phaseNumber = + serializers.deserialize( + value, + specifiedType: const FullType(int), + )! + as int; + break; + } + } + + return result.build(); + } +} + +class _$GlobNodeStateSerializer implements StructuredSerializer { + @override + final Iterable types = const [GlobNodeState, _$GlobNodeState]; + @override + final String wireName = 'GlobNodeState'; + + @override + Iterable serialize( + Serializers serializers, + GlobNodeState object, { + FullType specifiedType = FullType.unspecified, + }) { + final result = [ + 'inputs', + serializers.serialize( + object.inputs, + specifiedType: const FullType(BuiltSet, const [ + const FullType(AssetId), + ]), + ), + 'pendingBuildAction', + serializers.serialize( + object.pendingBuildAction, + specifiedType: const FullType(PendingBuildAction), + ), + 'results', + serializers.serialize( + object.results, + specifiedType: const FullType(BuiltList, const [ + const FullType(AssetId), + ]), + ), + ]; + + return result; + } + + @override + GlobNodeState deserialize( + Serializers serializers, + Iterable serialized, { + FullType specifiedType = FullType.unspecified, + }) { + final result = new GlobNodeStateBuilder(); + + final iterator = serialized.iterator; + while (iterator.moveNext()) { + final key = iterator.current! as String; + iterator.moveNext(); + final Object? value = iterator.current; + switch (key) { + case 'inputs': + result.inputs.replace( + serializers.deserialize( + value, + specifiedType: const FullType(BuiltSet, const [ + const FullType(AssetId), + ]), + )! + as BuiltSet, + ); + break; + case 'pendingBuildAction': + result.pendingBuildAction = + serializers.deserialize( + value, + specifiedType: const FullType(PendingBuildAction), + )! + as PendingBuildAction; + break; + case 'results': + result.results.replace( + serializers.deserialize( + value, + specifiedType: const FullType(BuiltList, const [ + const FullType(AssetId), + ]), + )! + as BuiltList, + ); + break; + } + } + + return result.build(); + } +} + +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 _$PostProcessAnchorNodeStateSerializer + implements StructuredSerializer { + @override + final Iterable types = const [ + PostProcessAnchorNodeState, + _$PostProcessAnchorNodeState, + ]; + @override + final String wireName = 'PostProcessAnchorNodeState'; + + @override + Iterable serialize( + Serializers serializers, + PostProcessAnchorNodeState object, { + FullType specifiedType = FullType.unspecified, + }) { + final result = []; + Object? value; + value = object.previousInputsDigest; + if (value != null) { + result + ..add('previousInputsDigest') + ..add( + serializers.serialize(value, specifiedType: const FullType(Digest)), + ); + } + return result; + } + + @override + PostProcessAnchorNodeState deserialize( + Serializers serializers, + Iterable serialized, { + FullType specifiedType = FullType.unspecified, + }) { + final result = new PostProcessAnchorNodeStateBuilder(); + + final iterator = serialized.iterator; + while (iterator.moveNext()) { + final key = iterator.current! as String; + iterator.moveNext(); + final Object? value = iterator.current; + switch (key) { + case 'previousInputsDigest': + result.previousInputsDigest = + serializers.deserialize( + value, + specifiedType: const FullType(Digest), + ) + as Digest?; + break; + } + } + + return result.build(); + } +} + +class _$PendingBuildActionSerializer + implements PrimitiveSerializer { + @override + final Iterable types = const [PendingBuildAction]; + @override + final String wireName = 'PendingBuildAction'; + + @override + Object serialize( + Serializers serializers, + PendingBuildAction object, { + FullType specifiedType = FullType.unspecified, + }) => object.name; + + @override + PendingBuildAction deserialize( + Serializers serializers, + Object serialized, { + FullType specifiedType = FullType.unspecified, + }) => PendingBuildAction.valueOf(serialized as String); +} + class _$AssetNode extends AssetNode { @override final AssetId id; @@ -750,7 +1625,7 @@ class GeneratedNodeStateBuilder class _$GlobNodeConfiguration extends GlobNodeConfiguration { @override - final Glob glob; + final String glob; @override final int phaseNumber; @@ -811,9 +1686,9 @@ class GlobNodeConfigurationBuilder implements Builder { _$GlobNodeConfiguration? _$v; - Glob? _glob; - Glob? get glob => _$this._glob; - set glob(Glob? glob) => _$this._glob = glob; + String? _glob; + String? get glob => _$this._glob; + set glob(String? glob) => _$this._glob = glob; int? _phaseNumber; int? get phaseNumber => _$this._phaseNumber; diff --git a/build_runner_core/lib/src/asset_graph/serialization.dart b/build_runner_core/lib/src/asset_graph/serialization.dart index 559fe6eb2..4c01a08f5 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 = 25; +const _version = 26; /// Deserializes an [AssetGraph] from a [Map]. class _AssetGraphDeserializer { @@ -67,179 +67,12 @@ class _AssetGraphDeserializer { graph._add(_deserializeAssetNode(serializedItem as List)); } - // Update the inputs of all generated nodes based on the outputs of the - // current nodes. - for (var node in graph.allNodes) { - // These aren't explicitly added as inputs. - if (node.type == NodeType.builderOptions) continue; - - for (var output in node.outputs) { - var inputsNode = graph.get(output); - if (inputsNode == null || - (inputsNode.type != NodeType.generated && - inputsNode.type != NodeType.glob)) { - log.severe( - 'Failed to locate $output referenced from ${node.id} ' - 'which is a ${node.runtimeType}. If you encounter this error ' - 'please copy the details from this message and add them to ' - 'https://github.com/dart-lang/build/issues/1804.', - ); - throw AssetGraphCorruptedException(); - } - graph.updateNode(inputsNode.id, (nodeBuilder) { - if (inputsNode.type == NodeType.generated) { - nodeBuilder.generatedNodeState.inputs.add(node.id); - } else { - nodeBuilder.globNodeState.inputs.add(node.id); - } - }); - } - - if (node.type == NodeType.postProcessAnchor) { - graph.updateNode( - node.postProcessAnchorNodeConfiguration!.primaryInput, - (nodeBuilder) { - nodeBuilder.anchorOutputs.add(node.id); - }, - ); - } - } - return graph; } - AssetNode _deserializeAssetNode(List serializedNode) { - AssetNode node; - var typeId = - _NodeType.values[serializedNode[_AssetField.nodeType.index] as int]; - var id = _idToAssetId[serializedNode[_AssetField.id.index] as int]!; - var serializedDigest = serializedNode[_AssetField.digest.index] as String?; - var digest = _deserializeDigest(serializedDigest); - switch (typeId) { - case _NodeType.source: - assert(serializedNode.length == _WrappedAssetNode._length); - node = AssetNode.source(id, lastKnownDigest: digest); - break; - case _NodeType.syntheticSource: - assert(serializedNode.length == _WrappedAssetNode._length); - node = AssetNode.missingSource(id); - break; - case _NodeType.generated: - assert(serializedNode.length == _WrappedGeneratedAssetNode._length); - var offset = _AssetField.values.length; - node = AssetNode.generated( - id, - phaseNumber: - serializedNode[_GeneratedField.phaseNumber.index + offset] as int, - primaryInput: - _idToAssetId[serializedNode[_GeneratedField.primaryInput.index + - offset] - as int]!, - pendingBuildAction: PendingBuildAction.valueOf( - serializedNode[_GeneratedField.state.index + offset] as String, - ), - wasOutput: _deserializeBool( - serializedNode[_GeneratedField.wasOutput.index + offset] as int, - ), - isFailure: _deserializeBool( - serializedNode[_GeneratedField.isFailure.index + offset] as int, - ), - builderOptionsId: - _idToAssetId[serializedNode[_GeneratedField.builderOptions.index + - offset] - as int]!, - lastKnownDigest: digest, - previousInputsDigest: _deserializeDigest( - serializedNode[_GeneratedField.previousInputsDigest.index + offset] - as String?, - ), - isHidden: _deserializeBool( - serializedNode[_GeneratedField.isHidden.index + offset] as int, - ), - ); - break; - case _NodeType.glob: - assert(serializedNode.length == _WrappedGlobAssetNode._length); - var offset = _AssetField.values.length; - node = AssetNode.glob( - id, - glob: Glob(serializedNode[_GlobField.glob.index + offset] as String), - phaseNumber: - serializedNode[_GlobField.phaseNumber.index + offset] as int, - pendingBuildAction: PendingBuildAction.valueOf( - serializedNode[_GlobField.state.index + offset] as String, - ), - lastKnownDigest: digest, - results: - _deserializeAssetIds( - serializedNode[_GlobField.results.index + offset] as List, - ).toList(), - ); - break; - case _NodeType.internal: - assert(serializedNode.length == _WrappedAssetNode._length); - node = AssetNode.internal(id, lastKnownDigest: digest); - break; - case _NodeType.builderOptions: - assert(serializedNode.length == _WrappedAssetNode._length); - node = AssetNode.builderOptions(id, lastKnownDigest: digest!); - break; - case _NodeType.placeholder: - assert(serializedNode.length == _WrappedAssetNode._length); - node = AssetNode.placeholder(id); - break; - case _NodeType.postProcessAnchor: - assert(serializedNode.length == _WrappedPostProcessAnchorNode._length); - var offset = _AssetField.values.length; - node = AssetNode.postProcessAnchor( - id, - primaryInput: - _idToAssetId[serializedNode[_PostAnchorField.primaryInput.index + - offset] - as int]!, - actionNumber: - serializedNode[_PostAnchorField.actionNumber.index + offset] - as int, - builderOptionsId: - _idToAssetId[serializedNode[_PostAnchorField - .builderOptions - .index + - offset] - as int]!, - previousInputsDigest: _deserializeDigest( - serializedNode[_PostAnchorField.previousInputsDigest.index + offset] - as String?, - ), - ); - break; - } - node = node.rebuild( - (b) => - b - ..outputs.addAll( - _deserializeAssetIds( - serializedNode[_AssetField.outputs.index] as List, - ), - ) - ..primaryOutputs.addAll( - _deserializeAssetIds( - serializedNode[_AssetField.primaryOutputs.index] as List, - ), - ) - ..deletedBy.addAll( - _deserializeAssetIds( - (serializedNode[_AssetField.deletedBy.index] as List) - .cast(), - ), - ), - ); - return node; - } - - Iterable _deserializeAssetIds(List serializedIds) => - serializedIds.map((id) => _idToAssetId[id]!); - - bool _deserializeBool(int value) => value != 0; + AssetNode _deserializeAssetNode(List serializedNode) => + serializers.deserializeWith(AssetNode.serializer, serializedNode) + as AssetNode; } /// Serializes an [AssetGraph] into a [Map]. @@ -268,7 +101,9 @@ class _AssetGraphSerializer { var result = { 'version': _version, 'dart_version': _graph.dartVersion, - 'nodes': _graph.allNodes.map(_serializeNode).toList(growable: false), + 'nodes': _graph.allNodes + .map((node) => serializers.serializeWith(AssetNode.serializer, node)) + .toList(growable: false), 'buildActionsDigest': _serializeDigest(_graph.buildPhasesDigest), 'packages': packages, 'assetPaths': assetPaths, @@ -280,18 +115,6 @@ class _AssetGraphSerializer { return utf8.encode(json.encode(result)); } - List _serializeNode(AssetNode node) { - if (node.type == NodeType.generated) { - return _WrappedGeneratedAssetNode(node, this); - } else if (node.type == NodeType.postProcessAnchor) { - return _WrappedPostProcessAnchorNode(node, this); - } else if (node.type == NodeType.glob) { - return _WrappedGlobAssetNode(node, this); - } else { - return _WrappedAssetNode(node, this); - } - } - int findAssetIndex( AssetId id, { required AssetId from, @@ -311,273 +134,8 @@ class _AssetGraphSerializer { } } -/// Used to serialize the type of a node using an int. -enum _NodeType { - source, - syntheticSource, - generated, - internal, - builderOptions, - placeholder, - postProcessAnchor, - glob, -} - -/// Field indexes for all [AssetNode]s -enum _AssetField { nodeType, id, outputs, primaryOutputs, digest, deletedBy } - -/// Field indexes for [AssetNode.generated]s -enum _GeneratedField { - primaryInput, - wasOutput, - isFailure, - phaseNumber, - state, - previousInputsDigest, - builderOptions, - isHidden, -} - -/// Field indexes for [AssetNode.glob]s -enum _GlobField { phaseNumber, state, glob, results } - -/// Field indexes for [AssetNode.postProcessAnchor]s. -enum _PostAnchorField { - actionNumber, - builderOptions, - previousInputsDigest, - primaryInput, -} - -/// Wraps an [AssetNode] in a class that implements [List] instead of -/// creating a new list for each one. -class _WrappedAssetNode extends Object with ListMixin implements List { - final AssetNode node; - final _AssetGraphSerializer serializer; - - _WrappedAssetNode(this.node, this.serializer); - - static final int _length = _AssetField.values.length; - - @override - int get length => _length; - - @override - set length(void _) => - throw UnsupportedError( - 'length setter not unsupported for WrappedAssetNode', - ); - - @override - Object? operator [](int index) { - var fieldId = _AssetField.values[index]; - switch (fieldId) { - case _AssetField.nodeType: - switch (node.type) { - case NodeType.source: - return _NodeType.source.index; - case NodeType.generated: - return _NodeType.generated.index; - case NodeType.glob: - return _NodeType.glob.index; - case NodeType.missingSource: - return _NodeType.syntheticSource.index; - case NodeType.internal: - return _NodeType.internal.index; - case NodeType.builderOptions: - return _NodeType.builderOptions.index; - - case NodeType.placeholder: - return _NodeType.placeholder.index; - case NodeType.postProcessAnchor: - return _NodeType.postProcessAnchor.index; - default: - throw UnsupportedError(node.type.name); - } - case _AssetField.id: - return serializer.findAssetIndex(node.id, from: node.id, field: 'id'); - case _AssetField.outputs: - return node.outputs - .map( - (id) => serializer.findAssetIndex( - id, - from: node.id, - field: 'outputs', - ), - ) - .toList(growable: false); - case _AssetField.primaryOutputs: - return node.primaryOutputs - .map( - (id) => serializer.findAssetIndex( - id, - from: node.id, - field: 'primaryOutputs', - ), - ) - .toList(growable: false); - case _AssetField.digest: - return _serializeDigest(node.lastKnownDigest); - case _AssetField.deletedBy: - return node.deletedBy - .map( - (id) => serializer.findAssetIndex( - id, - from: node.id, - field: 'deletedBy', - ), - ) - .toList(growable: false); - } - } - - @override - void operator []=(void _, void _) => - throw UnsupportedError('[]= not supported for WrappedAssetNode'); -} - -/// Wraps an [AssetNode.generated] in a class that implements [List] instead of -/// creating a new list for each one. -class _WrappedGeneratedAssetNode extends _WrappedAssetNode { - final AssetNode generatedNode; - - /// Offset in the serialized format for additional fields in this class but - /// not in [_WrappedAssetNode]. - /// - /// Indexes below this number are forwarded to `super[index]`. - static final int _serializedOffset = _AssetField.values.length; - - static final int _length = _serializedOffset + _GeneratedField.values.length; - - @override - int get length => _length; - - _WrappedGeneratedAssetNode( - this.generatedNode, - _AssetGraphSerializer serializer, - ) : super(generatedNode, serializer); - - @override - Object? operator [](int index) { - if (index < _serializedOffset) return super[index]; - var fieldId = _GeneratedField.values[index - _serializedOffset]; - final configuration = generatedNode.generatedNodeConfiguration!; - final state = generatedNode.generatedNodeState!; - return switch (fieldId) { - _GeneratedField.primaryInput => serializer.findAssetIndex( - configuration.primaryInput, - from: generatedNode.id, - field: 'primaryInput', - ), - _GeneratedField.wasOutput => _serializeBool(state.wasOutput), - _GeneratedField.isFailure => _serializeBool(state.isFailure), - _GeneratedField.phaseNumber => configuration.phaseNumber, - _GeneratedField.state => state.pendingBuildAction.name, - _GeneratedField.previousInputsDigest => _serializeDigest( - state.previousInputsDigest, - ), - _GeneratedField.builderOptions => serializer.findAssetIndex( - configuration.builderOptionsId, - from: generatedNode.id, - field: 'builderOptions', - ), - _GeneratedField.isHidden => _serializeBool(configuration.isHidden), - }; - } -} - -/// Wraps an [AssetNode.glob] in a class that implements [List] instead of -/// creating a new list for each one. -class _WrappedGlobAssetNode extends _WrappedAssetNode { - final AssetNode globNode; - - /// Offset in the serialized format for additional fields in this class but - /// not in [_WrappedAssetNode]. - /// - /// Indexes below this number are forwarded to `super[index]`. - static final int _serializedOffset = _AssetField.values.length; - - static final int _length = _serializedOffset + _GlobField.values.length; - - @override - int get length => _length; - - _WrappedGlobAssetNode(this.globNode, _AssetGraphSerializer serializer) - : super(globNode, serializer); - - @override - Object? operator [](int index) { - if (index < _serializedOffset) return super[index]; - var fieldId = _GlobField.values[index - _serializedOffset]; - final configuration = globNode.globNodeConfiguration!; - final state = globNode.globNodeState!; - return switch (fieldId) { - _GlobField.phaseNumber => configuration.phaseNumber, - _GlobField.state => state.pendingBuildAction.name, - _GlobField.glob => configuration.glob.pattern, - _GlobField.results => state.results - .map( - (id) => serializer.findAssetIndex( - id, - from: globNode.id, - field: 'results', - ), - ) - .toList(growable: false), - }; - } -} - -/// Wraps a [AssetNode.postProcessAnchor] in a class that implements [List] -/// instead of creating a new list for each one. -class _WrappedPostProcessAnchorNode extends _WrappedAssetNode { - final AssetNode wrappedNode; - - /// Offset in the serialized format for additional fields in this class but - /// not in [_WrappedAssetNode]. - /// - /// Indexes below this number are forwarded to `super[index]`. - static final int _serializedOffset = _AssetField.values.length; - - static final int _length = _serializedOffset + _PostAnchorField.values.length; - - @override - int get length => _length; - - _WrappedPostProcessAnchorNode( - this.wrappedNode, - _AssetGraphSerializer serializer, - ) : super(wrappedNode, serializer); - - @override - Object? operator [](int index) { - if (index < _serializedOffset) return super[index]; - var fieldId = _PostAnchorField.values[index - _serializedOffset]; - final nodeConfiguration = wrappedNode.postProcessAnchorNodeConfiguration!; - final nodeState = wrappedNode.postProcessAnchorNodeState!; - return switch (fieldId) { - _PostAnchorField.actionNumber => nodeConfiguration.actionNumber, - _PostAnchorField.builderOptions => serializer.findAssetIndex( - nodeConfiguration.builderOptionsId, - from: wrappedNode.id, - field: 'builderOptions', - ), - _PostAnchorField.previousInputsDigest => _serializeDigest( - nodeState.previousInputsDigest, - ), - _PostAnchorField.primaryInput => serializer.findAssetIndex( - nodeConfiguration.primaryInput, - from: wrappedNode.id, - field: 'primaryInput', - ), - }; - } -} - Digest? _deserializeDigest(String? serializedDigest) => serializedDigest == null ? null : Digest(base64.decode(serializedDigest)); String? _serializeDigest(Digest? digest) => digest == null ? null : base64.encode(digest.bytes); - -int _serializeBool(bool value) => value ? 1 : 0; diff --git a/build_runner_core/lib/src/generate/build_impl.dart b/build_runner_core/lib/src/generate/build_impl.dart index 9aeb315b4..5b0fb393f 100644 --- a/build_runner_core/lib/src/generate/build_impl.dart +++ b/build_runner_core/lib/src/generate/build_impl.dart @@ -11,6 +11,7 @@ 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 'package:logging/logging.dart'; import 'package:path/path.dart' as p; import 'package:watcher/watcher.dart'; @@ -679,6 +680,8 @@ class _SingleBuild { } // 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); @@ -910,6 +913,7 @@ class _SingleBuild { return _lazyGlobs.putIfAbsent(globId, () async { final globNodeConfiguration = _assetGraph.get(globId)!.globNodeConfiguration!; + final glob = Glob(globNodeConfiguration.glob); // Generated files that match the glob. final generatedFileInputs = []; @@ -924,7 +928,7 @@ class _SingleBuild { (node.type != NodeType.generated || node.generatedNodeConfiguration!.phaseNumber < globNodeConfiguration.phaseNumber) && - globNodeConfiguration.glob.matches(node.id.path)) { + glob.matches(node.id.path)) { if (node.type == NodeType.generated) { generatedFileInputs.add(node.id); } else { diff --git a/build_runner_core/lib/src/generate/single_step_reader_writer.dart b/build_runner_core/lib/src/generate/single_step_reader_writer.dart index 4b2c33168..91d61b662 100644 --- a/build_runner_core/lib/src/generate/single_step_reader_writer.dart +++ b/build_runner_core/lib/src/generate/single_step_reader_writer.dart @@ -356,7 +356,7 @@ class SingleStepReaderWriter extends AssetReader var streamCompleter = StreamCompleter(); - _buildGlobNode(glob).then((globNodeId) { + _buildGlobNode(glob.pattern).then((globNodeId) { inputTracker.add(globNodeId); final globNode = _runningBuild.assetGraph.get(globNodeId)!; streamCompleter.setSourceStream( @@ -431,7 +431,7 @@ class SingleStepReaderWriter extends AssetReader /// Retrieves an existing node from `_runningBuild.assetGraph` if it's /// available; if not, adds one. Then, gets the built glob from /// `runningBuild.globNodeBuilder`, which might return an existing result. - Future _buildGlobNode(Glob glob) async { + Future _buildGlobNode(String glob) async { var globNodeId = AssetNode.createGlobNodeId( _runningBuildStep!.primaryPackage, glob, diff --git a/build_runner_core/test/asset_graph/graph_test.dart b/build_runner_core/test/asset_graph/graph_test.dart index ba9aba99a..4eb26ace4 100644 --- a/build_runner_core/test/asset_graph/graph_test.dart +++ b/build_runner_core/test/asset_graph/graph_test.dart @@ -13,7 +13,6 @@ 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/generate/phase.dart'; import 'package:crypto/crypto.dart'; -import 'package:glob/glob.dart'; import 'package:test/test.dart'; import 'package:watcher/watcher.dart'; @@ -91,7 +90,7 @@ void main() { test('serialize/deserialize', () { var globNode = AssetNode.glob( makeAssetId(), - glob: Glob('**/*.dart'), + glob: '**/*.dart', phaseNumber: 0, pendingBuildAction: PendingBuildAction.build, inputs: HashSet(), @@ -474,7 +473,7 @@ void main() { () async { var globNode = AssetNode.glob( primaryInputId.addExtension('.glob'), - glob: Glob('lib/*.cool'), + glob: 'lib/*.cool', phaseNumber: 0, pendingBuildAction: PendingBuildAction.none, inputs: HashSet(),