Unverified Commit dea35da0 authored by Emmanuel Garcia's avatar Emmanuel Garcia Committed by GitHub

Log additional Android build failures (#43941)

parent 924e48ee
......@@ -156,6 +156,7 @@ Future<void> checkGradleDependencies() async {
/// 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 @@ Future<void> buildGradleApp({
if (!_isSupportedVersion(project.android)) {
_exitWithUnsupportedProjectMessage();
}
final Directory buildDirectory = project.android.buildDirectory;
final bool usesAndroidX = isAppUsingAndroidX(project.android.hostAppGradleRoot);
if (usesAndroidX) {
......@@ -255,7 +257,7 @@ Future<void> buildGradleApp({
await buildPluginsAsAar(
project,
androidBuildInfo,
buildDirectory: project.android.buildDirectory.childDirectory('app'),
buildDirectory: buildDirectory.childDirectory('app'),
);
}
......@@ -340,7 +342,7 @@ Future<void> buildGradleApp({
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 @@ Future<void> buildGradleApp({
break;
}
}
// Pipe stdout/sterr from Gradle.
// Pipe stdout/stderr from Gradle.
return line;
},
);
......@@ -363,7 +365,7 @@ Future<void> buildGradleApp({
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 @@ Future<void> buildGradleApp({
);
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 @@ Future<void> buildGradleApp({
// 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 @@ Future<void> buildGradleApp({
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 @@ Future<void> buildGradleApp({
}
// 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 @@ Future<void> buildPluginsAsAar(
} 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 @@ Future<void> buildPluginsAsAar(
@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 @@ Iterable<File> findApkFiles(
}
return const <File>[];
});
if (apks.isEmpty) {
_exitWithExpectedFileNotFound(
project: project,
fileExtension: '.apk',
);
}
return apks;
}
@visibleForTesting
......@@ -716,5 +714,31 @@ File findBundleFile(FlutterProject project, BuildInfo buildInfo) {
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.'
);
}
......@@ -14,7 +14,7 @@ import 'gradle_utils.dart';
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 @@ class GradleHandledError{
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 @@ final GradleHandledError androidXFailureHandler = GradleHandledError(
// 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 @@ final GradleHandledError androidXFailureHandler = GradleHandledError(
'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 @@ final GradleHandledError androidXFailureHandler = GradleHandledError(
// 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 @@ final GradleHandledError androidXFailureHandler = GradleHandledError(
'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;
......
......@@ -279,7 +279,7 @@ Command: /home/android/gradlew assembleRelease
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 @@ Command: /home/android/gradlew assembleRelease
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 @@ Command: /home/android/gradlew assembleRelease
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 @@ Command: /home/android/gradlew assembleRelease
verify(mockUsage.sendEvent(
any,
any,
label: 'gradle--android-x-failure',
label: 'gradle-android-x-failure',
parameters: <String, String>{
'cd43': 'not-using-jetifier',
},
......
......@@ -132,6 +132,8 @@ void main() {
});
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 @@ void main() {
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 @@ void main() {
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 @@ void main() {
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 @@ plugin2=${plugin2.path}
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 @@ plugin2=${plugin2.path}
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 @@ plugin2=${plugin2.path}
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 @@ plugin2=${plugin2.path}
verify(mockUsage.sendEvent(
any,
any,
label: 'gradle--random-event-label-failure',
label: 'gradle-random-event-label-failure',
parameters: anyNamed('parameters'),
)).called(1);
......
......@@ -320,7 +320,7 @@ flutter:
verify(mockUsage.sendEvent(
'build',
'apk',
label: 'gradle--r8-failure',
label: 'gradle-r8-failure',
parameters: anyNamed('parameters'),
)).called(1);
},
......
......@@ -306,7 +306,7 @@ flutter:
verify(mockUsage.sendEvent(
'build',
'appbundle',
label: 'gradle--r8-failure',
label: 'gradle-r8-failure',
parameters: anyNamed('parameters'),
)).called(1);
},
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment