Unverified Commit eba2a520 authored by gmackall's avatar gmackall Committed by GitHub

Add migrator to upgrade gradle version when conflict with Android Studio...

Add migrator to upgrade gradle version when conflict with Android Studio bundled Java version is detected (#124085)

This PR adds an android project migrator that checks the version of android studio and the version of gradle for conflicts, and upgrades to 7.4 if a conflict is detected. For more detail about the particular conflict, see https://github.com/flutter/flutter/issues/122376.

The PR also upgrades older gradle versions being used in integration testing to 7.4. 

Fixes/related to: https://github.com/flutter/flutter/issues/122376 and https://github.com/flutter/flutter/issues/123636
parent fb36bba3
......@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-all.zip
......@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-all.zip
......@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-all.zip
......@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-all.zip
......@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-all.zip
......@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-all.zip
......@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-all.zip
......@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-all.zip
......@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-all.zip
......@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-all.zip
......@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-all.zip
......@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-all.zip
......@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-all.zip
......@@ -31,8 +31,10 @@ import '../project.dart';
import '../reporting/reporting.dart';
import 'android_builder.dart';
import 'android_sdk.dart';
import 'android_studio.dart';
import 'gradle_errors.dart';
import 'gradle_utils.dart';
import 'migrations/android_studio_java_gradle_conflict_migration.dart';
import 'migrations/top_level_gradle_build_file_migration.dart';
import 'multidex.dart';
......@@ -137,13 +139,16 @@ class AndroidGradleBuilder implements AndroidBuilder {
required Usage usage,
required GradleUtils gradleUtils,
required Platform platform,
required AndroidStudio? androidStudio,
}) : _logger = logger,
_fileSystem = fileSystem,
_artifacts = artifacts,
_usage = usage,
_gradleUtils = gradleUtils,
_androidStudio = androidStudio,
_fileSystemUtils = FileSystemUtils(fileSystem: fileSystem, platform: platform),
_processUtils = ProcessUtils(logger: logger, processManager: processManager);
_processUtils = ProcessUtils(logger: logger, processManager: processManager),
_platform = platform;
final Logger _logger;
final ProcessUtils _processUtils;
......@@ -152,6 +157,8 @@ class AndroidGradleBuilder implements AndroidBuilder {
final Usage _usage;
final GradleUtils _gradleUtils;
final FileSystemUtils _fileSystemUtils;
final AndroidStudio? _androidStudio;
final Platform _platform;
/// Builds the AAR and POM files for the current Flutter module or plugin.
@override
......@@ -258,6 +265,15 @@ class AndroidGradleBuilder implements AndroidBuilder {
final List<ProjectMigrator> migrators = <ProjectMigrator>[
TopLevelGradleBuildFileMigration(project.android, _logger),
AndroidStudioJavaGradleConflictMigration(_logger,
project: project.android,
androidStudio: _androidStudio,
fileSystem: _fileSystem,
processUtils: _processUtils,
platform: _platform,
os: globals.os,
androidSdk: globals.androidSdk)
,
];
final ProjectMigration migration = ProjectMigration(migrators);
......
......@@ -59,6 +59,22 @@ const String maxKnownAgpVersion = '8.1';
final RegExp _androidGradlePluginRegExp =
RegExp(r'com\.android\.tools\.build:gradle:(\d+\.\d+\.\d+)');
// Expected content format (with lines above and below).
// Version can have 2 or 3 numbers.
// 'distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-all.zip'
// '^\s*' protects against commented out lines.
final RegExp distributionUrlRegex =
RegExp(r'^\s*distributionUrl\s*=\s*.*\.zip', multiLine: true);
// Modified version of the gradle distribution url match designed to only match
// gradle.org urls so that we can guarantee any modifications to the url
// still points to a hosted zip.
final RegExp gradleOrgVersionMatch =
RegExp(
r'^\s*distributionUrl\s*=\s*https\\://services\.gradle\.org/distributions/gradle-((?:\d|\.)+)-(.*)\.zip',
multiLine: true
);
// From https://docs.gradle.org/current/userguide/command_line_interface.html#command_line_interface
const String gradleVersionFlag = r'--version';
......@@ -169,42 +185,49 @@ String getGradleVersionForAndroidPlugin(Directory directory, Logger logger) {
return getGradleVersionFor(androidPluginVersion ?? 'unknown');
}
/// Returns the gradle file from the top level directory.
/// The returned file is not guaranteed to be present.
File getGradleWrapperFile(Directory directory) {
return directory.childDirectory(gradleDirectoryName)
.childDirectory(gradleWrapperDirectoryName)
.childFile(gradleWrapperPropertiesFilename);
}
/// Parses the gradle wrapper distribution url to return a string containing
/// the version number.
///
/// Expected input is of the form '...gradle-7.4.2-all.zip', and the output
/// would be of the form '7.4.2'.
String? parseGradleVersionFromDistributionUrl(String? distributionUrl) {
if (distributionUrl == null) {
return null;
}
final List<String> zipParts = distributionUrl.split('-');
if (zipParts.length < 2) {
return null;
}
return zipParts[1];
}
/// Returns either the gradle-wrapper.properties value from the passed in
/// [directory] or if not present the version available in local path.
///
/// If gradle version is not found null is returned.
/// [directory] should be and android directory with a build.gradle file.
/// [directory] should be an android directory with a build.gradle file.
Future<String?> getGradleVersion(
Directory directory, Logger logger, ProcessManager processManager) async {
final File propertiesFile = directory
.childDirectory(gradleDirectoryName)
.childDirectory(gradleWrapperDirectoryName)
.childFile(gradleWrapperPropertiesFilename);
final File propertiesFile = getGradleWrapperFile(directory);
if (propertiesFile.existsSync()) {
final String wrapperFileContent = propertiesFile.readAsStringSync();
// Expected content format (with lines above and below).
// Version can have 2 or 3 numbers.
// 'distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-all.zip'
// '^\s*' protects against commented out lines.
final RegExp distributionUrlRegex =
RegExp(r'^\s*distributionUrl\s?=\s?.*\.zip', multiLine: true);
final RegExpMatch? distributionUrl =
distributionUrlRegex.firstMatch(wrapperFileContent);
if (distributionUrl != null) {
// Expected content: 'gradle-7.4.2-all.zip'
final String? gradleZip = distributionUrl.group(0);
if (gradleZip != null) {
final List<String> zipParts = gradleZip.split('-');
if (zipParts.length > 2) {
final String gradleVersion = zipParts[1];
return gradleVersion;
} else {
// Did not find gradle zip url. Likely this is a bug in our parsing.
logger.printWarning(_formatParseWarning(wrapperFileContent));
}
final String? gradleVersion =
parseGradleVersionFromDistributionUrl(distributionUrl.group(0));
if (gradleVersion != null) {
return gradleVersion;
} else {
// Did not find gradle zip url. Likely this is a bug in our parsing.
logger.printWarning(_formatParseWarning(wrapperFileContent));
......@@ -248,7 +271,7 @@ OS: Mac OS X 13.2.1 aarch64
final RegExpMatch? version =
gradleVersionRegex.firstMatch(gradleVersionVerbose);
if (version == null) {
// Most likley a bug in our parse implementation/regex.
// Most likely a bug in our parse implementation/regex.
logger.printWarning(_formatParseWarning(gradleVersionVerbose));
return null;
}
......@@ -300,7 +323,7 @@ String _formatParseWarning(String content) {
//
// Source of truth found here:
// https://developer.android.com/studio/releases/gradle-plugin#updating-gradle
// AGP has a minimim version of gradle required but no max starting at
// AGP has a minimum version of gradle required but no max starting at
// AGP version 2.3.0+.
bool validateGradleAndAgp(Logger logger,
{required String? gradleV, required String? agpV}) {
......
// 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 'package:meta/meta.dart';
import '../../base/file_system.dart';
import '../../base/os.dart';
import '../../base/platform.dart';
import '../../base/process.dart';
import '../../base/project_migrator.dart';
import '../../base/version.dart';
import '../../project.dart';
import '../android_sdk.dart';
import '../android_studio.dart';
import '../gradle_utils.dart';
// Android Studio 2022.2 "Flamingo" is the first to bundle a Java 17 JDK.
// Previous versions bundled a Java 11 JDK.
@visibleForTesting
final Version androidStudioFlamingo = Version(2022, 2, 0);
const String _gradleVersion7_6_1 = r'7.6.1';
// String that can be placed in the gradle-wrapper.properties to opt out of this
// migrator.
@visibleForTesting
const String optOutFlag = 'NoFlutterGradleWrapperUpgrade';
// Only the major version matters.
final Version flamingoBundledJava = Version(17, 0, 0);
// These gradle versions were chosen because they
// 1. Were output by 'flutter create' at some point in flutter's history and
// 2. Are less than 7.3, the lowest supported gradle version for JDK 17
const List<String> gradleVersionsToUpgradeFrom =
<String>['5.6.2', '6.7'];
// Define log messages as constants to re-use in testing.
@visibleForTesting
const String gradleWrapperNotFound =
'gradle-wrapper.properties not found, skipping Gradle-Java version compatibility check.';
@visibleForTesting
const String androidStudioNotFound =
'Android Studio version could not be detected, '
'skipping Gradle-Java version compatibility check.';
@visibleForTesting
const String androidStudioVersionBelowFlamingo =
'Version of Android Studio is less than Flamingo (the first impacted version),'
' no migration attempted.';
@visibleForTesting
const String javaVersionNot17 =
'Version of Java is different than impacted version, no migration attempted.';
@visibleForTesting
const String javaVersionNotFound =
'Version of Java not found, no migration attempted.';
@visibleForTesting
const String conflictDetected = 'Conflict detected between versions of Android Studio '
'and Gradle, upgrading Gradle version from current to 7.4';
@visibleForTesting
const String gradleVersionNotFound = 'Failed to parse Gradle version from distribution url, '
'skipping Gradle-Java version compatibility check.';
@visibleForTesting
const String optOutFlagEnabled = 'Skipping Android Studio Java-Gradle compatibility '
"because opt out flag: '$optOutFlag' is enabled in gradle-wrapper.properties file.";
/// Migrate to a newer version of Gradle when the existing one does not support
/// the version of Java provided by the detected Android Studio version.
///
/// For more info see the Gradle-Java compatibility matrix:
/// https://docs.gradle.org/current/userguide/compatibility.html
class AndroidStudioJavaGradleConflictMigration extends ProjectMigrator {
AndroidStudioJavaGradleConflictMigration(
super.logger,
{required AndroidProject project,
AndroidStudio? androidStudio,
required FileSystem fileSystem,
required ProcessUtils processUtils,
required Platform platform,
required OperatingSystemUtils os,
AndroidSdk? androidSdk,
}) : _gradleWrapperPropertiesFile = getGradleWrapperFile(project.hostAppGradleRoot),
_androidStudio = androidStudio,
_fileSystem = fileSystem,
_processUtils = processUtils,
_platform = platform,
_os = os,
_androidSdk = androidSdk;
final File _gradleWrapperPropertiesFile;
final AndroidStudio? _androidStudio;
final FileSystem _fileSystem;
final ProcessUtils _processUtils;
final Platform _platform;
final OperatingSystemUtils _os;
final AndroidSdk? _androidSdk;
@override
void migrate() {
if (!_gradleWrapperPropertiesFile.existsSync()) {
logger.printTrace(gradleWrapperNotFound);
return;
}
if (_androidStudio == null) {
logger.printTrace(androidStudioNotFound);
return;
} else if (_androidStudio!.version!.major < androidStudioFlamingo.major) {
logger.printTrace(androidStudioVersionBelowFlamingo);
return;
}
final String? javaVersionString = _androidSdk?.getJavaVersion(
androidStudio: _androidStudio,
fileSystem: _fileSystem,
operatingSystemUtils: _os,
platform: _platform,
processUtils: _processUtils,
);
final Version? javaVersion = Version.parse(javaVersionString);
if (javaVersion == null) {
logger.printTrace(javaVersionNotFound);
return;
}
if (javaVersion.major != flamingoBundledJava.major) {
logger.printTrace(javaVersionNot17);
return;
}
processFileLines(_gradleWrapperPropertiesFile);
}
@override
String migrateFileContents(String fileContents) {
if (fileContents.contains(optOutFlag)) {
logger.printTrace(optOutFlagEnabled);
return fileContents;
}
final RegExpMatch? gradleDistributionUrl = gradleOrgVersionMatch.firstMatch(fileContents);
if (gradleDistributionUrl == null || gradleDistributionUrl.groupCount < 1) {
logger.printTrace(gradleVersionNotFound);
return fileContents;
}
final String existingVersionString = gradleDistributionUrl[1]!;
if (gradleVersionsToUpgradeFrom.contains(existingVersionString)) {
logger.printStatus('Conflict detected between Android Studio Java version and Gradle version, '
'upgrading Gradle version from $existingVersionString to $_gradleVersion7_6_1.');
final String gradleDistributionUrlString = gradleDistributionUrl.group(0)!;
final String upgradedDistributionUrl =
gradleDistributionUrlString.replaceAll(existingVersionString, _gradleVersion7_6_1);
fileContents = fileContents.replaceFirst(gradleOrgVersionMatch, upgradedDistributionUrl);
}
return fileContents;
}
}
......@@ -94,6 +94,7 @@ Future<T> runInContext<T>(
usage: globals.flutterUsage,
gradleUtils: globals.gradleUtils!,
platform: globals.platform,
androidStudio: globals.androidStudio,
),
AndroidLicenseValidator: () => AndroidLicenseValidator(
operatingSystemUtils: globals.os,
......
......@@ -8,6 +8,7 @@ import 'package:flutter_tools/src/android/android_sdk.dart';
import 'package:flutter_tools/src/android/android_studio.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/version.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/commands/build_apk.dart';
import 'package:flutter_tools/src/globals.dart' as globals;
......@@ -454,4 +455,7 @@ class FakeAndroidSdk extends Fake implements AndroidSdk {
class FakeAndroidStudio extends Fake implements AndroidStudio {
@override
String get javaPath => 'java';
@override
Version get version => Version(2021, 3, 1);
}
......@@ -53,6 +53,7 @@ void main() {
usage: testUsage,
gradleUtils: FakeGradleUtils(),
platform: FakePlatform(),
androidStudio: FakeAndroidStudio(),
);
processManager.addCommand(const FakeCommand(
command: <String>[
......@@ -144,6 +145,7 @@ void main() {
usage: testUsage,
gradleUtils: FakeGradleUtils(),
platform: FakePlatform(),
androidStudio: FakeAndroidStudio(),
);
processManager.addCommand(const FakeCommand(
command: <String>[
......@@ -210,6 +212,7 @@ void main() {
usage: testUsage,
gradleUtils: FakeGradleUtils(),
platform: FakePlatform(),
androidStudio: FakeAndroidStudio(),
);
const FakeCommand fakeCmd = FakeCommand(
......@@ -314,6 +317,7 @@ void main() {
usage: testUsage,
gradleUtils: FakeGradleUtils(),
platform: FakePlatform(),
androidStudio: FakeAndroidStudio(),
);
processManager.addCommand(const FakeCommand(
command: <String>[
......@@ -405,6 +409,7 @@ void main() {
usage: testUsage,
gradleUtils: FakeGradleUtils(),
platform: FakePlatform(),
androidStudio: FakeAndroidStudio(),
);
processManager.addCommand(FakeCommand(
command: const <String>[
......@@ -468,6 +473,7 @@ void main() {
usage: testUsage,
gradleUtils: FakeGradleUtils(),
platform: FakePlatform(),
androidStudio: FakeAndroidStudio(),
);
processManager.addCommand(const FakeCommand(
command: <String>[
......@@ -574,6 +580,7 @@ void main() {
'HOME': '/home',
},
),
androidStudio: FakeAndroidStudio(),
);
processManager.addCommand(const FakeCommand(
command: <String>[
......@@ -670,6 +677,7 @@ void main() {
usage: testUsage,
gradleUtils: FakeGradleUtils(),
platform: FakePlatform(),
androidStudio: FakeAndroidStudio(),
);
processManager.addCommand(const FakeCommand(
command: <String>[
......@@ -796,6 +804,7 @@ android {
usage: testUsage,
gradleUtils: FakeGradleUtils(),
platform: FakePlatform(),
androidStudio: FakeAndroidStudio(),
);
processManager.addCommand(const FakeCommand(
command: <String>[
......@@ -829,6 +838,7 @@ BuildVariant: paidProfile
usage: testUsage,
gradleUtils: FakeGradleUtils(),
platform: FakePlatform(),
androidStudio: FakeAndroidStudio(),
);
processManager.addCommand(const FakeCommand(
command: <String>[
......@@ -858,6 +868,7 @@ Gradle Crashed
usage: testUsage,
gradleUtils: FakeGradleUtils(),
platform: FakePlatform(),
androidStudio: FakeAndroidStudio(),
);
processManager.addCommand(const FakeCommand(
command: <String>[
......@@ -922,6 +933,7 @@ Gradle Crashed
usage: testUsage,
gradleUtils: FakeGradleUtils(),
platform: FakePlatform(),
androidStudio: FakeAndroidStudio(),
);
processManager.addCommand(const FakeCommand(
command: <String>[
......@@ -979,6 +991,7 @@ Gradle Crashed
usage: testUsage,
gradleUtils: FakeGradleUtils(),
platform: FakePlatform(),
androidStudio: FakeAndroidStudio(),
);
processManager.addCommand(const FakeCommand(
command: <String>[
......@@ -1037,6 +1050,7 @@ Gradle Crashed
usage: testUsage,
gradleUtils: FakeGradleUtils(),
platform: FakePlatform(),
androidStudio: FakeAndroidStudio(),
);
processManager.addCommand(const FakeCommand(
command: <String>[
......@@ -1114,6 +1128,7 @@ Gradle Crashed
usage: testUsage,
gradleUtils: FakeGradleUtils(),
platform: FakePlatform(),
androidStudio: FakeAndroidStudio(),
);
processManager.addCommand(const FakeCommand(
command: <String>[
......@@ -1191,6 +1206,7 @@ Gradle Crashed
usage: testUsage,
gradleUtils: FakeGradleUtils(),
platform: FakePlatform(),
androidStudio: FakeAndroidStudio(),
);
processManager.addCommand(const FakeCommand(
command: <String>[
......@@ -1268,6 +1284,7 @@ Gradle Crashed
usage: testUsage,
gradleUtils: FakeGradleUtils(),
platform: FakePlatform(),
androidStudio: FakeAndroidStudio(),
);
processManager.addCommand(const FakeCommand(
command: <String>[
......@@ -1346,6 +1363,7 @@ Gradle Crashed
usage: testUsage,
gradleUtils: FakeGradleUtils(),
platform: FakePlatform(),
androidStudio: FakeAndroidStudio(),
);
processManager.addCommand(
const FakeCommand(command: <String>[
......@@ -1405,6 +1423,7 @@ Gradle Crashed
usage: testUsage,
gradleUtils: FakeGradleUtils(),
platform: FakePlatform(),
androidStudio: FakeAndroidStudio(),
);
processManager.addCommand(const FakeCommand(
command: <String>[
......@@ -1491,6 +1510,7 @@ Gradle Crashed
usage: testUsage,
gradleUtils: FakeGradleUtils(),
platform: FakePlatform(),
androidStudio: FakeAndroidStudio(),
);
processManager.addCommand(const FakeCommand(
command: <String>[
......@@ -1577,6 +1597,7 @@ Gradle Crashed
usage: testUsage,
gradleUtils: FakeGradleUtils(),
platform: FakePlatform(),
androidStudio: FakeAndroidStudio(),
);
processManager.addCommand(const FakeCommand(
command: <String>[
......@@ -1663,6 +1684,7 @@ Gradle Crashed
usage: testUsage,
gradleUtils: FakeGradleUtils(),
platform: FakePlatform(),
androidStudio: FakeAndroidStudio(),
);
processManager.addCommand(const FakeCommand(
command: <String>[
......
......@@ -4,14 +4,48 @@
import 'package:file/file.dart';
import 'package:file/memory.dart';
import 'package:flutter_tools/src/android/android_sdk.dart';
import 'package:flutter_tools/src/android/android_studio.dart';
import 'package:flutter_tools/src/android/gradle_utils.dart';
import 'package:flutter_tools/src/android/migrations/android_studio_java_gradle_conflict_migration.dart';
import 'package:flutter_tools/src/android/migrations/top_level_gradle_build_file_migration.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/os.dart';
import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/base/process.dart';
import 'package:flutter_tools/src/base/version.dart';
import 'package:flutter_tools/src/project.dart';
import 'package:test/fake.dart';
import '../../src/common.dart';
import '../../src/context.dart';
const String otherGradleVersionWrapper = r'''
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.6-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
''';
const String gradleWrapperToMigrate = r'''
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
''';
const String gradleWrapperToMigrateTo = r'''
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
''';
final Version androidStudioDolphin = Version(2021, 3, 1);
void main() {
group('Android migration', () {
group('migrate the Gradle "clean" task to lazy declaration', () {
......@@ -77,6 +111,151 @@ tasks.register("clean", Delete) {
'''));
});
});
group('migrate the gradle version to one that does not conflict with the '
'Android Studio-provided java version', () {
late MemoryFileSystem memoryFileSystem;
late BufferLogger bufferLogger;
late FakeAndroidProject project;
late File gradleWrapperPropertiesFile;
setUp(() {
memoryFileSystem = MemoryFileSystem.test();
bufferLogger = BufferLogger.test();
project = FakeAndroidProject(
root: memoryFileSystem.currentDirectory.childDirectory('android')..createSync(),
);
project.hostAppGradleRoot.childDirectory(gradleDirectoryName)
.childDirectory(gradleWrapperDirectoryName)
.createSync(recursive: true);
gradleWrapperPropertiesFile = project.hostAppGradleRoot
.childDirectory(gradleDirectoryName)
.childDirectory(gradleWrapperDirectoryName)
.childFile(gradleWrapperPropertiesFilename);
});
testWithoutContext('skipped if files are missing', () {
final AndroidStudioJavaGradleConflictMigration migration = AndroidStudioJavaGradleConflictMigration(
bufferLogger,
project: project,
androidStudio: FakeAndroidStudio(version: androidStudioDolphin),
fileSystem: FakeFileSystem(),
processUtils: FakeProcessUtils(),
platform: FakePlatform(),
os: FakeOperatingSystemUtils(),
androidSdk: FakeAndroidSdk(javaVersion: '17'),
);
migration.migrate();
expect(gradleWrapperPropertiesFile.existsSync(), isFalse);
expect(bufferLogger.traceText, contains(gradleWrapperNotFound));
});
testWithoutContext('skipped if android studio is null', () {
final AndroidStudioJavaGradleConflictMigration migration = AndroidStudioJavaGradleConflictMigration(
bufferLogger,
project: project,
fileSystem: FakeFileSystem(),
processUtils: FakeProcessUtils(),
platform: FakePlatform(),
os: FakeOperatingSystemUtils(),
androidSdk: FakeAndroidSdk(javaVersion: '17'),
);
gradleWrapperPropertiesFile.writeAsStringSync(gradleWrapperToMigrate);
migration.migrate();
expect(bufferLogger.traceText, contains(androidStudioNotFound));
expect(gradleWrapperPropertiesFile.readAsStringSync(),
gradleWrapperToMigrate);
});
testWithoutContext('skipped if android studio version is less than flamingo', () {
final AndroidStudioJavaGradleConflictMigration migration = AndroidStudioJavaGradleConflictMigration(
bufferLogger,
project: project,
androidStudio: FakeAndroidStudio(version: androidStudioDolphin),
fileSystem: FakeFileSystem(),
processUtils: FakeProcessUtils(),
platform: FakePlatform(),
os: FakeOperatingSystemUtils(),
androidSdk: FakeAndroidSdk(javaVersion: '17'),
);
gradleWrapperPropertiesFile.writeAsStringSync(gradleWrapperToMigrate);
migration.migrate();
expect(gradleWrapperPropertiesFile.readAsStringSync(), gradleWrapperToMigrate);
expect(bufferLogger.traceText, contains(androidStudioVersionBelowFlamingo));
});
testWithoutContext('skipped if bundled java version is less than 17', () {
final AndroidStudioJavaGradleConflictMigration migration = AndroidStudioJavaGradleConflictMigration(
bufferLogger,
project: project,
androidStudio: FakeAndroidStudio(version: androidStudioFlamingo),
fileSystem: FakeFileSystem(),
processUtils: FakeProcessUtils(),
platform: FakePlatform(),
os: FakeOperatingSystemUtils(),
androidSdk: FakeAndroidSdk(javaVersion: '16'),
);
gradleWrapperPropertiesFile.writeAsStringSync(gradleWrapperToMigrate);
migration.migrate();
expect(gradleWrapperPropertiesFile.readAsStringSync(), gradleWrapperToMigrate);
expect(bufferLogger.traceText, contains(javaVersionNot17));
});
testWithoutContext('nothing is changed if gradle version not one that was '
'used by flutter create', () {
final AndroidStudioJavaGradleConflictMigration migration = AndroidStudioJavaGradleConflictMigration(
bufferLogger,
project: project,
androidStudio: FakeAndroidStudio(version: androidStudioFlamingo),
fileSystem: FakeFileSystem(),
processUtils: FakeProcessUtils(),
platform: FakePlatform(),
os: FakeOperatingSystemUtils(),
androidSdk: FakeAndroidSdk(javaVersion: '17'),
);
gradleWrapperPropertiesFile.writeAsStringSync(otherGradleVersionWrapper);
migration.migrate();
expect(gradleWrapperPropertiesFile.readAsStringSync(), otherGradleVersionWrapper);
expect(bufferLogger.traceText, isEmpty);
});
testWithoutContext('change is made with one of the specific gradle versions'
' we migrate for', () {
final AndroidStudioJavaGradleConflictMigration migration = AndroidStudioJavaGradleConflictMigration(
bufferLogger,
project: project,
androidStudio: FakeAndroidStudio(version: androidStudioFlamingo),
fileSystem: FakeFileSystem(),
processUtils: FakeProcessUtils(),
platform: FakePlatform(),
os: FakeOperatingSystemUtils(),
androidSdk: FakeAndroidSdk(javaVersion: '17'),
);
gradleWrapperPropertiesFile.writeAsStringSync(gradleWrapperToMigrate);
migration.migrate();
expect(gradleWrapperPropertiesFile.readAsStringSync(), gradleWrapperToMigrateTo);
expect(bufferLogger.statusText, contains('Conflict detected between Android Studio Java version and Gradle version, '
'upgrading Gradle version from 6.7 to 7.6.1.'));
});
testWithoutContext('change is not made when opt out flag is set', () {
final AndroidStudioJavaGradleConflictMigration migration = AndroidStudioJavaGradleConflictMigration(
bufferLogger,
project: project,
androidStudio: FakeAndroidStudio(version: androidStudioFlamingo),
fileSystem: FakeFileSystem(),
processUtils: FakeProcessUtils(),
platform: FakePlatform(),
os: FakeOperatingSystemUtils(),
androidSdk: FakeAndroidSdk(javaVersion: '17'),
);
gradleWrapperPropertiesFile.writeAsStringSync(gradleWrapperToMigrate + optOutFlag);
migration.migrate();
expect(gradleWrapperPropertiesFile.readAsStringSync(), gradleWrapperToMigrate + optOutFlag);
expect(bufferLogger.traceText, contains(optOutFlagEnabled));
});
});
});
}
......@@ -86,3 +265,38 @@ class FakeAndroidProject extends Fake implements AndroidProject {
@override
Directory hostAppGradleRoot;
}
class FakeAndroidStudio extends Fake implements AndroidStudio {
FakeAndroidStudio({required Version version}) {
_version = version;
}
late Version _version;
@override
Version get version => _version;
}
class FakeAndroidSdk extends Fake implements AndroidSdk {
FakeAndroidSdk({required String javaVersion}) {
_javaVersion = javaVersion;
}
late String _javaVersion;
@override
String? getJavaVersion({
required AndroidStudio? androidStudio,
required FileSystem fileSystem,
required OperatingSystemUtils operatingSystemUtils,
required Platform platform,
required ProcessUtils processUtils,
}) {
return _javaVersion;
}
}
class FakeFileSystem extends Fake implements FileSystem {}
class FakeProcessUtils extends Fake implements ProcessUtils {}
class FakePlatform extends Fake implements Platform {}
class FakeOperatingSystemUtils extends Fake implements OperatingSystemUtils {}
......@@ -216,6 +216,20 @@ void main() {
expect(getGradlewFileName(windowsPlatform), 'gradlew.bat');
});
testWithoutContext('returns the gradle properties file', () async {
final Directory androidDirectory = fileSystem.directory('/android')
..createSync();
final Directory wrapperDirectory = androidDirectory
.childDirectory(gradleDirectoryName)
.childDirectory(gradleWrapperDirectoryName)
..createSync(recursive: true);
final File expectedFile = await wrapperDirectory
.childFile(gradleWrapperPropertiesFilename)
.create();
final File gradleWrapperFile = getGradleWrapperFile(androidDirectory);
expect(gradleWrapperFile.path, expectedFile.path);
});
testWithoutContext('returns the gradle wrapper version', () async {
const String expectedVersion = '7.4.2';
final Directory androidDirectory = fileSystem.directory('/android')
......@@ -535,6 +549,32 @@ allprojects {
}
});
group('Parse gradle version from distribution url', () {
testWithoutContext('null distribution url returns null version', () {
expect(parseGradleVersionFromDistributionUrl(null), null);
});
testWithoutContext('unparseable format returns null', () {
const String distributionUrl = 'aString';
expect(parseGradleVersionFromDistributionUrl(distributionUrl), null);
});
testWithoutContext("recognizable 'all' format returns correct version", () {
const String distributionUrl = r'distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip';
expect(parseGradleVersionFromDistributionUrl(distributionUrl), '6.7');
});
testWithoutContext("recognizable 'bin' format returns correct version", () {
const String distributionUrl = r'distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-bin.zip';
expect(parseGradleVersionFromDistributionUrl(distributionUrl), '6.7');
});
testWithoutContext("recognizable 'rc' format returns correct version", () {
const String distributionUrl = r'distributionUrl=https\://services.gradle.org/distributions/gradle-8.1-rc-3-all.zip';
expect(parseGradleVersionFromDistributionUrl(distributionUrl), '8.1');
});
});
group('validates java/gradle versions', () {
final List<JavaGradleTestData> testData = <JavaGradleTestData>[
// Values too new *these need to update* when
......
......@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-all.zip
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