From b0a22998593fc605c723dee8ff4d9315c32cfe2c Mon Sep 17 00:00:00 2001 From: Christopher Fujino Date: Tue, 5 Jan 2021 12:34:13 -0800 Subject: [PATCH] Ensure apps can build while using AGP 3.3.0 (#71964) (#72754) Co-authored-by: Emmanuel Garcia --- ...android_plugin_example_app_build_test.dart | 85 +++++++++++++++++-- packages/flutter_tools/gradle/flutter.gradle | 35 ++++++-- 2 files changed, 108 insertions(+), 12 deletions(-) diff --git a/dev/devicelab/bin/tasks/android_plugin_example_app_build_test.dart b/dev/devicelab/bin/tasks/android_plugin_example_app_build_test.dart index 469a4d06b08c..e15f02eb8d1a 100644 --- a/dev/devicelab/bin/tasks/android_plugin_example_app_build_test.dart +++ b/dev/devicelab/bin/tasks/android_plugin_example_app_build_test.dart @@ -15,7 +15,6 @@ final String gradlewExecutable = Platform.isWindows ? '.\\$gradlew' : './$gradle /// Tests that a plugin example app can be built using the current Flutter Gradle plugin. Future main() async { await task(() async { - section('Find Java'); final String javaHome = await findJavaHome(); @@ -32,22 +31,47 @@ Future main() async { options: ['--android', '--no-ios'], ); - final Directory tempDir = Directory.systemTemp.createTempSync('flutter_plugin_test.'); - final Directory projectDir = Directory(path.join(tempDir.path, 'plugin_test')); + final Directory tempDir = + Directory.systemTemp.createTempSync('flutter_plugin_test.'); + final Directory projectDir = + Directory(path.join(tempDir.path, 'plugin_test')); try { await inDirectory(tempDir, () async { await flutter( 'create', - options: ['--template=plugin', '--platforms=android', 'plugin_test'], + options: [ + '--template=plugin', + '--platforms=android', + 'plugin_test', + ], ); }); - final Directory exampleAppDir = Directory(path.join(projectDir.path, 'example')); + final Directory exampleAppDir = + Directory(path.join(projectDir.path, 'example')); if (!exists(exampleAppDir)) { return TaskResult.failure('Example app directory doesn\'t exist'); } - section('Run flutter build apk'); + final File buildGradleFile = + File(path.join(exampleAppDir.path, 'android', 'build.gradle')); + + if (!exists(buildGradleFile)) { + return TaskResult.failure('$buildGradleFile doesn\'t exist'); + } + + final String buildGradle = buildGradleFile.readAsStringSync(); + final RegExp androidPluginRegExp = + RegExp(r'com\.android\.tools\.build:gradle:(\d+\.\d+\.\d+)'); + + section('Use AGP 4.1.0'); + + String newBuildGradle = buildGradle.replaceAll( + androidPluginRegExp, 'com.android.tools.build:gradle:4.1.0'); + print(newBuildGradle); + buildGradleFile.writeAsString(newBuildGradle); + + section('Run flutter build apk using AGP 4.1.0'); await inDirectory(exampleAppDir, () async { await flutter( @@ -72,6 +96,55 @@ Future main() async { return TaskResult.failure('Failed to build app-release.apk'); } + section('Clean'); + + await inDirectory(exampleAppDir, () async { + await flutter('clean'); + }); + + section('Remove Gradle wrapper'); + + Directory(path.join(exampleAppDir.path, 'android', 'gradle', 'wrapper')) + .deleteSync(recursive: true); + + section('Use AGP 3.3.0'); + + newBuildGradle = buildGradle.replaceAll( + androidPluginRegExp, 'com.android.tools.build:gradle:3.3.0'); + print(newBuildGradle); + buildGradleFile.writeAsString(newBuildGradle); + + section('Enable R8 in gradle.properties'); + + final File gradleProperties = + File(path.join(exampleAppDir.path, 'android', 'gradle.properties')); + + if (!exists(gradleProperties)) { + return TaskResult.failure('$gradleProperties doesn\'t exist'); + } + + gradleProperties.writeAsString(''' +org.gradle.jvmargs=-Xmx1536M +android.useAndroidX=true +android.enableJetifier=true +android.enableR8=true'''); + + section('Run flutter build apk using AGP 3.3.0'); + + await inDirectory(exampleAppDir, () async { + await flutter( + 'build', + options: [ + 'apk', + '--target-platform=android-arm', + ], + ); + }); + + if (!exists(File(exampleApk))) { + return TaskResult.failure('Failed to build app-release.apk'); + } + return TaskResult.success(null); } on TaskResult catch (taskResult) { return taskResult; diff --git a/packages/flutter_tools/gradle/flutter.gradle b/packages/flutter_tools/gradle/flutter.gradle index 579cdea997cf..8ea1ea2af528 100644 --- a/packages/flutter_tools/gradle/flutter.gradle +++ b/packages/flutter_tools/gradle/flutter.gradle @@ -20,6 +20,7 @@ import org.gradle.api.tasks.InputFiles import org.gradle.api.tasks.OutputDirectory import org.gradle.api.tasks.TaskAction import org.gradle.api.tasks.bundling.Jar +import org.gradle.util.VersionNumber buildscript { repositories { @@ -304,7 +305,7 @@ class FlutterPlugin implements Plugin { project.dependencies { implementation pluginProject } - Closure addEmbeddingCompileOnlyDependency = { buildType -> + Closure addEmbeddingDependencyToPlugin = { buildType -> String flutterBuildMode = buildModeFor(buildType) // In AGP 3.5, the embedding must be added as an API implementation, // so java8 features are desugared against the runtime classpath. @@ -317,17 +318,39 @@ class FlutterPlugin implements Plugin { pluginProject.android.buildTypes { "${buildType.name}" {} } - pluginProject.dependencies.add( - "${buildType.name}CompileOnly", - "io.flutter:flutter_embedding_$flutterBuildMode:$engineVersion") + // The embedding is a compileOnly dependency of a Flutter plugin, + // however prior to Gradle 6.0.0, it must be an API dependency. + // + // Not doing so, causes transitive dependency resolution conflicts. + // That is, the embedding dependencies resolved in the plugin are + // different than the ones resolved in the app. + if (isGradleVersionGraterOrEqualThan('6.0.0')) { + addCompileOnlyDependency( + pluginProject, + buildType.name, + "io.flutter:flutter_embedding_$flutterBuildMode:$engineVersion" + ) + } else { + addApiDependencies( + pluginProject, + buildType.name, + "io.flutter:flutter_embedding_$flutterBuildMode:$engineVersion" + ) + } } // Wait until the Android plugin loaded. pluginProject.afterEvaluate { - project.android.buildTypes.each addEmbeddingCompileOnlyDependency - project.android.buildTypes.whenObjectAdded addEmbeddingCompileOnlyDependency + project.android.buildTypes.each addEmbeddingDependencyToPlugin + project.android.buildTypes.whenObjectAdded addEmbeddingDependencyToPlugin } } + // Returns `true` if the current Gradle version is greater or equal to the given version. + private isGradleVersionGraterOrEqualThan(String version) { + return VersionNumber.parse(project.gradle.gradleVersion) + .compareTo(VersionNumber.parse(version)) >= 0 + } + // Returns `true` if the given path contains an `android/build.gradle` file. // // TODO(egarciad): Fix https://github.com/flutter/flutter/issues/39657.