Skip to content

Commit

Permalink
Re-land "Ensure flutter build apk --release optimizes+shrinks platfor…
Browse files Browse the repository at this point in the history
…m code" (flutter#153868)

Re-lands flutter#136880, fixes flutter#136879.

Additions to/things that are different from the original PR:
- Adds an entry to `gradle_errors.dart` that tells people when they run into the R8 bug because of using AGP 7.3.0 (https://issuetracker.google.com/issues/242308990).
- Previous PR moved templates off of AGP 7.3.0.
- Packages repo has been moved off AGP 7.3.0 (flutter/packages#7432).

Also, unrelatedly:
- Deletes an entry in `gradle_errors.dart` that informed people to build with `--no-shrink`. This flag [doesn't do anything](flutter/website#11022 (comment)), so it can't be the solution to any error.
- Uniquely lowers the priority of the `incompatibleKotlinVersionHandler`. This is necessary because the ordering of the errors doesn't fully determine the priority of which handler we decide to use, but also the order of the log lines. The kotlin error lines often print before the other error lines, so putting it last in the list of handlers isn't sufficient to lower it to be the lowest priority handler.
  • Loading branch information
gmackall authored and Buchimi committed Sep 2, 2024
1 parent dea1b94 commit cca3087
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 89 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,7 @@ class FlutterPlugin implements Plugin<Project> {
shrinkResources(isBuiltAsApp(project))
// Fallback to `android/app/proguard-rules.pro`.
// This way, custom Proguard rules can be configured as needed.
proguardFiles(project.android.getDefaultProguardFile("proguard-android.txt"), flutterProguardRules, "proguard-rules.pro")
proguardFiles(project.android.getDefaultProguardFile("proguard-android-optimize.txt"), flutterProguardRules, "proguard-rules.pro")
}
}
}
Expand Down
7 changes: 6 additions & 1 deletion packages/flutter_tools/lib/src/android/gradle.dart
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,12 @@ class AndroidGradleBuilder implements AndroidBuilder {
GradleHandledError? detectedGradleError;
String? detectedGradleErrorLine;
String? consumeLog(String line) {
if (detectedGradleError != null) {
// The log lines that trigger incompatibleKotlinVersionHandler don't
// always indicate an error, and there are times that that handler
// covers up a more important error handler. Uniquely set it to be
// the lowest priority handler by allowing it to be overridden.
if (detectedGradleError != null
&& detectedGradleError != incompatibleKotlinVersionHandler) {
// Pipe stdout/stderr from Gradle.
return line;
}
Expand Down
66 changes: 37 additions & 29 deletions packages/flutter_tools/lib/src/android/gradle_errors.dart
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,10 @@ final List<GradleHandledError> gradleErrors = <GradleHandledError>[
networkErrorHandler,
permissionDeniedErrorHandler,
flavorUndefinedHandler,
r8FailureHandler,
r8DexingBugInAgp73Handler,
minSdkVersionHandler,
transformInputIssueHandler,
lockFileDepMissingHandler,
incompatibleKotlinVersionHandler,
minCompileSdkVersionHandler,
jvm11RequiredHandler,
outdatedGradleHandler,
Expand All @@ -81,6 +80,7 @@ final List<GradleHandledError> gradleErrors = <GradleHandledError>[
remoteTerminatedHandshakeHandler,
couldNotOpenCacheDirectoryHandler,
incompatibleCompileSdk35AndAgpVersionHandler,
incompatibleKotlinVersionHandler, // This handler should always be last, as its key log output is sometimes in error messages with other root causes.
];

const String _boxTitle = 'Flutter Fix';
Expand Down Expand Up @@ -198,28 +198,6 @@ final GradleHandledError zipExceptionHandler = GradleHandledError(
eventLabel: 'zip-exception',
);

// R8 failure.
@visibleForTesting
final GradleHandledError r8FailureHandler = GradleHandledError(
test: _lineMatcher(const <String>[
'com.android.tools.r8',
]),
handler: ({
required String line,
required FlutterProject project,
required bool usesAndroidX,
}) async {
globals.printBox(
'${globals.logger.terminal.warningMark} The shrinker may have failed to optimize the Java bytecode.\n'
'To disable the shrinker, pass the `--no-shrink` flag to this command.\n'
'To learn more, see: https://developer.android.com/studio/build/shrink-code',
title: _boxTitle,
);
return GradleBuildStatus.exit;
},
eventLabel: 'r8',
);

/// Handle Gradle error thrown when Gradle needs to download additional
/// Android SDK components (e.g. Platform Tools), and the license
/// for that component has not been accepted.
Expand Down Expand Up @@ -429,7 +407,8 @@ final GradleHandledError lockFileDepMissingHandler = GradleHandledError(
eventLabel: 'lock-dep-issue',
);

@visibleForTesting
// This handler is made visible in other files so that we can uniquely set it
// to be the lowest priority error.
final GradleHandledError incompatibleKotlinVersionHandler = GradleHandledError(
test: _lineMatcher(const <String>[
'was compiled with an incompatible version of Kotlin',
Expand Down Expand Up @@ -642,6 +621,17 @@ final GradleHandledError couldNotOpenCacheDirectoryHandler = GradleHandledError(
eventLabel: 'could-not-open-cache-directory',
);


String _getAgpLocation(FlutterProject project) {
return '''
The version of AGP that your project uses is likely defined in:
${project.android.settingsGradleFile.path},
in the 'plugins' closure.
Alternatively, if your project was created with an older version of the templates, it is likely
in the buildscript.dependencies closure of the top-level build.gradle:
${project.android.hostAppGradleFile.path}.''';
}

@visibleForTesting
final GradleHandledError incompatibleCompileSdk35AndAgpVersionHandler = GradleHandledError(
test: (String line) => line.contains('RES_TABLE_TYPE_TYPE entry offsets overlap actual entry data'),
Expand All @@ -652,10 +642,7 @@ final GradleHandledError incompatibleCompileSdk35AndAgpVersionHandler = GradleHa
}) async {
globals.printBox(
'${globals.logger.terminal.warningMark} Using compileSdk 35 requires Android Gradle Plugin (AGP) 8.1.0 or higher.'
' \n Please upgrade to a newer AGP version. The version of AGP that your project uses is likely'
" defined in:\n${project.android.settingsGradleFile.path},\nin the 'plugins' closure. \n Alternatively, if your "
'project was created with an older version of the templates, it is likely \nin the buildscript.dependencies '
'closure of the top-level build.gradle:\n${project.android.hostAppGradleFile.path}.\n\n Finally, if you have a'
' \n Please upgrade to a newer AGP version.${_getAgpLocation(project)}\n\n Finally, if you have a'
' strong reason to avoid upgrading AGP, you can temporarily lower the compileSdk version in the following file:\n${project.android.appGradleFile.path}',
title: _boxTitle,
);
Expand All @@ -664,3 +651,24 @@ final GradleHandledError incompatibleCompileSdk35AndAgpVersionHandler = GradleHa
},
eventLabel: 'incompatible-compile-sdk-and-agp',
);

@visibleForTesting
final GradleHandledError r8DexingBugInAgp73Handler = GradleHandledError(
test: (String line) => line.contains('com.android.tools.r8.internal') && line.contains(': Unused argument with users'),
handler: ({
required String line,
required FlutterProject project,
required bool usesAndroidX,
}) async {
globals.printBox('''
${globals.logger.terminal.warningMark} Version 7.3 of the Android Gradle Plugin (AGP) uses a version of R8 that contains a bug which causes this error (see more info at https://issuetracker.google.com/issues/242308990).
To fix this error, update to a newer version of AGP (at least 7.4.0).
${_getAgpLocation(project)}''',
title: _boxTitle,
);

return GradleBuildStatus.exit;
},
eventLabel: 'r8-dexing-bug-in-AGP-7.3'
);
Original file line number Diff line number Diff line change
Expand Up @@ -426,61 +426,6 @@ void main() {
AndroidStudio: () => FakeAndroidStudio(),
});

testUsingContext('guides the user when the shrinker fails', () async {
final String projectPath = await createProject(tempDir, arguments: <String>['--no-pub', '--template=app', '--platform=android']);
const String r8StdoutWarning =
"Execution failed for task ':app:transformClassesAndResourcesWithR8ForStageInternal'.\n"
'> com.android.tools.r8.CompilationFailedException: Compilation failed to complete';
processManager.addCommand(FakeCommand(
command: <String>[
gradlew,
'-q',
'-Ptarget-platform=android-arm,android-arm64,android-x64',
'-Ptarget=${globals.fs.path.join(tempDir.path, 'flutter_project', 'lib', 'main.dart')}',
'-Pbase-application-name=android.app.Application',
'-Pdart-obfuscation=false',
'-Ptrack-widget-creation=true',
'-Ptree-shake-icons=true',
'assembleRelease',
],
exitCode: 1,
stdout: r8StdoutWarning,
));

await expectLater(
() => runBuildApkCommand(
projectPath,
),
throwsToolExit(message: 'Gradle task assembleRelease failed with exit code 1'),
);
expect(
testLogger.statusText, allOf(
containsIgnoringWhitespace('The shrinker may have failed to optimize the Java bytecode.'),
containsIgnoringWhitespace('To disable the shrinker, pass the `--no-shrink` flag to this command.'),
containsIgnoringWhitespace('To learn more, see: https://developer.android.com/studio/build/shrink-code'),
)
);

expect(
analytics.sentEvents,
contains(
Event.flutterBuildInfo(
label: 'gradle-r8-failure',
buildType: 'gradle',
),
),
);
expect(processManager, hasNoRemainingExpectations);
},
overrides: <Type, Generator>{
AndroidSdk: () => mockAndroidSdk,
Java: () => null,
FlutterProjectFactory: () => FakeFlutterProjectFactory(tempDir),
ProcessManager: () => processManager,
Analytics: () => analytics,
AndroidStudio: () => FakeAndroidStudio(),
});

testUsingContext("reports when the app isn't using AndroidX", () async {
final String projectPath = await createProject(tempDir, arguments: <String>['--no-pub', '--template=app', '--platform=android']);
// Simulate a non-androidx project.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,10 @@ void main() {
networkErrorHandler,
permissionDeniedErrorHandler,
flavorUndefinedHandler,
r8FailureHandler,
r8DexingBugInAgp73Handler,
minSdkVersionHandler,
transformInputIssueHandler,
lockFileDepMissingHandler,
incompatibleKotlinVersionHandler,
minCompileSdkVersionHandler,
jvm11RequiredHandler,
outdatedGradleHandler,
Expand All @@ -52,6 +51,7 @@ void main() {
remoteTerminatedHandshakeHandler,
couldNotOpenCacheDirectoryHandler,
incompatibleCompileSdk35AndAgpVersionHandler,
incompatibleKotlinVersionHandler,
])
);
});
Expand Down Expand Up @@ -1320,7 +1320,6 @@ Execution failed for task ':app:bundleReleaseResources'.
'│ /android/settings.gradle, │\n'
"│ in the 'plugins' closure. │\n"
'│ Alternatively, if your project was created with an older version of the templates, it is likely │\n'
'│ │\n'
'│ in the buildscript.dependencies closure of the top-level build.gradle: │\n'
'│ /android/build.gradle. │\n'
'│ │\n'
Expand All @@ -1337,6 +1336,43 @@ Execution failed for task ':app:bundleReleaseResources'.
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
});

testUsingContext('AGP 7.3.0 R8 bug', () async {
const String errorExample = r'''
ERROR:/Users/mackall/.gradle/caches/transforms-3/bd2c84591857c6d4c308221ffece862e/transformed/jetified-media3-exoplayer-dash-1.4.0-runtime.jar: R8: com.android.tools.r8.internal.Y10: Unused argument with users in androidx
''';

await r8DexingBugInAgp73Handler.handler(
line: errorExample,
project: FlutterProject.fromDirectoryTest(fileSystem.currentDirectory),
usesAndroidX: true,
);

expect(
testLogger.statusText,
contains(
'\n'
'┌─ Flutter Fix ────────────────────────────────────────────────────────────────────────────────────┐\n'
'│ [!] Version 7.3 of the Android Gradle Plugin (AGP) uses a version of R8 that contains a bug │\n'
'│ which causes this error (see more info at https://issuetracker.google.com/issues/242308990). │\n'
'│ To fix this error, update to a newer version of AGP (at least 7.4.0). │\n'
'│ │\n'
'│ The version of AGP that your project uses is likely defined in: │\n'
'│ /android/settings.gradle, │\n'
"│ in the 'plugins' closure. │\n"
'│ Alternatively, if your project was created with an older version of the templates, it is likely │\n'
'│ in the buildscript.dependencies closure of the top-level build.gradle: │\n'
'│ /android/build.gradle. │\n'
'└──────────────────────────────────────────────────────────────────────────────────────────────────┘\n'
''
)
);
}, overrides: <Type, Generator>{
GradleUtils: () => FakeGradleUtils(),
Platform: () => fakePlatform('android'),
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
});
}

bool formatTestErrorMessage(String errorMessage, GradleHandledError error) {
Expand Down

0 comments on commit cca3087

Please sign in to comment.