-
Notifications
You must be signed in to change notification settings - Fork 204
/
builder_ordering.dart
49 lines (45 loc) · 2.15 KB
/
builder_ordering.dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// Copyright (c) 2017, 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_config/build_config.dart';
import 'package:graphs/graphs.dart';
/// Put [builders] into an order such that any builder which specifies
/// [BuilderDefinition.requiredInputs] will come after any builder which
/// produces a desired output.
///
/// Builders will be ordered such that their `required_inputs` and `runs_before`
/// constraints are met, but the rest of the ordering is arbitrary.
Iterable<BuilderDefinition> findBuilderOrder(
Iterable<BuilderDefinition> builders,
Map<String, GlobalBuilderConfig> globalBuilderConfigs) {
final consistentOrderBuilders = builders.toList()
..sort((a, b) => a.key.compareTo(b.key));
Iterable<BuilderDefinition> dependencies(BuilderDefinition parent) =>
consistentOrderBuilders.where((child) =>
_hasInputDependency(parent, child) ||
_mustRunBefore(parent, child, globalBuilderConfigs));
var components = stronglyConnectedComponents<BuilderDefinition>(
consistentOrderBuilders,
dependencies,
equals: (a, b) => a.key == b.key,
hashCode: (b) => b.key.hashCode,
);
return components.map((component) {
if (component.length > 1) {
throw ArgumentError('Required input cycle for ${component.toList()}');
}
return component.single;
}).toList();
}
/// Whether [parent] has a `required_input` that wants to read outputs produced
/// by [child].
bool _hasInputDependency(BuilderDefinition parent, BuilderDefinition child) {
final childOutputs = child.buildExtensions.values.expand((v) => v).toSet();
return parent.requiredInputs
.any((input) => childOutputs.any((output) => output.endsWith(input)));
}
/// Whether [child] specifies that it wants to run before [parent].
bool _mustRunBefore(BuilderDefinition parent, BuilderDefinition child,
Map<String, GlobalBuilderConfig?> globalBuilderConfigs) =>
child.runsBefore.contains(parent.key) ||
(globalBuilderConfigs[child.key]?.runsBefore.contains(parent.key) ?? false);