From 7ab653c6cad04f09c6ee2293c059e29698ffaa89 Mon Sep 17 00:00:00 2001 From: Jonah Williams Date: Fri, 11 Oct 2019 07:16:11 -0700 Subject: [PATCH] Add error logging to flutter generate (#42209) --- .../lib/src/commands/generate.dart | 33 ++++++-- .../hermetic/generate_test.dart | 82 +++++++++++++++++++ 2 files changed, 109 insertions(+), 6 deletions(-) create mode 100644 packages/flutter_tools/test/commands.shard/hermetic/generate_test.dart diff --git a/packages/flutter_tools/lib/src/commands/generate.dart b/packages/flutter_tools/lib/src/commands/generate.dart index 20a426874e2..5426d379281 100644 --- a/packages/flutter_tools/lib/src/commands/generate.dart +++ b/packages/flutter_tools/lib/src/commands/generate.dart @@ -2,9 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import '../base/common.dart'; +import '../base/file_system.dart'; import '../cache.dart'; import '../codegen.dart'; +import '../convert.dart'; +import '../globals.dart'; import '../project.dart'; import '../runner/flutter_command.dart'; @@ -18,9 +20,6 @@ class GenerateCommand extends FlutterCommand { @override String get name => 'generate'; - @override - bool get hidden => true; - @override Future> get requiredArtifacts async => const { DevelopmentArtifact.universal, @@ -34,12 +33,34 @@ class GenerateCommand extends FlutterCommand { codegenDaemon.startBuild(); await for (CodegenStatus codegenStatus in codegenDaemon.buildResults) { if (codegenStatus == CodegenStatus.Failed) { - throwToolExit('Code generation failed'); + printError('Code generation failed.'); + break; } if (codegenStatus ==CodegenStatus.Succeeded) { break; } } - return null; + // Check for errors output in the build_runner cache. + final Directory buildDirectory = flutterProject.dartTool.childDirectory('build'); + final Directory errorCacheParent = buildDirectory.listSync().firstWhere((FileSystemEntity entity) { + return entity is Directory && entity.childDirectory('error_cache').existsSync(); + }, orElse: () => null); + if (errorCacheParent == null) { + return null; + } + final Directory errorCache = errorCacheParent.childDirectory('error_cache'); + for (File errorFile in errorCache.listSync(recursive: true).whereType()) { + try { + final List errorData = json.decode(errorFile.readAsStringSync()); + final List stackData = errorData[1]; + printError(errorData.first); + printError(stackData[0]); + printError(stackData[1]); + printError(StackTrace.fromString(stackData[2]).toString()); + } catch (err) { + printError('Error reading error in ${errorFile.path}'); + } + } + return const FlutterCommandResult(ExitStatus.fail); } } diff --git a/packages/flutter_tools/test/commands.shard/hermetic/generate_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/generate_test.dart new file mode 100644 index 00000000000..51391ba9456 --- /dev/null +++ b/packages/flutter_tools/test/commands.shard/hermetic/generate_test.dart @@ -0,0 +1,82 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter_tools/src/base/file_system.dart'; +import 'package:flutter_tools/src/base/logger.dart'; +import 'package:flutter_tools/src/cache.dart'; +import 'package:flutter_tools/src/codegen.dart'; +import 'package:flutter_tools/src/commands/generate.dart'; +import 'package:flutter_tools/src/convert.dart'; +import 'package:flutter_tools/src/globals.dart'; +import 'package:mockito/mockito.dart'; + +import '../../src/common.dart'; +import '../../src/mocks.dart'; +import '../../src/testbed.dart'; + +void main() { + Testbed testbed; + MockCodeGenerator mockCodeGenerator; + MockCodegenDaemon mockCodegenDaemon; + + setUpAll(() { + Cache.disableLocking(); + }); + + tearDownAll(() { + Cache.enableLocking(); + }); + + setUp(() { + mockCodegenDaemon = MockCodegenDaemon(); + mockCodeGenerator = MockCodeGenerator(); + when(mockCodegenDaemon.buildResults).thenAnswer((Invocation invocation) { + return Stream.fromIterable([ + CodegenStatus.Started, + CodegenStatus.Succeeded, + ]); + }); + when(mockCodeGenerator.daemon(any)).thenAnswer((Invocation invocation) async { + return mockCodegenDaemon; + }); + testbed = Testbed(overrides: { + CodeGenerator: () => mockCodeGenerator, + }); + }); + + test('Outputs error information from flutter generate', () => testbed.run(() async { + final GenerateCommand command = GenerateCommand(); + final BufferLogger bufferLogger = logger; + applyMocksToCommand(command); + fs.file(fs.path.join('lib', 'main.dart')) + ..createSync(recursive: true); + + fs.currentDirectory + .childDirectory('.dart_tool') + .childDirectory('build') + .childDirectory('abcdefg') + .childDirectory('error_cache') + .childFile('foo_error') + ..createSync(recursive: true) + ..writeAsStringSync(json.encode([ + 'foo builder', + [ + 'a', + 'b', + StackTrace.current.toString(), + ] + ])); + + await createTestCommandRunner(command) + .run(const ['generate']); + + expect(bufferLogger.errorText, contains('a')); + expect(bufferLogger.errorText, contains('b')); + expect(bufferLogger.errorText, contains('foo builder')); + expect(bufferLogger.errorText, isNot(contains('Error reading error'))); + })); +} + +class MockCodeGenerator extends Mock implements CodeGenerator { } +class MockCodegenDaemon extends Mock implements CodegenDaemon { }