Log additional Android build failures (#43941)
diff --git a/packages/flutter_tools/lib/src/android/gradle.dart b/packages/flutter_tools/lib/src/android/gradle.dart
index dff67bd..ed11fff 100644
--- a/packages/flutter_tools/lib/src/android/gradle.dart
+++ b/packages/flutter_tools/lib/src/android/gradle.dart
@@ -156,6 +156,7 @@
/// Tries to create `settings_aar.gradle` in an app project by removing the subprojects
/// from the existing `settings.gradle` file. This operation will fail if the existing
/// `settings.gradle` file has local edits.
+@visibleForTesting
void createSettingsAarGradle(Directory androidDirectory) {
final File newSettingsFile = androidDirectory.childFile('settings_aar.gradle');
if (newSettingsFile.existsSync()) {
@@ -232,6 +233,7 @@
if (!_isSupportedVersion(project.android)) {
_exitWithUnsupportedProjectMessage();
}
+ final Directory buildDirectory = project.android.buildDirectory;
final bool usesAndroidX = isAppUsingAndroidX(project.android.hostAppGradleRoot);
if (usesAndroidX) {
@@ -255,7 +257,7 @@
await buildPluginsAsAar(
project,
androidBuildInfo,
- buildDirectory: project.android.buildDirectory.childDirectory('app'),
+ buildDirectory: buildDirectory.childDirectory('app'),
);
}
@@ -340,7 +342,7 @@
return null;
}
if (detectedGradleError != null) {
- // Pipe stdout/sterr from Gradle.
+ // Pipe stdout/stderr from Gradle.
return line;
}
for (final GradleHandledError gradleError in localGradleErrors) {
@@ -351,7 +353,7 @@
break;
}
}
- // Pipe stdout/sterr from Gradle.
+ // Pipe stdout/stderr from Gradle.
return line;
},
);
@@ -363,7 +365,7 @@
if (exitCode != 0) {
if (detectedGradleError == null) {
- BuildEvent('gradle--unkown-failure').send();
+ BuildEvent('gradle-unkown-failure').send();
throwToolExit(
'Gradle task $assembleTask failed with exit code $exitCode',
exitCode: exitCode,
@@ -377,7 +379,7 @@
);
if (retries >= 1) {
- final String successEventLabel = 'gradle--${detectedGradleError.eventLabel}-success';
+ final String successEventLabel = 'gradle-${detectedGradleError.eventLabel}-success';
switch (status) {
case GradleBuildStatus.retry:
await buildGradleApp(
@@ -407,7 +409,7 @@
// noop.
}
}
- BuildEvent('gradle--${detectedGradleError.eventLabel}-failure').send();
+ BuildEvent('gradle-${detectedGradleError.eventLabel}-failure').send();
throwToolExit(
'Gradle task $assembleTask failed with exit code $exitCode',
exitCode: exitCode,
@@ -417,10 +419,6 @@
if (isBuildingBundle) {
final File bundleFile = findBundleFile(project, buildInfo);
- if (bundleFile == null) {
- throwToolExit('Gradle build failed to produce an Android bundle package.');
- }
-
final String appSize = (buildInfo.mode == BuildMode.debug)
? '' // Don't display the size when building a debug variant.
: ' (${getSizeAsMB(bundleFile.lengthSync())})';
@@ -433,10 +431,6 @@
}
// Gradle produced an APK.
final Iterable<File> apkFiles = findApkFiles(project, androidBuildInfo);
- if (apkFiles.isEmpty) {
- throwToolExit('Gradle build failed to produce an Android package.');
- }
-
final Directory apkDirectory = getApkDirectory(project);
// Copy the first APK to app.apk, so `flutter run` can find it.
// TODO(egarciad): Handle multiple APKs.
@@ -640,7 +634,7 @@
} on ToolExit {
// Log the entire plugin entry in `.flutter-plugins` since it
// includes the plugin name and the version.
- BuildEvent('plugin-aar-failure', eventError: plugin).send();
+ BuildEvent('gradle-plugin-aar-failure', eventError: plugin).send();
throwToolExit('The plugin $pluginName could not be built due to the issue above.');
}
}
@@ -650,14 +644,11 @@
@visibleForTesting
Iterable<File> findApkFiles(
FlutterProject project,
- AndroidBuildInfo androidBuildInfo)
-{
+ AndroidBuildInfo androidBuildInfo,
+) {
final Iterable<String> apkFileNames = _apkFilesFor(androidBuildInfo);
- if (apkFileNames.isEmpty) {
- return const <File>[];
- }
final Directory apkDirectory = getApkDirectory(project);
- return apkFileNames.expand<File>((String apkFileName) {
+ final Iterable<File> apks = apkFileNames.expand<File>((String apkFileName) {
File apkFile = apkDirectory.childFile(apkFileName);
if (apkFile.existsSync()) {
return <File>[apkFile];
@@ -682,6 +673,13 @@
}
return const <File>[];
});
+ if (apks.isEmpty) {
+ _exitWithExpectedFileNotFound(
+ project: project,
+ fileExtension: '.apk',
+ );
+ }
+ return apks;
}
@visibleForTesting
@@ -716,5 +714,31 @@
return bundleFile;
}
}
+ _exitWithExpectedFileNotFound(
+ project: project,
+ fileExtension: '.aab',
+ );
return null;
}
+
+/// Throws a [ToolExit] exception and logs the event.
+void _exitWithExpectedFileNotFound({
+ @required FlutterProject project,
+ @required String fileExtension,
+}) {
+ assert(project != null);
+ assert(fileExtension != null);
+
+ final String androidGradlePluginVersion =
+ getGradleVersionForAndroidPlugin(project.android.hostAppGradleRoot);
+ BuildEvent('gradle-expected-file-not-found',
+ settings:
+ 'androidGradlePluginVersion: $androidGradlePluginVersion, '
+ 'fileExtension: $fileExtension'
+ ).send();
+ throwToolExit(
+ 'Gradle build failed to produce an $fileExtension file. '
+ 'It\'s likely that this file was generated under ${project.android.buildDirectory.path}, '
+ 'but the tool couldn\'t find it.'
+ );
+}
diff --git a/packages/flutter_tools/lib/src/android/gradle_errors.dart b/packages/flutter_tools/lib/src/android/gradle_errors.dart
index bcec1a7..ac94bd1 100644
--- a/packages/flutter_tools/lib/src/android/gradle_errors.dart
+++ b/packages/flutter_tools/lib/src/android/gradle_errors.dart
@@ -14,7 +14,7 @@
typedef GradleErrorTest = bool Function(String);
/// A Gradle error handled by the tool.
-class GradleHandledError{
+class GradleHandledError {
const GradleHandledError({
this.test,
this.handler,
@@ -33,14 +33,14 @@
bool shouldBuildPluginAsAar,
}) handler;
- /// The [BuildEvent] label is named gradle--[eventLabel].
+ /// The [BuildEvent] label is named gradle-[eventLabel].
/// If not empty, the build event is logged along with
/// additional metadata such as the attempt number.
final String eventLabel;
}
/// The status of the Gradle build.
-enum GradleBuildStatus{
+enum GradleBuildStatus {
/// The tool cannot recover from the failure and should exit.
exit,
/// The tool can retry the exact same build.
@@ -180,7 +180,7 @@
// If the app doesn't use any plugin, then it's unclear where
// the incompatibility is coming from.
BuildEvent(
- 'gradle--android-x-failure',
+ 'gradle-android-x-failure',
eventError: 'app-not-using-plugins',
).send();
}
@@ -192,7 +192,7 @@
'Please migrate your app to AndroidX. See https://goo.gl/CP92wY.'
);
BuildEvent(
- 'gradle--android-x-failure',
+ 'gradle-android-x-failure',
eventError: 'app-not-using-androidx',
).send();
}
@@ -201,7 +201,7 @@
// by this point the app is using AndroidX, the plugins are built as
// AARs, Jetifier translated Support libraries for AndroidX equivalents.
BuildEvent(
- 'gradle--android-x-failure',
+ 'gradle-android-x-failure',
eventError: 'using-jetifier',
).send();
}
@@ -211,7 +211,7 @@
'The tool is about to try using Jetfier to solve the incompatibility.'
);
BuildEvent(
- 'gradle--android-x-failure',
+ 'gradle-android-x-failure',
eventError: 'not-using-jetifier',
).send();
return GradleBuildStatus.retryWithAarPlugins;
diff --git a/packages/flutter_tools/test/general.shard/android/gradle_errors_test.dart b/packages/flutter_tools/test/general.shard/android/gradle_errors_test.dart
index aca7919..8e05d75 100644
--- a/packages/flutter_tools/test/general.shard/android/gradle_errors_test.dart
+++ b/packages/flutter_tools/test/general.shard/android/gradle_errors_test.dart
@@ -279,7 +279,7 @@
verify(mockUsage.sendEvent(
any,
any,
- label: 'gradle--android-x-failure',
+ label: 'gradle-android-x-failure',
parameters: <String, String>{
'cd43': 'app-not-using-plugins',
},
@@ -312,7 +312,7 @@
verify(mockUsage.sendEvent(
any,
any,
- label: 'gradle--android-x-failure',
+ label: 'gradle-android-x-failure',
parameters: <String, String>{
'cd43': 'app-not-using-androidx',
},
@@ -338,7 +338,7 @@
verify(mockUsage.sendEvent(
any,
any,
- label: 'gradle--android-x-failure',
+ label: 'gradle-android-x-failure',
parameters: <String, String>{
'cd43': 'using-jetifier',
},
@@ -371,7 +371,7 @@
verify(mockUsage.sendEvent(
any,
any,
- label: 'gradle--android-x-failure',
+ label: 'gradle-android-x-failure',
parameters: <String, String>{
'cd43': 'not-using-jetifier',
},
diff --git a/packages/flutter_tools/test/general.shard/android/gradle_test.dart b/packages/flutter_tools/test/general.shard/android/gradle_test.dart
index 0571175..ae81bdf 100644
--- a/packages/flutter_tools/test/general.shard/android/gradle_test.dart
+++ b/packages/flutter_tools/test/general.shard/android/gradle_test.dart
@@ -132,6 +132,8 @@
});
group('findBundleFile', () {
+ final Usage mockUsage = MockUsage();
+
testUsingContext('Finds app bundle when flavor contains underscores in release mode', () {
final FlutterProject project = generateFakeAppBundle('foo_barRelease', 'app.aab');
final File bundle = findBundleFile(project, const BuildInfo(BuildMode.release, 'foo_bar'));
@@ -281,9 +283,39 @@
FileSystem: () => MemoryFileSystem(),
ProcessManager: () => FakeProcessManager.any(),
});
+
+ testUsingContext('aab not found', () {
+ final FlutterProject project = FlutterProject.current();
+ expect(
+ () {
+ findBundleFile(project, const BuildInfo(BuildMode.debug, 'foo_bar'));
+ },
+ throwsToolExit(
+ message:
+ 'Gradle build failed to produce an .aab file. It\'s likely that this file '
+ 'was generated under ${project.android.buildDirectory.path}, but the tool couldn\'t find it.'
+ )
+ );
+ verify(
+ mockUsage.sendEvent(
+ any,
+ any,
+ label: 'gradle-expected-file-not-found',
+ parameters: const <String, String> {
+ 'cd37': 'androidGradlePluginVersion: 5.6.2, fileExtension: .aab',
+ },
+ ),
+ ).called(1);
+ }, overrides: <Type, Generator>{
+ FileSystem: () => MemoryFileSystem(),
+ ProcessManager: () => FakeProcessManager.any(),
+ Usage: () => mockUsage,
+ });
});
group('findApkFiles', () {
+ final Usage mockUsage = MockUsage();
+
testUsingContext('Finds APK without flavor in release', () {
final FlutterProject project = MockFlutterProject();
final AndroidProject androidProject = MockAndroidProject();
@@ -352,6 +384,37 @@
FileSystem: () => MemoryFileSystem(),
ProcessManager: () => FakeProcessManager.any(),
});
+
+ testUsingContext('apk not found', () {
+ final FlutterProject project = FlutterProject.current();
+ expect(
+ () {
+ findApkFiles(
+ project,
+ const AndroidBuildInfo(BuildInfo(BuildMode.debug, 'foo_bar')),
+ );
+ },
+ throwsToolExit(
+ message:
+ 'Gradle build failed to produce an .apk file. It\'s likely that this file '
+ 'was generated under ${project.android.buildDirectory.path}, but the tool couldn\'t find it.'
+ )
+ );
+ verify(
+ mockUsage.sendEvent(
+ any,
+ any,
+ label: 'gradle-expected-file-not-found',
+ parameters: const <String, String> {
+ 'cd37': 'androidGradlePluginVersion: 5.6.2, fileExtension: .apk',
+ },
+ ),
+ ).called(1);
+ }, overrides: <Type, Generator>{
+ FileSystem: () => MemoryFileSystem(),
+ ProcessManager: () => FakeProcessManager.any(),
+ Usage: () => mockUsage,
+ });
});
group('gradle build', () {
@@ -365,15 +428,6 @@
AndroidSdk: () => null,
});
- // Regression test for https://github.com/flutter/flutter/issues/34700
- testUsingContext('Does not return nulls in apk list', () {
- const AndroidBuildInfo buildInfo = AndroidBuildInfo(BuildInfo.debug);
- expect(findApkFiles(FlutterProject.current(), buildInfo), <File>[]);
- }, overrides: <Type, Generator>{
- FileSystem: () => MemoryFileSystem(),
- ProcessManager: () => FakeProcessManager.any(),
- });
-
test('androidXPluginWarningRegex should match lines with the AndroidX plugin warnings', () {
final List<String> nonMatchingLines = <String>[
':app:preBuild UP-TO-DATE',
@@ -1118,7 +1172,7 @@
verify(mockUsage.sendEvent(
any,
any,
- label: 'gradle--random-event-label-failure',
+ label: 'gradle-random-event-label-failure',
parameters: anyNamed('parameters'),
)).called(1);
@@ -1199,7 +1253,7 @@
verify(mockUsage.sendEvent(
any,
any,
- label: 'gradle--random-event-label-failure',
+ label: 'gradle-random-event-label-failure',
parameters: anyNamed('parameters'),
)).called(1);
@@ -1287,7 +1341,7 @@
verify(mockUsage.sendEvent(
any,
any,
- label: 'gradle--random-event-label-success',
+ label: 'gradle-random-event-label-success',
parameters: anyNamed('parameters'),
)).called(1);
@@ -1373,7 +1427,7 @@
verify(mockUsage.sendEvent(
any,
any,
- label: 'gradle--random-event-label-failure',
+ label: 'gradle-random-event-label-failure',
parameters: anyNamed('parameters'),
)).called(1);
diff --git a/packages/flutter_tools/test/general.shard/commands/build_apk_test.dart b/packages/flutter_tools/test/general.shard/commands/build_apk_test.dart
index 4970b0b..ca40523 100644
--- a/packages/flutter_tools/test/general.shard/commands/build_apk_test.dart
+++ b/packages/flutter_tools/test/general.shard/commands/build_apk_test.dart
@@ -320,7 +320,7 @@
verify(mockUsage.sendEvent(
'build',
'apk',
- label: 'gradle--r8-failure',
+ label: 'gradle-r8-failure',
parameters: anyNamed('parameters'),
)).called(1);
},
diff --git a/packages/flutter_tools/test/general.shard/commands/build_appbundle_test.dart b/packages/flutter_tools/test/general.shard/commands/build_appbundle_test.dart
index 8f79b4a..6b2b3e0 100644
--- a/packages/flutter_tools/test/general.shard/commands/build_appbundle_test.dart
+++ b/packages/flutter_tools/test/general.shard/commands/build_appbundle_test.dart
@@ -306,7 +306,7 @@
verify(mockUsage.sendEvent(
'build',
'appbundle',
- label: 'gradle--r8-failure',
+ label: 'gradle-r8-failure',
parameters: anyNamed('parameters'),
)).called(1);
},