Skip to content

Commit f9640ab

Browse files
authored
Support experiments in the build script (#3467)
Fixes #3466
1 parent dbab06b commit f9640ab

File tree

10 files changed

+805
-250
lines changed

10 files changed

+805
-250
lines changed

.github/workflows/dart.yml

Lines changed: 706 additions & 209 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

_test/test/build_integration_test.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,9 @@ void main() {
6363
]);
6464

6565
expect(result.exitCode, isNot(0));
66-
expect(result.stdout, contains('Unknown experiment: fake-experiment'));
66+
expect(result.stdout, contains('Failed to precompile build script'));
67+
expect(result.stdout, contains('Unknown experiment: fake-experiment'),
68+
skip: 'https://github.com/dart-lang/webdev/issues/2003');
6769
});
6870
});
6971

build_runner/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
- Mention --build-filter option in the README.
44
- Update to build_daemon 4.0.
5+
- Support enabling experiments in build scripts.
56

67
## 2.4.0
78

build_runner/bin/build_runner.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,9 @@ Future<void> main(List<String> args) async {
9595
if (localCommandNames.contains(commandName)) {
9696
exitCode = await commandRunner.runCommand(parsedArgs) ?? 1;
9797
} else {
98-
while ((exitCode = await generateAndRun(args)) == ExitCode.tempFail.code) {}
98+
var experiments = parsedArgs.command!['enable-experiment'] as List<String>?;
99+
while ((exitCode = await generateAndRun(args, experiments: experiments)) ==
100+
ExitCode.tempFail.code) {}
99101
}
100102
await logListener.cancel();
101103
}

build_runner/dart_test.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
presets:
2+
experiments:
3+
include_tags: experiments
4+
15
tags:
26
integration:
37
timeout: 16x
8+
experiments:
9+
skip: 'Only ran if `-P experiments` is passed'
10+
presets: {experiments: {skip: false}}

build_runner/lib/src/build_script_generate/bootstrap.dart

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,13 @@ final _logger = Logger('Bootstrap');
2828
/// If an exit code of 75 is returned, this function should be re-ran.
2929
Future<int> generateAndRun(
3030
List<String> args, {
31+
List<String>? experiments,
3132
Logger? logger,
3233
Future<String> Function() generateBuildScript = generateBuildScript,
3334
void Function(Object error, StackTrace stackTrace) handleUncaughtError =
3435
_defaultHandleUncaughtError,
3536
}) async {
37+
experiments ??= [];
3638
logger ??= _logger;
3739
ReceivePort? exitPort;
3840
ReceivePort? errorPort;
@@ -66,7 +68,7 @@ Future<int> generateAndRun(
6668
return ExitCode.config.code;
6769
}
6870

69-
scriptExitCode = await _createKernelIfNeeded(logger);
71+
scriptExitCode = await _createKernelIfNeeded(logger, experiments);
7072
if (scriptExitCode != 0) return scriptExitCode!;
7173

7274
exitPort = ReceivePort();
@@ -133,7 +135,8 @@ Future<int> generateAndRun(
133135
///
134136
/// Returns zero for success or a number for failure which should be set to the
135137
/// exit code.
136-
Future<int> _createKernelIfNeeded(Logger logger) async {
138+
Future<int> _createKernelIfNeeded(
139+
Logger logger, List<String> experiments) async {
137140
var assetGraphFile = File(assetGraphPathFor(scriptKernelLocation));
138141
var kernelFile = File(scriptKernelLocation);
139142
var kernelCacheFile = File(scriptKernelCachedLocation);
@@ -145,7 +148,7 @@ Future<int> _createKernelIfNeeded(Logger logger) async {
145148
await kernelFile.rename(scriptKernelCachedLocation);
146149
logger.warning(
147150
'Invalidated precompiled build script due to missing asset graph.');
148-
} else if (!await _checkImportantPackageDeps()) {
151+
} else if (!await _checkImportantPackageDepsAndExperiments(experiments)) {
149152
await kernelFile.rename(scriptKernelCachedLocation);
150153
logger.warning(
151154
'Invalidated precompiled build script due to core package update');
@@ -157,6 +160,7 @@ Future<int> _createKernelIfNeeded(Logger logger) async {
157160
scriptLocation,
158161
scriptKernelCachedLocation,
159162
'lib/_internal/vm_platform_strong.dill',
163+
enabledExperiments: experiments,
160164
printIncrementalDependencies: false,
161165
);
162166

@@ -209,7 +213,7 @@ This is likely caused by a misconfigured builder definition.
209213
return ExitCode.config.code;
210214
}
211215
// Create _previousLocationsFile.
212-
await _checkImportantPackageDeps();
216+
await _checkImportantPackageDepsAndExperiments(experiments);
213217
}
214218
return 0;
215219
}
@@ -222,28 +226,30 @@ final _previousLocationsFile =
222226
File(p.url.join(p.url.dirname(scriptKernelLocation), '.packageLocations'));
223227

224228
/// Returns whether the [_importantPackages] are all pointing at same locations
225-
/// from the previous run.
229+
/// from the previous run, and [experiments] are the same as the last run.
226230
///
227231
/// Also updates the [_previousLocationsFile] with the new locations if not.
228232
///
229233
/// This is used to detect potential changes to the user facing api and
230234
/// pre-emptively resolve them by precompiling the build script again, see
231235
/// https://github.com/dart-lang/build/issues/1929.
232-
Future<bool> _checkImportantPackageDeps() async {
236+
Future<bool> _checkImportantPackageDepsAndExperiments(
237+
List<String> experiments) async {
233238
var currentLocations = await Future.wait(_importantPackages.map((pkg) =>
234239
Isolate.resolvePackageUri(
235240
Uri(scheme: 'package', path: '$pkg/fake.dart'))));
236-
var currentLocationsContent = currentLocations.join('\n');
241+
var fileContents =
242+
currentLocations.map((uri) => '$uri').followedBy(experiments).join('\n');
237243

238244
if (!_previousLocationsFile.existsSync()) {
239245
_logger.fine('Core package locations file does not exist');
240-
_previousLocationsFile.writeAsStringSync(currentLocationsContent);
246+
_previousLocationsFile.writeAsStringSync(fileContents);
241247
return false;
242248
}
243249

244-
if (currentLocationsContent != _previousLocationsFile.readAsStringSync()) {
250+
if (fileContents != _previousLocationsFile.readAsStringSync()) {
245251
_logger.fine('Core packages locations have changed');
246-
_previousLocationsFile.writeAsStringSync(currentLocationsContent);
252+
_previousLocationsFile.writeAsStringSync(fileContents);
247253
return false;
248254
}
249255

build_runner/mono_pkg.yaml

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
sdk:
2+
- dev
23
- main
34

45
stages:
@@ -8,10 +9,13 @@ stages:
89
- analyze: --fatal-infos .
910
- unit_test:
1011
- test: -x integration --test-randomize-ordering-seed=random
12+
- test: -P experiments --test-randomize-ordering-seed=random
13+
sdk:
14+
- 3.0.0-277.0.dev
1115
- e2e_test:
1216
# TODO: enable stack trace chaining https://github.com/dart-lang/build/issues/2894
13-
- test: -t integration --total-shards 5 --shard-index 0 --test-randomize-ordering-seed=random --no-chain-stack-traces
14-
- test: -t integration --total-shards 5 --shard-index 1 --test-randomize-ordering-seed=random --no-chain-stack-traces
15-
- test: -t integration --total-shards 5 --shard-index 2 --test-randomize-ordering-seed=random --no-chain-stack-traces
16-
- test: -t integration --total-shards 5 --shard-index 3 --test-randomize-ordering-seed=random --no-chain-stack-traces
17-
- test: -t integration --total-shards 5 --shard-index 4 --test-randomize-ordering-seed=random --no-chain-stack-traces
17+
- test: -t integration --total-shards 5 --shard-index 0 --test-randomize-ordering-seed=random --no-chain-stack-traces -j 1
18+
- test: -t integration --total-shards 5 --shard-index 1 --test-randomize-ordering-seed=random --no-chain-stack-traces -j 1
19+
- test: -t integration --total-shards 5 --shard-index 2 --test-randomize-ordering-seed=random --no-chain-stack-traces -j 1
20+
- test: -t integration --total-shards 5 --shard-index 3 --test-randomize-ordering-seed=random --no-chain-stack-traces -j 1
21+
- test: -t integration --total-shards 5 --shard-index 4 --test-randomize-ordering-seed=random --no-chain-stack-traces -j 1
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Copyright (c) 2023, 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+
@Tags(['experiments'])
6+
@TestOn('vm')
7+
import 'package:build_runner/build_script_generate.dart';
8+
import 'package:logging/logging.dart';
9+
import 'package:test/test.dart';
10+
11+
void main() {
12+
test('build scripts can use experiments', () async {
13+
final logger = Logger.detached('test')..level = Level.ALL;
14+
logger.onRecord.listen((r) => printOnFailure(r.message));
15+
final exitCode = await generateAndRun(
16+
[],
17+
experiments: ['records'],
18+
generateBuildScript: () async {
19+
return '''
20+
// @dart=3.0
21+
import 'dart:io';
22+
import 'dart:isolate';
23+
24+
void main(List<String> _, SendPort sendPort) {
25+
var x = (1, 2);
26+
sendPort.send(x.\$2);
27+
}
28+
''';
29+
},
30+
logger: logger,
31+
);
32+
expect(exitCode, 2);
33+
});
34+
}

build_runner/test/daemon/daemon_test.dart

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,8 @@ main() {
101101

102102
Future<void> _startDaemon(
103103
{BuildMode buildMode = BuildMode.Auto,
104-
List<String> options = const []}) async {
104+
List<String> options = const [],
105+
bool expectFailure = false}) async {
105106
var args = [
106107
'build_runner',
107108
'daemon',
@@ -126,7 +127,11 @@ main() {
126127
unawaited(daemon.exitCode.then((exitCode) {
127128
printOnFailure('GOT EXIT CODE: $exitCode');
128129
}));
129-
expect(await stdoutLines!.contains(readyToConnectLog), isTrue);
130+
if (expectFailure) {
131+
expect(await daemon.exitCode, isNot(0));
132+
} else {
133+
expect(await stdoutLines!.contains(readyToConnectLog), isTrue);
134+
}
130135
}
131136

132137
group('Build Daemon', () {
@@ -152,17 +157,10 @@ main() {
152157
}, skip: 'https://github.com/dart-lang/build/issues/3438');
153158

154159
test('supports --enable-experiment option', () async {
155-
await _startDaemon(options: ['--enable-experiment=fake-experiment']);
156-
var client =
157-
await _startClient(options: ['--enable-experiment=fake-experiment'])
158-
..registerBuildTarget(webTarget)
159-
..startBuild();
160-
clients.add(client);
161-
await expectLater(
162-
client.buildResults,
163-
// TODO: Check for specific message about a bad experiment
164-
emitsThrough((BuildResults b) =>
165-
b.results.first.status == BuildStatus.failed));
160+
// TODO: Check for specific message about a bad experiment
161+
await _startDaemon(
162+
options: ['--enable-experiment=fake-experiment'],
163+
expectFailure: true);
166164
});
167165

168166
test('does not shut down down on build script change when configured',

tool/ci.sh

Lines changed: 15 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)