Unverified Commit 8ba086a2 authored by Gray Mackall's avatar Gray Mackall Committed by GitHub

[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))): https://github.com/flutter/flutter/pull/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 https://github.com/flutter/flutter/pull/142008, and was able to recreate the failure and verify that it was resolved by 1).
parent 634fcc71
......@@ -4,8 +4,8 @@
plugins {
id "com.android.application"
id "dev.flutter.flutter-gradle-plugin"
id "kotlin-android"
id "dev.flutter.flutter-gradle-plugin"
}
android {
......
......@@ -28,8 +28,9 @@ gradlePlugin {
dependencies {
// When bumping, also update:
// * ndkVersion in FlutterExtension in packages/flutter_tools/gradle/src/main/flutter.groovy
// * ndkVersion in FlutterExtension in packages/flutter_tools/gradle/src/main/groovy/flutter.groovy
// * AGP version in the buildscript block in packages/flutter_tools/gradle/src/main/groovy/flutter.groovy
// * AGP version in the buildscript block in packages/flutter_tools/gradle/src/main/kotlin/dependency_version_checker.gradle.kts
// * AGP version constants in packages/flutter_tools/lib/src/android/gradle_utils.dart
// * AGP version in buildscript block in packages/flutter_tools/gradle/src/main/flutter.groovy
compileOnly("com.android.tools.build:gradle:7.3.0")
}
......@@ -113,7 +113,8 @@ buildscript {
}
dependencies {
// When bumping, also update:
// * ndkVersion in FlutterExtension in packages/flutter_tools/gradle/src/main/flutter.groovy
// * ndkVersion in FlutterExtension in packages/flutter_tools/gradle/src/main/groovy/flutter.groovy
// * AGP version in the buildscript block in packages/flutter_tools/gradle/src/main/kotlin/dependency_version_checker.gradle.kts
// * AGP version constants in packages/flutter_tools/lib/src/android/gradle_utils.dart
// * AGP version in dependencies block in packages/flutter_tools/gradle/build.gradle.kts
classpath("com.android.tools.build:gradle:7.3.0")
......@@ -321,6 +322,27 @@ class FlutterPlugin implements Plugin<Project> {
String flutterExecutableName = Os.isFamily(Os.FAMILY_WINDOWS) ? "flutter.bat" : "flutter"
flutterExecutable = Paths.get(flutterRoot.absolutePath, "bin", flutterExecutableName).toFile()
// Validate that the provided Gradle, Java, AGP, and KGP versions are all within our
// supported range.
// TODO(gmackall) Dependency version checking is currently implemented as an additional
// Gradle plugin because we can't import it from Groovy code. As part of the Groovy
// -> Kotlin migration, we should remove this complexity and perform the checks inside
// of the main Flutter Gradle Plugin.
// See https://github.com/flutter/flutter/issues/121541#issuecomment-1920363687.
final Boolean shouldSkipDependencyChecks = project.hasProperty("skipDependencyChecks") && project.getProperty("skipDependencyChecks");
if (!shouldSkipDependencyChecks) {
try {
final String dependencyCheckerPluginPath = Paths.get(flutterRoot.absolutePath,
"packages", "flutter_tools", "gradle", "src", "main", "kotlin",
"dependency_version_checker.gradle.kts")
project.apply from: dependencyCheckerPluginPath
} catch (Exception ignored) {
project.logger.error("Warning: Flutter was unable to detect project Gradle, Java, " +
"AGP, and KGP versions. Skipping dependency version checking. Error was: "
+ ignored)
}
}
// Use Kotlin DSL to handle baseApplicationName logic due to Groovy dynamic dispatch bug.
project.apply from: Paths.get(flutterRoot.absolutePath, "packages", "flutter_tools", "gradle", "src", "main", "kotlin", "flutter.gradle.kts")
......@@ -1515,6 +1537,8 @@ abstract class BaseFlutterTask extends DefaultTask {
@Optional @Input
Boolean validateDeferredComponents
@Optional @Input
Boolean skipDependencyChecks
@Optional @Input
String flavor
......
......@@ -29,6 +29,11 @@ version.
SDK versions are updated (you should see these fail if you do not fix them
preemptively).
Also, make sure to also update to the same version in the following places:
- The version in the buildscript block in `packages/flutter_tools/gradle/src/main/groovy/flutter.groovy`.
- The version in the buildscript block in `packages/flutter_tools/gradle/src/main/kotlin/dependency_version_checker.gradle.kts`.
- The version in the dependencies block in `packages/flutter_tools/gradle/build.gradle.kts`.
#### Gradle
When updating the Gradle version used in project templates
(`templateDefaultGradleVersion`), make sure that:
......
......@@ -370,6 +370,9 @@ class AndroidGradleBuilder implements AndroidBuilder {
if (!buildInfo.androidGradleDaemon) {
command.add('--no-daemon');
}
if (buildInfo.androidSkipBuildDependencyValidation) {
command.add('-PskipDependencyChecks=true');
}
final LocalEngineInfo? localEngineInfo = _artifacts.localEngineInfo;
if (localEngineInfo != null) {
final Directory localEngineRepo = _getLocalEngineRepo(
......
......@@ -43,6 +43,7 @@ class BuildInfo {
this.nullSafetyMode = NullSafetyMode.sound,
this.codeSizeDirectory,
this.androidGradleDaemon = true,
this.androidSkipBuildDependencyValidation = false,
this.packageConfig = PackageConfig.empty,
this.initializeFromDill,
this.assumeInitializeFromDillUpToDate = false,
......@@ -158,6 +159,10 @@ class BuildInfo {
/// The Gradle daemon may also be disabled in the Android application's properties file.
final bool androidGradleDaemon;
/// Whether to skip checking of individual versions of our Android build time
/// dependencies.
final bool androidSkipBuildDependencyValidation;
/// Additional key value pairs that are passed directly to the gradle project via the `-P`
/// flag.
final List<String> androidProjectArgs;
......
......@@ -147,6 +147,7 @@ abstract final class FlutterOptions {
static const String kAndroidGradleDaemon = 'android-gradle-daemon';
static const String kDeferredComponents = 'deferred-components';
static const String kAndroidProjectArgs = 'android-project-arg';
static const String kAndroidSkipBuildDependencyValidation = 'android-skip-build-dependency-validation';
static const String kInitializeFromDill = 'initialize-from-dill';
static const String kAssumeInitializeFromDillUpToDate = 'assume-initialize-from-dill-up-to-date';
static const String kNativeAssetsYamlFile = 'native-assets-yaml-file';
......@@ -974,6 +975,12 @@ abstract class FlutterCommand extends Command<void> {
defaultsTo: true,
hide: hide,
);
argParser.addFlag(
FlutterOptions.kAndroidSkipBuildDependencyValidation,
help: 'Whether to skip version checking for Java, Gradle, '
'the Android Gradle Plugin (AGP), and the Kotlin Gradle Plugin (KGP)'
' during Android builds.',
);
argParser.addMultiOption(
FlutterOptions.kAndroidProjectArgs,
help: 'Additional arguments specified as key=value that are passed directly to the gradle '
......@@ -1228,6 +1235,9 @@ abstract class FlutterCommand extends Command<void> {
final bool androidGradleDaemon = !argParser.options.containsKey(FlutterOptions.kAndroidGradleDaemon)
|| boolArg(FlutterOptions.kAndroidGradleDaemon);
final bool androidSkipBuildDependencyValidation = !argParser.options.containsKey(FlutterOptions.kAndroidSkipBuildDependencyValidation)
|| boolArg(FlutterOptions.kAndroidSkipBuildDependencyValidation);
final List<String> androidProjectArgs = argParser.options.containsKey(FlutterOptions.kAndroidProjectArgs)
? stringsArg(FlutterOptions.kAndroidProjectArgs)
: <String>[];
......@@ -1323,6 +1333,7 @@ abstract class FlutterCommand extends Command<void> {
nullSafetyMode: nullSafetyMode,
codeSizeDirectory: codeSizeDirectory,
androidGradleDaemon: androidGradleDaemon,
androidSkipBuildDependencyValidation: androidSkipBuildDependencyValidation,
packageConfig: packageConfig,
androidProjectArgs: androidProjectArgs,
initializeFromDill: argParser.options.containsKey(FlutterOptions.kInitializeFromDill)
......
plugins {
id "com.android.application"
id "kotlin-android"
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
id "dev.flutter.flutter-gradle-plugin"
}
......
plugins {
id "com.android.application"
id "kotlin-android"
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
id "dev.flutter.flutter-gradle-plugin"
}
......
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:io';
import 'package:file/src/interface/file_system_entity.dart';
import '../integration.shard/test_utils.dart';
import '../src/common.dart';
import '../src/context.dart';
const String gradleSettingsFileContent = r'''
pluginManagement {
def flutterSdkPath = {
def properties = new Properties()
file("local.properties").withInputStream { properties.load(it) }
def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
return flutterSdkPath
}()
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
id "com.android.application" version "AGP_REPLACE_ME" apply false
id "org.jetbrains.kotlin.android" version "KGP_REPLACE_ME" apply false
}
include ":app"
''';
const String agpReplacementString = 'AGP_REPLACE_ME';
const String kgpReplacementString = 'KGP_REPLACE_ME';
const String gradleWrapperPropertiesFileContent = r'''
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-GRADLE_REPLACE_ME-all.zip
''';
const String gradleReplacementString = 'GRADLE_REPLACE_ME';
// This test is currently on the preview shard (but not using the preview
// version of Android) because it is the only one using Java 11. This test
// requires Java 11 due to the intentionally low version of Gradle.
void main() {
late Directory tempDir;
setUpAll(() async {
tempDir = createResolvedTempDirectorySync('run_test.');
});
tearDownAll(() async {
tryToDelete(tempDir as FileSystemEntity);
});
testUsingContext(
'AGP version out of "warn" support band prints warning but still builds', () async {
// Create a new flutter project.
final String flutterBin = fileSystem.path.join(getFlutterRoot(), 'bin', 'flutter');
ProcessResult result = await processManager.run(<String>[
flutterBin,
'create',
'dependency_checker_app',
'--platforms=android',
], workingDirectory: tempDir.path);
expect(result, const ProcessResultMatcher());
const String gradleVersion = '7.5';
const String agpVersion = '4.2.0';
const String kgpVersion = '1.7.10';
final Directory app = Directory(fileSystem.path.join(tempDir.path, 'dependency_checker_app'));
// Modify gradle version to passed in version.
final File gradleWrapperProperties = File(fileSystem.path.join(
app.path, 'android', 'gradle', 'wrapper', 'gradle-wrapper.properties'));
final String propertyContent = gradleWrapperPropertiesFileContent.replaceFirst(
gradleReplacementString,
gradleVersion,
);
await gradleWrapperProperties.writeAsString(propertyContent, flush: true);
final File gradleSettings = File(fileSystem.path.join(
app.path, 'android', 'settings.gradle'));
final String settingsContent = gradleSettingsFileContent
.replaceFirst(agpReplacementString, agpVersion)
.replaceFirst(kgpReplacementString, kgpVersion);
await gradleSettings.writeAsString(settingsContent, flush: true);
// Ensure that gradle files exists from templates.
result = await processManager.run(<String>[
flutterBin,
'build',
'apk',
'--debug',
], workingDirectory: app.path);
expect(result, const ProcessResultMatcher());
expect(result.stderr, contains('Please upgrade your Android Gradle '
'Plugin version'));
});
testUsingContext(
'Gradle version out of "warn" support band prints warning but still builds', () async {
// Create a new flutter project.
final String flutterBin = fileSystem.path.join(getFlutterRoot(), 'bin', 'flutter');
ProcessResult result = await processManager.run(<String>[
flutterBin,
'create',
'dependency_checker_app',
'--platforms=android',
], workingDirectory: tempDir.path);
expect(result, const ProcessResultMatcher());
const String gradleVersion = '7.0';
const String agpVersion = '4.2.0';
const String kgpVersion = '1.7.10';
final Directory app = Directory(fileSystem.path.join(tempDir.path, 'dependency_checker_app'));
// Modify gradle version to passed in version.
final File gradleWrapperProperties = File(fileSystem.path.join(
app.path, 'android', 'gradle', 'wrapper', 'gradle-wrapper.properties'));
final String propertyContent = gradleWrapperPropertiesFileContent.replaceFirst(
gradleReplacementString,
gradleVersion,
);
await gradleWrapperProperties.writeAsString(propertyContent, flush: true);
final File gradleSettings = File(fileSystem.path.join(
app.path, 'android', 'settings.gradle'));
final String settingsContent = gradleSettingsFileContent
.replaceFirst(agpReplacementString, agpVersion)
.replaceFirst(kgpReplacementString, kgpVersion);
await gradleSettings.writeAsString(settingsContent, flush: true);
// Ensure that gradle files exists from templates.
result = await processManager.run(<String>[
flutterBin,
'build',
'apk',
'--debug',
], workingDirectory: app.path);
expect(result, const ProcessResultMatcher());
expect(result.stderr, contains('Please upgrade your Gradle version'));
});
testUsingContext(
'Kotlin version out of "warn" support band prints warning but still builds', () async {
// Create a new flutter project.
final String flutterBin = fileSystem.path.join(getFlutterRoot(), 'bin', 'flutter');
ProcessResult result = await processManager.run(<String>[
flutterBin,
'create',
'dependency_checker_app',
'--platforms=android',
], workingDirectory: tempDir.path);
expect(result, const ProcessResultMatcher());
const String gradleVersion = '7.5';
const String agpVersion = '7.4.0';
const String kgpVersion = '1.4.10';
final Directory app = Directory(fileSystem.path.join(tempDir.path, 'dependency_checker_app'));
// Modify gradle version to passed in version.
final File gradleWrapperProperties = File(fileSystem.path.join(
app.path, 'android', 'gradle', 'wrapper', 'gradle-wrapper.properties'));
final String propertyContent = gradleWrapperPropertiesFileContent.replaceFirst(
gradleReplacementString,
gradleVersion,
);
await gradleWrapperProperties.writeAsString(propertyContent, flush: true);
final File gradleSettings = File(fileSystem.path.join(
app.path, 'android', 'settings.gradle'));
final String settingsContent = gradleSettingsFileContent
.replaceFirst(agpReplacementString, agpVersion)
.replaceFirst(kgpReplacementString, kgpVersion);
await gradleSettings.writeAsString(settingsContent, flush: true);
// Ensure that gradle files exists from templates.
result = await processManager.run(<String>[
flutterBin,
'build',
'apk',
'--debug',
], workingDirectory: app.path);
expect(result, const ProcessResultMatcher());
expect(result.stderr, contains('Please upgrade your Kotlin version'));
});
// TODO(gmackall): Add tests for build blocking when the
// corresponding error versions are enabled.
}
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