Skip to content

Commit 35e6c62

Browse files
authored
Return exit codes from run, and send them to the parent isolate. (#878)
1 parent 4bc2806 commit 35e6c62

File tree

10 files changed

+105
-51
lines changed

10 files changed

+105
-51
lines changed

analysis_options.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ analyzer:
66
unused_local_variable: error
77
dead_code: error
88
override_on_non_overriding_method: error
9+
exclude:
10+
- "test/goldens/generated_build_script.dart"
911
linter:
1012
rules:
1113
# Errors

build_runner/CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
## 0.7.7
2+
3+
- The top level `run` method now returns an `int` which represents an `exitCode`
4+
for the command that was executed.
5+
- For now we still set the exitCode manually as well but this will likely
6+
change in the next breaking release. In manual scripts you should `await`
7+
the call to `run` and assign that to `exitCode` to be future-proofed.
8+
19
## 0.7.6
210

311
- Update to package:build version `0.12.0`.

build_runner/bin/build_runner.dart

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,17 +41,27 @@ Future<Null> main(List<String> args) async {
4141

4242
var exitPort = new ReceivePort();
4343
var errorPort = new ReceivePort();
44-
var errorListener = errorPort.listen((e) => stderr.writeAll(e as List, '\n'));
45-
await Isolate.spawnUri(new Uri.file(p.absolute(scriptLocation)), args, null,
44+
var messagePort = new ReceivePort();
45+
var errorListener = errorPort.listen((e) {
46+
stderr.writeAll(e as List, '\n');
47+
if (exitCode == 0) exitCode = 1;
48+
});
49+
await Isolate.spawnUri(
50+
new Uri.file(p.absolute(scriptLocation)), args, messagePort.sendPort,
4651
onExit: exitPort.sendPort, onError: errorPort.sendPort);
52+
try {
53+
exitCode = await messagePort.first as int;
54+
} on StateError catch (_) {
55+
if (exitCode == 0) exitCode = 1;
56+
}
4757
await exitPort.first;
4858
await errorListener.cancel();
4959
await logListener?.cancel();
5060
}
5161

5262
const _generateCommand = 'generate-build-script';
5363

54-
class _GenerateBuildScript extends Command {
64+
class _GenerateBuildScript extends Command<int> {
5565
@override
5666
final description = 'Generate a script to run builds and print the file path '
5767
'with no other logging. Useful for wrapping builds with other tools.';

build_runner/lib/src/build_script_generate/build_script_generate.dart

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,14 +61,25 @@ Future<Iterable<Expression>> _findBuilderApplications() async {
6161
/// A method forwarding to `run`.
6262
Method _main() => new Method((b) => b
6363
..name = 'main'
64-
..lambda = true
64+
..modifier = MethodModifier.async
6565
..requiredParameters.add(new Parameter((b) => b
6666
..name = 'args'
6767
..type = new TypeReference((b) => b
6868
..symbol = 'List'
6969
..types.add(refer('String')))))
70-
..body = refer('run', 'package:build_runner/build_runner.dart')
71-
.call([refer('args'), refer('_builders')]).code);
70+
..optionalParameters.add(new Parameter((b) => b
71+
..name = 'sendPort'
72+
..type = refer('SendPort', 'dart:isolate')))
73+
..body = new Block.of([
74+
refer('run', 'package:build_runner/build_runner.dart')
75+
.call([refer('args'), refer('_builders')])
76+
.awaited
77+
.assignVar('result')
78+
.statement,
79+
refer('sendPort')
80+
.nullSafeProperty('send')
81+
.call([refer('result')]).statement,
82+
]));
7283

7384
/// An expression calling `apply` with appropriate setup for a Builder.
7485
Expression _applyBuilder(BuilderDefinition definition) =>

build_runner/lib/src/entrypoint/options.dart

Lines changed: 57 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ const _verbose = 'verbose';
2323
final _pubBinary = Platform.isWindows ? 'pub.bat' : 'pub';
2424

2525
/// Unified command runner for all build_runner commands.
26-
class BuildCommandRunner extends CommandRunner {
26+
class BuildCommandRunner extends CommandRunner<int> {
2727
final List<BuilderApplication> builderApplications;
2828

2929
BuildCommandRunner(List<BuilderApplication> builderApplications)
@@ -139,7 +139,7 @@ class _ServeTarget {
139139
_ServeTarget(this.dir, this.port);
140140
}
141141

142-
abstract class _BaseCommand extends Command {
142+
abstract class _BaseCommand extends Command<int> {
143143
List<BuilderApplication> get builderApplications =>
144144
(runner as BuildCommandRunner).builderApplications;
145145

@@ -201,14 +201,19 @@ class _BuildCommand extends _BaseCommand {
201201
'Performs a single build on the specified targets and then exits.';
202202

203203
@override
204-
Future<Null> run() async {
204+
Future<int> run() async {
205205
var options = _readOptions();
206-
await build(builderApplications,
206+
var result = await build(builderApplications,
207207
deleteFilesByDefault: options.deleteFilesByDefault,
208208
enableLowResourcesMode: options.enableLowResourcesMode,
209209
assumeTty: options.assumeTty,
210210
outputDir: options.outputDir,
211211
verbose: options.verbose);
212+
if (result.status == BuildStatus.success) {
213+
return ExitCode.success.code;
214+
} else {
215+
return 1;
216+
}
212217
}
213218
}
214219

@@ -224,7 +229,7 @@ class _WatchCommand extends _BaseCommand {
224229
'rebuilding as appropriate.';
225230

226231
@override
227-
Future<Null> run() async {
232+
Future<int> run() async {
228233
var options = _readOptions();
229234
var handler = await watch(builderApplications,
230235
deleteFilesByDefault: options.deleteFilesByDefault,
@@ -234,6 +239,7 @@ class _WatchCommand extends _BaseCommand {
234239
verbose: options.verbose);
235240
await handler.currentBuild;
236241
await handler.buildResults.drain();
242+
return ExitCode.success.code;
237243
}
238244
}
239245

@@ -257,7 +263,7 @@ class _ServeCommand extends _WatchCommand {
257263
_ServeOptions _readOptions() => new _ServeOptions.fromParsedArgs(argResults);
258264

259265
@override
260-
Future<Null> run() async {
266+
Future<int> run() async {
261267
var options = _readOptions();
262268
var handler = await watch(builderApplications,
263269
deleteFilesByDefault: options.deleteFilesByDefault,
@@ -273,6 +279,8 @@ class _ServeCommand extends _WatchCommand {
273279
}
274280
await handler.buildResults.drain();
275281
await Future.wait(servers.map((server) => server.close()));
282+
283+
return ExitCode.success.code;
276284
}
277285
}
278286

@@ -291,39 +299,52 @@ class _TestCommand extends _BaseCommand {
291299
'using the compiled assets.';
292300

293301
@override
294-
Future<Null> run() async {
295-
var packageGraph = new PackageGraph.forThisPackage();
296-
_ensureBuildTestDependency(packageGraph);
297-
var options = _readOptions();
298-
// We always need an output dir when running tests, so we create a tmp dir
299-
// if the user didn't specify one.
300-
var outputDir = options.outputDir ??
301-
Directory.systemTemp
302-
.createTempSync('build_runner_test')
303-
.absolute
304-
.uri
305-
.toFilePath();
306-
await build(builderApplications,
307-
deleteFilesByDefault: options.deleteFilesByDefault,
308-
enableLowResourcesMode: options.enableLowResourcesMode,
309-
assumeTty: options.assumeTty,
310-
outputDir: outputDir,
311-
verbose: options.verbose,
312-
packageGraph: packageGraph);
313-
314-
// Run the tests!
315-
await _runTests(outputDir);
316-
317-
// Clean up the output dir if one wasn't explicitly asked for.
318-
if (options.outputDir == null) {
319-
await new Directory(outputDir).delete(recursive: true);
302+
Future<int> run() async {
303+
_SharedOptions options;
304+
String outputDir;
305+
try {
306+
var packageGraph = new PackageGraph.forThisPackage();
307+
_ensureBuildTestDependency(packageGraph);
308+
options = _readOptions();
309+
// We always need an output dir when running tests, so we create a tmp dir
310+
// if the user didn't specify one.
311+
outputDir = options.outputDir ??
312+
Directory.systemTemp
313+
.createTempSync('build_runner_test')
314+
.absolute
315+
.uri
316+
.toFilePath();
317+
var result = await build(builderApplications,
318+
deleteFilesByDefault: options.deleteFilesByDefault,
319+
enableLowResourcesMode: options.enableLowResourcesMode,
320+
assumeTty: options.assumeTty,
321+
outputDir: outputDir,
322+
verbose: options.verbose,
323+
packageGraph: packageGraph);
324+
325+
if (result.status == BuildStatus.failure) {
326+
stdout.writeln('Skipping tests due to build failure');
327+
return 1;
328+
}
329+
330+
var testExitCode = await _runTests(outputDir);
331+
if (testExitCode != 0) {
332+
// No need to log - should see failed tests in the console.
333+
exitCode = testExitCode;
334+
}
335+
return testExitCode;
336+
} finally {
337+
// Clean up the output dir if one wasn't explicitly asked for.
338+
if (options.outputDir == null && outputDir != null) {
339+
await new Directory(outputDir).delete(recursive: true);
340+
}
341+
342+
await ProcessManager.terminateStdIn();
320343
}
321-
322-
await ProcessManager.terminateStdIn();
323344
}
324345

325346
/// Runs tests using [precompiledPath] as the precompiled test directory.
326-
Future<Null> _runTests(String precompiledPath) async {
347+
Future<int> _runTests(String precompiledPath) async {
327348
stdout.writeln('Running tests...\n');
328349
var extraTestArgs = argResults.rest;
329350
var testProcess = await new ProcessManager().spawn(
@@ -334,11 +355,7 @@ class _TestCommand extends _BaseCommand {
334355
'--precompiled',
335356
precompiledPath,
336357
]..addAll(extraTestArgs));
337-
var testExitCode = await testProcess.exitCode;
338-
if (testExitCode != 0) {
339-
// No need to log - should see failed tests in the console.
340-
exitCode = testExitCode;
341-
}
358+
return testProcess.exitCode;
342359
}
343360
}
344361

build_runner/lib/src/entrypoint/run.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import 'options.dart';
1010

1111
/// A common entrypoint to parse command line arguments and build or serve with
1212
/// [builders].
13-
Future run(List<String> args, List<BuilderApplication> builders) async {
13+
Future<int> run(List<String> args, List<BuilderApplication> builders) {
1414
var runner = new BuildCommandRunner(builders);
15-
await runner.run(args);
15+
return runner.run(args);
1616
}

build_runner/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: build_runner
2-
version: 0.7.6
2+
version: 0.7.7
33
description: Tools to write binaries that run builders.
44
author: Dart Team <misc@dartlang.org>
55
homepage: https://github.com/dart-lang/build

e2e_example/test/common/utils.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ Future<Null> expectTestsFail({bool useManualScript}) async {
206206
var result =
207207
useManualScript ? await _runManualTests() : await _runAutoTests();
208208
expect(result.stdout, contains('Some tests failed'));
209+
expect(result.exitCode, isNot(0));
209210
}
210211

211212
Future<Null> expectTestsPass(

e2e_example/test/goldens/generated_build_script.dart

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import 'package:provides_builder/builders.dart' as _i2;
33
import 'package:build_test/builder.dart' as _i3;
44
import 'package:build_config/build_config.dart' as _i4;
55
import 'package:build_web_compilers/builders.dart' as _i5;
6+
import 'dart:isolate' as _i6;
67

78
final _builders = [
89
_i1.apply('provides_builder|some_not_applied_builder', [_i2.notApplied],
@@ -33,4 +34,7 @@ final _builders = [
3334
include: const ['web/**', 'test/**_test.dart'],
3435
exclude: const ['test/**.node_test.dart', 'test/**.vm_test.dart']))
3536
];
36-
main(List<String> args) => _i1.run(args, _builders);
37+
main(List<String> args, [_i6.SendPort sendPort]) async {
38+
var result = await _i1.run(args, _builders);
39+
sendPort?.send(result);
40+
}

e2e_example/tool/build.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// BSD-style license that can be found in the LICENSE file.
44

55
import 'dart:async';
6+
import 'dart:io';
67

78
import 'package:build/build.dart';
89
import 'package:build_web_compilers/build_web_compilers.dart';
@@ -38,7 +39,7 @@ Future main(List<String> args) async {
3839
include: const ['web/**', 'test/**.browser_test.dart']))
3940
];
4041

41-
await run(args, builders);
42+
exitCode = await run(args, builders);
4243
}
4344

4445
class ThrowingBuilder extends Builder {

0 commit comments

Comments
 (0)