Skip to content

Commit 9480f60

Browse files
committed
Update the phases api, add PhaseGroup, Phase, BuildAction.
All top level methods now take a PhaseGroup instead of a List<List<Phase>>.
1 parent 46f99fb commit 9480f60

File tree

14 files changed

+225
-250
lines changed

14 files changed

+225
-250
lines changed

e2e_example/lib/copy_builder.dart

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,9 @@ class CopyBuilder extends Builder {
2020
List<AssetId> declareOutputs(AssetId inputId) => [_copiedAssetId(inputId)];
2121

2222
/// Only runs on the root package, and copies all *.txt files.
23-
static List<Phase> buildPhases(PackageGraph graph) {
24-
var phase = new Phase([
25-
new CopyBuilder()
26-
], [
27-
new InputSet(graph.root.name, filePatterns: ['**/*.txt'])
28-
]);
29-
return [phase];
23+
static void addPhases(PhaseGroup group, PackageGraph graph) {
24+
group.newPhase().addAction(
25+
new CopyBuilder(), new InputSet(graph.root.name, ['**/*.txt']));
3026
}
3127
}
3228

e2e_example/tool/build.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,14 @@ import 'package:e2e_example/copy_builder.dart';
1010
main() async {
1111
/// Builds a full package dependency graph for the current package.
1212
var graph = new PackageGraph.forThisPackage();
13+
var phases = new PhaseGroup();
1314

1415
/// Give [Builder]s access to a [PackageGraph] so they can choose which
1516
/// packages to run on. This simplifies user code a lot, and helps to mitigate
1617
/// the transitive deps issue.
17-
var phases = CopyBuilder.buildPhases(graph);
18+
CopyBuilder.addPhases(phases, graph);
1819

19-
var result = await build([phases]);
20+
var result = await build(phases);
2021

2122
if (result.status == BuildStatus.Success) {
2223
stdout.writeln(result);

e2e_example/tool/watch.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,14 @@ import 'package:e2e_example/copy_builder.dart';
1010
main() async {
1111
/// Builds a full package dependency graph for the current package.
1212
var graph = new PackageGraph.forThisPackage();
13+
var phases = new PhaseGroup();
1314

1415
/// Give [Builder]s access to a [PackageGraph] so they can choose which
1516
/// packages to run on. This simplifies user code a lot, and helps to mitigate
1617
/// the transitive deps issue.
17-
var phases = CopyBuilder.buildPhases(graph);
18+
CopyBuilder.addPhases(phases, graph);
1819

19-
await for (var result in watch([phases])) {
20+
await for (var result in watch(phases)) {
2021
if (result.status == BuildStatus.Success) {
2122
stdout.writeln(result);
2223
} else {

lib/src/generate/build.dart

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ import 'watch_impl.dart';
3737
/// By default the [ProcessSignal.SIGINT] stream is used. In this mode, it
3838
/// will simply consume the first event and allow the build to continue.
3939
/// Multiple termination events will cause a normal shutdown.
40-
Future<BuildResult> build(List<List<Phase>> phaseGroups,
40+
Future<BuildResult> build(PhaseGroup phaseGroup,
4141
{PackageGraph packageGraph,
4242
AssetReader reader,
4343
AssetWriter writer,
@@ -50,7 +50,7 @@ Future<BuildResult> build(List<List<Phase>> phaseGroups,
5050
writer: writer,
5151
logLevel: logLevel,
5252
onLog: onLog);
53-
var buildImpl = new BuildImpl(options, phaseGroups);
53+
var buildImpl = new BuildImpl(options, phaseGroup);
5454

5555
/// Run the build!
5656
var futureResult = buildImpl.runBuild();
@@ -82,7 +82,7 @@ Future<BuildResult> build(List<List<Phase>> phaseGroups,
8282
/// first event will allow any ongoing builds to finish, and then the program
8383
/// will complete normally. Subsequent events are not handled (and will
8484
/// typically cause a shutdown).
85-
Stream<BuildResult> watch(List<List<Phase>> phaseGroups,
85+
Stream<BuildResult> watch(PhaseGroup phaseGroup,
8686
{PackageGraph packageGraph,
8787
AssetReader reader,
8888
AssetWriter writer,
@@ -99,7 +99,7 @@ Stream<BuildResult> watch(List<List<Phase>> phaseGroups,
9999
onLog: onLog,
100100
debounceDelay: debounceDelay,
101101
directoryWatcherFactory: directoryWatcherFactory);
102-
var watchImpl = new WatchImpl(options, phaseGroups);
102+
var watchImpl = new WatchImpl(options, phaseGroup);
103103

104104
var resultStream = watchImpl.runWatch();
105105

@@ -119,7 +119,7 @@ Stream<BuildResult> watch(List<List<Phase>> phaseGroups,
119119
/// By default a static server will be set up to serve [directory] at
120120
/// [address]:[port], but instead a [requestHandler] may be provided for custom
121121
/// behavior.
122-
Stream<BuildResult> serve(List<List<Phase>> phaseGroups,
122+
Stream<BuildResult> serve(PhaseGroup phaseGroup,
123123
{PackageGraph packageGraph,
124124
AssetReader reader,
125125
AssetWriter writer,
@@ -143,7 +143,7 @@ Stream<BuildResult> serve(List<List<Phase>> phaseGroups,
143143
directory: directory,
144144
address: address,
145145
port: port);
146-
var watchImpl = new WatchImpl(options, phaseGroups);
146+
var watchImpl = new WatchImpl(options, phaseGroup);
147147

148148
var resultStream = watchImpl.runWatch();
149149
var serverStarted = startServer(watchImpl, options);

lib/src/generate/build_impl.dart

Lines changed: 36 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -37,17 +37,18 @@ class BuildImpl {
3737
final AssetReader _reader;
3838
final AssetWriter _writer;
3939
final PackageGraph _packageGraph;
40-
final List<List<Phase>> _phaseGroups;
40+
final List<List<BuildAction>> _buildActions;
4141
final _inputsByPackage = <String, Set<AssetId>>{};
4242
bool _buildRunning = false;
4343
final _logger = new Logger('Build');
4444

4545
bool _isFirstBuild = true;
4646

47-
BuildImpl(BuildOptions options, this._phaseGroups)
47+
BuildImpl(BuildOptions options, PhaseGroup phaseGroup)
4848
: _reader = options.reader,
4949
_writer = options.writer,
50-
_packageGraph = options.packageGraph;
50+
_packageGraph = options.packageGraph,
51+
_buildActions = phaseGroup.buildActions;
5152

5253
/// Runs a build
5354
///
@@ -313,20 +314,17 @@ class BuildImpl {
313314
// No cache file exists, run `declareOutputs` on all phases and collect all
314315
// outputs which conflict with existing assets.
315316
final conflictingOutputs = new Set<AssetId>();
316-
for (var group in _phaseGroups) {
317+
for (var phase in _buildActions) {
317318
final groupOutputIds = <AssetId>[];
318-
for (var phase in group) {
319-
var inputs = _matchingInputs(phase.inputSets);
319+
for (var action in phase) {
320+
var inputs = _matchingInputs(action.inputSet);
320321
for (var input in inputs) {
321-
for (var builder in phase.builders) {
322-
var outputs = builder.declareOutputs(input);
323-
324-
groupOutputIds.addAll(outputs);
325-
for (var output in outputs) {
326-
if (tempInputsByPackage[output.package]?.contains(output) ==
327-
true) {
328-
conflictingOutputs.add(output);
329-
}
322+
var outputs = action.builder.declareOutputs(input);
323+
324+
groupOutputIds.addAll(outputs);
325+
for (var output in outputs) {
326+
if (tempInputsByPackage[output.package]?.contains(output) == true) {
327+
conflictingOutputs.add(output);
330328
}
331329
}
332330
}
@@ -375,53 +373,48 @@ class BuildImpl {
375373
}
376374
}
377375

378-
/// Runs the [_phaseGroups] and returns a [Future<BuildResult>] which
379-
/// completes once all [Phase]s are done.
376+
/// Runs the [Phase]s in [_buildActions] and returns a [Future<BuildResult>]
377+
/// which completes once all [BuildAction]s are done.
380378
Future<BuildResult> _runPhases() async {
381379
final outputs = <Asset>[];
382-
int phaseGroupNum = 0;
383-
for (var group in _phaseGroups) {
380+
for (var phase in _buildActions) {
384381
/// Collects all the ids for files which are output by this stage. This
385382
/// also includes files which didn't get regenerated because they weren't,
386383
/// dirty unlike [outputs] which only gets files which were explicitly
387384
/// generated in this build.
388-
final groupOutputIds = new Set<AssetId>();
389-
for (var phase in group) {
390-
var inputs = _matchingInputs(phase.inputSets);
391-
for (var builder in phase.builders) {
392-
// TODO(jakemac): Optimize, we can run all the builders in a phase
393-
// at the same time instead of sequentially.
394-
await for (var output
395-
in _runBuilder(builder, inputs, phaseGroupNum, groupOutputIds)) {
396-
outputs.add(output);
397-
}
385+
final phaseOutputIds = new Set<AssetId>();
386+
for (var action in phase) {
387+
var inputs = _matchingInputs(action.inputSet);
388+
// TODO(jakemac): Optimize, we can run all the builders in a phase
389+
// at the same time instead of sequentially.
390+
await for (var output
391+
in _runBuilder(action.builder, inputs, phaseOutputIds)) {
392+
outputs.add(output);
398393
}
399394
}
400395

401396
/// Once the group is done, add all outputs so they can be used in the next
402397
/// phase.
403-
for (var outputId in groupOutputIds) {
398+
for (var outputId in phaseOutputIds) {
404399
_inputsByPackage.putIfAbsent(
405400
outputId.package, () => new Set<AssetId>());
406401
_inputsByPackage[outputId.package].add(outputId);
407402
}
408-
phaseGroupNum++;
409403
}
410404
return new BuildResult(BuildStatus.Success, BuildType.Full, outputs);
411405
}
412406

413407
/// Initializes the map of all the available inputs by package.
414408
Future _initializeInputsByPackage() async {
415409
final packages = new Set<String>();
416-
for (var group in _phaseGroups) {
417-
for (var phase in group) {
418-
for (var inputSet in phase.inputSets) {
419-
packages.add(inputSet.package);
420-
}
410+
for (var phase in _buildActions) {
411+
for (var action in phase) {
412+
packages.add(action.inputSet.package);
421413
}
422414
}
423415

424-
var inputSets = packages.map((package) => new InputSet(package));
416+
var inputSets = packages.map((package) => new InputSet(
417+
package, [package == _packageGraph.root.name ? '**/*' : 'lib/**']));
425418
var allInputs = await _reader.listAssetIds(inputSets).toList();
426419
_inputsByPackage.clear();
427420
for (var input in allInputs) {
@@ -434,14 +427,12 @@ class BuildImpl {
434427
}
435428

436429
/// Gets a list of all inputs matching [inputSets].
437-
Set<AssetId> _matchingInputs(Iterable<InputSet> inputSets) {
430+
Set<AssetId> _matchingInputs(InputSet inputSet) {
438431
var inputs = new Set<AssetId>();
439-
for (var inputSet in inputSets) {
440-
assert(_inputsByPackage.containsKey(inputSet.package));
441-
for (var input in _inputsByPackage[inputSet.package]) {
442-
if (inputSet.globs.any((g) => g.matches(input.path))) {
443-
inputs.add(input);
444-
}
432+
assert(_inputsByPackage.containsKey(inputSet.package));
433+
for (var input in _inputsByPackage[inputSet.package]) {
434+
if (inputSet.globs.any((g) => g.matches(input.path))) {
435+
inputs.add(input);
445436
}
446437
}
447438
return inputs;
@@ -458,7 +449,7 @@ class BuildImpl {
458449

459450
/// Runs [builder] with [inputs] as inputs.
460451
Stream<Asset> _runBuilder(Builder builder, Iterable<AssetId> primaryInputs,
461-
int phaseGroupNum, Set<AssetId> groupOutputs) async* {
452+
Set<AssetId> groupOutputs) async* {
462453
for (var input in primaryInputs) {
463454
var expectedOutputs = builder.declareOutputs(input);
464455

lib/src/generate/input_set.dart

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,17 @@ class InputSet {
1616
/// listed.
1717
final List<Glob> globs;
1818

19-
InputSet(this.package, {Iterable<String> filePatterns})
20-
: this.globs = _globsFor(filePatterns);
21-
}
19+
InputSet(this.package, Iterable<String> globs)
20+
: this.globs =
21+
new List.unmodifiable(globs.map((pattern) => new Glob(pattern)));
2222

23-
List<Glob> _globsFor(Iterable<String> filePatterns) {
24-
filePatterns ??= ['**/*'];
25-
return new List.unmodifiable(
26-
filePatterns.map((pattern) => new Glob(pattern)));
23+
String toString() {
24+
var buffer = new StringBuffer()
25+
..write('InputSet: package `$package` with globs');
26+
for (var glob in globs) {
27+
buffer.write(' `${glob.pattern}`');
28+
}
29+
buffer.writeln('');
30+
return buffer.toString();
31+
}
2732
}

lib/src/generate/phase.dart

Lines changed: 66 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,73 @@
44
import '../builder/builder.dart';
55
import 'input_set.dart';
66

7-
/// A single phase in the build process. None of the [Builder]s in a single
8-
/// phase should depend on any of the outputs of other [Builder]s in that same
9-
/// phase.
7+
/// One action in a [Phase].
8+
///
9+
/// Comprised of a single [Builder] and a single [InputSet].
10+
///
11+
/// These should be constructed using [Phase#addAction].
12+
class BuildAction {
13+
final Builder builder;
14+
final InputSet inputSet;
15+
16+
BuildAction._(this.builder, this.inputSet);
17+
}
18+
19+
/// A collection of [BuildAction]s that can be ran simultaneously.
20+
///
21+
/// None of the [BuildAction]s in a phase may read the outputs of other
22+
/// [BuildAction]s in the same phase.
1023
class Phase {
11-
/// The list of all [Builder]s that should be run on all [InputSet]s.
12-
final List<Builder> builders;
24+
final _actions = <BuildAction>[];
25+
List<BuildAction> get actions => new List.unmodifiable(_actions);
26+
27+
/// Creates a new [BuildAction] and adds it to this [Phase].
28+
addAction(Builder builder, InputSet inputSet) {
29+
_actions.add(new BuildAction._(builder, inputSet));
30+
}
31+
}
32+
33+
/// A list of [Phase]s which will be ran sequentially. Later [Phase]s may read
34+
/// the outputs of previous [Phase]s.
35+
class PhaseGroup {
36+
final _phases = <Phase>[];
37+
List<List<BuildAction>> get buildActions =>
38+
new List.unmodifiable(_phases.map((phase) => phase.actions));
39+
40+
PhaseGroup();
41+
42+
/// Helper method for the simple use case of a single [Builder] and single
43+
/// [InputSet].
44+
factory PhaseGroup.singleAction(Builder builder, InputSet inputSet) {
45+
var group = new PhaseGroup();
46+
group.newPhase().addAction(builder, inputSet);
47+
return group;
48+
}
49+
50+
/// Creates a new [Phase] and adds it to this [PhaseGroup].
51+
Phase newPhase() {
52+
var phase = new Phase();
53+
_phases.add(phase);
54+
return phase;
55+
}
56+
57+
/// Adds a [Phase] to this [PhaseGroup].
58+
void addPhase(Phase phase) {
59+
_phases.add(phase);
60+
}
1361

14-
/// The list of all [InputSet]s that should be used as primary inputs.
15-
final List<InputSet> inputSets;
62+
String toString() {
63+
var buffer = new StringBuffer();
64+
for (int i = 0; i < _phases.length; i++) {
65+
buffer.writeln('Phase $i:');
66+
for (var action in _phases[i].actions) {
67+
buffer
68+
..writeln(' Action:')
69+
..writeln(' Builder: ${action.builder}')
70+
..writeln(' ${action.inputSet}');
71+
}
72+
}
1673

17-
Phase(List<Builder> builders, List<InputSet> inputSets)
18-
: builders = new List.unmodifiable(builders),
19-
inputSets = new List.unmodifiable(inputSets);
74+
return buffer.toString();
75+
}
2076
}

lib/src/generate/watch_impl.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,12 +61,12 @@ class WatchImpl {
6161
/// Whether we are in the process of terminating.
6262
bool _terminating = false;
6363

64-
WatchImpl(BuildOptions options, List<List<Phase>> phaseGroups)
64+
WatchImpl(BuildOptions options, PhaseGroup phaseGroup)
6565
: _directoryWatcherFactory = options.directoryWatcherFactory,
6666
_debounceDelay = options.debounceDelay,
6767
_writer = options.writer,
6868
_packageGraph = options.packageGraph,
69-
_buildImpl = new BuildImpl(options, phaseGroups);
69+
_buildImpl = new BuildImpl(options, phaseGroup);
7070

7171
/// Completes after the current build is done, and stops further builds from
7272
/// happening.

0 commit comments

Comments
 (0)