Skip to content

Commit 5909d7c

Browse files
authored
Skip duplicate checks on startup. (#4233)
1 parent 423ca3d commit 5909d7c

File tree

11 files changed

+89
-37
lines changed

11 files changed

+89
-37
lines changed

build_runner/lib/src/bootstrap/bootstrapper.dart

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ class Bootstrapper {
4848
await _writeBuildScript();
4949

5050
// Compile if there was any change.
51-
if (!_compiler.checkFreshness().outputIsFresh) {
51+
if (!_compiler.checkFreshness(digestsAreFresh: false).outputIsFresh) {
5252
final result = await _compiler.compile(experiments: experiments);
5353
if (!result.succeeded) {
5454
if (result.messages != null) {
@@ -93,15 +93,25 @@ class Bootstrapper {
9393
}
9494

9595
/// Checks freshness of the entrypoint script compiled to kernel.
96-
Future<FreshnessResult> checkKernelFreshness() async {
96+
///
97+
/// Set [digestsAreFresh] if digests were very recently updated. Then, they
98+
/// will be re-used from disk if possible instead of recomputed.
99+
Future<FreshnessResult> checkKernelFreshness({
100+
required bool digestsAreFresh,
101+
}) async {
97102
if (!ChildProcess.isRunning) {
98103
// Any real use or realistic test has a child process; so this is only hit
99104
// in small tests. Return "fresh" so nothing related to recompiling is
100105
// triggered.
101106
return FreshnessResult(outputIsFresh: true);
102107
}
108+
if (digestsAreFresh) {
109+
final maybeResult = _compiler.checkFreshness(digestsAreFresh: true);
110+
if (maybeResult.outputIsFresh) return maybeResult;
111+
// Digest file must be missing, continue to compile.
112+
}
103113
await _writeBuildScript();
104-
return _compiler.checkFreshness();
114+
return _compiler.checkFreshness(digestsAreFresh: false);
105115
}
106116

107117
/// Whether [path] is a dependency of the entrypoint script compiled to

build_runner/lib/src/bootstrap/depfile.dart

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,27 @@ class Depfile {
2929

3030
/// Checks whether the inputs mentioned in the depfile are fresh.
3131
///
32-
/// It is fresh if it has not changed and none of its inputs have changed.
33-
FreshnessResult checkFreshness() {
32+
/// They are fresh if the output exists, the depfile exists, the digest file
33+
/// exists and none of the input files has changed.
34+
///
35+
/// Set [digestsAreFresh] if digests were very recently updated. Then, they
36+
/// will be re-used from disk if possible instead of recomputed.
37+
FreshnessResult checkFreshness({required bool digestsAreFresh}) {
3438
final outputFile = File(outputPath);
3539
if (!outputFile.existsSync()) return FreshnessResult(outputIsFresh: false);
3640
final depsFile = File(depfilePath);
3741
if (!depsFile.existsSync()) return FreshnessResult(outputIsFresh: false);
3842
final digestFile = File(digestPath);
3943
if (!digestFile.existsSync()) return FreshnessResult(outputIsFresh: false);
4044
final digests = digestFile.readAsStringSync();
41-
final expectedDigests = _computeDigest();
45+
46+
if (digestsAreFresh) {
47+
_depfilePaths ??= _readPaths();
48+
return FreshnessResult(outputIsFresh: true, digest: digests);
49+
}
50+
51+
_depfilePaths = _readPaths();
52+
final expectedDigests = _digestPaths();
4253
return digests == expectedDigests
4354
? FreshnessResult(outputIsFresh: true, digest: digests)
4455
: FreshnessResult(outputIsFresh: false);
@@ -53,18 +64,18 @@ class Depfile {
5364
/// Writes a digest of all input files mentioned in [depfilePath] to
5465
/// [digestPath].
5566
void writeDigest() {
56-
File(digestPath).writeAsStringSync(_computeDigest());
67+
_depfilePaths = _readPaths();
68+
File(digestPath).writeAsStringSync(_digestPaths());
5769
}
5870

59-
String _computeDigest() {
71+
/// Reads input paths from the depfile.
72+
Set<String> _readPaths() {
6073
final depsFile = File(depfilePath).readAsStringSync();
61-
final paths = parse(depsFile);
62-
_depfilePaths = paths.toSet();
63-
return _digestPaths(paths);
74+
return parse(depsFile);
6475
}
6576

6677
@visibleForTesting
67-
static List<String> parse(String deps) {
78+
static Set<String> parse(String deps) {
6879
// The depfile has the output path followed by a colon then a
6980
// space-separated list of input paths. Backslashes and spaces in paths are
7081
// backslash escaped.
@@ -75,19 +86,25 @@ class Depfile {
7586
.replaceAll(r'\ ', '\u0000')
7687
// And unescape backslashes.
7788
.replaceAll(r'\\', r'\')
78-
.split(' ')
79-
.map((item) => item.replaceAll('\u0000', ' '));
80-
81-
final result = items.skip(1).toList();
82-
// File ends in a newline.
83-
result.last = result.last.substring(0, result.last.length - 1);
89+
.split(' ');
90+
91+
final result = <String>{};
92+
// The first item is the output path, skip it.
93+
for (var i = 1; i != items.length; ++i) {
94+
final item = items[i];
95+
final path = item.replaceAll('\u0000', ' ');
96+
// File ends in a newline.
97+
result.add(
98+
i == items.length - 1 ? path.substring(0, path.length - 1) : path,
99+
);
100+
}
84101
return result;
85102
}
86103

87-
String _digestPaths(Iterable<String> deps) {
104+
String _digestPaths() {
88105
final digestSink = AccumulatorSink<Digest>();
89106
final result = md5.startChunkedConversion(digestSink);
90-
for (final dep in deps) {
107+
for (final dep in _depfilePaths!) {
91108
final file = File(dep);
92109
if (file.existsSync()) {
93110
result.add([1]);

build_runner/lib/src/bootstrap/kernel_compiler.dart

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,11 @@ class KernelCompiler {
2121
);
2222

2323
/// Checks freshness of the build script compiled kernel.
24-
FreshnessResult checkFreshness() => _outputDepfile.checkFreshness();
24+
///
25+
/// Set [digestsAreFresh] if digests were very recently updated. Then, they
26+
/// will be re-used from disk if possible instead of recomputed.
27+
FreshnessResult checkFreshness({required bool digestsAreFresh}) =>
28+
_outputDepfile.checkFreshness(digestsAreFresh: digestsAreFresh);
2529

2630
/// Checks whether [path] in a dependency of the build script compiled kernel.
2731
///

build_runner/lib/src/build/build_series.dart

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -206,22 +206,32 @@ class BuildSeries {
206206
///
207207
/// For further builds, pass the changes since the previous builds as
208208
/// [updates].
209+
///
210+
/// Set [recentlyBootstrapped] to skip doing checks that are done during
211+
/// bootstrapping. If [recentlyBootstrapped] then [updates] must be empty.
209212
Future<BuildResult> run(
210213
Map<AssetId, ChangeType> updates, {
214+
required bool recentlyBootstrapped,
211215
BuiltSet<BuildDirectory>? buildDirs,
212216
BuiltSet<BuildFilter>? buildFilters,
213217
}) async {
214218
if (_closingCompleter.isCompleted) {
215219
throw StateError('BuildSeries was closed.');
216220
}
217221

218-
final kernelFreshness =
219-
await _buildPlan.bootstrapper.checkKernelFreshness();
220-
if (!kernelFreshness.outputIsFresh) {
221-
final result = BuildResult.buildScriptChanged();
222-
_buildResultsController.add(result);
223-
await close();
224-
return result;
222+
if (recentlyBootstrapped) {
223+
if (updates.isNotEmpty) {
224+
throw StateError('`recentlyBootstrapped` but updates not empty.');
225+
}
226+
} else {
227+
final kernelFreshness = await _buildPlan.bootstrapper
228+
.checkKernelFreshness(digestsAreFresh: false);
229+
if (!kernelFreshness.outputIsFresh) {
230+
final result = BuildResult.buildScriptChanged();
231+
_buildResultsController.add(result);
232+
await close();
233+
return result;
234+
}
225235
}
226236

227237
if (updates.keys.any(_isBuildConfiguration)) {

build_runner/lib/src/build_plan/build_plan.dart

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -96,14 +96,20 @@ class BuildPlan {
9696
/// Files that should be deleted before restarting or building are accumulated
9797
/// in [filesToDelete] and [foldersToDelete]. Call [deleteFilesAndFolders] to
9898
/// delete them.
99+
///
100+
/// Set [recentlyBootstrapped] to false to do checks that are also done during
101+
/// bootstrapping.
99102
static Future<BuildPlan> load({
100103
required BuilderFactories builderFactories,
101104
required BuildOptions buildOptions,
102105
required TestingOverrides testingOverrides,
106+
bool recentlyBootstrapped = true,
103107
}) async {
104108
final bootstrapper = Bootstrapper();
105109
var restartIsNeeded = false;
106-
final kernelFreshness = await bootstrapper.checkKernelFreshness();
110+
final kernelFreshness = await bootstrapper.checkKernelFreshness(
111+
digestsAreFresh: recentlyBootstrapped,
112+
);
107113
if (!kernelFreshness.outputIsFresh) {
108114
restartIsNeeded = true;
109115
}
@@ -350,15 +356,16 @@ class BuildPlan {
350356

351357
/// Reloads the build plan.
352358
///
353-
/// Works just like a new load of the build plan, but supresses the usual log
354-
/// output.
359+
/// Works just like a new load of the build plan, but sets
360+
/// `recentlyBootstrapped` to `false` to redo checks from bootstrapping.
355361
///
356362
/// The caller must call [deleteFilesAndFolders] on the result and check
357363
/// [restartIsNeeded].
358364
Future<BuildPlan> reload() => BuildPlan.load(
359365
builderFactories: builderFactories,
360366
buildOptions: buildOptions,
361367
testingOverrides: testingOverrides,
368+
recentlyBootstrapped: false,
362369
);
363370
}
364371

build_runner/lib/src/commands/build_command.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ class BuildCommand implements BuildRunnerCommand {
5757
}
5858

5959
final buildSeries = BuildSeries(buildPlan);
60-
final result = await buildSeries.run({});
60+
final result = await buildSeries.run({}, recentlyBootstrapped: true);
6161
await buildSeries.close();
6262
return result;
6363
}

build_runner/lib/src/commands/daemon/daemon_builder.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ class BuildRunnerDaemonBuilder implements DaemonBuilder {
110110
final mergedChanges = collectChanges([changes]);
111111
final result = await buildSeries.run(
112112
mergedChanges,
113+
recentlyBootstrapped: false,
113114
buildDirs: buildDirs.build(),
114115
buildFilters: buildFilters.build(),
115116
);

build_runner/lib/src/commands/watch/watcher.dart

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,13 +78,16 @@ class Watcher {
7878
.ignore();
7979

8080
await graphWatcher.ready;
81-
await _buildSeries.run({});
81+
await _buildSeries.run({}, recentlyBootstrapped: true);
8282
}
8383

8484
Future<BuildResult> _doBuild(List<List<AssetChange>> changes) async {
8585
final mergedChanges = collectChanges(changes);
8686
_expectedDeletes.clear();
87-
final result = await _buildSeries.run(mergedChanges);
87+
final result = await _buildSeries.run(
88+
mergedChanges,
89+
recentlyBootstrapped: false,
90+
);
8891
return result;
8992
}
9093
}

build_runner/test/common/test_phases.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ Future<TestBuildersResult> testPhases(
154154

155155
BuildResult result;
156156
final buildSeries = BuildSeries(buildPlan);
157-
result = await buildSeries.run({});
157+
result = await buildSeries.run({}, recentlyBootstrapped: true);
158158
await buildSeries.close();
159159

160160
if (checkBuildStatus) {

build_runner/test/integration_tests/kernel_compiler_test.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import 'dart:io';
2727
import 'package:build_runner/src/bootstrap/kernel_compiler.dart';
2828
void main() async {
2929
final compiler = KernelCompiler();
30-
if (compiler.checkFreshness().outputIsFresh) {
30+
if (compiler.checkFreshness(digestsAreFresh: false).outputIsFresh) {
3131
stdout.write('fresh\n');
3232
} else {
3333
stdout.write('compiling\n');

0 commit comments

Comments
 (0)