33// BSD-style license that can be found in the LICENSE file.
44
55import 'dart:async' ;
6- import 'dart:io' ;
7- import 'dart:isolate' ;
6+ import 'dart:io' show Directory;
87
98import 'package:args/args.dart' ;
109import 'package:args/command_runner.dart' ;
11- import 'package:stack_trace/stack_trace.dart' ;
10+ import 'package:build_daemon/client.dart' ;
11+ import 'package:build_daemon/data/build_status.dart' ;
12+ import 'package:build_daemon/data/build_target.dart' ;
13+ import 'package:logging/logging.dart' ;
1214
15+ import '../daemon_client.dart' ;
16+ import '../logging.dart' ;
1317import 'configuration.dart' ;
1418import 'shared.dart' ;
1519
16- const _bootstrapScript = r'''
17- import 'dart:io';
18- import 'dart:isolate';
19-
20- import 'package:build_runner/build_script_generate.dart';
21- import 'package:path/path.dart' as p;
22-
23- void main(List<String> args, [SendPort sendPort]) async {
24- var buildScript = await generateBuildScript();
25- var scriptFile = new File(scriptLocation)..createSync(recursive: true);
26- scriptFile.writeAsStringSync(buildScript);
27- sendPort.send(p.absolute(scriptLocation));
28- }
29- ''' ;
30- const _packagesFileName = '.packages' ;
31-
32- Future <Uri > _buildRunnerScript () async {
33- var packagesFile = File (_packagesFileName);
34- if (! packagesFile.existsSync ()) {
35- throw FileSystemException (
36- 'A `$_packagesFileName ` file does not exist in the target directory.' ,
37- packagesFile.absolute.path);
38- }
39-
40- var dataUri = Uri .dataFromString (_bootstrapScript);
41-
42- var messagePort = ReceivePort ();
43- var exitPort = ReceivePort ();
44- var errorPort = ReceivePort ();
45-
46- try {
47- await Isolate .spawnUri (dataUri, [], messagePort.sendPort,
48- onExit: exitPort.sendPort,
49- onError: errorPort.sendPort,
50- errorsAreFatal: true ,
51- packageConfig: Uri .file (_packagesFileName));
52-
53- var allErrorsFuture = errorPort.forEach ((error) {
54- var errorList = error as List ;
55- var message = errorList[0 ] as String ;
56- var stack = StackTrace .fromString (errorList[1 ] as String );
57-
58- stderr.writeln (message);
59- stderr.writeln (stack);
60- });
61-
62- var items = await Future .wait ([
63- messagePort.toList (),
64- allErrorsFuture,
65- exitPort.first.whenComplete (() {
66- messagePort.close ();
67- errorPort.close ();
68- })
69- ]);
70-
71- var messages = items[0 ] as List ;
72- if (messages.isEmpty) {
73- throw StateError ('An error occurred while bootstrapping.' );
74- }
75-
76- assert (messages.length == 1 );
77- return Uri .file (messages.single as String );
78- } finally {
79- messagePort.close ();
80- exitPort.close ();
81- errorPort.close ();
82- }
83- }
84-
8520/// Command to execute pub run build_runner build.
8621class BuildCommand extends Command <int > {
8722 @override
@@ -98,71 +33,74 @@ class BuildCommand extends Command<int> {
9833 }
9934
10035 @override
101- Future <int > run () {
36+ Future <int > run () async {
10237 if (argResults.rest.isNotEmpty) {
10338 throw UsageException (
10439 'Arguments were provided that are not supported: '
10540 '"${argResults .rest .join (' ' )}".' ,
10641 argParser.usage);
10742 }
108- return runCore ('build' , extraArgs: ['--fail-on-severe' ]);
109- }
11043
111- Future <int > runCore (String command, {List <String > extraArgs}) async {
11244 var configuration = Configuration .fromArgs (argResults);
45+ setVerbosity (configuration.verbose);
11346 var pubspecLock = await readPubspecLock (configuration);
114- final arguments = [command]
115- ..addAll (extraArgs ?? const [])
116- ..addAll (buildRunnerArgs (pubspecLock, configuration));
117-
118- stdout.write ('Creating build script' );
119- var stopwatch = Stopwatch ()..start ();
120- var buildRunnerScript = await _buildRunnerScript ();
121- stdout.writeln (', took ${stopwatch .elapsedMilliseconds }ms' );
122-
123- var exitCode = 0 ;
124-
125- // Heavily inspired by dart-lang/build @ 0c77443dd7
126- // /build_runner/bin/build_runner.dart#L58-L85
127- var exitPort = ReceivePort ();
128- var errorPort = ReceivePort ();
129- var messagePort = ReceivePort ();
130- var errorListener = errorPort.listen ((e) {
131- stderr.writeln ('\n\n You have hit a bug in build_runner' );
132- stderr.writeln ('Please file an issue with reproduction steps at '
133- 'https://github.com/dart-lang/build/issues\n\n ' );
134- final error = e[0 ];
135- final trace = e[1 ] as String ;
136- stderr.writeln (error);
137- stderr.writeln (Trace .parse (trace).terse);
138- if (exitCode == 0 ) exitCode = 1 ;
139- });
47+ final arguments = buildRunnerArgs (pubspecLock, configuration);
14048
14149 try {
142- await Isolate .spawnUri (buildRunnerScript, arguments, messagePort.sendPort,
143- onExit: exitPort.sendPort,
144- onError: errorPort.sendPort,
145- automaticPackageResolution: true );
146- StreamSubscription exitCodeListener;
147- exitCodeListener = messagePort.listen ((isolateExitCode) {
148- if (isolateExitCode is ! int ) {
149- throw StateError (
150- 'Bad response from isolate, expected an exit code but got '
151- '$isolateExitCode ' );
50+ logHandler (Level .INFO , 'Connecting to the build daemon...' );
51+ var client = await connectClient (
52+ Directory .current.path,
53+ arguments,
54+ (serverLog) {
55+ var recordLevel = levelForLog (serverLog) ?? Level .INFO ;
56+ logHandler (recordLevel, trimLevel (recordLevel, serverLog.log));
57+ },
58+ );
59+ OutputLocation outputLocation;
60+ if (configuration.outputPath != null ) {
61+ outputLocation = OutputLocation ((b) => b
62+ ..output = configuration.outputPath
63+ ..useSymlinks = false
64+ ..hoist = configuration.outputInput.isNotEmpty);
65+ }
66+ client.registerBuildTarget (DefaultBuildTarget ((b) => b
67+ ..target = configuration.outputInput
68+ ..outputLocation = outputLocation? .toBuilder ()));
69+ client.startBuild ();
70+ var exitCode = 0 ;
71+ var gotBuildStart = false ;
72+ await for (final result in client.buildResults) {
73+ var targetResult = result.results.firstWhere (
74+ (buildResult) => buildResult.target == configuration.outputInput,
75+ orElse: () => null );
76+ if (targetResult == null ) continue ;
77+ // We ignore any builds that happen before we get a `started` event,
78+ // because those could be stale (from some other client).
79+ gotBuildStart =
80+ gotBuildStart || targetResult.status == BuildStatus .started;
81+ if (! gotBuildStart) continue ;
82+
83+ // Shouldn't happen, but being a bit defensive here.
84+ if (targetResult.status == BuildStatus .started) continue ;
85+
86+ if (targetResult.status == BuildStatus .failed) {
87+ exitCode = 1 ;
15288 }
153- exitCode = isolateExitCode as int ;
154- exitCodeListener.cancel ();
155- exitCodeListener = null ;
156- });
157- await exitPort.first;
158- await errorListener.cancel ();
159- await exitCodeListener? .cancel ();
16089
90+ if (targetResult.error? .isNotEmpty == true ) {
91+ logHandler (Level .SEVERE , targetResult.error);
92+ }
93+ break ;
94+ }
95+ await client.close ();
16196 return exitCode;
162- } finally {
163- exitPort.close ();
164- errorPort.close ();
165- messagePort.close ();
97+ } on OptionsSkew catch (_) {
98+ logHandler (
99+ Level .SEVERE ,
100+ 'Incompatible options with current running build daemon.\n\n '
101+ 'Please stop other WebDev instances running in this directory '
102+ 'before starting a new instance with these options.\n\n ' );
103+ return 1 ;
166104 }
167105 }
168106}
0 commit comments