Re-land "Ensure flutter build apk --release optimizes+shrinks platform code" (#153868)

Re-lands https://github.com/flutter/flutter/pull/136880, fixes https://github.com/flutter/flutter/issues/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 (https://github.com/flutter/packages/pull/7432).

Also, unrelatedly:
- Deletes an entry in `gradle_errors.dart` that informed people to build with `--no-shrink`. This flag [doesn't do anything](https://github.com/flutter/website/pull/11022#issuecomment-2297294421), 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.
This commit is contained in:
Gray Mackall 2024-08-23 09:44:08 -07:00 committed by GitHub
parent 1cdff3616e
commit b2de4dfc2b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 83 additions and 89 deletions

View File

@ -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")
}
}
}

View File

@ -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;
}

View File

@ -67,11 +67,10 @@ final List<GradleHandledError> gradleErrors = <GradleHandledError>[
networkErrorHandler,
permissionDeniedErrorHandler,
flavorUndefinedHandler,
r8FailureHandler,
r8DexingBugInAgp73Handler,
minSdkVersionHandler,
transformInputIssueHandler,
lockFileDepMissingHandler,
incompatibleKotlinVersionHandler,
minCompileSdkVersionHandler,
jvm11RequiredHandler,
outdatedGradleHandler,
@ -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';
@ -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.
@ -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',
@ -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'),
@ -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,
);
@ -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'
);

View File

@ -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.

View File

@ -38,11 +38,10 @@ void main() {
networkErrorHandler,
permissionDeniedErrorHandler,
flavorUndefinedHandler,
r8FailureHandler,
r8DexingBugInAgp73Handler,
minSdkVersionHandler,
transformInputIssueHandler,
lockFileDepMissingHandler,
incompatibleKotlinVersionHandler,
minCompileSdkVersionHandler,
jvm11RequiredHandler,
outdatedGradleHandler,
@ -52,6 +51,7 @@ void main() {
remoteTerminatedHandshakeHandler,
couldNotOpenCacheDirectoryHandler,
incompatibleCompileSdk35AndAgpVersionHandler,
incompatibleKotlinVersionHandler,
])
);
});
@ -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'
@ -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) {