Unverified Commit 9a72e1c6 authored by Gray Mackall's avatar Gray Mackall Committed by GitHub

Allow plugins to use compileSdkPreview (#131901)

Fixes https://github.com/flutter/flutter/issues/124748

Based (heavily) off https://github.com/flutter/flutter/pull/104662
parent eb0890c9
...@@ -1076,6 +1076,36 @@ targets: ...@@ -1076,6 +1076,36 @@ targets:
- bin/** - bin/**
- .ci.yaml - .ci.yaml
- name: Linux android_preview_tool_integration_tests
bringup: true
recipe: flutter/flutter_drone
timeout: 60
properties:
add_recipes_cq: "true"
# This makes use of UpsideDownCake, a preview version of android. Preview versions eventually
# get removed from the sdk manager, so it is hosted on CIPD to ensure integration testing
# doesn't flake when that happens.
# https://chrome-infra-packages.appspot.com/p/flutter/android/sdk/all/linux-amd64/+/version:udcv1
dependencies: >-
[
{"dependency": "android_sdk", "version": "version:udcv1"},
{"dependency": "chrome_and_driver", "version": "version:119.0.6045.9"},
{"dependency": "clang", "version": "git_revision:5d5aba78dbbee75508f01bcaa69aedb2ab79065a"},
{"dependency": "cmake", "version": "build_id:8787856497187628321"},
{"dependency": "goldctl", "version": "git_revision:720a542f6fe4f92922c3b8f0fdcc4d2ac6bb83cd"},
{"dependency": "ninja", "version": "version:1.9.0"},
{"dependency": "open_jdk", "version": "version:11"}
]
shard: android_preview_tool_integration_tests
tags: >
["framework", "hostonly", "shard", "linux"]
test_timeout_secs: "2700"
runIf:
- dev/**
- packages/flutter_tools/**
- bin/**
- .ci.yaml
- name: Linux tool_tests_commands - name: Linux tool_tests_commands
recipe: flutter/flutter_drone recipe: flutter/flutter_drone
timeout: 60 timeout: 60
......
...@@ -253,6 +253,7 @@ Future<void> main(List<String> args) async { ...@@ -253,6 +253,7 @@ Future<void> main(List<String> args) async {
// web_tool_tests is also used by HHH: https://dart.googlesource.com/recipes/+/refs/heads/master/recipes/dart/flutter_engine.py // web_tool_tests is also used by HHH: https://dart.googlesource.com/recipes/+/refs/heads/master/recipes/dart/flutter_engine.py
'web_tool_tests': _runWebToolTests, 'web_tool_tests': _runWebToolTests,
'tool_integration_tests': _runIntegrationToolTests, 'tool_integration_tests': _runIntegrationToolTests,
'android_preview_tool_integration_tests': _runAndroidPreviewIntegrationToolTests,
'tool_host_cross_arch_tests': _runToolHostCrossArchTests, 'tool_host_cross_arch_tests': _runToolHostCrossArchTests,
// All the unit/widget tests run using `flutter test --platform=chrome --web-renderer=html` // All the unit/widget tests run using `flutter test --platform=chrome --web-renderer=html`
'web_tests': _runWebHtmlUnitTests, 'web_tests': _runWebHtmlUnitTests,
...@@ -477,6 +478,20 @@ Future<void> _runIntegrationToolTests() async { ...@@ -477,6 +478,20 @@ Future<void> _runIntegrationToolTests() async {
); );
} }
Future<void> _runAndroidPreviewIntegrationToolTests() async {
final List<String> allTests = Directory(path.join(_toolsPath, 'test', 'android_preview_integration.shard'))
.listSync(recursive: true).whereType<File>()
.map<String>((FileSystemEntity entry) => path.relative(entry.path, from: _toolsPath))
.toList();
await _runDartTest(
_toolsPath,
forceSingleCore: true,
testPaths: _selectIndexOfTotalSubshard<String>(allTests),
collectMetrics: true,
);
}
Future<void> _runToolTests() async { Future<void> _runToolTests() async {
await selectSubshard(<String, ShardRunner>{ await selectSubshard(<String, ShardRunner>{
'general': _runGeneralToolTests, 'general': _runGeneralToolTests,
......
...@@ -441,7 +441,7 @@ class FlutterPlugin implements Plugin<Project> { ...@@ -441,7 +441,7 @@ class FlutterPlugin implements Plugin<Project> {
pluginProject.afterEvaluate { pluginProject.afterEvaluate {
// Checks if there is a mismatch between the plugin compileSdkVersion and the project compileSdkVersion. // Checks if there is a mismatch between the plugin compileSdkVersion and the project compileSdkVersion.
if (pluginProject.android.compileSdkVersion > project.android.compileSdkVersion) { if (pluginProject.android.compileSdkVersion > project.android.compileSdkVersion) {
project.logger.quiet("Warning: The plugin ${pluginName} requires Android SDK version ${pluginProject.android.compileSdkVersion.substring(8)}.") project.logger.quiet("Warning: The plugin ${pluginName} requires Android SDK version ${getCompileSdkFromProject(pluginProject)}.")
project.logger.quiet("For more information about build configuration, see $kWebsiteDeploymentAndroidBuildConfig.") project.logger.quiet("For more information about build configuration, see $kWebsiteDeploymentAndroidBuildConfig.")
} }
...@@ -483,9 +483,11 @@ class FlutterPlugin implements Plugin<Project> { ...@@ -483,9 +483,11 @@ class FlutterPlugin implements Plugin<Project> {
/** Prints error message and fix for any plugin compileSdkVersion or ndkVersion that are higher than the project. */ /** Prints error message and fix for any plugin compileSdkVersion or ndkVersion that are higher than the project. */
private void detectLowCompileSdkVersionOrNdkVersion() { private void detectLowCompileSdkVersionOrNdkVersion() {
project.afterEvaluate { project.afterEvaluate {
int projectCompileSdkVersion = Integer.MAX_VALUE // Default to int max if using a preview version to skip the sdk check. // Default to int max if using a preview version to skip the sdk check.
if (project.android.compileSdkVersion.substring(8).isInteger()) { // Stable versions use ints, legacy preview uses string. int projectCompileSdkVersion = Integer.MAX_VALUE
projectCompileSdkVersion = project.android.compileSdkVersion.substring(8) as int // Stable versions use ints, legacy preview uses string.
if (getCompileSdkFromProject(project).isInteger()) {
projectCompileSdkVersion = getCompileSdkFromProject(project) as int
} }
int maxPluginCompileSdkVersion = projectCompileSdkVersion int maxPluginCompileSdkVersion = projectCompileSdkVersion
String ndkVersionIfUnspecified = "21.1.6352462" /* The default for AGP 4.1.0 used in old templates. */ String ndkVersionIfUnspecified = "21.1.6352462" /* The default for AGP 4.1.0 used in old templates. */
...@@ -496,7 +498,12 @@ class FlutterPlugin implements Plugin<Project> { ...@@ -496,7 +498,12 @@ class FlutterPlugin implements Plugin<Project> {
getPluginList().each { plugin -> getPluginList().each { plugin ->
Project pluginProject = project.rootProject.findProject(plugin.key) Project pluginProject = project.rootProject.findProject(plugin.key)
pluginProject.afterEvaluate { pluginProject.afterEvaluate {
int pluginCompileSdkVersion = pluginProject.android.compileSdkVersion.substring(8) as int // Default to int min if using a preview version to skip the sdk check.
int pluginCompileSdkVersion = Integer.MIN_VALUE;
// Stable versions use ints, legacy preview uses string.
if (getCompileSdkFromProject(pluginProject).isInteger()) {
pluginCompileSdkVersion = getCompileSdkFromProject(pluginProject) as int;
}
maxPluginCompileSdkVersion = Math.max(pluginCompileSdkVersion, maxPluginCompileSdkVersion) maxPluginCompileSdkVersion = Math.max(pluginCompileSdkVersion, maxPluginCompileSdkVersion)
String pluginNdkVersion = pluginProject.android.ndkVersion ?: ndkVersionIfUnspecified String pluginNdkVersion = pluginProject.android.ndkVersion ?: ndkVersionIfUnspecified
maxPluginNdkVersion = mostRecentSemanticVersion(pluginNdkVersion, maxPluginNdkVersion) maxPluginNdkVersion = mostRecentSemanticVersion(pluginNdkVersion, maxPluginNdkVersion)
...@@ -515,6 +522,14 @@ class FlutterPlugin implements Plugin<Project> { ...@@ -515,6 +522,14 @@ class FlutterPlugin implements Plugin<Project> {
} }
} }
/**
* Returns the portion of the compileSdkVersion string that corresponds to either the numeric
* or string version.
*/
private String getCompileSdkFromProject(Project gradleProject) {
return gradleProject.android.compileSdkVersion.substring(8);
}
/** /**
* Returns `true` if the given path contains an `android/build.gradle` file. * Returns `true` if the given path contains an `android/build.gradle` file.
*/ */
......
# Android preview integration tests
This directory contains integration tests which would otherwise live in `integration.shard`,
but require a dependency on a CIPD-hosted preview version of Android (and therefore their own
test shard). For additional information see the README in the `../integration.shard` directory.
\ No newline at end of file
...@@ -5,13 +5,17 @@ ...@@ -5,13 +5,17 @@
import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/io.dart'; import 'package:flutter_tools/src/base/io.dart';
import '../integration.shard/test_utils.dart';
import '../src/common.dart'; import '../src/common.dart';
import 'test_utils.dart';
void main() { void main() {
late Directory tempDir; late Directory tempDir;
late String flutterBin; late String flutterBin;
late Directory exampleAppDir; late Directory exampleAppDir;
late Directory pluginDir;
final RegExp compileSdkVersionMatch = RegExp(r'compileSdk [\w.]+');
final String builtApkPath = <String>['build', 'app', 'outputs', 'flutter-apk', 'app-debug.apk']
.join(platform.pathSeparator);
setUp(() async { setUp(() async {
tempDir = createResolvedTempDirectorySync('flutter_plugin_test.'); tempDir = createResolvedTempDirectorySync('flutter_plugin_test.');
...@@ -20,7 +24,8 @@ void main() { ...@@ -20,7 +24,8 @@ void main() {
'bin', 'bin',
'flutter', 'flutter',
); );
exampleAppDir = tempDir.childDirectory('aaa').childDirectory('example'); pluginDir = tempDir.childDirectory('aaa');
exampleAppDir = pluginDir.childDirectory('example');
processManager.runSync(<String>[ processManager.runSync(<String>[
flutterBin, flutterBin,
...@@ -40,12 +45,12 @@ void main() { ...@@ -40,12 +45,12 @@ void main() {
'build succeeds targeting string compileSdkVersion', 'build succeeds targeting string compileSdkVersion',
() async { () async {
final File buildGradleFile = exampleAppDir.childDirectory('android').childDirectory('app').childFile('build.gradle'); final File buildGradleFile = exampleAppDir.childDirectory('android').childDirectory('app').childFile('build.gradle');
// write a build.gradle with compileSdk version as `android-Tiramisu` which is a string preview version // write a build.gradle with compileSdkVersion as `android-UpsideDownCake` which is a string preview version
buildGradleFile.writeAsStringSync( buildGradleFile.writeAsStringSync(
buildGradleFile.readAsStringSync().replaceFirst('compileSdk flutter.compileSdkVersion', 'compileSdk "android-Tiramisu"'), buildGradleFile.readAsStringSync().replaceFirst(compileSdkVersionMatch, 'compileSdkVersion "android-UpsideDownCake"'),
flush: true flush: true
); );
expect(buildGradleFile.readAsStringSync(), contains('compileSdk "android-Tiramisu"')); expect(buildGradleFile.readAsStringSync(), contains('compileSdkVersion "android-UpsideDownCake"'));
final ProcessResult result = await processManager.run(<String>[ final ProcessResult result = await processManager.run(<String>[
flutterBin, flutterBin,
...@@ -54,13 +59,13 @@ void main() { ...@@ -54,13 +59,13 @@ void main() {
'apk', 'apk',
'--debug', '--debug',
], workingDirectory: exampleAppDir.path); ], workingDirectory: exampleAppDir.path);
expect(result.stdout, contains('Built build/app/outputs/flutter-apk/app-debug.apk.'));
expect(exampleAppDir.childDirectory('build') expect(exampleAppDir.childDirectory('build')
.childDirectory('app') .childDirectory('app')
.childDirectory('outputs') .childDirectory('outputs')
.childDirectory('apk') .childDirectory('apk')
.childDirectory('debug') .childDirectory('debug')
.childFile('app-debug.apk').existsSync(), true); .childFile('app-debug.apk').existsSync(), true);
expect(result.stdout, contains('Built $builtApkPath'));
}, },
); );
...@@ -68,12 +73,12 @@ void main() { ...@@ -68,12 +73,12 @@ void main() {
'build succeeds targeting string compileSdkPreview', 'build succeeds targeting string compileSdkPreview',
() async { () async {
final File buildGradleFile = exampleAppDir.childDirectory('android').childDirectory('app').childFile('build.gradle'); final File buildGradleFile = exampleAppDir.childDirectory('android').childDirectory('app').childFile('build.gradle');
// write a build.gradle with compileSdkPreview as `Tiramisu` which is a string preview version // write a build.gradle with compileSdkPreview as `UpsideDownCake` which is a string preview version
buildGradleFile.writeAsStringSync( buildGradleFile.writeAsStringSync(
buildGradleFile.readAsStringSync().replaceFirst('compileSdk flutter.compileSdkVersion', 'compileSdkPreview "Tiramisu"'), buildGradleFile.readAsStringSync().replaceFirst(compileSdkVersionMatch, 'compileSdkPreview "UpsideDownCake"'),
flush: true flush: true
); );
expect(buildGradleFile.readAsStringSync(), contains('compileSdkPreview "Tiramisu"')); expect(buildGradleFile.readAsStringSync(), contains('compileSdkPreview "UpsideDownCake"'));
final ProcessResult result = await processManager.run(<String>[ final ProcessResult result = await processManager.run(<String>[
flutterBin, flutterBin,
...@@ -82,13 +87,49 @@ void main() { ...@@ -82,13 +87,49 @@ void main() {
'apk', 'apk',
'--debug', '--debug',
], workingDirectory: exampleAppDir.path); ], workingDirectory: exampleAppDir.path);
expect(result.stdout, contains('Built build/app/outputs/flutter-apk/app-debug.apk.'));
expect(exampleAppDir.childDirectory('build') expect(exampleAppDir.childDirectory('build')
.childDirectory('app') .childDirectory('app')
.childDirectory('outputs') .childDirectory('outputs')
.childDirectory('apk') .childDirectory('apk')
.childDirectory('debug') .childDirectory('debug')
.childFile('app-debug.apk').existsSync(), true); .childFile('app-debug.apk').existsSync(), true);
expect(result.stdout, contains('Built $builtApkPath'));
},
);
test(
'build succeeds when both example app and plugin target compileSdkPreview',
() async {
final File appBuildGradleFile = exampleAppDir.childDirectory('android').childDirectory('app').childFile('build.gradle');
// write a build.gradle with compileSdkPreview as `UpsideDownCake` which is a string preview version
appBuildGradleFile.writeAsStringSync(
appBuildGradleFile.readAsStringSync().replaceFirst(compileSdkVersionMatch, 'compileSdkPreview "UpsideDownCake"'),
flush: true
);
expect(appBuildGradleFile.readAsStringSync(), contains('compileSdkPreview "UpsideDownCake"'));
final File pluginBuildGradleFile = pluginDir.childDirectory('android').childFile('build.gradle');
// change the plugin build.gradle to use a preview compile sdk version
pluginBuildGradleFile.writeAsStringSync(
pluginBuildGradleFile.readAsStringSync().replaceFirst(compileSdkVersionMatch, 'compileSdkPreview "UpsideDownCake"'),
flush: true
);
expect(pluginBuildGradleFile.readAsStringSync(), contains('compileSdkPreview "UpsideDownCake"'));
final ProcessResult result = await processManager.run(<String>[
flutterBin,
...getLocalEngineArguments(),
'build',
'apk',
'--debug',
], workingDirectory: exampleAppDir.path);
expect(exampleAppDir.childDirectory('build')
.childDirectory('app')
.childDirectory('outputs')
.childDirectory('apk')
.childDirectory('debug')
.childFile('app-debug.apk').existsSync(), true);
expect(result.stdout, contains('Built $builtApkPath'));
}, },
); );
} }
...@@ -20,3 +20,7 @@ information for the `flutter` tool (since they are black-box tests that ...@@ -20,3 +20,7 @@ information for the `flutter` tool (since they are black-box tests that
run the tool as a subprocess, rather than being unit tests). For this run the tool as a subprocess, rather than being unit tests). For this
reason, they are in a separate shard when running on continuous reason, they are in a separate shard when running on continuous
integration and are not run when calculating coverage. integration and are not run when calculating coverage.
## Adding new test files
When adding a new test file make sure that it ends with `_test.dart`, or else it will not be run.
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