diff --git a/dev/devicelab/bin/tasks/build_aar_plugin_test.dart b/dev/devicelab/bin/tasks/build_aar_plugin_test.dart deleted file mode 100644 index e0df9586d1..0000000000 --- a/dev/devicelab/bin/tasks/build_aar_plugin_test.dart +++ /dev/null @@ -1,138 +0,0 @@ -// Copyright 2014 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:io'; - -import 'package:flutter_devicelab/framework/framework.dart'; -import 'package:flutter_devicelab/framework/task_result.dart'; -import 'package:flutter_devicelab/framework/utils.dart'; -import 'package:path/path.dart' as path; - -final String gradlew = Platform.isWindows ? 'gradlew.bat' : 'gradlew'; -final String gradlewExecutable = Platform.isWindows ? '.\\$gradlew' : './$gradlew'; - -/// Tests that AARs can be built on plugin projects. -Future main() async { - await task(() async { - - section('Find Java'); - - final String? javaHome = await findJavaHome(); - if (javaHome == null) - return TaskResult.failure('Could not find Java'); - print('\nUsing JAVA_HOME=$javaHome'); - - section('Create plugin project'); - - final Directory tempDir = Directory.systemTemp.createTempSync('flutter_module_test.'); - final Directory projectDir = Directory(path.join(tempDir.path, 'hello')); - try { - await inDirectory(tempDir, () async { - await flutter( - 'create', - options: [ - '--org', 'io.flutter.devicelab', - '--template', 'plugin', - 'hello', - ], - ); - }); - - section('Build release AAR'); - - await inDirectory(projectDir, () async { - await flutter( - 'build', - options: ['aar', '--verbose', '--release'], - ); - }); - - final String repoPath = path.join( - projectDir.path, - 'build', - 'outputs', - 'repo', - ); - - final File releaseAar = File(path.join( - repoPath, - 'io', - 'flutter', - 'devicelab', - 'hello', - 'hello_release', - '1.0', - 'hello_release-1.0.aar', - )); - - if (!exists(releaseAar)) { - return TaskResult.failure('Failed to build the release AAR file.'); - } - - final File releasePom = File(path.join( - repoPath, - 'io', - 'flutter', - 'devicelab', - 'hello', - 'hello_release', - '1.0', - 'hello_release-1.0.pom', - )); - - if (!exists(releasePom)) { - return TaskResult.failure('Failed to build the release POM file.'); - } - - section('Build debug AAR'); - - await inDirectory(projectDir, () async { - await flutter( - 'build', - options: [ - 'aar', - '--verbose', - '--debug', - ], - ); - }); - - final File debugAar = File(path.join( - repoPath, - 'io', - 'flutter', - 'devicelab', - 'hello', - 'hello_debug', - '1.0', - 'hello_debug-1.0.aar', - )); - - if (!exists(debugAar)) { - return TaskResult.failure('Failed to build the debug AAR file.'); - } - - final File debugPom = File(path.join( - repoPath, - 'io', - 'flutter', - 'devicelab', - 'hello', - 'hello_debug', - '1.0', - 'hello_debug-1.0.pom', - )); - - if (!exists(debugPom)) { - return TaskResult.failure('Failed to build the debug POM file.'); - } - - return TaskResult.success(null); - } catch (e) { - return TaskResult.failure(e.toString()); - } finally { - rmTree(tempDir); - } - }); -} diff --git a/dev/devicelab/bin/tasks/gradle_migrate_settings_test.dart b/dev/devicelab/bin/tasks/gradle_migrate_settings_test.dart deleted file mode 100644 index 46ed8539e4..0000000000 --- a/dev/devicelab/bin/tasks/gradle_migrate_settings_test.dart +++ /dev/null @@ -1,180 +0,0 @@ -// Copyright 2014 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:io'; - -import 'package:flutter_devicelab/framework/framework.dart'; -import 'package:flutter_devicelab/framework/task_result.dart'; -import 'package:flutter_devicelab/framework/utils.dart'; -import 'package:path/path.dart' as path; - -final String gradlew = Platform.isWindows ? 'gradlew.bat' : 'gradlew'; -final String gradlewExecutable = Platform.isWindows ? '.\\$gradlew' : './$gradlew'; - -/// Tests that [settings_aar.gradle] is created when possible. -Future main() async { - await task(() async { - - section('Find Java'); - - final String? javaHome = await findJavaHome(); - if (javaHome == null) - return TaskResult.failure('Could not find Java'); - print('\nUsing JAVA_HOME=$javaHome'); - - section('Create app project'); - - final Directory tempDir = Directory.systemTemp.createTempSync('flutter_module_test.'); - final Directory projectDir = Directory(path.join(tempDir.path, 'hello')); - try { - await inDirectory(tempDir, () async { - await flutter( - 'create', - options: ['hello'], - ); - }); - - section('Override settings.gradle V1'); - - final String relativeNewSettingsGradle = path.join('android', 'settings_aar.gradle'); - - section('Build APK'); - - late String stdout; - await inDirectory(projectDir, () async { - stdout = await evalFlutter( - 'build', - options: [ - 'apk', - '--flavor', 'does-not-exist', - ], - canFail: true, // The flavor doesn't exist. - ); - }); - - const String newFileContent = "include ':app'"; - - final File settingsGradle = File(path.join(projectDir.path, 'android', 'settings.gradle')); - final File newSettingsGradle = File(path.join(projectDir.path, 'android', 'settings_aar.gradle')); - - if (!newSettingsGradle.existsSync()) { - return TaskResult.failure('Expected file: `${newSettingsGradle.path}`.'); - } - - if (newSettingsGradle.readAsStringSync().trim() != newFileContent) { - return TaskResult.failure('Expected to create `${newSettingsGradle.path}` V1.'); - } - - if (!stdout.contains('Creating `$relativeNewSettingsGradle`') || - !stdout.contains('`$relativeNewSettingsGradle` created successfully')) { - return TaskResult.failure('Expected update message in stdout.'); - } - - section('Override settings.gradle V2'); - - const String deprecatedFileContentV2 = r''' -include ':app' - -def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() - -def plugins = new Properties() -def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') -if (pluginsFile.exists()) { - pluginsFile.withInputStream { stream -> plugins.load(stream) } -} - -plugins.each { name, path -> - def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() - include ":$name" - project(":$name").projectDir = pluginDirectory -} -'''; - settingsGradle.writeAsStringSync(deprecatedFileContentV2, flush: true); - newSettingsGradle.deleteSync(); - - section('Build APK'); - - await inDirectory(projectDir, () async { - stdout = await evalFlutter( - 'build', - options: [ - 'apk', - '--flavor', 'does-not-exist', - ], - canFail: true, // The flavor doesn't exist. - ); - }); - - if (newSettingsGradle.readAsStringSync().trim() != newFileContent) { - return TaskResult.failure('Expected to create `${newSettingsGradle.path}` V2.'); - } - - if (!stdout.contains('Creating `$relativeNewSettingsGradle`') || - !stdout.contains('`$relativeNewSettingsGradle` created successfully')) { - return TaskResult.failure('Expected update message in stdout.'); - } - - section('Override settings.gradle with custom logic'); - - const String customDeprecatedFileContent = r''' -include ':app' - -def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() - -def plugins = new Properties() -def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') -if (pluginsFile.exists()) { - pluginsFile.withInputStream { stream -> plugins.load(stream) } -} - -plugins.each { name, path -> - def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() - include ":$name" - project(":$name").projectDir = pluginDirectory -} -// some custom logic -'''; - settingsGradle.writeAsStringSync(customDeprecatedFileContent, flush: true); - newSettingsGradle.deleteSync(); - - section('Build APK'); - - final StringBuffer stderr = StringBuffer(); - await inDirectory(projectDir, () async { - stdout = await evalFlutter( - 'build', - options: [ - 'apk', - '--flavor', 'does-not-exist', - ], - canFail: true, // The flavor doesn't exist. - stderr: stderr, - ); - }); - - if (newSettingsGradle.existsSync()) { - return TaskResult.failure('Unexpected file: `${newSettingsGradle.path}`.'); - } - - if (!stdout.contains('Creating `$relativeNewSettingsGradle`')) { - return TaskResult.failure('Expected update message in stdout.'); - } - - if (stdout.contains('`$relativeNewSettingsGradle` created successfully')) { - return TaskResult.failure('Unexpected message in stdout.'); - } - - if (!stderr.toString().contains('Flutter tried to create the file ' - '`$relativeNewSettingsGradle`, but failed.')) { - return TaskResult.failure('Expected failure message in stdout.'); - } - - return TaskResult.success(null); - } catch (e) { - return TaskResult.failure(e.toString()); - } finally { - rmTree(tempDir); - } - }); -} diff --git a/packages/flutter_tools/gradle/flutter.gradle b/packages/flutter_tools/gradle/flutter.gradle index 41f7873158..5a9131d7e3 100644 --- a/packages/flutter_tools/gradle/flutter.gradle +++ b/packages/flutter_tools/gradle/flutter.gradle @@ -289,51 +289,8 @@ class FlutterPlugin implements Plugin { * Finally, the project's `settings.gradle` loads each plugin's android directory as a subproject. */ private void configurePlugins() { - if (!buildPluginAsAar()) { - getPluginList().each this.&configurePluginProject - getPluginDependencies().each this.&configurePluginDependencies - return - } - project.repositories { - maven { - url "${getPluginBuildDir()}/outputs/repo" - } - } - getPluginList().each { pluginName, pluginPath -> - configurePluginAar(pluginName, pluginPath, project) - } - } - - private static final Pattern GROUP_PATTERN = ~/group\s+\'(.+)\'/ - private static final Pattern PROJECT_NAME_PATTERN = ~/rootProject\.name\s+=\s+\'(.+)\'/ - - // Adds the plugin AAR dependency to the app project. - private void configurePluginAar(String pluginName, String pluginPath, Project project) { - // Extract the group id from the plugin's build.gradle. - // This is `group ''` - File pluginBuildFile = project.file(Paths.get(pluginPath, "android", "build.gradle")); - if (!pluginBuildFile.exists()) { - throw new GradleException("Plugin $pluginName doesn't have the required file $pluginBuildFile.") - } - - Matcher groupParts = GROUP_PATTERN.matcher(pluginBuildFile.text) - assert groupParts.count == 1 - assert groupParts.hasGroup() - String groupId = groupParts[0][1] - - // Extract the artifact name from the plugin's settings.gradle. - // This is `rootProject.name = ''` - File pluginSettings = project.file(Paths.get(pluginPath, "android", "settings.gradle")); - if (!pluginSettings.exists()) { - throw new GradleException("Plugin $pluginName doesn't have the required file $pluginSettings.") - } - Matcher projectNameParts = PROJECT_NAME_PATTERN.matcher(pluginSettings.text) - assert projectNameParts.count == 1 - assert projectNameParts.hasGroup() - String artifactId = "${projectNameParts[0][1]}_release" - - assert !groupId.empty - project.dependencies.add("api", "$groupId:$artifactId:+") + getPluginList().each this.&configurePluginProject + getPluginDependencies().each this.&configurePluginDependencies } // Adds the plugin project dependency to the app project . @@ -544,10 +501,6 @@ class FlutterPlugin implements Plugin { return project.plugins.hasPlugin("com.android.application"); } - private static Boolean buildPluginAsAar() { - return System.getProperty('build-plugins-as-aars') == 'true' - } - // Returns true if the build mode is supported by the current call to Gradle. // This only relevant when using a local engine. Because the engine // is built for a specific mode, the call to Gradle must match that mode. diff --git a/packages/flutter_tools/lib/src/android/gradle.dart b/packages/flutter_tools/lib/src/android/gradle.dart index 988fd02232..be013075f0 100644 --- a/packages/flutter_tools/lib/src/android/gradle.dart +++ b/packages/flutter_tools/lib/src/android/gradle.dart @@ -105,57 +105,6 @@ Iterable _apkFilesFor(AndroidBuildInfo androidBuildInfo) { return ['app$flavorString-$buildType.apk']; } -/// 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, Logger logger) { - final FileSystem fileSystem = androidDirectory.fileSystem; - final File newSettingsFile = androidDirectory.childFile('settings_aar.gradle'); - if (newSettingsFile.existsSync()) { - return; - } - final File currentSettingsFile = androidDirectory.childFile('settings.gradle'); - if (!currentSettingsFile.existsSync()) { - return; - } - final String currentFileContent = currentSettingsFile.readAsStringSync(); - - final String newSettingsRelativeFile = fileSystem.path.relative(newSettingsFile.path); - final Status status = logger.startProgress('✏️ Creating `$newSettingsRelativeFile`...'); - - final String flutterRoot = fileSystem.path.absolute(Cache.flutterRoot!); - final File legacySettingsDotGradleFiles = fileSystem.file(fileSystem.path.join(flutterRoot, 'packages','flutter_tools', - 'gradle', 'settings.gradle.legacy_versions')); - assert(legacySettingsDotGradleFiles.existsSync()); - final String settingsAarContent = fileSystem.file(fileSystem.path.join(flutterRoot, 'packages','flutter_tools', - 'gradle', 'settings_aar.gradle.tmpl')).readAsStringSync(); - - // Get the `settings.gradle` content variants that should be patched. - final List existingVariants = legacySettingsDotGradleFiles.readAsStringSync().split(';EOF'); - existingVariants.add(settingsAarContent); - - bool exactMatch = false; - for (final String fileContentVariant in existingVariants) { - if (currentFileContent.trim() == fileContentVariant.trim()) { - exactMatch = true; - break; - } - } - if (!exactMatch) { - status.cancel(); - logger.printStatus('${logger.terminal.warningMark} Flutter tried to create the file `$newSettingsRelativeFile`, but failed.'); - // Print how to manually update the file. - logger.printStatus(fileSystem.file(fileSystem.path.join(flutterRoot, 'packages','flutter_tools', - 'gradle', 'manual_migration_settings.gradle.md')).readAsStringSync()); - throwToolExit('Please create the file and run this command again.'); - } - // Copy the new file. - newSettingsFile.writeAsStringSync(settingsAarContent); - status.stop(); - logger.printStatus('${logger.terminal.successMark} `$newSettingsRelativeFile` created successfully.'); -} - /// An implementation of the [AndroidBuilder] that delegates to gradle. class AndroidGradleBuilder implements AndroidBuilder { AndroidGradleBuilder({ @@ -262,17 +211,13 @@ class AndroidGradleBuilder implements AndroidBuilder { /// * [target] is the target dart entry point. Typically, `lib/main.dart`. /// * If [isBuildingBundle] is `true`, then the output artifact is an `*.aab`, /// otherwise the output artifact is an `*.apk`. - /// * The plugins are built as AARs if [shouldBuildPluginAsAar] is `true`. This isn't set by default - /// because it makes the build slower proportional to the number of plugins. /// * [retries] is the max number of build retries in case one of the [GradleHandledError] handler - /// returns [GradleBuildStatus.retry] or [GradleBuildStatus.retryWithAarPlugins]. Future buildGradleApp({ required FlutterProject project, required AndroidBuildInfo androidBuildInfo, required String target, required bool isBuildingBundle, required List localGradleErrors, - bool shouldBuildPluginAsAar = false, bool validateDeferredComponents = true, bool deferredComponentsEnabled = false, int retries = 1, @@ -286,7 +231,6 @@ class AndroidGradleBuilder implements AndroidBuilder { if (!project.android.isSupportedVersion) { _exitWithUnsupportedProjectMessage(_usage, _logger.terminal); } - final Directory buildDirectory = project.android.buildDirectory; final bool usesAndroidX = isAppUsingAndroidX(project.android.hostAppGradleRoot); if (usesAndroidX) { @@ -304,16 +248,6 @@ class AndroidGradleBuilder implements AndroidBuilder { // from the local.properties file. updateLocalProperties(project: project, buildInfo: androidBuildInfo.buildInfo); - if (shouldBuildPluginAsAar) { - // Create a settings.gradle that doesn't import the plugins as subprojects. - createSettingsAarGradle(project.android.hostAppGradleRoot, _logger); - await buildPluginsAsAar( - project, - androidBuildInfo, - buildDirectory: buildDirectory.childDirectory('app'), - ); - } - final BuildInfo buildInfo = androidBuildInfo.buildInfo; final String assembleTask = isBuildingBundle ? getBundleTaskFor(buildInfo) @@ -392,13 +326,6 @@ class AndroidGradleBuilder implements AndroidBuilder { if (androidBuildInfo.splitPerAbi) { command.add('-Psplit-per-abi=true'); } - if (shouldBuildPluginAsAar) { - // Pass a system flag instead of a project flag, so this flag can be - // read from include_flutter.groovy. - command.add('-Dbuild-plugins-as-aars=true'); - // Don't use settings.gradle from the current project since it includes the plugins as subprojects. - command.add('--settings-file=settings_aar.gradle'); - } if (androidBuildInfo.fastStart) { command.add('-Pfast-start=true'); } @@ -407,12 +334,6 @@ class AndroidGradleBuilder implements AndroidBuilder { GradleHandledError? detectedGradleError; String? detectedGradleErrorLine; String? consumeLog(String line) { - // This message was removed from first-party plugins, - // but older plugin versions still display this message. - if (androidXPluginWarningRegex.hasMatch(line)) { - // Don't pipe. - return null; - } if (detectedGradleError != null) { // Pipe stdout/stderr from Gradle. return line; @@ -468,7 +389,6 @@ class AndroidGradleBuilder implements AndroidBuilder { line: detectedGradleErrorLine!, project: project, usesAndroidX: usesAndroidX, - shouldBuildPluginAsAar: shouldBuildPluginAsAar, ); if (retries >= 1) { @@ -481,19 +401,6 @@ class AndroidGradleBuilder implements AndroidBuilder { target: target, isBuildingBundle: isBuildingBundle, localGradleErrors: localGradleErrors, - shouldBuildPluginAsAar: shouldBuildPluginAsAar, - retries: retries - 1, - ); - BuildEvent(successEventLabel, type: 'gradle', flutterUsage: _usage).send(); - return; - case GradleBuildStatus.retryWithAarPlugins: - await buildGradleApp( - project: project, - androidBuildInfo: androidBuildInfo, - target: target, - isBuildingBundle: isBuildingBundle, - localGradleErrors: localGradleErrors, - shouldBuildPluginAsAar: true, retries: retries - 1, ); BuildEvent(successEventLabel, type: 'gradle', flutterUsage: _usage).send(); @@ -749,57 +656,6 @@ class AndroidGradleBuilder implements AndroidBuilder { color: TerminalColor.green, ); } - - /// Builds the plugins as AARs. - @visibleForTesting - Future buildPluginsAsAar( - FlutterProject flutterProject, - AndroidBuildInfo androidBuildInfo, { - required Directory buildDirectory, - }) async { - final File flutterPluginFile = flutterProject.flutterPluginsFile; - if (!flutterPluginFile.existsSync()) { - return; - } - final List plugins = flutterPluginFile.readAsStringSync().split('\n'); - for (final String plugin in plugins) { - final List pluginParts = plugin.split('='); - if (pluginParts.length != 2) { - continue; - } - final Directory pluginDirectory = _fileSystem.directory(pluginParts.last); - assert(pluginDirectory.existsSync()); - - final String pluginName = pluginParts.first; - final File buildGradleFile = pluginDirectory.childDirectory('android').childFile('build.gradle'); - if (!buildGradleFile.existsSync()) { - _logger.printTrace("Skipping plugin $pluginName since it doesn't have a android/build.gradle file"); - continue; - } - _logger.printStatus('Building plugin $pluginName...'); - try { - await buildGradleAar( - project: FlutterProject.fromDirectory(pluginDirectory), - androidBuildInfo: AndroidBuildInfo( - BuildInfo( - BuildMode.release, // Plugins are built as release. - null, // Plugins don't define flavors. - treeShakeIcons: androidBuildInfo.buildInfo.treeShakeIcons, - packagesPath: androidBuildInfo.buildInfo.packagesPath, - ), - ), - target: '', - outputDirectory: buildDirectory, - buildNumber: '1.0' - ); - } on ToolExit { - // Log the entire plugin entry in `.flutter-plugins` since it - // includes the plugin name and the version. - BuildEvent('gradle-plugin-aar-failure', type: 'gradle', eventError: plugin, flutterUsage: _usage).send(); - throwToolExit('The plugin $pluginName could not be built due to the issue above.'); - } - } - } } /// Prints how to consume the AAR from a host app. diff --git a/packages/flutter_tools/lib/src/android/gradle_errors.dart b/packages/flutter_tools/lib/src/android/gradle_errors.dart index f4422b9093..2b94ef9b14 100644 --- a/packages/flutter_tools/lib/src/android/gradle_errors.dart +++ b/packages/flutter_tools/lib/src/android/gradle_errors.dart @@ -31,7 +31,6 @@ class GradleHandledError { required String line, required FlutterProject project, required bool usesAndroidX, - required bool shouldBuildPluginAsAar, }) handler; /// The [BuildEvent] label is named gradle-[eventLabel]. @@ -46,8 +45,6 @@ enum GradleBuildStatus { exit, /// The tool can retry the exact same build. retry, - /// The tool can build the plugins as AAR and retry the build. - retryWithAarPlugins, } /// Returns a simple test function that evaluates to `true` if at least one of @@ -74,7 +71,6 @@ final List gradleErrors = [ minSdkVersion, transformInputIssue, lockFileDepMissing, - androidXFailureHandler, // Keep last since the pattern is broader. ]; // Permission defined error message. @@ -87,7 +83,6 @@ final GradleHandledError permissionDeniedErrorHandler = GradleHandledError( required String line, required FlutterProject project, required bool usesAndroidX, - required bool shouldBuildPluginAsAar, }) async { globals.printStatus('${globals.logger.terminal.warningMark} Gradle does not have execution permission.', emphasis: true); globals.printStatus( @@ -124,7 +119,6 @@ final GradleHandledError networkErrorHandler = GradleHandledError( required String line, required FlutterProject project, required bool usesAndroidX, - required bool shouldBuildPluginAsAar, }) async { globals.printError( '${globals.logger.terminal.warningMark} Gradle threw an error while downloading artifacts from the network. ' @@ -154,7 +148,6 @@ final GradleHandledError r8FailureHandler = GradleHandledError( required String line, required FlutterProject project, required bool usesAndroidX, - required bool shouldBuildPluginAsAar, }) async { globals.printStatus('${globals.logger.terminal.warningMark} The shrinker may have failed to optimize the Java bytecode.', emphasis: true); globals.printStatus('To disable the shrinker, pass the `--no-shrink` flag to this command.', indent: 4); @@ -164,91 +157,6 @@ final GradleHandledError r8FailureHandler = GradleHandledError( eventLabel: 'r8', ); -// AndroidX failure. -// -// This regex is intentionally broad. AndroidX errors can manifest in multiple -// different ways and each one depends on the specific code config and -// filesystem paths of the project. Throwing the broadest net possible here to -// catch all known and likely cases. -// -// Example stack traces: -// https://github.com/flutter/flutter/issues/27226 "AAPT: error: resource android:attr/fontVariationSettings not found." -// https://github.com/flutter/flutter/issues/27106 "Android resource linking failed|Daemon: AAPT2|error: failed linking references" -// https://github.com/flutter/flutter/issues/27493 "error: cannot find symbol import androidx.annotation.NonNull;" -// https://github.com/flutter/flutter/issues/23995 "error: package android.support.annotation does not exist import android.support.annotation.NonNull;" -final RegExp _androidXFailureRegex = RegExp(r'(AAPT|androidx|android\.support)'); - -final RegExp androidXPluginWarningRegex = RegExp(r'\*{57}' - r"|WARNING: This version of (\w+) will break your Android build if it or its dependencies aren't compatible with AndroidX." - r'|See https://goo.gl/CP92wY for more information on the problem and how to fix it.' - r'|This warning prints for all Android build failures. The real root cause of the error may be unrelated.'); - -@visibleForTesting -final GradleHandledError androidXFailureHandler = GradleHandledError( - test: (String line) { - return !androidXPluginWarningRegex.hasMatch(line) && - _androidXFailureRegex.hasMatch(line); - }, - handler: ({ - required String line, - required FlutterProject project, - required bool usesAndroidX, - required bool shouldBuildPluginAsAar, - }) async { - final bool hasPlugins = project.flutterPluginsFile.existsSync(); - if (!hasPlugins) { - // If the app doesn't use any plugin, then it's unclear where - // the incompatibility is coming from. - BuildEvent( - 'gradle-android-x-failure', - type: 'gradle', - eventError: 'app-not-using-plugins', - flutterUsage: globals.flutterUsage, - ).send(); - } - if (hasPlugins && !usesAndroidX) { - // If the app isn't using AndroidX, then the app is likely using - // a plugin already migrated to AndroidX. - globals.printStatus( - 'AndroidX incompatibilities may have caused this build to fail. ' - 'Please migrate your app to AndroidX. See https://goo.gl/CP92wY .' - ); - BuildEvent( - 'gradle-android-x-failure', - type: 'gradle', - eventError: 'app-not-using-androidx', - flutterUsage: globals.flutterUsage, - ).send(); - } - if (hasPlugins && usesAndroidX && shouldBuildPluginAsAar) { - // This is a dependency conflict instead of an AndroidX failure since - // 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', - type: 'gradle', - eventError: 'using-jetifier', - flutterUsage: globals.flutterUsage, - ).send(); - } - if (hasPlugins && usesAndroidX && !shouldBuildPluginAsAar) { - globals.printStatus( - 'The build failed likely due to AndroidX incompatibilities in a plugin. ' - 'The tool is about to try using Jetifier to solve the incompatibility.' - ); - BuildEvent( - 'gradle-android-x-failure', - type: 'gradle', - eventError: 'not-using-jetifier', - flutterUsage: globals.flutterUsage, - ).send(); - return GradleBuildStatus.retryWithAarPlugins; - } - return GradleBuildStatus.exit; - }, - eventLabel: 'android-x', -); - /// 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. @@ -261,7 +169,6 @@ final GradleHandledError licenseNotAcceptedHandler = GradleHandledError( required String line, required FlutterProject project, required bool usesAndroidX, - required bool shouldBuildPluginAsAar, }) async { const String licenseNotAcceptedMatcher = r'You have not accepted the license agreements of the following SDK components:\s*\[(.+)\]'; @@ -295,7 +202,6 @@ final GradleHandledError flavorUndefinedHandler = GradleHandledError( required String line, required FlutterProject project, required bool usesAndroidX, - required bool shouldBuildPluginAsAar, }) async { final RunResult tasksRunResult = await globals.processUtils.run( [ @@ -368,7 +274,6 @@ final GradleHandledError minSdkVersion = GradleHandledError( required String line, required FlutterProject project, required bool usesAndroidX, - required bool shouldBuildPluginAsAar, }) async { final File gradleFile = project.directory .childDirectory('android') @@ -409,7 +314,6 @@ final GradleHandledError transformInputIssue = GradleHandledError( required String line, required FlutterProject project, required bool usesAndroidX, - required bool shouldBuildPluginAsAar, }) async { final File gradleFile = project.directory .childDirectory('android') @@ -443,7 +347,6 @@ final GradleHandledError lockFileDepMissing = GradleHandledError( required String line, required FlutterProject project, required bool usesAndroidX, - required bool shouldBuildPluginAsAar, }) async { final File gradleFile = project.directory .childDirectory('android') diff --git a/packages/flutter_tools/test/general.shard/android/android_gradle_builder_test.dart b/packages/flutter_tools/test/general.shard/android/android_gradle_builder_test.dart index f969326e3d..12f87afaf4 100644 --- a/packages/flutter_tools/test/general.shard/android/android_gradle_builder_test.dart +++ b/packages/flutter_tools/test/general.shard/android/android_gradle_builder_test.dart @@ -101,7 +101,6 @@ void main() { String line, FlutterProject project, bool usesAndroidX, - bool shouldBuildPluginAsAar, }) async { handlerCalled = true; return GradleBuildStatus.exit; @@ -206,7 +205,6 @@ void main() { String line, FlutterProject project, bool usesAndroidX, - bool shouldBuildPluginAsAar, }) async { return GradleBuildStatus.retry; }, @@ -290,7 +288,6 @@ void main() { String line, FlutterProject project, bool usesAndroidX, - bool shouldBuildPluginAsAar, }) async { handlerCalled = true; return GradleBuildStatus.exit; @@ -454,7 +451,6 @@ void main() { String line, FlutterProject project, bool usesAndroidX, - bool shouldBuildPluginAsAar, }) async { return GradleBuildStatus.retry; }, @@ -570,117 +566,6 @@ void main() { )); }); - testUsingContext('Can retry gradle build with plugins built as AARs', () async { - final AndroidGradleBuilder builder = AndroidGradleBuilder( - logger: logger, - processManager: processManager, - fileSystem: fileSystem, - artifacts: Artifacts.test(), - usage: testUsage, - gradleUtils: FakeGradleUtils(), - platform: FakePlatform(), - ); - processManager.addCommand(const FakeCommand( - command: [ - 'gradlew', - '-q', - '-Ptarget-platform=android-arm,android-arm64,android-x64', - '-Ptarget=lib/main.dart', - '-Pdart-obfuscation=false', - '-Ptrack-widget-creation=false', - '-Ptree-shake-icons=false', - 'assembleRelease', - ], - exitCode: 1, - stderr: '\nSome gradle message\n', - )); - processManager.addCommand(const FakeCommand( - command: [ - 'gradlew', - '-q', - '-Ptarget-platform=android-arm,android-arm64,android-x64', - '-Ptarget=lib/main.dart', - '-Pdart-obfuscation=false', - '-Ptrack-widget-creation=false', - '-Ptree-shake-icons=false', - '-Dbuild-plugins-as-aars=true', - '--settings-file=settings_aar.gradle', - 'assembleRelease' - ], - exitCode: 1, - stderr: '\nSome gradle message\n', - )); - - fileSystem.directory('android') - .childFile('build.gradle') - .createSync(recursive: true); - - fileSystem.directory('android') - .childFile('gradle.properties') - .createSync(recursive: true); - - fileSystem.directory('android') - .childDirectory('app') - .childFile('build.gradle') - ..createSync(recursive: true) - ..writeAsStringSync('apply from: irrelevant/flutter.gradle'); - - int testFnCalled = 0; - bool builtPluginAsAar = false; - await expectLater(() async { - await builder.buildGradleApp( - project: FlutterProject.fromDirectoryTest(fileSystem.currentDirectory), - androidBuildInfo: const AndroidBuildInfo( - BuildInfo( - BuildMode.release, - null, - treeShakeIcons: false, - ), - ), - target: 'lib/main.dart', - isBuildingBundle: false, - localGradleErrors: [ - GradleHandledError( - test: (String line) { - if (line.contains('Some gradle message')) { - testFnCalled++; - return true; - } - return false; - }, - handler: ({ - String line, - FlutterProject project, - bool usesAndroidX, - bool shouldBuildPluginAsAar, - }) async { - if (testFnCalled == 2) { - builtPluginAsAar = shouldBuildPluginAsAar; - } - return GradleBuildStatus.retryWithAarPlugins; - }, - eventLabel: 'random-event-label', - ), - ], - ); - }, throwsToolExit( - message: 'Gradle task assembleRelease failed with exit code 1' - )); - - expect(testFnCalled, equals(2)); - expect(builtPluginAsAar, isTrue); - - expect(testUsage.events, contains( - const TestUsageEvent( - 'build', - 'gradle', - label: 'gradle-random-event-label-failure', - parameters: CustomDimensions(), - ), - )); - expect(processManager, hasNoRemainingExpectations); - }); - testUsingContext('indicates that an APK has been built successfully', () async { final AndroidGradleBuilder builder = AndroidGradleBuilder( logger: logger, 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 d5443fc2a4..eb820c7d2d 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 @@ -12,7 +12,6 @@ import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/platform.dart'; import 'package:flutter_tools/src/globals_null_migrated.dart' as globals; import 'package:flutter_tools/src/project.dart'; -import 'package:flutter_tools/src/reporting/reporting.dart'; import '../../src/common.dart'; import '../../src/context.dart'; @@ -31,10 +30,8 @@ void main() { minSdkVersion, transformInputIssue, lockFileDepMissing, - androidXFailureHandler, ]) ); - expect(gradleErrors.last, equals(androidXFailureHandler)); }); }); @@ -355,155 +352,6 @@ Command: /home/android/gradlew assembleRelease }); }); - group('AndroidX', () { - final TestUsage testUsage = TestUsage(); - - testWithoutContext('pattern', () { - expect(androidXFailureHandler.test( - 'AAPT: error: resource android:attr/fontVariationSettings not found.' - ), isTrue); - - expect(androidXFailureHandler.test( - 'AAPT: error: resource android:attr/ttcIndex not found.' - ), isTrue); - - expect(androidXFailureHandler.test( - 'error: package android.support.annotation does not exist' - ), isTrue); - - expect(androidXFailureHandler.test( - 'import android.support.annotation.NonNull;' - ), isTrue); - - expect(androidXFailureHandler.test( - 'import androidx.annotation.NonNull;' - ), isTrue); - - expect(androidXFailureHandler.test( - 'Daemon: AAPT2 aapt2-3.2.1-4818971-linux Daemon #0' - ), isTrue); - }); - - testUsingContext('handler - no plugins', () async { - final GradleBuildStatus status = await androidXFailureHandler - .handler(line: '', project: FlutterProject.fromDirectoryTest(globals.fs.currentDirectory)); - - expect(testUsage.events, contains( - TestUsageEvent( - 'build', - 'gradle', - label: 'gradle-android-x-failure', - parameters: CustomDimensions.fromMap({ - 'cd43': 'app-not-using-plugins', - }), - ), - )); - - expect(status, equals(GradleBuildStatus.exit)); - }, overrides: { - FileSystem: () => MemoryFileSystem.test(), - ProcessManager: () => FakeProcessManager.any(), - Usage: () => testUsage, - }); - - testUsingContext('handler - plugins and no AndroidX', () async { - globals.fs.file('.flutter-plugins').createSync(recursive: true); - - final GradleBuildStatus status = await androidXFailureHandler - .handler( - line: '', - project: FlutterProject.fromDirectoryTest(globals.fs.currentDirectory), - usesAndroidX: false, - ); - - expect(testLogger.statusText, - contains( - 'AndroidX incompatibilities may have caused this build to fail. ' - 'Please migrate your app to AndroidX. See https://goo.gl/CP92wY .' - ) - ); - - expect(testUsage.events, contains( - TestUsageEvent( - 'build', - 'gradle', - label: 'gradle-android-x-failure', - parameters: CustomDimensions.fromMap({ - 'cd43': 'app-not-using-androidx', - }), - ), - )); - - expect(status, equals(GradleBuildStatus.exit)); - }, overrides: { - FileSystem: () => MemoryFileSystem.test(), - ProcessManager: () => FakeProcessManager.any(), - Usage: () => testUsage, - }); - - testUsingContext('handler - plugins, AndroidX, and AAR', () async { - globals.fs.file('.flutter-plugins').createSync(recursive: true); - - final GradleBuildStatus status = await androidXFailureHandler.handler( - line: '', - project: FlutterProject.fromDirectoryTest(globals.fs.currentDirectory), - usesAndroidX: true, - shouldBuildPluginAsAar: true, - ); - - expect(testUsage.events, contains( - TestUsageEvent( - 'build', - 'gradle', - label: 'gradle-android-x-failure', - parameters: CustomDimensions.fromMap({ - 'cd43': 'using-jetifier', - }), - ), - )); - - expect(status, equals(GradleBuildStatus.exit)); - }, overrides: { - FileSystem: () => MemoryFileSystem.test(), - ProcessManager: () => FakeProcessManager.any(), - Usage: () => testUsage, - }); - - testUsingContext('handler - plugins, AndroidX, and no AAR', () async { - globals.fs.file('.flutter-plugins').createSync(recursive: true); - - final GradleBuildStatus status = await androidXFailureHandler.handler( - line: '', - project: FlutterProject.fromDirectoryTest(globals.fs.currentDirectory), - usesAndroidX: true, - shouldBuildPluginAsAar: false, - ); - - expect(testLogger.statusText, - contains( - 'The build failed likely due to AndroidX incompatibilities in a plugin. ' - 'The tool is about to try using Jetifier to solve the incompatibility.' - ) - ); - - expect(testUsage.events, contains( - TestUsageEvent( - 'build', - 'gradle', - label: 'gradle-android-x-failure', - parameters: CustomDimensions.fromMap({ - 'cd43': 'not-using-jetifier', - }), - ), - )); - expect(status, equals(GradleBuildStatus.retryWithAarPlugins)); - }, overrides: { - FileSystem: () => MemoryFileSystem.test(), - ProcessManager: () => FakeProcessManager.any(), - Usage: () => testUsage, - }); - }); - group('permission errors', () { testUsingContext('pattern', () async { const String errorMessage = ''' 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 f76ee66b7f..e3fca949c4 100644 --- a/packages/flutter_tools/test/general.shard/android/gradle_test.dart +++ b/packages/flutter_tools/test/general.shard/android/gradle_test.dart @@ -7,7 +7,6 @@ import 'package:file/memory.dart'; import 'package:flutter_tools/src/android/android_sdk.dart'; import 'package:flutter_tools/src/android/gradle.dart'; -import 'package:flutter_tools/src/android/gradle_errors.dart'; import 'package:flutter_tools/src/android/gradle_utils.dart'; import 'package:flutter_tools/src/artifacts.dart'; import 'package:flutter_tools/src/base/common.dart'; @@ -18,7 +17,6 @@ import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/globals_null_migrated.dart' as globals; import 'package:flutter_tools/src/project.dart'; -import 'package:flutter_tools/src/reporting/reporting.dart'; import 'package:test/fake.dart'; import '../../src/common.dart'; @@ -194,117 +192,6 @@ void main() { }, overrides: { AndroidSdk: () => null, }); - - test('androidXPluginWarningRegex should match lines with the AndroidX plugin warnings', () { - final List nonMatchingLines = [ - ':app:preBuild UP-TO-DATE', - 'BUILD SUCCESSFUL in 0s', - 'Generic plugin AndroidX text', - '', - ]; - final List matchingLines = [ - '*********************************************************************************************************************************', - "WARNING: This version of image_picker will break your Android build if it or its dependencies aren't compatible with AndroidX.", - 'See https://goo.gl/CP92wY for more information on the problem and how to fix it.', - 'This warning prints for all Android build failures. The real root cause of the error may be unrelated.', - ]; - for (final String m in nonMatchingLines) { - expect(androidXPluginWarningRegex.hasMatch(m), isFalse); - } - for (final String m in matchingLines) { - expect(androidXPluginWarningRegex.hasMatch(m), isTrue); - } - }); - }); - - group('Config files', () { - Directory tempDir; - FileSystem fileSystem; - - setUp(() { - fileSystem = MemoryFileSystem.test(); - tempDir = fileSystem.systemTempDirectory.createTempSync('flutter_settings_aar_test.'); - }); - - testUsingContext('create settings_aar.gradle when current settings.gradle loads plugins', () { - const String currentSettingsGradle = r''' -include ':app' - -def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() - -def plugins = new Properties() -def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') -if (pluginsFile.exists()) { - pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } -} - -plugins.each { name, path -> - def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() - if (pluginDirectory.exists()) { - include ":$name" - project(":$name").projectDir = pluginDirectory - } -} -'''; - - const String settingsAarFile = ''' -include ':app' -'''; - - tempDir.childFile('settings.gradle').writeAsStringSync(currentSettingsGradle); - - final String toolGradlePath = fileSystem.path.join( - fileSystem.path.absolute(Cache.flutterRoot), - 'packages', - 'flutter_tools', - 'gradle'); - fileSystem.directory(toolGradlePath).createSync(recursive: true); - fileSystem.file(fileSystem.path.join(toolGradlePath, 'settings.gradle.legacy_versions')) - .writeAsStringSync(currentSettingsGradle); - - fileSystem.file(fileSystem.path.join(toolGradlePath, 'settings_aar.gradle.tmpl')) - .writeAsStringSync(settingsAarFile); - - createSettingsAarGradle(tempDir, testLogger); - - expect(testLogger.statusText, contains('created successfully')); - expect(tempDir.childFile('settings_aar.gradle').existsSync(), isTrue); - }, overrides: { - FileSystem: () => fileSystem, - ProcessManager: () => FakeProcessManager.any(), - }); - - testUsingContext("create settings_aar.gradle when current settings.gradle doesn't load plugins", () { - const String currentSettingsGradle = ''' -include ':app' -'''; - - const String settingsAarFile = ''' -include ':app' -'''; - - tempDir.childFile('settings.gradle').writeAsStringSync(currentSettingsGradle); - - final String toolGradlePath = fileSystem.path.join( - fileSystem.path.absolute(Cache.flutterRoot), - 'packages', - 'flutter_tools', - 'gradle'); - fileSystem.directory(toolGradlePath).createSync(recursive: true); - fileSystem.file(fileSystem.path.join(toolGradlePath, 'settings.gradle.legacy_versions')) - .writeAsStringSync(currentSettingsGradle); - - fileSystem.file(fileSystem.path.join(toolGradlePath, 'settings_aar.gradle.tmpl')) - .writeAsStringSync(settingsAarFile); - - createSettingsAarGradle(tempDir, testLogger); - - expect(testLogger.statusText, contains('created successfully')); - expect(tempDir.childFile('settings_aar.gradle').existsSync(), isTrue); - }, overrides: { - FileSystem: () => fileSystem, - ProcessManager: () => FakeProcessManager.any(), - }); }); group('Gradle local.properties', () { @@ -618,197 +505,6 @@ flutter: }); }); - group('buildPluginsAsAar', () { - FileSystem fs; - FakeProcessManager fakeProcessManager; - FakeAndroidSdk androidSdk; - AndroidGradleBuilder builder; - BufferLogger logger; - Platform platform; - - setUp(() { - logger = BufferLogger.test(); - fs = MemoryFileSystem.test(); - fakeProcessManager = FakeProcessManager.empty(); - androidSdk = FakeAndroidSdk(); - platform = FakePlatform(operatingSystem: 'linux'); - - builder = AndroidGradleBuilder( - logger: logger, - processManager: fakeProcessManager, - fileSystem: fs, - artifacts: Artifacts.test(), - usage: TestUsage(), - gradleUtils: FakeGradleUtils(), - platform: platform, - ); - }); - - testUsingContext('calls gradle', () async { - final Directory androidDirectory = globals.fs.directory('android.'); - androidDirectory.createSync(); - androidDirectory - .childFile('pubspec.yaml') - .writeAsStringSync('name: irrelevant'); - - final Directory plugin1 = globals.fs.directory('plugin1.'); - plugin1 - ..createSync() - ..childFile('pubspec.yaml') - .writeAsStringSync(''' -name: irrelevant -flutter: - plugin: - androidPackage: irrelevant -'''); - - plugin1 - .childDirectory('android') - .childFile('build.gradle') - .createSync(recursive: true); - - final Directory plugin2 = globals.fs.directory('plugin2.'); - plugin2 - ..createSync() - ..childFile('pubspec.yaml') - .writeAsStringSync(''' -name: irrelevant -flutter: - plugin: - androidPackage: irrelevant -'''); - - plugin2 - .childDirectory('android') - .childFile('build.gradle') - .createSync(recursive: true); - - androidDirectory - .childFile('.flutter-plugins') - .writeAsStringSync(''' -plugin1=${plugin1.path} -plugin2=${plugin2.path} -'''); - final Directory buildDirectory = androidDirectory - .childDirectory('build'); - buildDirectory - .childDirectory('outputs') - .childDirectory('repo') - .createSync(recursive: true); - - final String flutterRoot = globals.fs.path.absolute(Cache.flutterRoot); - final String initScript = globals.fs.path.join( - flutterRoot, - 'packages', - 'flutter_tools', - 'gradle', - 'aar_init_script.gradle', - ); - - fakeProcessManager.addCommands([ - FakeCommand( - command: [ - 'gradlew', - '-I=$initScript', - '-Pflutter-root=$flutterRoot', - '-Poutput-dir=${buildDirectory.path}', - '-Pis-plugin=true', - '-PbuildNumber=1.0', - '-q', - '-Pdart-obfuscation=false', - '-Ptrack-widget-creation=false', - '-Ptree-shake-icons=true', - '-Ptarget-platform=android-arm,android-arm64,android-x64', - 'assembleAarRelease', - ], - workingDirectory: plugin1.childDirectory('android').path, - ), - FakeCommand( - command: [ - 'gradlew', - '-I=$initScript', - '-Pflutter-root=$flutterRoot', - '-Poutput-dir=${buildDirectory.path}', - '-Pis-plugin=true', - '-PbuildNumber=1.0', - '-q', - '-Pdart-obfuscation=false', - '-Ptrack-widget-creation=false', - '-Ptree-shake-icons=true', - '-Ptarget-platform=android-arm,android-arm64,android-x64', - 'assembleAarRelease', - ], - workingDirectory: plugin2.childDirectory('android').path, - )]); - - await builder.buildPluginsAsAar( - FlutterProject.fromDirectoryTest(androidDirectory), - const AndroidBuildInfo(BuildInfo( - BuildMode.release, - '', - treeShakeIcons: true, - dartObfuscation: true, - buildNumber: '2.0' - )), - buildDirectory: buildDirectory, - ); - expect(fakeProcessManager.hasRemainingExpectations, isFalse); - }, overrides: { - AndroidSdk: () => androidSdk, - FileSystem: () => fs, - Platform: () => platform, - ProcessManager: () => fakeProcessManager, - GradleUtils: () => FakeGradleUtils(), - }); - - testUsingContext('skips plugin without a android/build.gradle file', () async { - final Directory androidDirectory = globals.fs.directory('android.'); - androidDirectory.createSync(); - androidDirectory - .childFile('pubspec.yaml') - .writeAsStringSync('name: irrelevant'); - - final Directory plugin1 = globals.fs.directory('plugin1.'); - plugin1 - ..createSync() - ..childFile('pubspec.yaml') - .writeAsStringSync(''' -name: irrelevant -flutter: - plugin: - androidPackage: irrelevant -'''); - - androidDirectory - .childFile('.flutter-plugins') - .writeAsStringSync(''' -plugin1=${plugin1.path} -'''); - // Create an empty android directory. - // https://github.com/flutter/flutter/issues/46898 - plugin1.childDirectory('android').createSync(); - - final Directory buildDirectory = androidDirectory.childDirectory('build'); - - buildDirectory - .childDirectory('outputs') - .childDirectory('repo') - .createSync(recursive: true); - - await builder.buildPluginsAsAar( - FlutterProject.fromDirectoryTest(androidDirectory), - const AndroidBuildInfo(BuildInfo.release), - buildDirectory: buildDirectory, - ); - expect(fakeProcessManager.hasRemainingExpectations, isFalse); - }, overrides: { - AndroidSdk: () => androidSdk, - FileSystem: () => fs, - ProcessManager: () => fakeProcessManager, - GradleUtils: () => FakeGradleUtils(), - }); - }); - group('printHowToConsumeAar', () { BufferLogger logger; FileSystem fileSystem;