diff --git a/packages/flutter_tools/lib/src/aot.dart b/packages/flutter_tools/lib/src/aot.dart index 50be745988..b76d2275a2 100644 --- a/packages/flutter_tools/lib/src/aot.dart +++ b/packages/flutter_tools/lib/src/aot.dart @@ -226,8 +226,11 @@ class AotBuilder { status?.stop(); if (!result.success) { for (ExceptionMeasurement measurement in result.exceptions.values) { - printError(measurement.exception.toString()); - printError(measurement.stackTrace.toString()); + printError('Target ${measurement.target} failed: ${measurement.exception}', + stackTrace: measurement.fatal + ? measurement.stackTrace + : null, + ); } throwToolExit('Failed to build aot.'); } diff --git a/packages/flutter_tools/lib/src/build_system/build_system.dart b/packages/flutter_tools/lib/src/build_system/build_system.dart index 3c303c6dcf..c81a43cffc 100644 --- a/packages/flutter_tools/lib/src/build_system/build_system.dart +++ b/packages/flutter_tools/lib/src/build_system/build_system.dart @@ -556,6 +556,8 @@ class _BuildInstance { } } } catch (exception, stackTrace) { + // TODO(jonahwilliams): throw specific exception for expected errors to mark + // as non-fatal. All others should be fatal. node.target.clearStamp(environment); passed = false; skipped = false; @@ -573,12 +575,15 @@ class _BuildInstance { /// Helper class to collect exceptions. class ExceptionMeasurement { - ExceptionMeasurement(this.target, this.exception, this.stackTrace); + ExceptionMeasurement(this.target, this.exception, this.stackTrace, {this.fatal = false}); final String target; final dynamic exception; final StackTrace stackTrace; + /// Whether this exception was a fatal build system error. + final bool fatal; + @override String toString() => 'target: $target\nexception:$exception\n$stackTrace'; } diff --git a/packages/flutter_tools/lib/src/bundle.dart b/packages/flutter_tools/lib/src/bundle.dart index 0193354ed2..8d157ea97f 100644 --- a/packages/flutter_tools/lib/src/bundle.dart +++ b/packages/flutter_tools/lib/src/bundle.dart @@ -126,8 +126,11 @@ Future buildWithAssemble({ if (!result.success) { for (ExceptionMeasurement measurement in result.exceptions.values) { - printError(measurement.exception.toString()); - printError(measurement.stackTrace.toString()); + printError('Target ${measurement.target} failed: ${measurement.exception}', + stackTrace: measurement.fatal + ? measurement.stackTrace + : null, + ); } throwToolExit('Failed to build bundle.'); } diff --git a/packages/flutter_tools/lib/src/commands/assemble.dart b/packages/flutter_tools/lib/src/commands/assemble.dart index b4758808fe..8b83775b9f 100644 --- a/packages/flutter_tools/lib/src/commands/assemble.dart +++ b/packages/flutter_tools/lib/src/commands/assemble.dart @@ -155,8 +155,12 @@ class AssembleCommand extends FlutterCommand { resourcePoolSize: argResults['resource-pool-size'], )); if (!result.success) { - for (MapEntry data in result.exceptions.entries) { - printError('Target ${data.key} failed: ${data.value.exception}', stackTrace: data.value.stackTrace); + for (ExceptionMeasurement measurement in result.exceptions.values) { + printError('Target ${measurement.target} failed: ${measurement.exception}', + stackTrace: measurement.fatal + ? measurement.stackTrace + : null, + ); } throwToolExit('build failed.'); } diff --git a/packages/flutter_tools/lib/src/web/compile.dart b/packages/flutter_tools/lib/src/web/compile.dart index f0d9524a1d..4f40ccc211 100644 --- a/packages/flutter_tools/lib/src/web/compile.dart +++ b/packages/flutter_tools/lib/src/web/compile.dart @@ -54,8 +54,11 @@ Future buildWeb( )); if (!result.success) { for (ExceptionMeasurement measurement in result.exceptions.values) { - printError(measurement.stackTrace.toString()); - printError(measurement.exception.toString()); + printError('Target ${measurement.target} failed: ${measurement.exception}', + stackTrace: measurement.fatal + ? measurement.stackTrace + : null, + ); } throwToolExit('Failed to compile application for the Web.'); } diff --git a/packages/flutter_tools/test/commands.shard/hermetic/assemble_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/assemble_test.dart index 176cb51e24..1b1a8ed76f 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/assemble_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/assemble_test.dart @@ -41,7 +41,8 @@ void main() { }); final CommandRunner commandRunner = createTestCommandRunner(AssembleCommand()); - expect(commandRunner.run(['assemble', 'debug_macos_bundle_flutter_assets']), throwsA(isInstanceOf())); + expect(commandRunner.run(['assemble', 'debug_macos_bundle_flutter_assets']), + throwsA(isInstanceOf())); }); testbed.test('Throws ToolExit if called with non-existent rule', () async { @@ -51,7 +52,25 @@ void main() { }); final CommandRunner commandRunner = createTestCommandRunner(AssembleCommand()); - expect(commandRunner.run(['assemble', '-o Output', 'undefined']), throwsA(isInstanceOf())); + expect(commandRunner.run(['assemble', '-o Output', 'undefined']), + throwsA(isInstanceOf())); + }); + + testbed.test('Does not log stack traces during build failure', () async { + final BufferLogger bufferLogger = logger; + final StackTrace testStackTrace = StackTrace.current; + when(buildSystem.build(any, any, buildSystemConfig: anyNamed('buildSystemConfig'))) + .thenAnswer((Invocation invocation) async { + return BuildResult(success: false, exceptions: { + 'hello': ExceptionMeasurement('hello', 'bar', testStackTrace), + }); + }); + final CommandRunner commandRunner = createTestCommandRunner(AssembleCommand()); + + await expectLater(commandRunner.run(['assemble', '-o Output', 'debug_macos_bundle_flutter_assets']), + throwsA(isInstanceOf())); + expect(bufferLogger.errorText, contains('bar')); + expect(bufferLogger.errorText, isNot(contains(testStackTrace.toString()))); }); testbed.test('Only writes input and output files when the values change', () async { @@ -65,7 +84,13 @@ void main() { }); final CommandRunner commandRunner = createTestCommandRunner(AssembleCommand()); - await commandRunner.run(['assemble', '-o Output', '--build-outputs=outputs', '--build-inputs=inputs', 'debug_macos_bundle_flutter_assets']); + await commandRunner.run([ + 'assemble', + '-o Output', + '--build-outputs=outputs', + '--build-inputs=inputs', + 'debug_macos_bundle_flutter_assets', + ]); final File inputs = fs.file('inputs'); final File outputs = fs.file('outputs'); @@ -75,7 +100,13 @@ void main() { final DateTime theDistantPast = DateTime(1991, 8, 23); inputs.setLastModifiedSync(theDistantPast); outputs.setLastModifiedSync(theDistantPast); - await commandRunner.run(['assemble', '-o Output', '--build-outputs=outputs', '--build-inputs=inputs', 'debug_macos_bundle_flutter_assets']); + await commandRunner.run([ + 'assemble', + '-o Output', + '--build-outputs=outputs', + '--build-inputs=inputs', + 'debug_macos_bundle_flutter_assets', + ]); expect(inputs.lastModifiedSync(), theDistantPast); expect(outputs.lastModifiedSync(), theDistantPast); @@ -87,7 +118,13 @@ void main() { inputFiles: [fs.file('foo'), fs.file('fizz')..createSync()], outputFiles: [fs.file('bar'), fs.file(fs.path.join('.dart_tool', 'fizz2'))..createSync(recursive: true)]); }); - await commandRunner.run(['assemble', '-o Output', '--build-outputs=outputs', '--build-inputs=inputs', 'debug_macos_bundle_flutter_assets']); + await commandRunner.run([ + 'assemble', + '-o Output', + '--build-outputs=outputs', + '--build-inputs=inputs', + 'debug_macos_bundle_flutter_assets', + ]); expect(inputs.readAsStringSync(), contains('foo')); expect(inputs.readAsStringSync(), contains('fizz'));