@@ -170,7 +170,7 @@ abstract class PackageLoopingCommand extends PluginCommand {
170170 /// messages. DO NOT RELY on someone noticing a warning; instead, use it for
171171 /// things that might be useful to someone debugging an unexpected result.
172172 void logWarning (String warningMessage) {
173- print ( Colorize ( warningMessage).. yellow () );
173+ _printColorized ( warningMessage, Styles . YELLOW );
174174 if (_currentPackageEntry != null ) {
175175 _packagesWithWarnings.add (_currentPackageEntry! );
176176 } else {
@@ -219,6 +219,8 @@ abstract class PackageLoopingCommand extends PluginCommand {
219219 _otherWarningCount = 0 ;
220220 _currentPackageEntry = null ;
221221
222+ final DateTime runStart = DateTime .now ();
223+
222224 await initializeRun ();
223225
224226 final List <PackageEnumerationEntry > targetPackages =
@@ -227,8 +229,9 @@ abstract class PackageLoopingCommand extends PluginCommand {
227229 final Map <PackageEnumerationEntry , PackageResult > results =
228230 < PackageEnumerationEntry , PackageResult > {};
229231 for (final PackageEnumerationEntry entry in targetPackages) {
232+ final DateTime packageStart = DateTime .now ();
230233 _currentPackageEntry = entry;
231- _printPackageHeading (entry);
234+ _printPackageHeading (entry, startTime : runStart );
232235
233236 // Command implementations should never see excluded packages; they are
234237 // included at this level only for logging.
@@ -246,11 +249,20 @@ abstract class PackageLoopingCommand extends PluginCommand {
246249 result = PackageResult .fail (< String > ['Unhandled exception' ]);
247250 }
248251 if (result.state == RunState .skipped) {
249- final String message =
250- '${indentation }SKIPPING: ${result .details .first }' ;
251- captureOutput ? print (message) : print (Colorize (message)..darkGray ());
252+ _printColorized ('${indentation }SKIPPING: ${result .details .first }' ,
253+ Styles .DARK_GRAY );
252254 }
253255 results[entry] = result;
256+
257+ // Only log an elapsed time for long output; for short output, comparing
258+ // the relative timestamps of successive entries should be trivial.
259+ if (shouldLogTiming && hasLongOutput) {
260+ final Duration elapsedTime = DateTime .now ().difference (packageStart);
261+ _printColorized (
262+ '\n [${entry .package .displayName } completed in '
263+ '${elapsedTime .inMinutes }m ${elapsedTime .inSeconds % 60 }s]' ,
264+ Styles .DARK_GRAY );
265+ }
254266 }
255267 _currentPackageEntry = null ;
256268
@@ -287,11 +299,20 @@ abstract class PackageLoopingCommand extends PluginCommand {
287299 /// Something is always printed to make it easier to distinguish between
288300 /// a command running for a package and producing no output, and a command
289301 /// not having been run for a package.
290- void _printPackageHeading (PackageEnumerationEntry entry) {
302+ void _printPackageHeading (PackageEnumerationEntry entry,
303+ {required DateTime startTime}) {
291304 final String packageDisplayName = entry.package.displayName;
292305 String heading = entry.excluded
293306 ? 'Not running for $packageDisplayName ; excluded'
294307 : 'Running for $packageDisplayName ' ;
308+
309+ if (shouldLogTiming) {
310+ final Duration relativeTime = DateTime .now ().difference (startTime);
311+ final String timeString = _formatDurationAsRelativeTime (relativeTime);
312+ heading =
313+ hasLongOutput ? '$heading [@$timeString ]' : '[$timeString ] $heading ' ;
314+ }
315+
295316 if (hasLongOutput) {
296317 heading = '''
297318
@@ -302,13 +323,7 @@ abstract class PackageLoopingCommand extends PluginCommand {
302323 } else if (! entry.excluded) {
303324 heading = '$heading ...' ;
304325 }
305- if (captureOutput) {
306- print (heading);
307- } else {
308- final Colorize colorizeHeading = Colorize (heading);
309- print (
310- entry.excluded ? colorizeHeading.darkGray () : colorizeHeading.cyan ());
311- }
326+ _printColorized (heading, entry.excluded ? Styles .DARK_GRAY : Styles .CYAN );
312327 }
313328
314329 /// Prints a summary of packges run, packages skipped, and warnings.
@@ -401,4 +416,21 @@ abstract class PackageLoopingCommand extends PluginCommand {
401416 }
402417 _printError (failureListFooter);
403418 }
419+
420+ /// Prints [message] in [color] unless [captureOutput] is set, in which case
421+ /// it is printed without color.
422+ void _printColorized (String message, Styles color) {
423+ if (captureOutput) {
424+ print (message);
425+ } else {
426+ print (Colorize (message)..apply (color));
427+ }
428+ }
429+
430+ /// Returns a duration [d] formatted as minutes:seconds. Does not use hours,
431+ /// since time logging is primarily intended for CI, where durations should
432+ /// always be less than an hour.
433+ String _formatDurationAsRelativeTime (Duration d) {
434+ return '${d .inMinutes }:${(d .inSeconds % 60 ).toString ().padLeft (2 , '0' )}' ;
435+ }
404436}
0 commit comments