Skip to content

Commit 8ba086a

Browse files
authored
[Re-re-land] Enforce a policy on supported Gradle, Java, AGP, and KGP versions (#143341)
This is a direct revert of (the revert of (the reland of (the policy pr))): flutter/flutter#143132. The only change is: 1. to put a conditional all on one line, because the packages repository has a test that uses an old flutter project to make sure nothing regresses. The old project uses an old gradle version, and the old gradle version bundles an old groovy version, and the old groovy version has a bug where lines that start with `&&` don't always work: https://issues.apache.org/jira/browse/GROOVY-7218 (I enjoy that the revert reason ends up providing another strong justification to go forward with the policy). Also thanks to @reidbaker for pointing out this bug. 2. I also made a slight formatting change to the messages that print when out of the support bounds, which I think looks slightly better. I tested this with on a branch that included a revert of flutter/flutter#142008, and was able to recreate the failure and verify that it was resolved by 1).
1 parent 634fcc7 commit 8ba086a

File tree

11 files changed

+593
-4
lines changed

11 files changed

+593
-4
lines changed

dev/integration_tests/ui/android/app/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44

55
plugins {
66
id "com.android.application"
7-
id "dev.flutter.flutter-gradle-plugin"
87
id "kotlin-android"
8+
id "dev.flutter.flutter-gradle-plugin"
99
}
1010

1111
android {

packages/flutter_tools/gradle/build.gradle.kts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,9 @@ gradlePlugin {
2828

2929
dependencies {
3030
// When bumping, also update:
31-
// * ndkVersion in FlutterExtension in packages/flutter_tools/gradle/src/main/flutter.groovy
31+
// * ndkVersion in FlutterExtension in packages/flutter_tools/gradle/src/main/groovy/flutter.groovy
32+
// * AGP version in the buildscript block in packages/flutter_tools/gradle/src/main/groovy/flutter.groovy
33+
// * AGP version in the buildscript block in packages/flutter_tools/gradle/src/main/kotlin/dependency_version_checker.gradle.kts
3234
// * AGP version constants in packages/flutter_tools/lib/src/android/gradle_utils.dart
33-
// * AGP version in buildscript block in packages/flutter_tools/gradle/src/main/flutter.groovy
3435
compileOnly("com.android.tools.build:gradle:7.3.0")
3536
}

packages/flutter_tools/gradle/src/main/groovy/flutter.groovy

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,8 @@ buildscript {
113113
}
114114
dependencies {
115115
// When bumping, also update:
116-
// * ndkVersion in FlutterExtension in packages/flutter_tools/gradle/src/main/flutter.groovy
116+
// * ndkVersion in FlutterExtension in packages/flutter_tools/gradle/src/main/groovy/flutter.groovy
117+
// * AGP version in the buildscript block in packages/flutter_tools/gradle/src/main/kotlin/dependency_version_checker.gradle.kts
117118
// * AGP version constants in packages/flutter_tools/lib/src/android/gradle_utils.dart
118119
// * AGP version in dependencies block in packages/flutter_tools/gradle/build.gradle.kts
119120
classpath("com.android.tools.build:gradle:7.3.0")
@@ -321,6 +322,27 @@ class FlutterPlugin implements Plugin<Project> {
321322
String flutterExecutableName = Os.isFamily(Os.FAMILY_WINDOWS) ? "flutter.bat" : "flutter"
322323
flutterExecutable = Paths.get(flutterRoot.absolutePath, "bin", flutterExecutableName).toFile()
323324

325+
// Validate that the provided Gradle, Java, AGP, and KGP versions are all within our
326+
// supported range.
327+
// TODO(gmackall) Dependency version checking is currently implemented as an additional
328+
// Gradle plugin because we can't import it from Groovy code. As part of the Groovy
329+
// -> Kotlin migration, we should remove this complexity and perform the checks inside
330+
// of the main Flutter Gradle Plugin.
331+
// See https://github.com/flutter/flutter/issues/121541#issuecomment-1920363687.
332+
final Boolean shouldSkipDependencyChecks = project.hasProperty("skipDependencyChecks") && project.getProperty("skipDependencyChecks");
333+
if (!shouldSkipDependencyChecks) {
334+
try {
335+
final String dependencyCheckerPluginPath = Paths.get(flutterRoot.absolutePath,
336+
"packages", "flutter_tools", "gradle", "src", "main", "kotlin",
337+
"dependency_version_checker.gradle.kts")
338+
project.apply from: dependencyCheckerPluginPath
339+
} catch (Exception ignored) {
340+
project.logger.error("Warning: Flutter was unable to detect project Gradle, Java, " +
341+
"AGP, and KGP versions. Skipping dependency version checking. Error was: "
342+
+ ignored)
343+
}
344+
}
345+
324346
// Use Kotlin DSL to handle baseApplicationName logic due to Groovy dynamic dispatch bug.
325347
project.apply from: Paths.get(flutterRoot.absolutePath, "packages", "flutter_tools", "gradle", "src", "main", "kotlin", "flutter.gradle.kts")
326348

@@ -1515,6 +1537,8 @@ abstract class BaseFlutterTask extends DefaultTask {
15151537
@Optional @Input
15161538
Boolean validateDeferredComponents
15171539

1540+
@Optional @Input
1541+
Boolean skipDependencyChecks
15181542
@Optional @Input
15191543
String flavor
15201544

packages/flutter_tools/gradle/src/main/kotlin/dependency_version_checker.gradle.kts

Lines changed: 330 additions & 0 deletions
Large diffs are not rendered by default.

packages/flutter_tools/lib/src/android/README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@ version.
2929
SDK versions are updated (you should see these fail if you do not fix them
3030
preemptively).
3131

32+
Also, make sure to also update to the same version in the following places:
33+
- The version in the buildscript block in `packages/flutter_tools/gradle/src/main/groovy/flutter.groovy`.
34+
- The version in the buildscript block in `packages/flutter_tools/gradle/src/main/kotlin/dependency_version_checker.gradle.kts`.
35+
- The version in the dependencies block in `packages/flutter_tools/gradle/build.gradle.kts`.
36+
3237
#### Gradle
3338
When updating the Gradle version used in project templates
3439
(`templateDefaultGradleVersion`), make sure that:

packages/flutter_tools/lib/src/android/gradle.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,9 @@ class AndroidGradleBuilder implements AndroidBuilder {
370370
if (!buildInfo.androidGradleDaemon) {
371371
command.add('--no-daemon');
372372
}
373+
if (buildInfo.androidSkipBuildDependencyValidation) {
374+
command.add('-PskipDependencyChecks=true');
375+
}
373376
final LocalEngineInfo? localEngineInfo = _artifacts.localEngineInfo;
374377
if (localEngineInfo != null) {
375378
final Directory localEngineRepo = _getLocalEngineRepo(

packages/flutter_tools/lib/src/build_info.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ class BuildInfo {
4343
this.nullSafetyMode = NullSafetyMode.sound,
4444
this.codeSizeDirectory,
4545
this.androidGradleDaemon = true,
46+
this.androidSkipBuildDependencyValidation = false,
4647
this.packageConfig = PackageConfig.empty,
4748
this.initializeFromDill,
4849
this.assumeInitializeFromDillUpToDate = false,
@@ -158,6 +159,10 @@ class BuildInfo {
158159
/// The Gradle daemon may also be disabled in the Android application's properties file.
159160
final bool androidGradleDaemon;
160161

162+
/// Whether to skip checking of individual versions of our Android build time
163+
/// dependencies.
164+
final bool androidSkipBuildDependencyValidation;
165+
161166
/// Additional key value pairs that are passed directly to the gradle project via the `-P`
162167
/// flag.
163168
final List<String> androidProjectArgs;

packages/flutter_tools/lib/src/runner/flutter_command.dart

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ abstract final class FlutterOptions {
147147
static const String kAndroidGradleDaemon = 'android-gradle-daemon';
148148
static const String kDeferredComponents = 'deferred-components';
149149
static const String kAndroidProjectArgs = 'android-project-arg';
150+
static const String kAndroidSkipBuildDependencyValidation = 'android-skip-build-dependency-validation';
150151
static const String kInitializeFromDill = 'initialize-from-dill';
151152
static const String kAssumeInitializeFromDillUpToDate = 'assume-initialize-from-dill-up-to-date';
152153
static const String kNativeAssetsYamlFile = 'native-assets-yaml-file';
@@ -974,6 +975,12 @@ abstract class FlutterCommand extends Command<void> {
974975
defaultsTo: true,
975976
hide: hide,
976977
);
978+
argParser.addFlag(
979+
FlutterOptions.kAndroidSkipBuildDependencyValidation,
980+
help: 'Whether to skip version checking for Java, Gradle, '
981+
'the Android Gradle Plugin (AGP), and the Kotlin Gradle Plugin (KGP)'
982+
' during Android builds.',
983+
);
977984
argParser.addMultiOption(
978985
FlutterOptions.kAndroidProjectArgs,
979986
help: 'Additional arguments specified as key=value that are passed directly to the gradle '
@@ -1228,6 +1235,9 @@ abstract class FlutterCommand extends Command<void> {
12281235
final bool androidGradleDaemon = !argParser.options.containsKey(FlutterOptions.kAndroidGradleDaemon)
12291236
|| boolArg(FlutterOptions.kAndroidGradleDaemon);
12301237

1238+
final bool androidSkipBuildDependencyValidation = !argParser.options.containsKey(FlutterOptions.kAndroidSkipBuildDependencyValidation)
1239+
|| boolArg(FlutterOptions.kAndroidSkipBuildDependencyValidation);
1240+
12311241
final List<String> androidProjectArgs = argParser.options.containsKey(FlutterOptions.kAndroidProjectArgs)
12321242
? stringsArg(FlutterOptions.kAndroidProjectArgs)
12331243
: <String>[];
@@ -1323,6 +1333,7 @@ abstract class FlutterCommand extends Command<void> {
13231333
nullSafetyMode: nullSafetyMode,
13241334
codeSizeDirectory: codeSizeDirectory,
13251335
androidGradleDaemon: androidGradleDaemon,
1336+
androidSkipBuildDependencyValidation: androidSkipBuildDependencyValidation,
13261337
packageConfig: packageConfig,
13271338
androidProjectArgs: androidProjectArgs,
13281339
initializeFromDill: argParser.options.containsKey(FlutterOptions.kInitializeFromDill)

packages/flutter_tools/templates/app_shared/android-java.tmpl/app/build.gradle.tmpl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
plugins {
22
id "com.android.application"
33
id "kotlin-android"
4+
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
45
id "dev.flutter.flutter-gradle-plugin"
56
}
67

packages/flutter_tools/templates/app_shared/android-kotlin.tmpl/app/build.gradle.tmpl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
plugins {
22
id "com.android.application"
33
id "kotlin-android"
4+
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
45
id "dev.flutter.flutter-gradle-plugin"
56
}
67

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
// Copyright 2014 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'dart:io';
6+
7+
import 'package:file/src/interface/file_system_entity.dart';
8+
9+
import '../integration.shard/test_utils.dart';
10+
import '../src/common.dart';
11+
import '../src/context.dart';
12+
13+
const String gradleSettingsFileContent = r'''
14+
pluginManagement {
15+
def flutterSdkPath = {
16+
def properties = new Properties()
17+
file("local.properties").withInputStream { properties.load(it) }
18+
def flutterSdkPath = properties.getProperty("flutter.sdk")
19+
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
20+
return flutterSdkPath
21+
}()
22+
23+
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
24+
25+
repositories {
26+
google()
27+
mavenCentral()
28+
gradlePluginPortal()
29+
}
30+
}
31+
32+
plugins {
33+
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
34+
id "com.android.application" version "AGP_REPLACE_ME" apply false
35+
id "org.jetbrains.kotlin.android" version "KGP_REPLACE_ME" apply false
36+
}
37+
38+
include ":app"
39+
40+
''';
41+
42+
const String agpReplacementString = 'AGP_REPLACE_ME';
43+
const String kgpReplacementString = 'KGP_REPLACE_ME';
44+
45+
const String gradleWrapperPropertiesFileContent = r'''
46+
distributionBase=GRADLE_USER_HOME
47+
distributionPath=wrapper/dists
48+
zipStoreBase=GRADLE_USER_HOME
49+
zipStorePath=wrapper/dists
50+
distributionUrl=https\://services.gradle.org/distributions/gradle-GRADLE_REPLACE_ME-all.zip
51+
52+
''';
53+
54+
const String gradleReplacementString = 'GRADLE_REPLACE_ME';
55+
56+
// This test is currently on the preview shard (but not using the preview
57+
// version of Android) because it is the only one using Java 11. This test
58+
// requires Java 11 due to the intentionally low version of Gradle.
59+
void main() {
60+
late Directory tempDir;
61+
62+
setUpAll(() async {
63+
tempDir = createResolvedTempDirectorySync('run_test.');
64+
});
65+
66+
tearDownAll(() async {
67+
tryToDelete(tempDir as FileSystemEntity);
68+
});
69+
70+
testUsingContext(
71+
'AGP version out of "warn" support band prints warning but still builds', () async {
72+
// Create a new flutter project.
73+
final String flutterBin = fileSystem.path.join(getFlutterRoot(), 'bin', 'flutter');
74+
ProcessResult result = await processManager.run(<String>[
75+
flutterBin,
76+
'create',
77+
'dependency_checker_app',
78+
'--platforms=android',
79+
], workingDirectory: tempDir.path);
80+
expect(result, const ProcessResultMatcher());
81+
const String gradleVersion = '7.5';
82+
const String agpVersion = '4.2.0';
83+
const String kgpVersion = '1.7.10';
84+
85+
final Directory app = Directory(fileSystem.path.join(tempDir.path, 'dependency_checker_app'));
86+
87+
// Modify gradle version to passed in version.
88+
final File gradleWrapperProperties = File(fileSystem.path.join(
89+
app.path, 'android', 'gradle', 'wrapper', 'gradle-wrapper.properties'));
90+
final String propertyContent = gradleWrapperPropertiesFileContent.replaceFirst(
91+
gradleReplacementString,
92+
gradleVersion,
93+
);
94+
await gradleWrapperProperties.writeAsString(propertyContent, flush: true);
95+
96+
final File gradleSettings = File(fileSystem.path.join(
97+
app.path, 'android', 'settings.gradle'));
98+
final String settingsContent = gradleSettingsFileContent
99+
.replaceFirst(agpReplacementString, agpVersion)
100+
.replaceFirst(kgpReplacementString, kgpVersion);
101+
await gradleSettings.writeAsString(settingsContent, flush: true);
102+
103+
104+
// Ensure that gradle files exists from templates.
105+
result = await processManager.run(<String>[
106+
flutterBin,
107+
'build',
108+
'apk',
109+
'--debug',
110+
], workingDirectory: app.path);
111+
expect(result, const ProcessResultMatcher());
112+
expect(result.stderr, contains('Please upgrade your Android Gradle '
113+
'Plugin version'));
114+
});
115+
116+
testUsingContext(
117+
'Gradle version out of "warn" support band prints warning but still builds', () async {
118+
// Create a new flutter project.
119+
final String flutterBin = fileSystem.path.join(getFlutterRoot(), 'bin', 'flutter');
120+
ProcessResult result = await processManager.run(<String>[
121+
flutterBin,
122+
'create',
123+
'dependency_checker_app',
124+
'--platforms=android',
125+
], workingDirectory: tempDir.path);
126+
expect(result, const ProcessResultMatcher());
127+
const String gradleVersion = '7.0';
128+
const String agpVersion = '4.2.0';
129+
const String kgpVersion = '1.7.10';
130+
131+
final Directory app = Directory(fileSystem.path.join(tempDir.path, 'dependency_checker_app'));
132+
133+
// Modify gradle version to passed in version.
134+
final File gradleWrapperProperties = File(fileSystem.path.join(
135+
app.path, 'android', 'gradle', 'wrapper', 'gradle-wrapper.properties'));
136+
final String propertyContent = gradleWrapperPropertiesFileContent.replaceFirst(
137+
gradleReplacementString,
138+
gradleVersion,
139+
);
140+
await gradleWrapperProperties.writeAsString(propertyContent, flush: true);
141+
142+
final File gradleSettings = File(fileSystem.path.join(
143+
app.path, 'android', 'settings.gradle'));
144+
final String settingsContent = gradleSettingsFileContent
145+
.replaceFirst(agpReplacementString, agpVersion)
146+
.replaceFirst(kgpReplacementString, kgpVersion);
147+
await gradleSettings.writeAsString(settingsContent, flush: true);
148+
149+
150+
// Ensure that gradle files exists from templates.
151+
result = await processManager.run(<String>[
152+
flutterBin,
153+
'build',
154+
'apk',
155+
'--debug',
156+
], workingDirectory: app.path);
157+
expect(result, const ProcessResultMatcher());
158+
expect(result.stderr, contains('Please upgrade your Gradle version'));
159+
});
160+
161+
testUsingContext(
162+
'Kotlin version out of "warn" support band prints warning but still builds', () async {
163+
// Create a new flutter project.
164+
final String flutterBin = fileSystem.path.join(getFlutterRoot(), 'bin', 'flutter');
165+
ProcessResult result = await processManager.run(<String>[
166+
flutterBin,
167+
'create',
168+
'dependency_checker_app',
169+
'--platforms=android',
170+
], workingDirectory: tempDir.path);
171+
expect(result, const ProcessResultMatcher());
172+
const String gradleVersion = '7.5';
173+
const String agpVersion = '7.4.0';
174+
const String kgpVersion = '1.4.10';
175+
176+
final Directory app = Directory(fileSystem.path.join(tempDir.path, 'dependency_checker_app'));
177+
178+
// Modify gradle version to passed in version.
179+
final File gradleWrapperProperties = File(fileSystem.path.join(
180+
app.path, 'android', 'gradle', 'wrapper', 'gradle-wrapper.properties'));
181+
final String propertyContent = gradleWrapperPropertiesFileContent.replaceFirst(
182+
gradleReplacementString,
183+
gradleVersion,
184+
);
185+
await gradleWrapperProperties.writeAsString(propertyContent, flush: true);
186+
187+
final File gradleSettings = File(fileSystem.path.join(
188+
app.path, 'android', 'settings.gradle'));
189+
final String settingsContent = gradleSettingsFileContent
190+
.replaceFirst(agpReplacementString, agpVersion)
191+
.replaceFirst(kgpReplacementString, kgpVersion);
192+
await gradleSettings.writeAsString(settingsContent, flush: true);
193+
194+
195+
// Ensure that gradle files exists from templates.
196+
result = await processManager.run(<String>[
197+
flutterBin,
198+
'build',
199+
'apk',
200+
'--debug',
201+
], workingDirectory: app.path);
202+
expect(result, const ProcessResultMatcher());
203+
expect(result.stderr, contains('Please upgrade your Kotlin version'));
204+
});
205+
206+
// TODO(gmackall): Add tests for build blocking when the
207+
// corresponding error versions are enabled.
208+
}

0 commit comments

Comments
 (0)