Skip to content

Commit 85198d5

Browse files
committed
Hacking.
1 parent 3c66222 commit 85198d5

File tree

8 files changed

+126
-44
lines changed

8 files changed

+126
-44
lines changed

build_runner/bin/src/commands/generate_build_script.dart

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,10 @@ class GenerateBuildScript extends Command<int> {
3333
var buildScript = await generateBuildScript();
3434
File(scriptLocation)
3535
..createSync(recursive: true)
36-
..writeAsStringSync(buildScript);
36+
..writeAsStringSync(buildScript.script);
37+
File(scriptDepsPath)
38+
..createSync(recursive: true)
39+
..writeAsStringSync(buildScript.dependencyPaths.join('\n'));
3740
print(p.absolute(scriptLocation));
3841
return 0;
3942
}

build_runner/lib/src/build_script_generate/bootstrap.dart

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -26,31 +26,29 @@ import 'build_script_generate.dart';
2626
/// Returns the exit code from running the build script.
2727
///
2828
/// If an exit code of 75 is returned, this function should be re-ran.
29+
///
30+
/// Pass [script] to override the default build script for testing.
2931
Future<int> generateAndRun(
3032
List<String> args, {
3133
List<String>? experiments,
3234
Logger? logger,
33-
Future<String> Function() generateBuildScript = generateBuildScript,
3435
void Function(Object error, StackTrace stackTrace) handleUncaughtError =
3536
_defaultHandleUncaughtError,
37+
GeneratedScript? script,
3638
}) {
3739
return buildLog.runWithLoggerDisplay(
3840
logger,
39-
() => _generateAndRun(
40-
args,
41-
experiments,
42-
generateBuildScript,
43-
handleUncaughtError,
44-
),
41+
() =>
42+
_generateAndRun(args, experiments, handleUncaughtError, script: script),
4543
);
4644
}
4745

4846
Future<int> _generateAndRun(
4947
List<String> args,
5048
List<String>? experiments,
51-
Future<String> Function() generateBuildScript,
52-
void Function(Object error, StackTrace stackTrace) handleUncaughtError,
53-
) async {
49+
void Function(Object error, StackTrace stackTrace) handleUncaughtError, {
50+
GeneratedScript? script,
51+
}) async {
5452
experiments ??= [];
5553
ReceivePort? exitPort;
5654
ReceivePort? errorPort;
@@ -72,15 +70,15 @@ Future<int> _generateAndRun(
7270
if (buildScript.existsSync()) {
7371
oldContents = buildScript.readAsStringSync();
7472
}
75-
var newContents = await generateBuildScript();
76-
if (newContents == oldContents) {
77-
buildLog.debug('same contents');
78-
}
73+
var newContents = script ?? await generateBuildScript();
7974
// Only trigger a build script update if necessary.
80-
if (newContents != oldContents) {
75+
if (newContents.script != oldContents) {
8176
buildScript
8277
..createSync(recursive: true)
83-
..writeAsStringSync(newContents);
78+
..writeAsStringSync(newContents.script);
79+
File(scriptDepsPath)
80+
..createSync(recursive: true)
81+
..writeAsStringSync(newContents.dependencyPaths.join('\n'));
8482
// Delete the kernel file so it will be rebuilt.
8583
final kernelFile = File(scriptKernelLocation);
8684
if (kernelFile.existsSync()) {

build_runner/lib/src/build_script_generate/build_script_generate.dart

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import '../package_graph/build_config_overrides.dart';
1717
import 'builder_ordering.dart';
1818

1919
const scriptLocation = '$entryPointDir/build.dart';
20+
const scriptDepsPath = '$entryPointDir/build.dart.deps';
2021
const scriptKernelLocation = '$scriptLocation$scriptKernelSuffix';
2122
const scriptKernelSuffix = '.dill';
2223
const scriptKernelCachedLocation =
@@ -25,7 +26,7 @@ const scriptKernelCachedSuffix = '.cached';
2526

2627
final _lastShortFormatDartVersion = Version(3, 6, 0);
2728

28-
Future<String> generateBuildScript() async {
29+
Future<GeneratedScript> generateBuildScript() async {
2930
buildLog.doing('Generating the build script.');
3031
final info = await findBuildScriptOptions();
3132
final builders = info.builderApplications;
@@ -55,14 +56,15 @@ Future<String> generateBuildScript() async {
5556
// the host<->isolate relationship changed in a breaking way, for example
5657
// if command line args or messages passed via sendports have changed
5758
// in a breaking way.
58-
return DartFormatter(languageVersion: _lastShortFormatDartVersion).format(
59-
'''
59+
final script = DartFormatter(
60+
languageVersion: _lastShortFormatDartVersion,
61+
).format('''
6062
// @dart=${_lastShortFormatDartVersion.major}.${_lastShortFormatDartVersion.minor}
6163
// ignore_for_file: directives_ordering
6264
// build_runner >=2.4.16
6365
${library.accept(emitter)}
64-
''',
65-
);
66+
''');
67+
return GeneratedScript(script: script, dependencyPaths: info.inputs);
6668
} on FormatterException {
6769
buildLog.error(
6870
'Generated build script could not be parsed. '
@@ -195,13 +197,20 @@ Future<BuildScriptInfo> findBuildScriptOptions({
195197
_applyPostProcessBuilder(builder),
196198
];
197199

198-
return BuildScriptInfo(applications);
200+
final inputs = <String>[];
201+
for (final package in packageGraph.allPackages.values) {
202+
inputs.add('${package.path}/build.yaml');
203+
}
204+
inputs.sort();
205+
206+
return BuildScriptInfo(inputs, applications);
199207
}
200208

201209
class BuildScriptInfo {
210+
final List<String> inputs;
202211
final Iterable<Expression> builderApplications;
203212

204-
BuildScriptInfo(this.builderApplications);
213+
BuildScriptInfo(this.inputs, this.builderApplications);
205214
}
206215

207216
/// A method forwarding to `run`.
@@ -409,3 +418,10 @@ extension ConvertToExpression on Object? {
409418
}
410419
}
411420
}
421+
422+
class GeneratedScript {
423+
String script;
424+
List<String> dependencyPaths;
425+
426+
GeneratedScript({required this.script, required this.dependencyPaths});
427+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// Copyright (c) 2025, 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+
5+
import 'dart:async';
6+
import 'dart:convert';
7+
import 'dart:io';
8+
9+
import 'package:convert/convert.dart';
10+
import 'package:crypto/crypto.dart';
11+
12+
void main(List<String> arguments) async {
13+
await Compiler().compile(arguments[0]);
14+
}
15+
16+
class Compiler {
17+
Future<void> compile(String path) async {
18+
final depfilePath = '$path.deps';
19+
final digestFilePath = '$path.digest';
20+
21+
final depfile = File(depfilePath);
22+
final digestFile = File(digestFilePath);
23+
24+
if (depfile.existsSync() && digestFile.existsSync()) {
25+
final expectedDigest = digestFile.readAsStringSync();
26+
final actualDigest = computeDigest(parseDepfile(depfilePath));
27+
if (expectedDigest == actualDigest) {
28+
print('Input digests matched, nothing to do.');
29+
return;
30+
} else {
31+
print(
32+
'Input digests changed from $expectedDigest to $actualDigest, rebuild.',
33+
);
34+
}
35+
}
36+
37+
final result = await Process.run('dart', [
38+
'compile',
39+
'kernel',
40+
path,
41+
'--depfile',
42+
depfilePath,
43+
]);
44+
if (result.exitCode != 0) {
45+
print('Compile failed: ${result.stdout} ${result.stderr}');
46+
exit(1);
47+
}
48+
final deps = parseDepfile(depfilePath);
49+
final digest = computeDigest(deps);
50+
digestFile.writeAsStringSync(digest);
51+
52+
//unawaited(dill.then((result) => print(result.stdout)));
53+
}
54+
55+
List<String> parseDepfile(String depfilePath) {
56+
// TODO(davidmorgan): handle spaces, they seem to be backslash escaped.
57+
final result =
58+
File(depfilePath).readAsStringSync().split(' ').skip(1).toList();
59+
// File ends in a newline.
60+
result.last = result.last.substring(0, result.last.length - 1);
61+
return result;
62+
}
63+
64+
String computeDigest(Iterable<String> deps) {
65+
final digestSink = AccumulatorSink<Digest>();
66+
final result = md5.startChunkedConversion(digestSink);
67+
for (final dep in deps) {
68+
result.add(File(dep).readAsBytesSync());
69+
}
70+
result.close();
71+
return base64.encode(digestSink.events.first.bytes);
72+
}
73+
}
6.3 MB
Binary file not shown.

build_runner/pubspec.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ dependencies:
2020
build_daemon: ^4.0.0
2121
build_runner_core: '9.2.1-wip'
2222
code_builder: ^4.2.0
23+
convert: ^3.1.2
2324
crypto: ^3.0.0
2425
dart_style: '>=2.3.7 <4.0.0'
2526
frontend_server_client: ">=3.0.0 <5.0.0"

build_runner/test/build_script_generate/bootstrap_test.dart

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,7 @@ void main() {
2828
test('writes dill', () async {
2929
await generateAndRun(
3030
[],
31-
generateBuildScript:
32-
() async => '''
31+
script: '''
3332
import 'dart:isolate';
3433
import 'package:build_runner/src/build_script_generate/build_process_state.dart';
3534
@@ -55,18 +54,17 @@ void main(_, [SendPort? sendPort]) async {
5554
''';
5655

5756
buildProcessState.isolateExitCode = 6;
58-
await generateAndRun([], generateBuildScript: () async => script);
57+
await generateAndRun([], script: script);
5958
expect(buildProcessState.isolateExitCode, 7);
60-
await generateAndRun([], generateBuildScript: () async => script);
59+
await generateAndRun([], script: script);
6160
expect(buildProcessState.isolateExitCode, 8);
6261
});
6362

6463
test('rewrites dill if script changed', () async {
6564
expect(
6665
await generateAndRun(
6766
[],
68-
generateBuildScript:
69-
() async => '''
67+
script: '''
7068
import 'dart:isolate';
7169
import 'package:build_runner/src/build_script_generate/build_process_state.dart';
7270
@@ -84,8 +82,7 @@ void main(_, [SendPort? sendPort]) async {
8482
expect(
8583
await generateAndRun(
8684
[],
87-
generateBuildScript:
88-
() async => '''
85+
script: '''
8986
import 'dart:isolate';
9087
import 'package:build_runner/src/build_script_generate/build_process_state.dart';
9188
@@ -109,8 +106,7 @@ void main(_, [SendPort? sendPort]) async {
109106
expect(
110107
await generateAndRun(
111108
[],
112-
generateBuildScript:
113-
() async => '''
109+
script: '''
114110
import 'dart:isolate';
115111
import 'package:build_runner/src/build_script_generate/build_process_state.dart';
116112
@@ -132,8 +128,7 @@ void main(_, [SendPort? sendPort]) async {
132128
expect(
133129
await generateAndRun(
134130
[],
135-
generateBuildScript:
136-
() async => '''
131+
script: '''
137132
import 'dart:isolate';
138133
import 'package:build_runner/src/build_script_generate/build_process_state.dart';
139134
@@ -156,17 +151,15 @@ void main(_, [SendPort? sendPort]) async {
156151
await expectLater(
157152
generateAndRun(
158153
[],
159-
generateBuildScript: () async {
160-
return '''
154+
script: '''
161155
import 'dart:isolate';
162156
import 'package:build_runner/src/build_script_generate/build_process_state.dart';
163157
164158
void main(_, [SendPort? sendPort]) async {
165159
await buildProcessState.receive(sendPort);
166160
throw 'expected error';
167161
}
168-
''';
169-
},
162+
''',
170163
handleUncaughtError: (err, trace) {
171164
error = err;
172165
stackTrace = trace;

build_runner/test/build_script_generate/experiments_test.dart

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,7 @@ void main() {
1717
final exitCode = await generateAndRun(
1818
[],
1919
experiments: ['records'],
20-
generateBuildScript: () async {
21-
return '''
20+
script: '''
2221
// @dart=3.0
2322
import 'dart:io';
2423
import 'dart:isolate';
@@ -30,8 +29,7 @@ void main() {
3029
buildProcessState.isolateExitCode = (x.\$2);
3130
buildProcessState.send(sendPort);
3231
}
33-
''';
34-
},
32+
''',
3533
logger: logger,
3634
);
3735
expect(exitCode, 2);

0 commit comments

Comments
 (0)