44
55import 'dart:async' ;
66
7+ import 'package:meta/meta.dart' ;
78import 'package:package_config/package_config.dart' ;
89import 'package:process/process.dart' ;
910
@@ -31,10 +32,6 @@ const String _kPubEnvironmentKey = 'PUB_ENVIRONMENT';
3132/// The console environment key used by the pub tool to find the cache directory.
3233const String _kPubCacheEnvironmentKey = 'PUB_CACHE' ;
3334
34- /// The UNAVAILABLE exit code returned by the pub tool.
35- /// (see https://github.com/dart-lang/pub/blob/master/lib/src/exit_codes.dart)
36- const int _kPubExitCodeUnavailable = 69 ;
37-
3835typedef MessageFilter = String ? Function (String message);
3936
4037/// globalCachePath is the directory in which the content of the localCachePath will be moved in
@@ -150,9 +147,20 @@ abstract class Pub {
150147 required Platform platform,
151148 required BotDetector botDetector,
152149 required Usage usage,
153- required Stdio stdio,
154150 }) = _DefaultPub ;
155151
152+ /// Create a [Pub] instance with a mocked [stdio] .
153+ @visibleForTesting
154+ factory Pub .test ({
155+ required FileSystem fileSystem,
156+ required Logger logger,
157+ required ProcessManager processManager,
158+ required Platform platform,
159+ required BotDetector botDetector,
160+ required Usage usage,
161+ required Stdio stdio,
162+ }) = _DefaultPub .test;
163+
156164 /// Runs `pub get` or `pub upgrade` for [project] .
157165 ///
158166 /// [context] provides extra information to package server requests to
@@ -214,6 +222,26 @@ class _DefaultPub implements Pub {
214222 required Platform platform,
215223 required BotDetector botDetector,
216224 required Usage usage,
225+ }) : _fileSystem = fileSystem,
226+ _logger = logger,
227+ _platform = platform,
228+ _botDetector = botDetector,
229+ _usage = usage,
230+ _processUtils = ProcessUtils (
231+ logger: logger,
232+ processManager: processManager,
233+ ),
234+ _processManager = processManager,
235+ _stdio = null ;
236+
237+ @visibleForTesting
238+ _DefaultPub .test ({
239+ required FileSystem fileSystem,
240+ required Logger logger,
241+ required ProcessManager processManager,
242+ required Platform platform,
243+ required BotDetector botDetector,
244+ required Usage usage,
217245 required Stdio stdio,
218246 }) : _fileSystem = fileSystem,
219247 _logger = logger,
@@ -234,7 +262,7 @@ class _DefaultPub implements Pub {
234262 final BotDetector _botDetector;
235263 final Usage _usage;
236264 final ProcessManager _processManager;
237- final Stdio _stdio;
265+ final Stdio ? _stdio;
238266
239267 @override
240268 Future <void > get ({
@@ -315,7 +343,7 @@ class _DefaultPub implements Pub {
315343 '--offline' ,
316344 '--example' ,
317345 ];
318- await _runWithRetries (
346+ await _runWithStdioInherited (
319347 args,
320348 command: command,
321349 context: context,
@@ -346,15 +374,15 @@ class _DefaultPub implements Pub {
346374 }
347375 }
348376
349- /// Runs pub with [arguments] .
377+ /// Runs pub with [arguments] and [ProcessStartMode.inheritStdio] mode .
350378 ///
351- /// Retries the command as long as the exit code is
352- /// `_kPubExitCodeUnavailable` .
379+ /// Uses [ProcessStartMode.normal] and [Pub._stdio] if [Pub.test] constructor
380+ /// was used .
353381 ///
354- /// Prints the stderr and stdout of the last run.
382+ /// Prints the stdout and stderr of the whole run.
355383 ///
356- /// Sends an analytics event
357- Future <void > _runWithRetries (
384+ /// Sends an analytics event.
385+ Future <void > _runWithStdioInherited (
358386 List <String > arguments, {
359387 required String command,
360388 required bool printProgress,
@@ -365,57 +393,47 @@ class _DefaultPub implements Pub {
365393 String ? flutterRootOverride,
366394 }) async {
367395 int exitCode;
368- int attempts = 0 ;
369- int duration = 1 ;
370-
371- List <_OutputLine >? output;
372- StreamSubscription <String > recordLines (Stream <List <int >> stream, _OutputStream streamName) {
373- return stream
374- .transform <String >(utf8.decoder)
375- .transform <String >(const LineSplitter ())
376- .listen ((String line) => output! .add (_OutputLine (line, streamName)));
377- }
378396
379- final Status ? status = printProgress
380- ? _logger.startProgress ('Running "flutter pub $command " in ${_fileSystem .path .basename (directory )}...' ,)
381- : null ;
397+ _logger.printStatus ('Running "flutter pub $command " in ${_fileSystem .path .basename (directory )}...' );
382398 final List <String > pubCommand = _pubCommand (arguments);
383399 final Map <String , String > pubEnvironment = await _createPubEnvironment (context, flutterRootOverride);
384400 try {
385- do {
386- output = < _OutputLine > [];
387- attempts += 1 ;
388- final io.Process process = await _processUtils.start (
401+ final io.Process process;
402+ final io.Stdio ? stdio = _stdio;
403+
404+ if (stdio != null ) {
405+ // Omit mode parameter and direct pub output to [Pub._stdio] for tests.
406+ process = await _processUtils.start (
389407 pubCommand,
390408 workingDirectory: _fileSystem.path.current,
391409 environment: pubEnvironment,
392410 );
393- final StreamSubscription <String > stdoutSubscription =
394- recordLines (process.stdout, _OutputStream .stdout);
395- final StreamSubscription <String > stderrSubscription =
396- recordLines (process.stderr, _OutputStream .stderr);
397411
398- exitCode = await process.exitCode;
412+ final StreamSubscription <List <int >> stdoutSubscription =
413+ process.stdout.listen (stdio.stdout.add);
414+ final StreamSubscription <List <int >> stderrSubscription =
415+ process.stderr.listen (stdio.stderr.add);
416+
417+ await Future .wait <void >(< Future <void >> [
418+ stdoutSubscription.asFuture <void >(),
419+ stderrSubscription.asFuture <void >(),
420+ ]);
421+
399422 unawaited (stdoutSubscription.cancel ());
400423 unawaited (stderrSubscription.cancel ());
424+ } else {
425+ // Let pub inherit stdio for normal operation.
426+ process = await _processUtils.start (
427+ pubCommand,
428+ workingDirectory: _fileSystem.path.current,
429+ environment: pubEnvironment,
430+ mode: ProcessStartMode .inheritStdio,
431+ );
432+ }
401433
402- if (retry && exitCode == _kPubExitCodeUnavailable) {
403- _logger.printStatus (
404- '$failureMessage (server unavailable) -- attempting retry $attempts in $duration '
405- 'second${ duration == 1 ? "" : "s" }...' ,
406- );
407- await Future <void >.delayed (Duration (seconds: duration));
408- if (duration < 64 ) {
409- duration *= 2 ;
410- }
411- // This will cause a retry.
412- output = null ;
413- }
414- } while (output == null );
415- status? .stop ();
434+ exitCode = await process.exitCode;
416435 // The exception is rethrown, so don't catch only Exceptions.
417436 } catch (exception) { // ignore: avoid_catches_without_on_clauses
418- status? .cancel ();
419437 if (exception is io.ProcessException ) {
420438 final StringBuffer buffer = StringBuffer ('${exception .message }\n ' );
421439 final String directoryExistsMessage = _fileSystem.directory (directory).existsSync ()
@@ -434,40 +452,19 @@ class _DefaultPub implements Pub {
434452 rethrow ;
435453 }
436454
437- if (printProgress) {
438- // Show the output of the last run.
439- for (final _OutputLine line in output) {
440- switch (line.stream) {
441- case _OutputStream .stdout:
442- _stdio.stdoutWrite ('${line .line }\n ' );
443- break ;
444- case _OutputStream .stderr:
445- _stdio.stderrWrite ('${line .line }\n ' );
446- break ;
447- }
448- }
449- }
450-
451455 final int code = exitCode;
452- String result = 'success' ;
453- if (output.any ((_OutputLine line) => line.line.contains ('version solving failed' ))) {
454- result = 'version-solving-failed' ;
455- } else if (code != 0 ) {
456- result = 'failure' ;
457- }
456+ final String result = code == 0 ? 'success' : 'failure' ;
458457 PubResultEvent (
459458 context: context.toAnalyticsString (),
460459 result: result,
461460 usage: _usage,
462461 ).send ();
463- final String lastPubMessage = output.isEmpty ? 'no message' : output.last.line;
464462
465463 if (code != 0 ) {
466464 final StringBuffer buffer = StringBuffer ('$failureMessage \n ' );
467465 buffer.writeln ('command: "${pubCommand .join (' ' )}"' );
468466 buffer.write (_stringifyPubEnv (pubEnvironment));
469467 buffer.writeln ('exit code: $code ' );
470- buffer.writeln ('last line of pub output: "${lastPubMessage .trim ()}"' );
471468 throwToolExit (
472469 buffer.toString (),
473470 exitCode: code,
@@ -813,14 +810,3 @@ class _DefaultPub implements Pub {
813810 return buffer.toString ();
814811 }
815812}
816-
817- class _OutputLine {
818- _OutputLine (this .line, this .stream);
819- final String line;
820- final _OutputStream stream;
821- }
822-
823- enum _OutputStream {
824- stdout,
825- stderr,
826- }
0 commit comments