Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Report changed assets through build daemon #3273

Merged
merged 4 commits into from Mar 28, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 3 additions & 1 deletion build_daemon/CHANGELOG.md
@@ -1,5 +1,7 @@
## 3.0.3-dev
## 3.1.0-dev

- Add `BuildResults.changedAssets` containing asset URIs changed during a
build.
- Updated the example to use `dart pub` instead of `pub`.

## 3.0.2
Expand Down
14 changes: 14 additions & 0 deletions build_daemon/lib/data/build_status.dart
Expand Up @@ -6,6 +6,8 @@ import 'package:built_collection/built_collection.dart';
import 'package:built_value/built_value.dart';
import 'package:built_value/serializer.dart';

import 'build_target.dart';

part 'build_status.g.dart';

class BuildStatus extends EnumClass {
Expand Down Expand Up @@ -59,4 +61,16 @@ abstract class BuildResults
BuildResults._();

BuiltList<BuildResult> get results;

/// A list of asset URIs that were modified since the last build.
///
/// This includes both sources that were updated and affected generated assets
/// that were rebuilt.
///
/// To avoid communication overhead for clients not interested in this field,
/// it is not set by default. To enable it, register at least one target with
/// [DefaultBuildTarget.reportChangedAssets].
/// However, build implementations can unconditionally set this field as it
/// is stripped out in the daemon server implementation.
BuiltList<Uri>? get changedAssets;
}
42 changes: 36 additions & 6 deletions build_daemon/lib/data/build_status.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions build_daemon/lib/data/build_target.dart
Expand Up @@ -2,6 +2,8 @@ import 'package:built_collection/built_collection.dart';
import 'package:built_value/built_value.dart';
import 'package:built_value/serializer.dart';

import 'build_status.dart';

part 'build_target.g.dart';

/// The string representation of a build target, e.g. folder path.
Expand All @@ -16,6 +18,10 @@ abstract class DefaultBuildTarget
static Serializer<DefaultBuildTarget> get serializer =>
_$defaultBuildTargetSerializer;

@BuiltValueHook(initializeBuilder: true)
static void _setDefaults(DefaultBuildTargetBuilder b) =>
b.reportChangedAssets = false;

factory DefaultBuildTarget([void Function(DefaultBuildTargetBuilder) b]) =
_$DefaultBuildTarget;

Expand All @@ -38,6 +44,13 @@ abstract class DefaultBuildTarget
/// - package:*/**
/// - $target/**
BuiltSet<String>? get buildFilters;

/// Whether the [BuildResults] events emitted for this target should report a
/// list of assets invalidated in a build.
///
/// This defaults to `false` to reduce the serialization overhead when this
/// information is not required.
bool get reportChangedAssets;
}

/// The location to write the build outputs.
Expand Down
36 changes: 33 additions & 3 deletions build_daemon/lib/data/build_target.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions build_daemon/lib/data/serializers.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 12 additions & 2 deletions build_daemon/lib/src/fakes/fake_test_builder.dart
Expand Up @@ -15,14 +15,16 @@ class FakeTestDaemonBuilder implements DaemonBuilder {
static final buildCompletedMessage = 'Build Completed';

final _outputStreamController = StreamController<ServerLog>();
final _buildsController = StreamController<BuildResults>.broadcast();

late final Stream<ServerLog> _logs;

FakeTestDaemonBuilder() {
_logs = _outputStreamController.stream.asBroadcastStream();
}

@override
Stream<BuildResults> get builds => Stream.empty();
Stream<BuildResults> get builds => _buildsController.stream;

@override
Stream<ServerLog> get logs => _logs;
Expand All @@ -34,8 +36,16 @@ class FakeTestDaemonBuilder implements DaemonBuilder {
..loggerName = loggerName
..level = Level.INFO
..message = buildCompletedMessage));

_buildsController.add(BuildResults(
(b) => b.changedAssets.add(Uri.parse('package:foo/bar.dart'))));
}

@override
Future<void> stop() => _outputStreamController.close();
Future<void> stop() {
return Future.wait([
_outputStreamController.close(),
_buildsController.close(),
]);
}
}
17 changes: 13 additions & 4 deletions build_daemon/lib/src/managers/build_target_manager.dart
Expand Up @@ -19,6 +19,7 @@ bool _shouldBuild(BuildTarget target, Iterable<WatchEvent> changes) =>
/// the Dart Build Daemon.
class BuildTargetManager {
var _buildTargets = <BuildTarget, Set<WebSocketChannel>>{};
final _channelSubscriptions = <WebSocketChannel, Set<BuildTarget>>{};

bool Function(BuildTarget, Iterable<WatchEvent>) shouldBuild;

Expand All @@ -37,16 +38,24 @@ class BuildTargetManager {
/// Adds a tracked build target with corresponding interested channel.
void addBuildTarget(BuildTarget target, WebSocketChannel channel) {
_buildTargets.putIfAbsent(target, () => <WebSocketChannel>{}).add(channel);
_channelSubscriptions.putIfAbsent(channel, () => {}).add(target);
}

/// Returns channels that are interested in the provided target.
Set<WebSocketChannel> channels(BuildTarget target) =>
_buildTargets[target] ?? <WebSocketChannel>{};

void removeChannel(WebSocketChannel channel) =>
_buildTargets = Map.fromEntries(_buildTargets.entries
.map((e) => MapEntry(e.key, e.value..remove(channel)))
.where((e) => e.value.isNotEmpty));
/// Returns build targets that the [channel] has added.
Iterable<BuildTarget> targetsFor(WebSocketChannel channel) {
return _channelSubscriptions[channel] ?? const Iterable.empty();
}

void removeChannel(WebSocketChannel channel) {
_buildTargets = Map.fromEntries(_buildTargets.entries
.map((e) => MapEntry(e.key, e.value..remove(channel)))
.where((e) => e.value.isNotEmpty));
_channelSubscriptions.remove(channel);
}

Set<BuildTarget> targetsForChanges(List<WatchEvent> changes) =>
targets.where((target) => shouldBuild(target, changes)).toSet();
Expand Down