Skip to content

Commit b9ffd3d

Browse files
authored
Take a single Builder in BuilderTransformer (#138)
Typical use case is to have a single Builder and this will make it easier to write helper code around using BuilderTransformers for codegen. - Take a single Builder in the constructor of BuilderTransformer - Remove looping around builders - Add MultiplexingBuilder to make it easier to compose builders for the few use cases that may need it - Update package version as a breaking change - Loosen version constraint from build_test since it is not impacted - Update tests to use the new interface and expect slightly different log messages
1 parent 3335c31 commit b9ffd3d

File tree

9 files changed

+117
-87
lines changed

9 files changed

+117
-87
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
## 0.5.0
2+
3+
- **BREAKING** BuilderTransformer must be constructed with a single Builder. Use
4+
the MultiplexingBuilder to cover cases with a list of builders
5+
- When using a MultiplexingBuilder if multiple Builders have overlapping outputs
6+
the entire step will not run rather than running builders up to the point
7+
where there is an overlap
8+
19
## 0.4.1+3
210

311
- With the default logger, print exceptions with a terse stack trace.

build_test/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 0.2.0+1
2+
3+
- Forwards compatible version bump in package:build
4+
15
## 0.2.0
26

37
- Upgrade build package to 0.4.0

build_test/pubspec.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
name: build_test
22
description: Utilities for writing unit tests of Builders.
3-
version: 0.2.0
3+
version: 0.2.0+1
44
author: Dart Team <[email protected]>
55
homepage: https://github.com/dart-lang/build
66

77
dependencies:
8-
build: ^0.4.0
8+
build: ">=0.4.0 <0.6.0"
99
logging: ^0.11.2
1010
test: ^0.12.0
1111
watcher: ^0.9.7

e2e_example/lib/transformer.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,5 @@ import 'copy_builder.dart';
77

88
/// A pub compatible Transformer built on top of [BuilderTransformer].
99
class CopyTransformer extends BuilderTransformer {
10-
CopyTransformer.asPlugin(_) : super([new CopyBuilder()]);
10+
CopyTransformer.asPlugin(_) : super(new CopyBuilder());
1111
}

lib/build.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export 'src/asset/writer.dart';
1111
export 'src/builder/build_step.dart';
1212
export 'src/builder/builder.dart';
1313
export 'src/builder/exceptions.dart';
14+
export 'src/builder/multiplexing_builder.dart';
1415
export 'src/builder/transformer_builder.dart';
1516
export 'src/generate/build.dart';
1617
export 'src/generate/build_result.dart';
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
import 'dart:async';
5+
6+
import '../asset/id.dart';
7+
import 'build_step.dart';
8+
import 'builder.dart';
9+
10+
/// A [Builder] that runs multiple delegate builders asynchronously.
11+
///
12+
/// **Note**: All builders are ran without ordering guarantees. Thus, none of
13+
/// the builders can use the outputs of other builders in this group. All
14+
/// builders must also have distinct outputs.
15+
class MultiplexingBuilder implements Builder {
16+
final Iterable<Builder> _builders;
17+
18+
MultiplexingBuilder(this._builders);
19+
20+
@override
21+
Future build(BuildStep buildStep) =>
22+
Future.wait(_builders.map((builder) => builder.build(buildStep)));
23+
24+
/// Collects declared outputs from all of the builders.
25+
///
26+
/// If multiple builders declare the same output it will appear in this List
27+
/// more than once. This should be considered an error.
28+
@override
29+
List<AssetId> declareOutputs(AssetId inputId) =>
30+
_collectOutputs(inputId).toList();
31+
32+
Iterable<AssetId> _collectOutputs(AssetId id) sync* {
33+
for (var builder in _builders) {
34+
yield* builder.declareOutputs(id);
35+
}
36+
}
37+
38+
@override
39+
String toString() => '$_builders';
40+
}

lib/src/transformer/transformer.dart

Lines changed: 46 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -17,84 +17,72 @@ import '../builder/build_step_impl.dart';
1717
import '../builder/builder.dart';
1818
import '../util/barback.dart';
1919

20-
/// A [Transformer] which runs multiple [Builder]s.
20+
/// A [Transformer] which runs a single [Builder] with a new [BuildStep].
2121
///
2222
/// By default all [BuilderTransformer]s are [DeclaringTransformer]s. If you
2323
/// wish to run as a [LazyTransformer], extend this class and mix in
2424
/// LazyTransformer.
2525
class BuilderTransformer implements Transformer, DeclaringTransformer {
26-
/// The builders that will run during this Transformer step.
27-
///
28-
/// **Note**: All [builders] are ran in the same phase, and there are no
29-
/// ordering guarantees. Thus, none of the [builders] can use the outputs of
30-
/// other [builders]. In order to do this you must create a [TransformerGroup]
31-
/// with multiple [BuilderTransformer]s.
32-
final List<Builder> builders;
33-
26+
final Builder _builder;
3427
final Resolvers _resolvers;
3528

36-
BuilderTransformer(this.builders, {Resolvers resolvers: const Resolvers()})
29+
BuilderTransformer(this._builder, {Resolvers resolvers: const Resolvers()})
3730
: this._resolvers = resolvers;
3831

3932
@override
4033
String get allowedExtensions => null;
4134

4235
@override
4336
bool isPrimary(barback.AssetId id) =>
44-
_expectedOutputs(id, builders).isNotEmpty;
37+
_builder.declareOutputs(toBuildAssetId(id)).isNotEmpty;
4538

4639
@override
4740
Future apply(Transform transform) async {
4841
var input = await toBuildAsset(transform.primaryInput);
4942
var reader = new _TransformAssetReader(transform);
5043
var writer = new _TransformAssetWriter(transform);
5144

52-
// Tracks all the expected outputs for each builder to make sure they don't
53-
// overlap.
54-
var allExpected = new Set<build.AssetId>();
55-
56-
// Run all builders at the same time.
57-
await Future.wait(builders.map((builder) async {
58-
var expected = _expectedOutputs(transform.primaryInput.id, [builder]);
59-
if (expected.isEmpty) return;
60-
61-
// Check that no expected outputs already exist.
62-
var preExistingFiles = [];
63-
for (var output in expected) {
64-
if (!allExpected.add(output) ||
65-
await transform.hasInput(toBarbackAssetId(output))) {
66-
preExistingFiles.add(toBarbackAssetId(output));
67-
}
68-
}
69-
if (preExistingFiles.isNotEmpty) {
70-
transform.logger.error(
71-
'Builder `$builder` declared outputs `$preExistingFiles` but those '
72-
'files already exist. That build step has been skipped.',
73-
asset: transform.primaryInput.id);
74-
return;
45+
var expected =
46+
_builder.declareOutputs(toBuildAssetId(transform.primaryInput.id));
47+
if (expected.isEmpty) return;
48+
49+
// Check for overlapping outputs.
50+
var uniqueExpected = new Set<build.AssetId>();
51+
var preExistingFiles = [];
52+
for (var output in expected) {
53+
if (!uniqueExpected.add(output) ||
54+
await transform.hasInput(toBarbackAssetId(output))) {
55+
preExistingFiles.add(toBarbackAssetId(output));
7556
}
57+
}
58+
if (preExistingFiles.isNotEmpty) {
59+
transform.logger.error(
60+
'Builder `$_builder` declared outputs `$preExistingFiles` but those '
61+
'files already exist. This build step has been skipped.',
62+
asset: transform.primaryInput.id);
63+
return;
64+
}
7665

77-
// Run the build step.
78-
var buildStep = new BuildStepImpl(
79-
input, expected, reader, writer, input.id.package, _resolvers);
80-
Logger.root.level = Level.ALL;
81-
var logSubscription = buildStep.logger.onRecord.listen((LogRecord log) {
82-
if (log.loggerName != buildStep.logger.fullName) return;
83-
84-
if (log.level <= Level.CONFIG) {
85-
transform.logger.fine(_logRecordToMessage(log));
86-
} else if (log.level <= Level.INFO) {
87-
transform.logger.info(_logRecordToMessage(log));
88-
} else if (log.level <= Level.WARNING) {
89-
transform.logger.warning(_logRecordToMessage(log));
90-
} else {
91-
transform.logger.error(_logRecordToMessage(log));
92-
}
93-
});
94-
await builder.build(buildStep);
95-
await buildStep.complete();
96-
await logSubscription.cancel();
97-
}));
66+
// Run the build step.
67+
var buildStep = new BuildStepImpl(
68+
input, expected, reader, writer, input.id.package, _resolvers);
69+
Logger.root.level = Level.ALL;
70+
var logSubscription = buildStep.logger.onRecord.listen((LogRecord log) {
71+
if (log.loggerName != buildStep.logger.fullName) return;
72+
73+
if (log.level <= Level.CONFIG) {
74+
transform.logger.fine(_logRecordToMessage(log));
75+
} else if (log.level <= Level.INFO) {
76+
transform.logger.info(_logRecordToMessage(log));
77+
} else if (log.level <= Level.WARNING) {
78+
transform.logger.warning(_logRecordToMessage(log));
79+
} else {
80+
transform.logger.error(_logRecordToMessage(log));
81+
}
82+
});
83+
await _builder.build(buildStep);
84+
await buildStep.complete();
85+
await logSubscription.cancel();
9886
}
9987

10088
String _logRecordToMessage(LogRecord log) {
@@ -111,13 +99,12 @@ class BuilderTransformer implements Transformer, DeclaringTransformer {
11199

112100
@override
113101
void declareOutputs(DeclaringTransform transform) {
114-
for (var outputId in _expectedOutputs(transform.primaryId, builders)) {
115-
transform.declareOutput(toBarbackAssetId(outputId));
116-
}
102+
var outputs = _builder.declareOutputs(toBuildAssetId(transform.primaryId));
103+
outputs.map(toBarbackAssetId).forEach(transform.declareOutput);
117104
}
118105

119106
@override
120-
String toString() => 'BuilderTransformer: [$builders]';
107+
String toString() => 'BuilderTransformer: $_builder';
121108
}
122109

123110
/// Very simple [AssetReader] which uses a [Transform].
@@ -159,11 +146,3 @@ class _TransformAssetWriter implements AssetWriter {
159146
Future delete(build.AssetId id) =>
160147
throw new UnsupportedError('_TransformAssetWriter can\'t delete files.');
161148
}
162-
163-
/// All the expected outputs for [id] given [builders].
164-
Iterable<build.AssetId> _expectedOutputs(
165-
barback.AssetId id, Iterable<Builder> builders) sync* {
166-
for (var builder in builders) {
167-
yield* builder.declareOutputs(toBuildAssetId(id));
168-
}
169-
}

pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: build
2-
version: 0.4.1+3
2+
version: 0.5.0
33
description: A build system for Dart.
44
author: Dart Team <[email protected]>
55
homepage: https://github.com/dart-lang/build

test/transformer/transformer_test.dart

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,12 @@ import 'package:transformer_test/utils.dart';
1111
import '../common/common.dart' hide testPhases;
1212

1313
void main() {
14-
var singleCopyTransformer = new BuilderTransformer([new CopyBuilder()]);
14+
var singleCopyTransformer = new BuilderTransformer(new CopyBuilder());
1515
var multiCopyTransformer =
16-
new BuilderTransformer([new CopyBuilder(numCopies: 2)]);
16+
new BuilderTransformer(new CopyBuilder(numCopies: 2));
1717
var singleAndMultiCopyTransformer = new BuilderTransformer(
18-
[new CopyBuilder(), new CopyBuilder(numCopies: 2)]);
18+
new MultiplexingBuilder(
19+
[new CopyBuilder(), new CopyBuilder(numCopies: 2)]));
1920

2021
testPhases('single builder, single output', [
2122
[singleCopyTransformer],
@@ -46,12 +47,14 @@ void main() {
4647

4748
testPhases('multiple builders, same outputs', [
4849
[
49-
new BuilderTransformer([new CopyBuilder(), new CopyBuilder()])
50+
new BuilderTransformer(
51+
new MultiplexingBuilder([new CopyBuilder(), new CopyBuilder()]))
5052
],
5153
], {
5254
'a|web/a.txt': 'hello',
5355
}, {}, messages: [
54-
_fileExistsError('CopyBuilder', ['a|web/a.txt.copy']),
56+
_fileExistsError(
57+
'${[new CopyBuilder(), new CopyBuilder()]}', ['a|web/a.txt.copy']),
5558
]);
5659

5760
testPhases('multiple phases', [
@@ -85,18 +88,15 @@ void main() {
8588
{'a|web/a.txt': 'hello', 'a|web/a.txt.copy': 'hello'},
8689
{},
8790
messages: [
88-
_fileExistsError("CopyBuilder", ["a|web/a.txt.copy"]),
91+
_fileExistsError('${new CopyBuilder()}', ['a|web/a.txt.copy']),
8992
],
9093
expectBarbackErrors: true);
9194

9295
// Gives a barback error only, we can't detect this situation.
9396
testPhases(
9497
'builders in the same phase can\'t output the same file',
9598
[
96-
[
97-
singleCopyTransformer,
98-
new BuilderTransformer([new CopyBuilder()])
99-
]
99+
[singleCopyTransformer, new BuilderTransformer(new CopyBuilder())]
100100
],
101101
{
102102
'a|web/a.txt': 'hello',
@@ -113,14 +113,12 @@ void main() {
113113
{'a|web/a.txt': 'hello'},
114114
{'a|web/a.txt.copy': 'hello'},
115115
messages: [
116-
_fileExistsError("CopyBuilder", ["a|web/a.txt.copy"]),
116+
_fileExistsError('${new CopyBuilder()}', ['a|web/a.txt.copy']),
117117
],
118118
expectBarbackErrors: true);
119119

120120
testPhases('loggers log errors', [
121-
[
122-
new BuilderTransformer([new LoggingCopyBuilder()])
123-
],
121+
[new BuilderTransformer(new LoggingCopyBuilder())],
124122
], {
125123
'a|web/a.txt': 'a',
126124
'a|web/b.txt': 'b',
@@ -152,7 +150,7 @@ class LoggingCopyBuilder extends CopyBuilder {
152150
}
153151

154152
String _fileExistsError(String builder, List<String> files) {
155-
return "error: Builder `Instance of '$builder'` declared outputs "
156-
"`$files` but those files already exist. That build step has been "
153+
return "error: Builder `$builder` declared outputs "
154+
"`$files` but those files already exist. This build step has been "
157155
"skipped.";
158156
}

0 commit comments

Comments
 (0)