Unverified Commit 594ff98a authored by Camille Simon's avatar Camille Simon Committed by GitHub

[Android] Add Java/AGP/Gradle incompatibility warning to `flutter create` (#131444)

Adds warning to `flutter create` command that checks if detected Java version is compatible with the template AGP and template Gradle versions. If a developer is building for Android and their Java version is incompatible with either the AGP or Gradle versions that Flutter currently supports by default for new Flutter projects, then

- a warning will show noting the incompatibility and
- steps will be shown to fix the issue, the recommended option being to configure a new compatible Java version given that Flutter knows we can support the template Gradle/AGP versions and updating them manually may be risky (feedback on this approach would be greatly appreciated!)

Given that the template AGP and Gradle versions are compatible, this PR assumes that the detected Java version may only conflict with one of the template AGP or Gradle versions because:
 - the minimum Java version for a given AGP version is less than the maximum Java version compatible for the minimum Gradle version required for that AGP version (too low a Java version will fail AGP compatibility test, but not Gradle compatibility).
- the maximum Java version compatible with minimum Gradle version for a given AGP version is higher than minimum Java version required for that AGP version (too high a Java version will fail Gradle compatibility test, but not AGP compatibility test).

Fixes https://github.com/flutter/flutter/issues/130515 in the sense that `flutter create foo`; `cd foo`; `flutter run` should always be successful.
parent 3a78e5c5
# Flutter Tools for Android
This section of the Flutter repository contains the command line developer tools
for building Flutter applications on Android. What follows are some notes about
updating this part of the tool.
## Updating Android dependencies
The Android dependencies that Flutter uses to run on Android
include the Android NDK and SDK versions, Gradle, the Kotlin Gradle Plugin,
and the Android Gradle Plugin (AGP). The template versions of these
dependencies can be found in [gradle_utils.dart](gradle_utils.dart).
Follow the guides below when*...
### Updating the template version of...
#### The Android SDK & NDK
All of the Android SDK/NDK versions noted in `gradle_utils.dart`
(`compileSdkVersion`, `minSdkVersion`, `targetSdkVersion`, `ndkVersion`)
versions should match the values in Flutter Gradle Plugin (`FlutterExtension`),
so updating any of these versions also requires an update in
[flutter.groovy](../../../gradle/src/main/groovy/flutter.groovy).
When updating the Android `compileSdkVersion`, `minSdkVersion`, or
`targetSdkVersion`, make sure that:
- Framework integration & benchmark tests are running with at least that SDK
version.
- Flutter tools tests that perform String checks with the current template
SDK verisons are updated (you should see these fail if you do not fix them
preemptively).
#### Gradle
When updating the Gradle version used in project templates
(`templateDefaultGradleVersion`), make sure that:
- Framework integration & benchmark tests are running with at least this Gradle
version.
- Flutter tools tests that perform String checks with the current template
Gradle version are updated (you should see these fail if you do not fix them
preemptively).
#### The Kotlin Gradle Plugin
When updating the Kotlin Gradle Plugin (KGP) version used in project templates
(`templateKotlinGradlePluginVersion`), make sure that the framework integration
& benchmark tests are running with at least this KGP version.
For information aboout the latest version, check https://kotlinlang.org/docs/releases.html#release-details.
#### The Android Gradle Plugin (AGP)
When updating the Android Gradle Plugin (AGP) versions used in project templates
(`templateAndroidGradlePluginVersion`, `templateAndroidGradlePluginVersionForModule`),
make sure that:
- Framework integration & benchmark tests are running with at least this AGP
version.
- Flutter tools tests that perform String checks with the current template
AGP verisons are updated (you should see these fail if you do not fix them
preemptively).
### A new version becomes available for...
#### Gradle
When new versions of Gradle become available, make sure to:
- Check if the maximum version of Gradle that we support
(`maxKnownAndSupportedGradleVersion`) can be updated, and if so, take the
necessary steps to ensure we are testing this version in CI.
- Check that the Java version that is one higher than we currently support
(`oneMajorVersionHigherJavaVersion`) based on current maximum supported
Gradle version is up-to-date.
- Update the `_javaGradleCompatList` that contains the Java/Gradle
compatibility information known to the tool.
- Update the test cases in [gradle_utils_test.dart](../../..test/general.shard/android/gradle_utils_test.dart) that test compatibility between Java and Gradle versions
(relevant tests should fail if you do not fix them preemptively, but should also
be marked inline).
- Update the test cases in [create_test.dart](../../../test/commands.shard/permeable/create_test.dart) that test for a warning for Java/Gradle incompatibilities as needed
(relevant tests should fail if you do not fix them preemptively).
For more information about the latest version, check https://gradle.org/releases/.
#### The Android Gradle Plugin (AGP)
When new versions of the Android Gradle Plugin become available, make sure to:
- Update the maximum version of AGP that we know of (`maxKnownAgpVersion`).
- Check if the maximum version of AGP that we support
(`maxKnownAndSupportedAgpVersion`) can be updated, and if so, take the necessary
steps to ensure that we are testing this version in CI.
- Update the `_javaAgpCompatList` that contains the Java/AGP compatibility
information known to the tool.
- Update the test cases in [gradle_utils_test.dart](../../..test/general.shard/android/gradle_utils_test.dart) that test compatibility between Java and AGP versions
(relevant tests should fail if you do not fix them preemptively, but should also
be marked inline).
- Update the test cases in [create_test.dart](../../../test/commands.shard/permeable/create_test.dart) that test for a warning for Java/AGP incompatibilities as needed
(relevant tests should fail if you do not fix them preemptively).
For information about the latest version, check https://developer.android.com/studio/releases/gradle-plugin#updating-gradle.
\* There is an ongoing effort to reduce these steps; see https://github.com/flutter/flutter/issues/134780.
......@@ -12,6 +12,7 @@ import '../base/os.dart';
import '../base/platform.dart';
import '../base/utils.dart';
import '../base/version.dart';
import '../base/version_range.dart';
import '../build_info.dart';
import '../cache.dart';
import '../globals.dart' as globals;
......@@ -24,33 +25,50 @@ import 'android_sdk.dart';
// In general, Flutter aims to default to the latest version.
// However, this currently requires to migrate existing integration tests to the latest supported values.
//
// For more information about the latest version, check:
// https://developer.android.com/studio/releases/gradle-plugin#updating-gradle
// https://kotlinlang.org/docs/releases.html#release-details
// Please see the README before changing any of these values.
const String templateDefaultGradleVersion = '7.5';
const String templateAndroidGradlePluginVersion = '7.3.0';
const String templateDefaultGradleVersionForModule = '7.3.0';
const String templateAndroidGradlePluginVersionForModule = '7.3.0';
const String templateKotlinGradlePluginVersion = '1.7.10';
// These versions should match the values in Flutter Gradle Plugin (FlutterExtension).
// The Flutter Gradle plugin is only applied to app projects, and modules that are built from source
// using (include_flutter.groovy).
// The remaining projects are: plugins, and modules compiled as AARs. In modules, the ephemeral directory
// `.android` is always regenerated after flutter pub get, so new versions are picked up after a
// Flutter upgrade.
// The Flutter Gradle Plugin is only applied to app projects, and modules that
// are built from source using (`include_flutter.groovy`). The remaining
// projects are: plugins, and modules compiled as AARs. In modules, the
// ephemeral directory `.android` is always regenerated after `flutter pub get`,
// so new versions are picked up after a Flutter upgrade.
//
// Please see the README before changing any of these values.
const String compileSdkVersion = '33';
const String minSdkVersion = '19';
const String targetSdkVersion = '33';
const String ndkVersion = '23.1.7779620';
// Update these when new major versions of Java are supported by new Gradle
// versions that we support.
// Source of truth: https://docs.gradle.org/current/userguide/compatibility.html
const String oneMajorVersionHigherJavaVersion = '20';
// Update this when new versions of Gradle come out including minor versions
// and should correspond to the maximum Gradle version we test in CI.
//
// Supported here means supported by the tooling for
// flutter analyze --suggestions and does not imply broader flutter support.
const String _maxKnownAndSupportedGradleVersion = '8.0.2';
const String maxKnownAndSupportedGradleVersion = '8.0.2';
// Update this when new versions of AGP come out.
//
// Supported here means tooling is aware of this version's Java <-> AGP
// compatibility.
@visibleForTesting
const String maxKnownAgpVersion = '8.1';
const String maxKnownAndSupportedAgpVersion = '8.1';
// Update this when new versions of AGP come out.
const String maxKnownAgpVersion = '8.3';
// Oldest documented version of AGP that has a listed minimum
// compatible Java version.
const String oldestDocumentedJavaAgpCompatibilityVersion = '4.2';
// Expected content:
// "classpath 'com.android.tools.build:gradle:7.3.0'"
......@@ -355,13 +373,13 @@ bool validateGradleAndAgp(Logger logger,
}
// Check highest supported version before checking unknown versions.
if (isWithinVersionRange(agpV, min: '8.0', max: maxKnownAgpVersion)) {
if (isWithinVersionRange(agpV, min: '8.0', max: maxKnownAndSupportedAgpVersion)) {
return isWithinVersionRange(gradleV,
min: '8.0', max: _maxKnownAndSupportedGradleVersion);
min: '8.0', max: maxKnownAndSupportedGradleVersion);
}
// Check if versions are newer than the max known versions.
if (isWithinVersionRange(agpV,
min: _maxKnownAndSupportedGradleVersion, max: '100.100')) {
min: maxKnownAndSupportedAgpVersion, max: '100.100')) {
// Assume versions we do not know about are valid but log.
final bool validGradle =
isWithinVersionRange(gradleV, min: '8.0', max: '100.00');
......@@ -374,42 +392,42 @@ bool validateGradleAndAgp(Logger logger,
// Max agp here is a made up version to contain all 7.4 changes.
if (isWithinVersionRange(agpV, min: '7.4', max: '7.5')) {
return isWithinVersionRange(gradleV,
min: '7.5', max: _maxKnownAndSupportedGradleVersion);
min: '7.5', max: maxKnownAndSupportedGradleVersion);
}
if (isWithinVersionRange(agpV,
min: '7.3', max: '7.4', inclusiveMax: false)) {
return isWithinVersionRange(gradleV,
min: '7.4', max: _maxKnownAndSupportedGradleVersion);
min: '7.4', max: maxKnownAndSupportedGradleVersion);
}
if (isWithinVersionRange(agpV,
min: '7.2', max: '7.3', inclusiveMax: false)) {
return isWithinVersionRange(gradleV,
min: '7.3.3', max: _maxKnownAndSupportedGradleVersion);
min: '7.3.3', max: maxKnownAndSupportedGradleVersion);
}
if (isWithinVersionRange(agpV,
min: '7.1', max: '7.2', inclusiveMax: false)) {
return isWithinVersionRange(gradleV,
min: '7.2', max: _maxKnownAndSupportedGradleVersion);
min: '7.2', max: maxKnownAndSupportedGradleVersion);
}
if (isWithinVersionRange(agpV,
min: '7.0', max: '7.1', inclusiveMax: false)) {
return isWithinVersionRange(gradleV,
min: '7.0', max: _maxKnownAndSupportedGradleVersion);
min: '7.0', max: maxKnownAndSupportedGradleVersion);
}
if (isWithinVersionRange(agpV,
min: '4.2.0', max: '7.0', inclusiveMax: false)) {
return isWithinVersionRange(gradleV,
min: '6.7.1', max: _maxKnownAndSupportedGradleVersion);
min: '6.7.1', max: maxKnownAndSupportedGradleVersion);
}
if (isWithinVersionRange(agpV,
min: '4.1.0', max: '4.2.0', inclusiveMax: false)) {
return isWithinVersionRange(gradleV,
min: '6.5', max: _maxKnownAndSupportedGradleVersion);
min: '6.5', max: maxKnownAndSupportedGradleVersion);
}
if (isWithinVersionRange(agpV,
min: '4.0.0', max: '4.1.0', inclusiveMax: false)) {
return isWithinVersionRange(gradleV,
min: '6.1.1', max: _maxKnownAndSupportedGradleVersion);
min: '6.1.1', max: maxKnownAndSupportedGradleVersion);
}
if (isWithinVersionRange(
agpV,
......@@ -417,7 +435,7 @@ bool validateGradleAndAgp(Logger logger,
max: '3.6.4',
)) {
return isWithinVersionRange(gradleV,
min: '5.6.4', max: _maxKnownAndSupportedGradleVersion);
min: '5.6.4', max: maxKnownAndSupportedGradleVersion);
}
if (isWithinVersionRange(
agpV,
......@@ -425,7 +443,7 @@ bool validateGradleAndAgp(Logger logger,
max: '3.5.4',
)) {
return isWithinVersionRange(gradleV,
min: '5.4.1', max: _maxKnownAndSupportedGradleVersion);
min: '5.4.1', max: maxKnownAndSupportedGradleVersion);
}
if (isWithinVersionRange(
agpV,
......@@ -433,7 +451,7 @@ bool validateGradleAndAgp(Logger logger,
max: '3.4.3',
)) {
return isWithinVersionRange(gradleV,
min: '5.1.1', max: _maxKnownAndSupportedGradleVersion);
min: '5.1.1', max: maxKnownAndSupportedGradleVersion);
}
if (isWithinVersionRange(
agpV,
......@@ -441,24 +459,20 @@ bool validateGradleAndAgp(Logger logger,
max: '3.3.3',
)) {
return isWithinVersionRange(gradleV,
min: '4.10.1', max: _maxKnownAndSupportedGradleVersion);
min: '4.10.1', max: maxKnownAndSupportedGradleVersion);
}
logger.printTrace('Unknown Gradle-Agp compatibility, $gradleV, $agpV');
return false;
}
// Validate that the [javaVersion] and Gradle version are compatible with
// each other.
//
// Source of truth:
// https://docs.gradle.org/current/userguide/compatibility.html#java
bool validateJavaGradle(Logger logger,
/// Validate that the [javaVersion] and Gradle version are compatible with
/// each other.
///
/// Source of truth:
/// https://docs.gradle.org/current/userguide/compatibility.html#java
bool validateJavaAndGradle(Logger logger,
{required String? javaV, required String? gradleV}) {
// Update these when new major versions of Java are supported by android.
// Supported means Java <-> Gradle support.
const String oneMajorVersionHigherJavaVersion = '20';
// https://docs.gradle.org/current/userguide/compatibility.html#java
const String oldestSupportedJavaVersion = '1.8';
const String oldestDocumentedJavaGradleCompatibility = '2.0';
......@@ -492,7 +506,7 @@ bool validateJavaGradle(Logger logger,
// Assume versions Java versions newer than [maxSupportedJavaVersion]
// required a higher gradle version.
final bool validGradle = isWithinVersionRange(gradleV,
min: _maxKnownAndSupportedGradleVersion, max: '100.00');
min: maxKnownAndSupportedGradleVersion, max: '100.00');
logger.printWarning(
'Newer than known valid Java version ($javaV), gradle ($gradleV).'
'\n Treating as valid configuration.');
......@@ -500,82 +514,7 @@ bool validateJavaGradle(Logger logger,
}
// Begin known Java <-> Gradle evaluation.
final List<JavaGradleCompat> compatList = <JavaGradleCompat>[
JavaGradleCompat(
javaMin: '19',
javaMax: '20',
gradleMin: '7.6',
gradleMax: _maxKnownAndSupportedGradleVersion,
),
JavaGradleCompat(
javaMin: '18',
javaMax: '19',
gradleMin: '7.5',
gradleMax: _maxKnownAndSupportedGradleVersion,
),
JavaGradleCompat(
javaMin: '17',
javaMax: '18',
gradleMin: '7.3',
gradleMax: _maxKnownAndSupportedGradleVersion,
),
JavaGradleCompat(
javaMin: '16',
javaMax: '17',
gradleMin: '7.0',
gradleMax: _maxKnownAndSupportedGradleVersion,
),
JavaGradleCompat(
javaMin: '15',
javaMax: '16',
gradleMin: '6.7',
gradleMax: _maxKnownAndSupportedGradleVersion,
),
JavaGradleCompat(
javaMin: '14',
javaMax: '15',
gradleMin: '6.3',
gradleMax: _maxKnownAndSupportedGradleVersion,
),
JavaGradleCompat(
javaMin: '13',
javaMax: '14',
gradleMin: '6.0',
gradleMax: _maxKnownAndSupportedGradleVersion,
),
JavaGradleCompat(
javaMin: '12',
javaMax: '13',
gradleMin: '5.4',
gradleMax: _maxKnownAndSupportedGradleVersion,
),
JavaGradleCompat(
javaMin: '11',
javaMax: '12',
gradleMin: '5.0',
gradleMax: _maxKnownAndSupportedGradleVersion,
),
// 1.11 is a made up java version to cover everything in 1.10.*
JavaGradleCompat(
javaMin: '1.10',
javaMax: '1.11',
gradleMin: '4.7',
gradleMax: _maxKnownAndSupportedGradleVersion,
),
JavaGradleCompat(
javaMin: '1.9',
javaMax: '1.10',
gradleMin: '4.3',
gradleMax: _maxKnownAndSupportedGradleVersion,
),
JavaGradleCompat(
javaMin: '1.8',
javaMax: '1.9',
gradleMin: '2.0',
gradleMax: _maxKnownAndSupportedGradleVersion,
),
];
for (final JavaGradleCompat data in compatList) {
for (final JavaGradleCompat data in _javaGradleCompatList) {
if (isWithinVersionRange(javaV, min: data.javaMin, max: data.javaMax, inclusiveMax: false)) {
return isWithinVersionRange(gradleV, min: data.gradleMin, max: data.gradleMax);
}
......@@ -585,6 +524,105 @@ bool validateJavaGradle(Logger logger,
return false;
}
/// Returns compatibility information for the valid range of Gradle versions for
/// the specified Java version.
///
/// Returns null when the tooling has not documented the compatibile Gradle
/// versions for the Java version (either the version is too old or too new). If
/// this seems like a mistake, the caller may need to update the
/// [_javaGradleCompatList] detailing Java/Gradle compatibility.
JavaGradleCompat? getValidGradleVersionRangeForJavaVersion(
Logger logger, {
required String javaV,
}) {
for (final JavaGradleCompat data in _javaGradleCompatList) {
if (isWithinVersionRange(javaV, min: data.javaMin, max: data.javaMax, inclusiveMax: false)) {
return data;
}
}
logger.printTrace('Unable to determine valid Gradle version range for Java version $javaV.');
return null;
}
/// Validate that the specified Java and Android Gradle Plugin (AGP) versions are
/// compatible with each other.
///
/// Returns true when the specified Java and AGP versions are
/// definitely compatible; otherwise, false is assumed by default. In addition,
/// this will return false when either a null Java or AGP version is provided.
///
/// Source of truth are the AGP release notes:
/// https://developer.android.com/build/releases/gradle-plugin
bool validateJavaAndAgp(Logger logger,
{required String? javaV, required String? agpV}) {
if (javaV == null || agpV == null) {
logger.printTrace(
'Java version or AGP version unknown ($javaV, $agpV).');
return false;
}
// Check if AGP version is too old to perform validation.
if (isWithinVersionRange(agpV,
min: '1.0', max: oldestDocumentedJavaAgpCompatibilityVersion, inclusiveMax: false)) {
logger.printTrace('AGP Version: $agpV is too old to determine Java compatibility.');
return false;
}
if (isWithinVersionRange(agpV,
min: maxKnownAndSupportedAgpVersion, max: '100.100', inclusiveMin: false)) {
logger.printTrace('AGP Version: $agpV is too new to determine Java compatibility.');
return false;
}
// Begin known Java <-> AGP evaluation.
for (final JavaAgpCompat data in _javaAgpCompatList) {
if (isWithinVersionRange(agpV, min: data.agpMin, max: data.agpMax)) {
return isWithinVersionRange(javaV, min: data.javaMin, max: '100.100');
}
}
logger.printTrace('Unknown Java-AGP compatibility $javaV, $agpV');
return false;
}
/// Returns compatibility information concerning the minimum AGP
/// version for the specified Java version.
JavaAgpCompat? getMinimumAgpVersionForJavaVersion(Logger logger,
{required String javaV}) {
for (final JavaAgpCompat data in _javaAgpCompatList) {
if (isWithinVersionRange(javaV, min: data.javaMin, max: '100.100')) {
return data;
}
}
logger.printTrace('Unable to determine minimum AGP version for specified Java version.');
return null;
}
/// Returns valid Java range for specified Gradle and AGP verisons.
///
/// Assumes that gradleV and agpV are compatible versions.
VersionRange getJavaVersionFor({required String gradleV, required String agpV}) {
// Find minimum Java version based on AGP compatibility.
String? minJavaVersion;
for (final JavaAgpCompat data in _javaAgpCompatList) {
if (isWithinVersionRange(agpV, min: data.agpMin, max: data.agpMax)) {
minJavaVersion = data.javaMin;
}
}
// Find maximum Java version based on Gradle compatibility.
String? maxJavaVersion;
for (final JavaGradleCompat data in _javaGradleCompatList.reversed) {
if (isWithinVersionRange(gradleV, min: data.gradleMin, max: maxKnownAndSupportedGradleVersion)) {
maxJavaVersion = data.javaMax;
}
}
return VersionRange(minJavaVersion, maxJavaVersion);
}
/// Returns the Gradle version that is required by the given Android Gradle plugin version
/// by picking the largest compatible version from
/// https://developer.android.com/studio/releases/gradle-plugin#updating-gradle
......@@ -607,7 +645,7 @@ String getGradleVersionFor(String androidPluginVersion) {
GradleForAgp(agpMin: '7.5.0', agpMax: '100.100', minRequiredGradle: '8.0'),
// Assume if AGP is newer than this code know about return the highest gradle
// version we know about.
GradleForAgp(agpMin: maxKnownAgpVersion, agpMax: maxKnownAgpVersion, minRequiredGradle: _maxKnownAndSupportedGradleVersion),
GradleForAgp(agpMin: maxKnownAgpVersion, agpMax: maxKnownAgpVersion, minRequiredGradle: maxKnownAndSupportedGradleVersion),
];
......@@ -617,7 +655,7 @@ String getGradleVersionFor(String androidPluginVersion) {
}
}
if (isWithinVersionRange(androidPluginVersion, min: maxKnownAgpVersion, max: '100.100')) {
return _maxKnownAndSupportedGradleVersion;
return maxKnownAndSupportedGradleVersion;
}
throwToolExit('Unsupported Android Plugin version: $androidPluginVersion.');
}
......@@ -708,17 +746,63 @@ void exitWithNoSdkMessage() {
}
// Data class to hold normal/defined Java <-> Gradle compatability criteria.
//
// The [javaMax] is exclusive in terms of supporting the noted [gradleMin],
// whereas [javaMin] is inclusive.
@immutable
class JavaGradleCompat {
JavaGradleCompat({
const JavaGradleCompat({
required this.javaMin,
required this.javaMax,
required this.gradleMin,
required this.gradleMax,
});
final String javaMin;
final String javaMax;
final String gradleMin;
final String gradleMax;
@override
bool operator ==(Object other) =>
other is JavaGradleCompat &&
other.javaMin == javaMin &&
other.javaMax == javaMax &&
other.gradleMin == gradleMin &&
other.gradleMax == gradleMax;
@override
int get hashCode => Object.hash(javaMin, javaMax, gradleMin, gradleMax);
}
// Data class to hold defined Java <-> AGP compatibility criteria.
//
// The [agpMin] and [agpMax] are inclusive in terms of having the
// noted [javaMin] and [javaDefault] versions.
@immutable
class JavaAgpCompat {
const JavaAgpCompat({
required this.javaMin,
required this.javaDefault,
required this.agpMin,
required this.agpMax,
});
final String javaMin;
final String javaDefault;
final String agpMin;
final String agpMax;
@override
bool operator ==(Object other) =>
other is JavaAgpCompat &&
other.javaMin == javaMin &&
other.javaDefault == javaDefault &&
other.agpMin == agpMin &&
other.agpMax == agpMax;
@override
int get hashCode => Object.hash(javaMin, javaDefault, agpMin, agpMax);
}
class GradleForAgp {
......@@ -727,6 +811,7 @@ class GradleForAgp {
required this.agpMax,
required this.minRequiredGradle,
});
final String agpMin;
final String agpMax;
final String minRequiredGradle;
......@@ -740,3 +825,112 @@ String getGradlewFileName(Platform platform) {
return 'gradlew';
}
}
/// List of compatible Java/Gradle versions.
///
/// Should be updated when a new version of Java is supported by a new version
/// of Gradle, as https://docs.gradle.org/current/userguide/compatibility.html
/// details.
List<JavaGradleCompat> _javaGradleCompatList = const <JavaGradleCompat>[
JavaGradleCompat(
javaMin: '19',
javaMax: '20',
gradleMin: '7.6',
gradleMax: maxKnownAndSupportedGradleVersion,
),
JavaGradleCompat(
javaMin: '18',
javaMax: '19',
gradleMin: '7.5',
gradleMax: maxKnownAndSupportedGradleVersion,
),
JavaGradleCompat(
javaMin: '17',
javaMax: '18',
gradleMin: '7.3',
gradleMax: maxKnownAndSupportedGradleVersion,
),
JavaGradleCompat(
javaMin: '16',
javaMax: '17',
gradleMin: '7.0',
gradleMax: maxKnownAndSupportedGradleVersion,
),
JavaGradleCompat(
javaMin: '15',
javaMax: '16',
gradleMin: '6.7',
gradleMax: maxKnownAndSupportedGradleVersion,
),
JavaGradleCompat(
javaMin: '14',
javaMax: '15',
gradleMin: '6.3',
gradleMax: maxKnownAndSupportedGradleVersion,
),
JavaGradleCompat(
javaMin: '13',
javaMax: '14',
gradleMin: '6.0',
gradleMax: maxKnownAndSupportedGradleVersion,
),
JavaGradleCompat(
javaMin: '12',
javaMax: '13',
gradleMin: '5.4',
gradleMax: maxKnownAndSupportedGradleVersion,
),
JavaGradleCompat(
javaMin: '11',
javaMax: '12',
gradleMin: '5.0',
gradleMax: maxKnownAndSupportedGradleVersion,
),
// 1.11 is a made up java version to cover everything in 1.10.*
JavaGradleCompat(
javaMin: '1.10',
javaMax: '1.11',
gradleMin: '4.7',
gradleMax: maxKnownAndSupportedGradleVersion,
),
JavaGradleCompat(
javaMin: '1.9',
javaMax: '1.10',
gradleMin: '4.3',
gradleMax: maxKnownAndSupportedGradleVersion,
),
JavaGradleCompat(
javaMin: '1.8',
javaMax: '1.9',
gradleMin: '2.0',
gradleMax: maxKnownAndSupportedGradleVersion,
),
];
// List of compatible Java/AGP versions, where agpMax versions are inclusive.
//
// Should be updated whenever a new version of AGP is released as
// https://developer.android.com/build/releases/gradle-plugin details.
List<JavaAgpCompat> _javaAgpCompatList = const <JavaAgpCompat>[
JavaAgpCompat(
javaMin: '17',
javaDefault: '17',
agpMin: '8.0',
agpMax: maxKnownAndSupportedAgpVersion,
),
JavaAgpCompat(
javaMin: '11',
javaDefault: '11',
agpMin: '7.0',
agpMax: '7.4',
),
JavaAgpCompat(
// You may use JDK 1.7 with AGP 4.2, but we treat 1.8 as the default since
// it is used by default for this AGP version and lower versions of Java
// are deprecated for executing Gradle.
javaMin: '1.8',
javaDefault: '1.8',
agpMin: '4.2',
agpMax: '4.2',
),
];
// 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' show immutable;
/// Data class that represents a range of versions in their String
/// representation.
///
/// Both the [versionMin] and [versionMax] are inclusive versions, and undefined
/// values represent an unknown minimum/maximum version.
@immutable
class VersionRange{
const VersionRange(
this.versionMin,
this.versionMax,
);
final String? versionMin;
final String? versionMax;
@override
bool operator ==(Object other) =>
other is VersionRange &&
other.versionMin == versionMin &&
other.versionMax == versionMax;
@override
int get hashCode => Object.hash(versionMin, versionMax);
}
......@@ -2,6 +2,8 @@
// 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 '../android/gradle_utils.dart' as gradle;
import '../base/common.dart';
import '../base/context.dart';
......@@ -9,6 +11,8 @@ import '../base/file_system.dart';
import '../base/net.dart';
import '../base/terminal.dart';
import '../base/utils.dart';
import '../base/version.dart';
import '../base/version_range.dart';
import '../convert.dart';
import '../dart/pub.dart';
import '../features.dart';
......@@ -501,6 +505,19 @@ Your $application code is in $relativeAppMain.
}
}
// Show warning for Java/AGP or Java/Gradle incompatibility if building for
// Android and Java version has been detected.
if (includeAndroid && globals.java?.version != null) {
_printIncompatibleJavaAgpGradleVersionsWarning(
javaVersion: versionToParsableString(globals.java?.version)!,
templateGradleVersion: templateContext['gradleVersion']! as String,
templateAgpVersion: templateContext['agpVersion']! as String,
templateAgpVersionForModule: templateContext['agpVersionForModule']! as String,
projectType: template,
projectDirPath: projectDirPath,
);
}
return FlutterCommandResult.success();
}
......@@ -853,3 +870,165 @@ For more details, see: https://flutter.dev/docs/get-started/web
''');
}
}
// Prints a warning if the specified Java version conflicts with either the
// template Gradle or AGP version.
//
// Assumes the specified templateGradleVersion and templateAgpVersion are
// compatible, meaning that the Java version may only conflict with one of the
// template Gradle or AGP versions.
void _printIncompatibleJavaAgpGradleVersionsWarning({
required String javaVersion,
required String templateGradleVersion,
required String templateAgpVersion,
required String templateAgpVersionForModule,
required FlutterProjectType projectType,
required String projectDirPath}) {
// Determine if the Java version specified conflicts with the template Gradle or AGP version.
final bool javaGradleVersionsCompatible = gradle.validateJavaAndGradle(globals.logger, javaV: javaVersion, gradleV: templateGradleVersion);
bool javaAgpVersionsCompatible = gradle.validateJavaAndAgp(globals.logger, javaV: javaVersion, agpV: templateAgpVersion);
String relevantTemplateAgpVersion = templateAgpVersion;
if (projectType == FlutterProjectType.module && Version.parse(templateAgpVersion)! < Version.parse(templateAgpVersionForModule)!) {
// If a module is being created, make sure to check for Java/AGP compatibility between the highest used version of AGP in the module template.
javaAgpVersionsCompatible = gradle.validateJavaAndAgp(globals.logger, javaV: javaVersion, agpV: templateAgpVersionForModule);
relevantTemplateAgpVersion = templateAgpVersionForModule;
}
if (javaGradleVersionsCompatible && javaAgpVersionsCompatible) {
return;
}
// Determine header of warning with recommended fix of re-configuring Java version.
final String incompatibleVersionsAndRecommendedOptionMessage = getIncompatibleJavaGradleAgpMessageHeader(javaGradleVersionsCompatible, templateGradleVersion, relevantTemplateAgpVersion, projectType.cliName);
if (!javaGradleVersionsCompatible) {
if (projectType == FlutterProjectType.plugin || projectType == FlutterProjectType.pluginFfi) {
// Only impacted files could be in sample code.
return;
}
// Gradle template version incompatible with Java version.
final gradle.JavaGradleCompat? validCompatibleGradleVersionRange = gradle.getValidGradleVersionRangeForJavaVersion(globals.logger, javaV: javaVersion);
final String compatibleGradleVersionMessage = validCompatibleGradleVersionRange == null ? '' : ' (compatible Gradle version range: ${validCompatibleGradleVersionRange.gradleMin} - ${validCompatibleGradleVersionRange.gradleMax})';
globals.printWarning('''
$incompatibleVersionsAndRecommendedOptionMessage
Alternatively, to continue using your configured Java version, update the Gradle
version specified in the following file to a compatible Gradle version$compatibleGradleVersionMessage:
${_getGradleWrapperPropertiesFilePath(projectType, projectDirPath)}
You may also update the Gradle version used by running
`./gradlew wrapper --gradle-version=<COMPATIBLE_GRADLE_VERSION>`.
See
https://docs.gradle.org/current/userguide/compatibility.html#java for details
on compatible Java/Gradle versions, and see
https://docs.gradle.org/current/userguide/gradle_wrapper.html#sec:upgrading_wrapper
for more details on using the Gradle Wrapper command to update the Gradle version
used.
''',
emphasis: true
);
return;
}
// AGP template version incompatible with Java version.
final gradle.JavaAgpCompat? minimumCompatibleAgpVersion = gradle.getMinimumAgpVersionForJavaVersion(globals.logger, javaV: javaVersion);
final String compatibleAgpVersionMessage = minimumCompatibleAgpVersion == null ? '' : ' (minimum compatible AGP version: ${minimumCompatibleAgpVersion.agpMin})';
final String gradleBuildFilePaths = ' - ${_getBuildGradleConfigurationFilePaths(projectType, projectDirPath)!.join('\n - ')}';
globals.printWarning('''
$incompatibleVersionsAndRecommendedOptionMessage
Alternatively, to continue using your configured Java version, update the AGP
version specified in the following files to a compatible AGP
version$compatibleAgpVersionMessage as necessary:
$gradleBuildFilePaths
See
https://developer.android.com/build/releases/gradle-plugin for details on
compatible Java/AGP versions.
''',
emphasis: true
);
}
// Returns incompatible Java/template Gradle/template AGP message header based
// on incompatibility and project type.
@visibleForTesting
String getIncompatibleJavaGradleAgpMessageHeader(
bool javaGradleVersionsCompatible,
String templateGradleVersion,
String templateAgpVersion,
String projectType) {
final String incompatibleDependency = javaGradleVersionsCompatible ? 'Android Gradle Plugin (AGP)' :'Gradle' ;
final String incompatibleDependencyVersion = javaGradleVersionsCompatible ? 'AGP version $templateAgpVersion' : 'Gradle version $templateGradleVersion';
final VersionRange validJavaRange = gradle.getJavaVersionFor(gradleV: templateGradleVersion, agpV: templateAgpVersion);
// validJavaRange should have non-null verisonMin and versionMax since it based on our template AGP and Gradle versions.
final String validJavaRangeMessage = '(minimum compatible version: ${validJavaRange.versionMin!}, maximum compatible version: ${validJavaRange.versionMax!})';
return '''
The configured version of Java detected may conflict with the $incompatibleDependency version in your new Flutter $projectType.
[RECOMMENDED] If so, to keep the default $incompatibleDependencyVersion, make
sure to download a compatible Java version
$validJavaRangeMessage.
You may configure this compatible Java version by running:
`flutter config --jdk-dir=<JDK_DIRECTORY>`
Note that this is a global configuration for Flutter.
''';
}
// Returns path of the gradle-wrapper.properties file for the specified
// generated project type.
String? _getGradleWrapperPropertiesFilePath(FlutterProjectType projectType, String projectDirPath) {
String gradleWrapperPropertiesFilePath = '';
switch (projectType) {
case FlutterProjectType.app:
case FlutterProjectType.skeleton:
gradleWrapperPropertiesFilePath = globals.fs.path.join(projectDirPath, 'android/gradle/wrapper/gradle-wrapper.properties');
case FlutterProjectType.module:
gradleWrapperPropertiesFilePath = globals.fs.path.join(projectDirPath, '.android/gradle/wrapper/gradle-wrapper.properties');
case FlutterProjectType.plugin:
case FlutterProjectType.pluginFfi:
case FlutterProjectType.package:
case FlutterProjectType.packageFfi:
// TODO(camsim99): Add relevant file path for packageFfi when Android is supported.
// No gradle-wrapper.properties files not part of sample code that
// can be determined.
return null;
}
return gradleWrapperPropertiesFilePath;
}
// Returns the path(s) of the build.gradle file(s) for the specified generated
// project type.
List<String>? _getBuildGradleConfigurationFilePaths(FlutterProjectType projectType, String projectDirPath) {
final List<String> buildGradleConfigurationFilePaths = <String>[];
switch (projectType) {
case FlutterProjectType.app:
case FlutterProjectType.skeleton:
case FlutterProjectType.pluginFfi:
buildGradleConfigurationFilePaths.add(globals.fs.path.join(projectDirPath, 'android/build.gradle'));
case FlutterProjectType.module:
const String moduleBuildGradleFilePath = '.android/build.gradle';
const String moduleAppBuildGradleFlePath = '.android/app/build.gradle';
const String moduleFlutterBuildGradleFilePath = '.android/Flutter/build.gradle';
buildGradleConfigurationFilePaths.addAll(<String>[
globals.fs.path.join(projectDirPath, moduleBuildGradleFilePath),
globals.fs.path.join(projectDirPath, moduleAppBuildGradleFlePath),
globals.fs.path.join(projectDirPath, moduleFlutterBuildGradleFilePath),
]);
case FlutterProjectType.plugin:
buildGradleConfigurationFilePaths.add(globals.fs.path.join(projectDirPath, 'android/app/build.gradle'));
case FlutterProjectType.package:
case FlutterProjectType.packageFfi:
// TODO(camsim99): Add any relevant file paths for packageFfi when Android is supported.
// No build.gradle file because there is no platform-specific implementation.
return null;
}
return buildGradleConfigurationFilePaths;
}
......@@ -422,9 +422,9 @@ abstract class CreateBase extends FlutterCommand {
'dartSdkVersionBounds': dartSdkVersionBounds,
'implementationTests': implementationTests,
'agpVersion': agpVersion,
'agpVersionForModule': gradle.templateAndroidGradlePluginVersionForModule,
'kotlinVersion': kotlinVersion,
'gradleVersion': gradleVersion,
'gradleVersionForModule': gradle.templateDefaultGradleVersionForModule,
'compileSdkVersion': gradle.compileSdkVersion,
'minSdkVersion': gradle.minSdkVersion,
'ndkVersion': gradle.ndkVersion,
......
......@@ -573,7 +573,7 @@ class AndroidProject extends FlutterProjectPlatform {
///
/// This is expected to be called from
/// flutter_tools/lib/src/project_validator.dart.
Future<ProjectValidatorResult> validateJavaGradleAgpVersions() async {
Future<ProjectValidatorResult> validateJavaAndGradleAgpVersions() async {
// Constructing ProjectValidatorResult happens here and not in
// flutter_tools/lib/src/project_validator.dart because of the additional
// Complexity of variable status values and error string formatting.
......@@ -599,7 +599,7 @@ class AndroidProject extends FlutterProjectPlatform {
hostAppGradleRoot, globals.logger, globals.processManager);
final String? agpVersion =
gradle.getAgpVersion(hostAppGradleRoot, globals.logger);
final String? javaVersion = _versionToParsableString(globals.java?.version);
final String? javaVersion = versionToParsableString(globals.java?.version);
// Assume valid configuration.
String description = validJavaGradleAgpString;
......@@ -607,7 +607,7 @@ class AndroidProject extends FlutterProjectPlatform {
final bool compatibleGradleAgp = gradle.validateGradleAndAgp(globals.logger,
gradleV: gradleVersion, agpV: agpVersion);
final bool compatibleJavaGradle = gradle.validateJavaGradle(globals.logger,
final bool compatibleJavaGradle = gradle.validateJavaAndGradle(globals.logger,
javaV: javaVersion, gradleV: gradleVersion);
// Begin description formatting.
......@@ -722,9 +722,9 @@ $javaGradleCompatUrl
'androidIdentifier': androidIdentifier,
'androidX': usesAndroidX,
'agpVersion': gradle.templateAndroidGradlePluginVersion,
'agpVersionForModule': gradle.templateAndroidGradlePluginVersionForModule,
'kotlinVersion': gradle.templateKotlinGradlePluginVersion,
'gradleVersion': gradle.templateDefaultGradleVersion,
'gradleVersionForModule': gradle.templateDefaultGradleVersionForModule,
'compileSdkVersion': gradle.compileSdkVersion,
'minSdkVersion': gradle.minSdkVersion,
'ndkVersion': gradle.ndkVersion,
......@@ -923,7 +923,7 @@ class CompatibilityResult {
}
/// Converts a [Version] to a string that can be parsed by [Version.parse].
String? _versionToParsableString(Version? version) {
String? versionToParsableString(Version? version) {
if (version == null) {
return null;
}
......
......@@ -227,7 +227,7 @@ class GeneralInfoProjectValidator extends ProjectValidator{
result.add(_materialDesignResult(flutterManifest));
result.add(_pluginValidatorResult(flutterManifest));
}
result.add(await project.android.validateJavaGradleAgpVersions());
result.add(await project.android.validateJavaAndGradleAgpVersions());
return result;
}
......
......@@ -8,7 +8,7 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:{{gradleVersionForModule}}'
classpath 'com.android.tools.build:gradle:{{agpVersionForModule}}'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
......
......@@ -3,6 +3,7 @@
// found in the LICENSE file.
import 'package:args/command_runner.dart';
import 'package:flutter_tools/src/android/java.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/commands/create.dart';
......@@ -141,7 +142,10 @@ void main() {
await runner.run(<String>['create', '--no-pub', '--template=package_ffi', 'testy6']);
expect((await command.usageValues).commandCreateProjectType, 'package_ffi');
}));
}),
overrides: <Type, Generator>{
Java: () => FakeJava(),
});
testUsingContext('set iOS host language type as usage value', () => testbed.run(() async {
final CreateCommand command = CreateCommand();
......@@ -160,8 +164,10 @@ void main() {
'testy',
]);
expect((await command.usageValues).commandCreateIosLanguage, 'objc');
}));
}),
overrides: <Type, Generator>{
Java: () => FakeJava(),
});
testUsingContext('set Android host language type as usage value', () => testbed.run(() async {
final CreateCommand command = CreateCommand();
......@@ -178,7 +184,9 @@ void main() {
'testy',
]);
expect((await command.usageValues).commandCreateAndroidLanguage, 'java');
}));
}), overrides: <Type, Generator>{
Java: () => FakeJava(),
});
testUsingContext('create --offline', () => testbed.run(() async {
final CreateCommand command = CreateCommand();
......@@ -189,6 +197,7 @@ void main() {
expect(command.argParser.options.containsKey('offline'), true);
expect(command.shouldUpdateCache, true);
}, overrides: <Type, Generator>{
Java: () => null,
Pub: () => fakePub,
}));
......
......@@ -6,6 +6,7 @@ import 'package:args/command_runner.dart';
import 'package:flutter_tools/src/android/android_builder.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/java.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/build_info.dart';
......@@ -295,6 +296,7 @@ void main() {
},
overrides: <Type, Generator>{
FlutterProjectFactory: () => FakeFlutterProjectFactory(tempDir),
Java: () => null,
ProcessManager: () => processManager,
FeatureFlags: () => TestFeatureFlags(isIOSEnabled: false),
AndroidStudio: () => FakeAndroidStudio(),
......
......@@ -6,6 +6,7 @@ import 'package:args/command_runner.dart';
import 'package:flutter_tools/src/android/android_builder.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/java.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';
......@@ -143,6 +144,7 @@ void main() {
},
overrides: <Type, Generator>{
AndroidSdk: () => null,
Java: () => null,
FlutterProjectFactory: () => FakeFlutterProjectFactory(tempDir),
ProcessManager: () => processManager,
AndroidStudio: () => FakeAndroidStudio(),
......@@ -174,6 +176,7 @@ void main() {
},
overrides: <Type, Generator>{
AndroidSdk: () => mockAndroidSdk,
Java: () => null,
FlutterProjectFactory: () => FakeFlutterProjectFactory(tempDir),
ProcessManager: () => processManager,
AndroidStudio: () => FakeAndroidStudio(),
......@@ -205,6 +208,7 @@ void main() {
},
overrides: <Type, Generator>{
AndroidSdk: () => mockAndroidSdk,
Java: () => null,
FlutterProjectFactory: () => FakeFlutterProjectFactory(tempDir),
ProcessManager: () => processManager,
AndroidStudio: () => FakeAndroidStudio(),
......@@ -236,6 +240,7 @@ void main() {
},
overrides: <Type, Generator>{
AndroidSdk: () => mockAndroidSdk,
Java: () => null,
FlutterProjectFactory: () => FakeFlutterProjectFactory(tempDir),
ProcessManager: () => processManager,
AndroidStudio: () => FakeAndroidStudio(),
......@@ -269,6 +274,7 @@ void main() {
},
overrides: <Type, Generator>{
AndroidSdk: () => mockAndroidSdk,
Java: () => null,
FlutterProjectFactory: () => FakeFlutterProjectFactory(tempDir),
ProcessManager: () => processManager,
AndroidStudio: () => FakeAndroidStudio(),
......@@ -320,6 +326,7 @@ void main() {
},
overrides: <Type, Generator>{
AndroidSdk: () => mockAndroidSdk,
Java: () => null,
FlutterProjectFactory: () => FakeFlutterProjectFactory(tempDir),
ProcessManager: () => processManager,
Usage: () => testUsage,
......@@ -374,6 +381,7 @@ void main() {
overrides: <Type, Generator>{
AndroidSdk: () => mockAndroidSdk,
FlutterProjectFactory: () => FakeFlutterProjectFactory(tempDir),
Java: () => null,
ProcessManager: () => processManager,
Usage: () => testUsage,
AndroidStudio: () => FakeAndroidStudio(),
......@@ -420,6 +428,7 @@ void main() {
overrides: <Type, Generator>{
AndroidSdk: () => mockAndroidSdk,
FlutterProjectFactory: () => FakeFlutterProjectFactory(tempDir),
Java: () => null,
ProcessManager: () => processManager,
Usage: () => testUsage,
AndroidStudio: () => FakeAndroidStudio(),
......
......@@ -8,18 +8,22 @@ import 'dart:io' as io;
import 'package:args/command_runner.dart';
import 'package:file_testing/file_testing.dart';
import 'package:flutter_tools/src/android/gradle_utils.dart' show templateAndroidGradlePluginVersion, templateAndroidGradlePluginVersionForModule, templateDefaultGradleVersion;
import 'package:flutter_tools/src/android/java.dart';
import 'package:flutter_tools/src/artifacts.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/net.dart';
import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/base/version.dart' as software;
import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/commands/create.dart';
import 'package:flutter_tools/src/commands/create_base.dart';
import 'package:flutter_tools/src/dart/pub.dart';
import 'package:flutter_tools/src/features.dart';
import 'package:flutter_tools/src/flutter_project_metadata.dart' show FlutterProjectType;
import 'package:flutter_tools/src/globals.dart' as globals;
import 'package:flutter_tools/src/project.dart';
import 'package:flutter_tools/src/version.dart';
......@@ -40,6 +44,8 @@ const String _kNoPlatformsMessage = "You've created a plugin project that doesn'
const String frameworkRevision = '12345678';
const String frameworkChannel = 'omega';
const String _kDisabledPlatformRequestedMessage = 'currently not supported on your local environment.';
const String _kIncompatibleJavaVersionMessage = 'The configured version of Java detected may conflict with the';
final String _kIncompatibleAgpVersionForModule = Version.parse(templateAndroidGradlePluginVersion) < Version.parse(templateAndroidGradlePluginVersionForModule) ? templateAndroidGradlePluginVersionForModule : templateAndroidGradlePluginVersion;
// This needs to be created from the local platform due to re-entrant flutter calls made in this test.
FakePlatform _kNoColorTerminalPlatform() => FakePlatform.fromPlatform(const LocalPlatform())..stdoutSupportsAnsi = false;
......@@ -1417,6 +1423,7 @@ void main() {
expect(xcodeProject, contains('DEVELOPMENT_TEAM = 3333CCCC33;'));
}, overrides: <Type, Generator>{
FlutterVersion: () => fakeFlutterVersion,
Java: () => null,
Platform: _kNoColorTerminalMacOSPlatform,
ProcessManager: () => fakeProcessManager,
});
......@@ -3403,6 +3410,228 @@ void main() {
Logger: () => logger,
});
testUsingContext('should not show warning for incompatible Java/template Gradle versions when Java version not found', () async {
Cache.flutterRoot = '../..';
final CreateCommand command = CreateCommand();
final CommandRunner<void> runner = createTestCommandRunner(command);
await runner.run(<String>['create', '--no-pub', '--platforms=android', projectDir.path]);
expect(logger.warningText, isNot(contains(_kIncompatibleJavaVersionMessage)));
}, overrides: <Type, Generator>{
Java: () => null,
Logger: () => logger,
});
testUsingContext('should not show warning for incompatible Java/template Gradle versions when created project type is irrelevant', () async {
Cache.flutterRoot = '../..';
final CreateCommand command = CreateCommand();
final CommandRunner<void> runner = createTestCommandRunner(command);
// Test not creating a project for Android.
await runner.run(<String>['create', '--no-pub', '--platforms=ios,windows,macos,linux', projectDir.path]);
tryToDelete(projectDir);
// Test creating a package (Dart-only code).
await runner.run(<String>['create', '--no-pub', '--template=package', projectDir.path]);
tryToDelete(projectDir);
// Test creating project types without configured Gradle versions.
await runner.run(<String>['create', '--no-pub', '--template=plugin', projectDir.path]);
tryToDelete(projectDir);
await runner.run(<String>['create', '--no-pub', '--template=plugin_ffi', projectDir.path]);
expect(logger.warningText, isNot(contains(getIncompatibleJavaGradleAgpMessageHeader(false, templateDefaultGradleVersion, templateAndroidGradlePluginVersion, 'app'))));
expect(logger.warningText, isNot(contains(getIncompatibleJavaGradleAgpMessageHeader(false, templateDefaultGradleVersion, templateAndroidGradlePluginVersion, 'package'))));
expect(logger.warningText, isNot(contains(getIncompatibleJavaGradleAgpMessageHeader(false, templateDefaultGradleVersion, templateAndroidGradlePluginVersion, 'plugin'))));
expect(logger.warningText, isNot(contains(getIncompatibleJavaGradleAgpMessageHeader(false, templateDefaultGradleVersion, templateAndroidGradlePluginVersion, 'pluginFfi'))));
}, overrides: <Type, Generator>{
Java: () => FakeJava(version: const software.Version.withText(1000, 0, 0, '1000.0.0')), // Too high a version for template Gradle versions.
Logger: () => logger,
});
testUsingContext('should not show warning for incompatible Java/template AGP versions when project type unrelated', () async {
Cache.flutterRoot = '../..';
final CreateCommand command = CreateCommand();
final CommandRunner<void> runner = createTestCommandRunner(command);
// Test not creating a project for Android.
await runner.run(<String>['create', '--no-pub', '--platforms=ios,windows,macos,linux', projectDir.path]);
tryToDelete(projectDir);
// Test creating a package (Dart-only code).
await runner.run(<String>['create', '--no-pub', '--template=package', projectDir.path]);
expect(logger.warningText, isNot(contains(getIncompatibleJavaGradleAgpMessageHeader(false, templateDefaultGradleVersion, templateAndroidGradlePluginVersion, 'app'))));
expect(logger.warningText, isNot(contains(getIncompatibleJavaGradleAgpMessageHeader(false, templateDefaultGradleVersion, templateAndroidGradlePluginVersion, 'package'))));
}, overrides: <Type, Generator>{
Java: () => FakeJava(version: const software.Version.withText(0, 0, 0, '0.0.0')), // Too low a version for template AGP versions.
Logger: () => logger,
});
testUsingContext('should show warning for incompatible Java/template Gradle versions when detected', () async {
Cache.flutterRoot = '../..';
final CreateCommand command = CreateCommand();
final CommandRunner<void> runner = createTestCommandRunner(command);
final List<FlutterProjectType> relevantProjectTypes = <FlutterProjectType>[FlutterProjectType.app, FlutterProjectType.skeleton, FlutterProjectType.module];
for (final FlutterProjectType projectType in relevantProjectTypes) {
final String relevantAgpVersion = projectType == FlutterProjectType.module ? _kIncompatibleAgpVersionForModule : templateAndroidGradlePluginVersion;
final String expectedMessage = getIncompatibleJavaGradleAgpMessageHeader(false, templateDefaultGradleVersion, relevantAgpVersion, projectType.cliName);
final String unexpectedMessage = getIncompatibleJavaGradleAgpMessageHeader(true, templateDefaultGradleVersion, relevantAgpVersion, projectType.cliName);
await runner.run(<String>['create', '--no-pub', '--template=${projectType.cliName}', if (projectType != FlutterProjectType.module) '--platforms=android', projectDir.path]);
// Check components of expected header warning message are printed.
expect(logger.warningText, contains(expectedMessage));
expect(logger.warningText, isNot(contains(unexpectedMessage)));
expect(logger.warningText, contains('./gradlew wrapper --gradle-version=<COMPATIBLE_GRADLE_VERSION>'));
expect(logger.warningText, contains('https://docs.gradle.org/current/userguide/compatibility.html#java'));
// Check expected file for updating Gradle version is present.
if (projectType == FlutterProjectType.app || projectType == FlutterProjectType.skeleton) {
expect(logger.warningText, contains(globals.fs.path.join(projectDir.path, 'android/gradle/wrapper/gradle-wrapper.properties')));
}
else {
// Project type is module.
expect(logger.warningText, contains(globals.fs.path.join(projectDir.path, '.android/gradle/wrapper/gradle-wrapper.properties')));
}
// Cleanup to reuse projectDir and logger checks.
tryToDelete(projectDir);
logger.clear();
}
}, overrides: <Type, Generator>{
Java: () => FakeJava(version: const software.Version.withText(500, 0, 0, '500.0.0')), // Too high a version for template Gradle versions.
Logger: () => logger,
});
testUsingContext('should show warning for incompatible Java/template AGP versions when detected', () async {
Cache.flutterRoot = '../..';
final CreateCommand command = CreateCommand();
final CommandRunner<void> runner = createTestCommandRunner(command);
final List<FlutterProjectType> relevantProjectTypes = <FlutterProjectType>[FlutterProjectType.app, FlutterProjectType.skeleton, FlutterProjectType.pluginFfi, FlutterProjectType.module, FlutterProjectType.plugin];
for (final FlutterProjectType projectType in relevantProjectTypes) {
final String relevantAgpVersion = projectType == FlutterProjectType.module ? _kIncompatibleAgpVersionForModule : templateAndroidGradlePluginVersion;
final String expectedMessage = getIncompatibleJavaGradleAgpMessageHeader(true, templateDefaultGradleVersion, relevantAgpVersion, projectType.cliName);
final String unexpectedMessage = getIncompatibleJavaGradleAgpMessageHeader(false, templateDefaultGradleVersion, relevantAgpVersion, projectType.cliName);
await runner.run(<String>['create', '--no-pub', '--template=${projectType.cliName}', if (projectType != FlutterProjectType.module) '--platforms=android', projectDir.path]);
// Check components of expected header warning message are printed.
expect(logger.warningText, contains(expectedMessage));
expect(logger.warningText, isNot(contains(unexpectedMessage)));
expect(logger.warningText, contains('https://developer.android.com/build/releases/gradle-plugin'));
// Check expected file(s) for updating AGP version is/are present.
if (projectType == FlutterProjectType.app || projectType == FlutterProjectType.skeleton || projectType == FlutterProjectType.pluginFfi) {
expect(logger.warningText, contains(globals.fs.path.join(projectDir.path, 'android/build.gradle')));
}
else if (projectType == FlutterProjectType.plugin) {
expect(logger.warningText, contains(globals.fs.path.join(projectDir.path, 'android/app/build.gradle')));
}
else {
// Project type is module.
expect(logger.warningText, contains(globals.fs.path.join(projectDir.path, '.android/build.gradle')));
expect(logger.warningText, contains(globals.fs.path.join(projectDir.path, '.android/app/build.gradle')));
expect(logger.warningText, contains(globals.fs.path.join(projectDir.path, '.android/Flutter/build.gradle')));
}
// Cleanup to reuse projectDir and logger checks.
tryToDelete(projectDir);
logger.clear();
}
}, overrides: <Type, Generator>{
Java: () => FakeJava(version: const software.Version.withText(1, 8, 0, '1.8.0')), // Too low a version for template AGP versions.
Logger: () => logger,
});
// The Java versions configured in the following tests will need updates as more Java versions are supported by AGP/Gradle:
testUsingContext('should not show warning for incompatible Java/template AGP/Gradle versions when not detected', () async {
Cache.flutterRoot = '../..';
final CreateCommand command = CreateCommand();
final CommandRunner<void> runner = createTestCommandRunner(command);
final List<FlutterProjectType> relevantProjectTypes = <FlutterProjectType>[FlutterProjectType.app, FlutterProjectType.skeleton, FlutterProjectType.pluginFfi, FlutterProjectType.module, FlutterProjectType.plugin];
for (final FlutterProjectType projectType in relevantProjectTypes) {
final String relevantAgpVersion = projectType == FlutterProjectType.module ? _kIncompatibleAgpVersionForModule : templateAndroidGradlePluginVersion;
final String unexpectedIncompatibleAgpMessage = getIncompatibleJavaGradleAgpMessageHeader(true, templateDefaultGradleVersion, relevantAgpVersion, projectType.cliName);
final String unexpectedIncompatibleGradleMessage = getIncompatibleJavaGradleAgpMessageHeader(false, templateDefaultGradleVersion, relevantAgpVersion, projectType.cliName);
await runner.run(<String>['create', '--no-pub', '--template=${projectType.cliName}', if (projectType != FlutterProjectType.module) '--platforms=android', projectDir.path]);
// We do not expect warnings for incompatible Java/template AGP versions if they are in fact, compatible.
expect(logger.warningText, isNot(contains(unexpectedIncompatibleAgpMessage)));
expect(logger.warningText, isNot(contains(unexpectedIncompatibleGradleMessage)));
// Cleanup to reuse projectDir and logger checks.
tryToDelete(projectDir);
logger.clear();
}
}, overrides: <Type, Generator>{
Java: () => FakeJava(version: const software.Version.withText(14, 0, 0, '14.0.0')), // Middle compatible Java version with current template AGP/Gradle versions.
Logger: () => logger,
});
testUsingContext('should not show warning for incompatible Java/template AGP/Gradle versions when not detected -- maximum compatible Java version', () async {
Cache.flutterRoot = '../..';
final CreateCommand command = CreateCommand();
final CommandRunner<void> runner = createTestCommandRunner(command);
final List<FlutterProjectType> relevantProjectTypes = <FlutterProjectType>[FlutterProjectType.app, FlutterProjectType.skeleton, FlutterProjectType.pluginFfi, FlutterProjectType.module, FlutterProjectType.plugin];
for (final FlutterProjectType projectType in relevantProjectTypes) {
final String relevantAgpVersion = projectType == FlutterProjectType.module ? _kIncompatibleAgpVersionForModule : templateAndroidGradlePluginVersion;
final String unexpectedIncompatibleAgpMessage = getIncompatibleJavaGradleAgpMessageHeader(true, templateDefaultGradleVersion, relevantAgpVersion, projectType.cliName);
final String unexpectedIncompatibleGradleMessage = getIncompatibleJavaGradleAgpMessageHeader(false, templateDefaultGradleVersion, relevantAgpVersion, projectType.cliName);
await runner.run(<String>['create', '--no-pub', '--template=${projectType.cliName}', if (projectType != FlutterProjectType.module) '--platforms=android', projectDir.path]);
// We do not expect warnings for incompatible Java/template AGP versions if they are in fact, compatible.
expect(logger.warningText, isNot(contains(unexpectedIncompatibleAgpMessage)));
expect(logger.warningText, isNot(contains(unexpectedIncompatibleGradleMessage)));
// Cleanup to reuse projectDir and logger checks.
tryToDelete(projectDir);
logger.clear();
}
}, overrides: <Type, Generator>{
Java: () => FakeJava(version: const software.Version.withText(17, 0, 0, '18.0.0')), // Maximum compatible Java version with current template AGP/Gradle versions.
Logger: () => logger,
});
testUsingContext('should not show warning for incompatible Java/template AGP/Gradle versions when not detected -- minimum compatible Java version', () async {
Cache.flutterRoot = '../..';
final CreateCommand command = CreateCommand();
final CommandRunner<void> runner = createTestCommandRunner(command);
final List<FlutterProjectType> relevantProjectTypes = <FlutterProjectType>[FlutterProjectType.app, FlutterProjectType.skeleton, FlutterProjectType.pluginFfi, FlutterProjectType.module, FlutterProjectType.plugin];
for (final FlutterProjectType projectType in relevantProjectTypes) {
final String relevantAgpVersion = projectType == FlutterProjectType.module ? _kIncompatibleAgpVersionForModule : templateAndroidGradlePluginVersion;
final String unexpectedIncompatibleAgpMessage = getIncompatibleJavaGradleAgpMessageHeader(true, templateDefaultGradleVersion, relevantAgpVersion, projectType.cliName);
final String unexpectedIncompatibleGradleMessage = getIncompatibleJavaGradleAgpMessageHeader(false, templateDefaultGradleVersion, relevantAgpVersion, projectType.cliName);
await runner.run(<String>['create', '--no-pub', '--template=${projectType.cliName}', if (projectType != FlutterProjectType.module) '--platforms=android', projectDir.path]);
// We do not expect warnings for incompatible Java/template AGP versions if they are in fact, compatible.
expect(logger.warningText, isNot(contains(unexpectedIncompatibleAgpMessage)));
expect(logger.warningText, isNot(contains(unexpectedIncompatibleGradleMessage)));
// Cleanup to reuse projectDir and logger checks.
tryToDelete(projectDir);
logger.clear();
}
}, overrides: <Type, Generator>{
Java: () => FakeJava(version: const software.Version.withText(11, 0, 0, '11.0.0')), // Minimum compatible Java version with current template AGP/Gradle versions.
Logger: () => logger,
});
testUsingContext('Does not double quote description in index.html on web', () async {
await _createProject(
projectDir,
......
......@@ -7,6 +7,7 @@ import 'package:flutter_tools/src/android/gradle_utils.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/base/version_range.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/project.dart';
import '../../src/common.dart';
......@@ -470,16 +471,20 @@ allprojects {
group('validates gradle/agp versions', () {
final List<GradleAgpTestData> testData = <GradleAgpTestData>[
// Values too new *these need to update* when
// Values too new *these need to be updated* when
// max known gradle and max known agp versions are updated:
// Newer tools version supports max gradle version.
GradleAgpTestData(true, agpVersion: '8.2', gradleVersion: '8.0'),
// Newer tools version does not even meet current gradle version requiremnts.
// Newer tools version does not even meet current gradle version requirements.
GradleAgpTestData(false, agpVersion: '8.2', gradleVersion: '7.3'),
// Newer tools version requires newer gradle version.
GradleAgpTestData(true, agpVersion: '8.3', gradleVersion: '8.1'),
// Minimims as defined in
// Template versions of Gradle/AGP.
GradleAgpTestData(true, agpVersion: templateAndroidGradlePluginVersion, gradleVersion: templateDefaultGradleVersion),
GradleAgpTestData(true, agpVersion: templateAndroidGradlePluginVersionForModule, gradleVersion: templateDefaultGradleVersion),
// Minimums as defined in
// https://developer.android.com/studio/releases/gradle-plugin#updating-gradle
GradleAgpTestData(true, agpVersion: '8.1', gradleVersion: '8.0'),
GradleAgpTestData(true, agpVersion: '8.0', gradleVersion: '8.0'),
......@@ -577,16 +582,15 @@ allprojects {
group('validates java/gradle versions', () {
final List<JavaGradleTestData> testData = <JavaGradleTestData>[
// Values too new *these need to update* when
// Values too new *these need to be updated* when
// max supported java and max known gradle versions are updated:
// Newer tools version does not even meet current gradle version requiremnts.
JavaGradleTestData(false, javaVersion: '20', gradleVersion: '7.5'),
// Newer tools version requires newer gradle version.
JavaGradleTestData(true, javaVersion: '20', gradleVersion: '8.1'),
// Max known unsupported java version.
JavaGradleTestData(true, javaVersion: '24', gradleVersion: '8.1'),
// Minimims as defined in
// Max known unsupported Java version.
JavaGradleTestData(true, javaVersion: '24', gradleVersion: maxKnownAndSupportedGradleVersion),
// Minimums as defined in
// https://docs.gradle.org/current/userguide/compatibility.html#java
JavaGradleTestData(true, javaVersion: '19', gradleVersion: '7.6'),
JavaGradleTestData(true, javaVersion: '18', gradleVersion: '7.5'),
......@@ -600,7 +604,7 @@ allprojects {
JavaGradleTestData(true, javaVersion: '1.10', gradleVersion: '4.7'),
JavaGradleTestData(true, javaVersion: '1.9', gradleVersion: '4.3'),
JavaGradleTestData(true, javaVersion: '1.8', gradleVersion: '2.0'),
// Gradle too old for java version.
// Gradle too old for Java version.
JavaGradleTestData(false, javaVersion: '19', gradleVersion: '6.7'),
JavaGradleTestData(false, javaVersion: '11', gradleVersion: '4.10.1'),
JavaGradleTestData(false, javaVersion: '1.9', gradleVersion: '4.1'),
......@@ -642,7 +646,7 @@ allprojects {
testWithoutContext(
'(Java, gradle): (${data.javaVersion}, ${data.gradleVersion})', () {
expect(
validateJavaGradle(
validateJavaAndGradle(
BufferLogger.test(),
javaV: data.javaVersion,
gradleV: data.gradleVersion,
......@@ -653,6 +657,314 @@ allprojects {
}
});
});
group('validates java/AGP versions', () {
final List<JavaAgpTestData> testData = <JavaAgpTestData>[
// Strictly too old Java versions for known AGP versions.
JavaAgpTestData(false, javaVersion: '1.6', agpVersion: maxKnownAgpVersion),
JavaAgpTestData(false, javaVersion: '1.6', agpVersion: maxKnownAndSupportedAgpVersion),
JavaAgpTestData(false, javaVersion: '1.6', agpVersion: '4.2'),
// Strictly too old AGP versions.
JavaAgpTestData(false, javaVersion: '1.8', agpVersion: '1.0'),
JavaAgpTestData(false, javaVersion: '1.8', agpVersion: '4.1'),
JavaAgpTestData(false, javaVersion: '1.8', agpVersion: '2.3'),
// Strictly too new Java versions for defined AGP versions.
JavaAgpTestData(true, javaVersion: '18', agpVersion: '8.1'),
JavaAgpTestData(true, javaVersion: '18', agpVersion: '7.4'),
JavaAgpTestData(true, javaVersion: '18', agpVersion: '4.2'),
// Strictly too new AGP versions.
// *The tests that follow need to be updated* when max supported AGP versions are updated:
JavaAgpTestData(false, javaVersion: '24', agpVersion: '8.3'),
JavaAgpTestData(false, javaVersion: '20', agpVersion: '8.3'),
JavaAgpTestData(false, javaVersion: '17', agpVersion: '8.3'),
// Java 17 & patch versions compatibility cases
// *The tests that follow need to be updated* when maxKnownAndSupportedAgpVersion is
// updated:
JavaAgpTestData(false, javaVersion: '17', agpVersion: '8.2'),
JavaAgpTestData(true, javaVersion: '17', agpVersion: maxKnownAndSupportedAgpVersion),
JavaAgpTestData(true, javaVersion: '17', agpVersion: '8.1'),
JavaAgpTestData(true, javaVersion: '17', agpVersion: '8.0'),
JavaAgpTestData(true, javaVersion: '17', agpVersion: '7.4'),
JavaAgpTestData(false, javaVersion: '17.0.3', agpVersion: '8.2'),
JavaAgpTestData(true, javaVersion: '17.0.3', agpVersion: maxKnownAndSupportedAgpVersion),
JavaAgpTestData(true, javaVersion: '17.0.3', agpVersion: '8.1'),
JavaAgpTestData(true, javaVersion: '17.0.3', agpVersion: '8.0'),
JavaAgpTestData(true, javaVersion: '17.0.3', agpVersion: '7.4'),
// Java 11 & patch versions compatibility cases
JavaAgpTestData(false, javaVersion: '11', agpVersion: '8.0'),
JavaAgpTestData(true, javaVersion: '11', agpVersion: '7.4'),
JavaAgpTestData(true, javaVersion: '11', agpVersion: '7.2'),
JavaAgpTestData(true, javaVersion: '11', agpVersion: '7.0'),
JavaAgpTestData(true, javaVersion: '11', agpVersion: '4.2'),
JavaAgpTestData(false, javaVersion: '11.0.18', agpVersion: '8.0'),
JavaAgpTestData(true, javaVersion: '11.0.18', agpVersion: '7.4'),
JavaAgpTestData(true, javaVersion: '11.0.18', agpVersion: '7.2'),
JavaAgpTestData(true, javaVersion: '11.0.18', agpVersion: '7.0'),
JavaAgpTestData(true, javaVersion: '11.0.18', agpVersion: '4.2'),
// Java 8 compatibility cases
JavaAgpTestData(false, javaVersion: '1.8', agpVersion: '7.0'),
JavaAgpTestData(true, javaVersion: '1.8', agpVersion: oldestDocumentedJavaAgpCompatibilityVersion), // agpVersion = 4.2
JavaAgpTestData(false, javaVersion: '1.8', agpVersion: '4.1'),
// Null value cases
// ignore: avoid_redundant_argument_values
JavaAgpTestData(false, javaVersion: null, agpVersion: '4.2'),
// ignore: avoid_redundant_argument_values
JavaAgpTestData(false, javaVersion: '1.8', agpVersion: null),
// ignore: avoid_redundant_argument_values
JavaAgpTestData(false, javaVersion: null, agpVersion: null),
];
for (final JavaAgpTestData data in testData) {
testWithoutContext(
'(Java, agp): (${data.javaVersion}, ${data.agpVersion})', () {
expect(
validateJavaAndAgp(
BufferLogger.test(),
javaV: data.javaVersion,
agpV: data.agpVersion,
),
data.validPair ? isTrue : isFalse,
reason: 'J: ${data.javaVersion}, G: ${data.agpVersion}');
});
}
});
group('detecting valid Gradle/AGP versions for given Java version and vice versa', () {
testWithoutContext('getValidGradleVersionRangeForJavaVersion returns valid Gradle version range for Java version', () {
final Logger testLogger = BufferLogger.test();
// Java version too high.
expect(getValidGradleVersionRangeForJavaVersion(testLogger, javaV: oneMajorVersionHigherJavaVersion), isNull);
// Maximum known Java version.
// *The test case that follows needs to be updated* when higher versions of Java are supported:
expect(
getValidGradleVersionRangeForJavaVersion(testLogger, javaV: '20'),
allOf(
equals(getValidGradleVersionRangeForJavaVersion(testLogger, javaV: '20.0.2')),
isNull));
// Known supported Java versions.
expect(
getValidGradleVersionRangeForJavaVersion(testLogger, javaV: '19'),
allOf(
equals(getValidGradleVersionRangeForJavaVersion(testLogger, javaV: '19.0.2')),
equals(
const JavaGradleCompat(
javaMin: '19',
javaMax: '20',
gradleMin: '7.6',
gradleMax: maxKnownAndSupportedGradleVersion))));
expect(
getValidGradleVersionRangeForJavaVersion(testLogger, javaV: '18'),
allOf(
equals(getValidGradleVersionRangeForJavaVersion(testLogger, javaV: '18.0.2')),
equals(
const JavaGradleCompat(
javaMin: '18',
javaMax: '19',
gradleMin: '7.5',
gradleMax: maxKnownAndSupportedGradleVersion))));
expect(
getValidGradleVersionRangeForJavaVersion(testLogger, javaV: '17'),
allOf(
equals(getValidGradleVersionRangeForJavaVersion(testLogger, javaV: '17.0.2')),
equals(
const JavaGradleCompat(
javaMin: '17',
javaMax: '18',
gradleMin: '7.3',
gradleMax: maxKnownAndSupportedGradleVersion))));
expect(
getValidGradleVersionRangeForJavaVersion(testLogger, javaV: '16'),
allOf(
equals(getValidGradleVersionRangeForJavaVersion(testLogger, javaV: '16.0.2')),
equals(
const JavaGradleCompat(
javaMin: '16',
javaMax: '17',
gradleMin: '7.0',
gradleMax: maxKnownAndSupportedGradleVersion))));
expect(
getValidGradleVersionRangeForJavaVersion(testLogger, javaV: '15'),
allOf(
equals(getValidGradleVersionRangeForJavaVersion(testLogger, javaV: '15.0.2')),
equals(
const JavaGradleCompat(
javaMin: '15',
javaMax: '16',
gradleMin: '6.7',
gradleMax: maxKnownAndSupportedGradleVersion))));
expect(
getValidGradleVersionRangeForJavaVersion(testLogger, javaV: '14'),
allOf(
equals(getValidGradleVersionRangeForJavaVersion(testLogger, javaV: '14.0.2')),
equals(
const JavaGradleCompat(
javaMin: '14',
javaMax: '15',
gradleMin: '6.3',
gradleMax: maxKnownAndSupportedGradleVersion))));
expect(
getValidGradleVersionRangeForJavaVersion(testLogger, javaV: '13'),
allOf(
equals(getValidGradleVersionRangeForJavaVersion(testLogger, javaV: '13.0.2')),
equals(
const JavaGradleCompat(
javaMin: '13',
javaMax: '14',
gradleMin: '6.0',
gradleMax: maxKnownAndSupportedGradleVersion))));
expect(
getValidGradleVersionRangeForJavaVersion(testLogger, javaV: '12'),
allOf(
equals(getValidGradleVersionRangeForJavaVersion(testLogger, javaV: '12.0.2')),
equals(
const JavaGradleCompat(
javaMin: '12',
javaMax: '13',
gradleMin: '5.4',
gradleMax: maxKnownAndSupportedGradleVersion))));
expect(
getValidGradleVersionRangeForJavaVersion(testLogger, javaV: '11'),
allOf(
equals(getValidGradleVersionRangeForJavaVersion(testLogger, javaV: '11.0.2')),
equals(
const JavaGradleCompat(
javaMin: '11',
javaMax: '12',
gradleMin: '5.0',
gradleMax: maxKnownAndSupportedGradleVersion))));
expect(
getValidGradleVersionRangeForJavaVersion(testLogger, javaV: '1.10'),
allOf(
equals(getValidGradleVersionRangeForJavaVersion(testLogger, javaV: '1.10.2')),
equals(
const JavaGradleCompat(
javaMin: '1.10',
javaMax: '1.11',
gradleMin: '4.7',
gradleMax: maxKnownAndSupportedGradleVersion))));
expect(
getValidGradleVersionRangeForJavaVersion(testLogger, javaV: '1.9'),
allOf(
equals(getValidGradleVersionRangeForJavaVersion(testLogger, javaV: '1.9.2')),
equals(
const JavaGradleCompat(
javaMin: '1.9',
javaMax: '1.10',
gradleMin: '4.3',
gradleMax: maxKnownAndSupportedGradleVersion))));
// Java 1.8 -- return oldest documented compatibility info
expect(
getValidGradleVersionRangeForJavaVersion(testLogger, javaV: '1.8'),
allOf(
equals(getValidGradleVersionRangeForJavaVersion(testLogger, javaV: '1.8.2')),
equals(
const JavaGradleCompat(
javaMin: '1.8',
javaMax: '1.9',
gradleMin: '2.0',
gradleMax: maxKnownAndSupportedGradleVersion))));
// Java version too low.
expect(
getValidGradleVersionRangeForJavaVersion(testLogger, javaV: '1.7'),
allOf(
equals(getValidGradleVersionRangeForJavaVersion(testLogger, javaV: '1.7.2')),
isNull));
});
testWithoutContext('getMinimumAgpVersionForJavaVersion returns minimum AGP version for Java version', () {
final Logger testLogger = BufferLogger.test();
// Maximum known Java version.
// *The test case that follows needs to be updated* as higher versions of AGP are supported:
expect(
getMinimumAgpVersionForJavaVersion(testLogger, javaV: oneMajorVersionHigherJavaVersion),
equals(
const JavaAgpCompat(
javaMin: '17',
javaDefault: '17',
agpMin: '8.0',
agpMax: '8.1')));
// Known Java versions.
expect(
getMinimumAgpVersionForJavaVersion(testLogger, javaV: '17'),
allOf(
equals(getMinimumAgpVersionForJavaVersion(testLogger, javaV: '17.0.2')),
equals(
const JavaAgpCompat(
javaMin: '17',
javaDefault: '17',
agpMin: '8.0',
agpMax: '8.1'))));
expect(
getMinimumAgpVersionForJavaVersion(testLogger, javaV: '15'),
allOf(
equals(getMinimumAgpVersionForJavaVersion(testLogger, javaV: '15.0.2')),
equals(
const JavaAgpCompat(
javaMin: '11',
javaDefault: '11',
agpMin: '7.0',
agpMax: '7.4'))));
expect(
getMinimumAgpVersionForJavaVersion(testLogger, javaV: '11'),
allOf(
equals(getMinimumAgpVersionForJavaVersion(testLogger, javaV: '11.0.2')),
equals(
const JavaAgpCompat(
javaMin: '11',
javaDefault: '11',
agpMin: '7.0',
agpMax: '7.4'))));
expect(
getMinimumAgpVersionForJavaVersion(testLogger, javaV: '1.9'),
allOf(
equals(getMinimumAgpVersionForJavaVersion(testLogger, javaV: '1.9.2')),
equals(
const JavaAgpCompat(
javaMin: '1.8',
javaDefault: '1.8',
agpMin: '4.2',
agpMax: '4.2'))));
expect(
getMinimumAgpVersionForJavaVersion(testLogger, javaV: '1.8'),
allOf(
equals(getMinimumAgpVersionForJavaVersion(testLogger, javaV: '1.8.2')),
equals(
const JavaAgpCompat(
javaMin: '1.8',
javaDefault: '1.8',
agpMin: '4.2',
agpMax: '4.2'))));
// Java version too low.
expect(
getMinimumAgpVersionForJavaVersion(testLogger, javaV: '1.7'),
allOf(
equals(getMinimumAgpVersionForJavaVersion(testLogger, javaV: '1.7.2')),
isNull));
});
testWithoutContext('getJavaVersionFor returns expected Java version range', () {
// Strictly too old Gradle and AGP versions.
expect(getJavaVersionFor(gradleV: '1.9', agpV: '4.1'), equals(const VersionRange(null, null)));
// Strictly too old Gradle or AGP version.
expect(getJavaVersionFor(gradleV: '1.9', agpV: '4.2'), equals(const VersionRange('1.8', null)));
expect(getJavaVersionFor(gradleV: '2.0', agpV: '4.1'), equals(const VersionRange(null, '1.9')));
// Strictly too new Gradle and AGP versions.
expect(getJavaVersionFor(gradleV: '8.1', agpV: '8.2'), equals(const VersionRange(null, null)));
// Strictly too new Gradle version and maximum version of AGP.
//*This test case will need its expected Java range updated when a new version of AGP is supported.*
expect(getJavaVersionFor(gradleV: '8.1', agpV: maxKnownAndSupportedAgpVersion), equals(const VersionRange('17', null)));
// Strictly too new AGP version and maximum version of Gradle.
//*This test case will need its expected Java range updated when a new version of Gradle is supported.*
expect(getJavaVersionFor(gradleV: maxKnownAndSupportedGradleVersion, agpV: '8.2'), equals(const VersionRange(null, '20')));
// Tests with a known compatible Gradle/AGP version pair.
expect(getJavaVersionFor(gradleV: '7.0', agpV: '7.2'), equals(const VersionRange('11', '17')));
expect(getJavaVersionFor(gradleV: '7.1', agpV: '7.2'), equals(const VersionRange('11', '17')));
expect(getJavaVersionFor(gradleV: '7.2.2', agpV: '7.2'), equals(const VersionRange('11', '17')));
expect(getJavaVersionFor(gradleV: '7.1', agpV: '7.0'), equals(const VersionRange('11', '17')));
expect(getJavaVersionFor(gradleV: '7.1', agpV: '7.2'), equals(const VersionRange('11', '17')));
expect(getJavaVersionFor(gradleV: '7.1', agpV: '7.4'), equals(const VersionRange('11', '17')));
});
});
}
class GradleAgpTestData {
......@@ -669,6 +981,13 @@ class JavaGradleTestData {
final bool validPair;
}
class JavaAgpTestData {
JavaAgpTestData(this.validPair, {this.javaVersion, this.agpVersion});
final String? agpVersion;
final String? javaVersion;
final bool validPair;
}
final Platform windowsPlatform = FakePlatform(
operatingSystem: 'windows',
environment: <String, String>{
......
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