Skip to content

Commit a96509a

Browse files
[tool] Validate gradle compileSdk (#8761)
Checks that plugins use `compileSdk` rather than the deprecated `compileSdkVersion`, and that if they set it to `flutter.compileSdkVersion` they require at least Flutter 3.27. Would have prevented flutter/flutter#164362
1 parent 90dfc0c commit a96509a

File tree

2 files changed

+155
-1
lines changed

2 files changed

+155
-1
lines changed

script/tool/lib/src/gradle_check_command.dart

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5+
import 'package:collection/collection.dart';
56
import 'package:file/file.dart';
67
import 'package:meta/meta.dart';
78
import 'package:pub_semver/pub_semver.dart';
@@ -127,6 +128,9 @@ class GradleCheckCommand extends PackageLoopingCommand {
127128
if (!_validateGradleDrivenLintConfig(package, lines)) {
128129
succeeded = false;
129130
}
131+
if (!_validateCompileSdkUsage(package, lines)) {
132+
succeeded = false;
133+
}
130134
return succeeded;
131135
}
132136

@@ -415,6 +419,47 @@ for more details.''';
415419
return true;
416420
}
417421

422+
bool _validateCompileSdkUsage(
423+
RepositoryPackage package, List<String> gradleLines) {
424+
final RegExp linePattern = RegExp(r'^\s*compileSdk');
425+
final RegExp legacySettingPattern = RegExp(r'^\s*compileSdkVersion');
426+
final String? compileSdkLine = gradleLines
427+
.firstWhereOrNull((String line) => linePattern.hasMatch(line));
428+
if (compileSdkLine == null) {
429+
printError('${indentation}No compileSdk or compileSdkVersion found.');
430+
return false;
431+
}
432+
if (legacySettingPattern.hasMatch(compileSdkLine)) {
433+
printError('${indentation}Please replace the deprecated '
434+
'"compileSdkVersion" setting with the newer "compileSdk"');
435+
return false;
436+
}
437+
if (compileSdkLine.contains('flutter.compileSdkVersion')) {
438+
final Pubspec pubspec = package.parsePubspec();
439+
final VersionConstraint? flutterConstraint =
440+
pubspec.environment['flutter'];
441+
final Version? minFlutterVersion =
442+
flutterConstraint != null && flutterConstraint is VersionRange
443+
? flutterConstraint.min
444+
: null;
445+
if (minFlutterVersion == null) {
446+
printError('${indentation}Unable to find a Flutter SDK version '
447+
'constraint. Use of flutter.compileSdkVersion requires a minimum '
448+
'Flutter version of 3.27');
449+
return false;
450+
}
451+
if (minFlutterVersion < Version(3, 27, 0)) {
452+
printError('${indentation}Use of flutter.compileSdkVersion requires a '
453+
'minimum Flutter version of 3.27, but this package currently '
454+
'supports $minFlutterVersion.\n'
455+
"${indentation}Please update the package's minimum Flutter SDK "
456+
'version to at least 3.27.');
457+
return false;
458+
}
459+
}
460+
return true;
461+
}
462+
418463
/// Validates whether the given [example]'s gradle content is configured to
419464
/// build its plugin target with javac lints enabled and treated as errors,
420465
/// if the enclosing package is a plugin.

script/tool/test/gradle_check_command_test.dart

Lines changed: 110 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ void main() {
4343
bool includeNamespace = true,
4444
bool commentNamespace = false,
4545
bool warningsConfigured = true,
46+
bool useDeprecatedCompileSdkVersion = false,
47+
String compileSdk = '33',
4648
}) {
4749
final File buildGradle = package
4850
.platformDirectory(FlutterPlatform.android)
@@ -88,7 +90,7 @@ apply plugin: 'com.android.library'
8890
${includeLanguageVersion ? javaSection : ''}
8991
android {
9092
${includeNamespace ? namespace : ''}
91-
compileSdk 33
93+
${useDeprecatedCompileSdkVersion ? 'compileSdkVersion' : 'compileSdk'} $compileSdk
9294
9395
defaultConfig {
9496
minSdkVersion 30
@@ -989,4 +991,111 @@ dependencies {
989991
);
990992
});
991993
});
994+
995+
group('compileSdk check', () {
996+
test('passes if set to a number', () async {
997+
const String packageName = 'a_package';
998+
final RepositoryPackage package =
999+
createFakePackage(packageName, packagesDir, isFlutter: true);
1000+
writeFakePluginBuildGradle(package,
1001+
includeLanguageVersion: true, compileSdk: '35');
1002+
writeFakeManifest(package);
1003+
final RepositoryPackage example = package.getExamples().first;
1004+
writeFakeExampleBuildGradles(example, pluginName: packageName);
1005+
writeFakeManifest(example, isApp: true);
1006+
1007+
final List<String> output =
1008+
await runCapturingPrint(runner, <String>['gradle-check']);
1009+
1010+
expect(
1011+
output,
1012+
containsAllInOrder(<Matcher>[
1013+
contains('Validating android/build.gradle'),
1014+
]),
1015+
);
1016+
});
1017+
1018+
test('passes if set to flutter.compileSdkVersion with Flutter 3.27+',
1019+
() async {
1020+
const String packageName = 'a_package';
1021+
final RepositoryPackage package = createFakePackage(
1022+
packageName, packagesDir,
1023+
isFlutter: true, flutterConstraint: '>=3.27.0');
1024+
writeFakePluginBuildGradle(package,
1025+
includeLanguageVersion: true,
1026+
compileSdk: 'flutter.compileSdkVersion');
1027+
writeFakeManifest(package);
1028+
final RepositoryPackage example = package.getExamples().first;
1029+
writeFakeExampleBuildGradles(example, pluginName: packageName);
1030+
writeFakeManifest(example, isApp: true);
1031+
1032+
final List<String> output =
1033+
await runCapturingPrint(runner, <String>['gradle-check']);
1034+
1035+
expect(
1036+
output,
1037+
containsAllInOrder(<Matcher>[
1038+
contains('Validating android/build.gradle'),
1039+
]),
1040+
);
1041+
});
1042+
1043+
test('fails if set to flutter.compileSdkVersion with Flutter <3.27',
1044+
() async {
1045+
const String packageName = 'a_package';
1046+
final RepositoryPackage package = createFakePackage(
1047+
packageName, packagesDir,
1048+
isFlutter: true, flutterConstraint: '>=3.24.0');
1049+
writeFakePluginBuildGradle(package,
1050+
includeLanguageVersion: true,
1051+
compileSdk: 'flutter.compileSdkVersion');
1052+
writeFakeManifest(package);
1053+
final RepositoryPackage example = package.getExamples().first;
1054+
writeFakeExampleBuildGradles(example, pluginName: packageName);
1055+
writeFakeManifest(example, isApp: true);
1056+
1057+
Error? commandError;
1058+
final List<String> output = await runCapturingPrint(
1059+
runner, <String>['gradle-check'], errorHandler: (Error e) {
1060+
commandError = e;
1061+
});
1062+
1063+
expect(commandError, isA<ToolExit>());
1064+
expect(
1065+
output,
1066+
containsAllInOrder(<Matcher>[
1067+
contains('Use of flutter.compileSdkVersion requires a minimum '
1068+
'Flutter version of 3.27, but this package currently supports '
1069+
'3.24.0'),
1070+
]),
1071+
);
1072+
});
1073+
1074+
test('fails if uses the legacy key', () async {
1075+
const String packageName = 'a_package';
1076+
final RepositoryPackage package =
1077+
createFakePackage(packageName, packagesDir, isFlutter: true);
1078+
writeFakePluginBuildGradle(package,
1079+
includeLanguageVersion: true, useDeprecatedCompileSdkVersion: true);
1080+
writeFakeManifest(package);
1081+
final RepositoryPackage example = package.getExamples().first;
1082+
writeFakeExampleBuildGradles(example, pluginName: packageName);
1083+
writeFakeManifest(example, isApp: true);
1084+
1085+
Error? commandError;
1086+
final List<String> output = await runCapturingPrint(
1087+
runner, <String>['gradle-check'], errorHandler: (Error e) {
1088+
commandError = e;
1089+
});
1090+
1091+
expect(commandError, isA<ToolExit>());
1092+
expect(
1093+
output,
1094+
containsAllInOrder(<Matcher>[
1095+
contains('Please replace the deprecated "compileSdkVersion" setting '
1096+
'with the newer "compileSdk"'),
1097+
]),
1098+
);
1099+
});
1100+
});
9921101
}

0 commit comments

Comments
 (0)