| // 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:flutter_tools/src/android/android_builder.dart'; | |
| import 'package:flutter_tools/src/base/file_system.dart' as file_system; | |
| import 'package:flutter_tools/src/build_info.dart'; | |
| import 'package:flutter_tools/src/globals.dart' as globals; | |
| import 'package:flutter_tools/src/project.dart'; | |
| import '../integration.shard/test_utils.dart'; | |
| import 'common.dart'; | |
| /// A fake implementation of [AndroidBuilder]. | |
| class FakeAndroidBuilder implements AndroidBuilder { | |
| @override | |
| Future<void> buildAar({ | |
| required FlutterProject project, | |
| required Set<AndroidBuildInfo> androidBuildInfo, | |
| required String target, | |
| required Future<void> Function(FlutterProject, {required bool releaseMode}) generateTooling, | |
| String? outputDirectoryPath, | |
| required String buildNumber, | |
| }) async {} | |
| @override | |
| Future<void> buildApk({ | |
| required FlutterProject project, | |
| required AndroidBuildInfo androidBuildInfo, | |
| required String target, | |
| bool configOnly = false, | |
| }) async {} | |
| @override | |
| Future<void> buildAab({ | |
| required FlutterProject project, | |
| required AndroidBuildInfo androidBuildInfo, | |
| required String target, | |
| bool validateDeferredComponents = true, | |
| bool deferredComponentsEnabled = false, | |
| bool configOnly = false, | |
| }) async {} | |
| @override | |
| Future<List<String>> getBuildVariants({required FlutterProject project}) async => | |
| const <String>[]; | |
| @override | |
| Future<String> outputsAppLinkSettings( | |
| String buildVariant, { | |
| required FlutterProject project, | |
| }) async => '/'; | |
| } | |
| /// Creates a [FlutterProject] in a directory named `flutter_project` | |
| /// within [directoryOverride]. | |
| class FakeFlutterProjectFactory extends FlutterProjectFactory { | |
| FakeFlutterProjectFactory(this.directoryOverride) | |
| : super(fileSystem: globals.fs, logger: globals.logger); | |
| final file_system.Directory directoryOverride; | |
| @override | |
| FlutterProject fromDirectory(Directory _) { | |
| projects.clear(); | |
| return super.fromDirectory(directoryOverride.childDirectory('flutter_project')); | |
| } | |
| } | |
| // The following test outline shares a lot of similarities with the one in | |
| // dev/devicelab/lib/framework/dependency_smoke_test_task_definition.dart | |
| // When making changes here, consider making the corresponding changes to that | |
| // file as well. | |
| const 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 agpReplacementString = 'AGP_REPLACE_ME'; | |
| const kgpReplacementString = 'KGP_REPLACE_ME'; | |
| const 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-bin.zip | |
| '''; | |
| const gradleReplacementString = 'GRADLE_REPLACE_ME'; | |
| const flutterCompileSdkString = 'flutter.compileSdkVersion'; | |
| class VersionTuple { | |
| VersionTuple({ | |
| required this.agpVersion, | |
| required this.gradleVersion, | |
| required this.kotlinVersion, | |
| this.compileSdkVersion, | |
| }); | |
| String agpVersion; | |
| String gradleVersion; | |
| String kotlinVersion; | |
| String? compileSdkVersion; | |
| @override | |
| String toString() { | |
| return '(AGP version: $agpVersion, Gradle version: $gradleVersion, Kotlin version: $kotlinVersion' | |
| '${(compileSdkVersion == null) ? '' : ', compileSdk version: $compileSdkVersion)'}'; | |
| } | |
| } | |
| /// Creates a new Flutter project with the specified AGP, Gradle, and Kotlin | |
| /// versions and then tries to call `flutter build apk`, returning the | |
| /// ProcessResult. | |
| Future<ProcessResult> buildFlutterApkWithSpecifiedDependencyVersions({ | |
| required VersionTuple versions, | |
| required Directory tempDir, | |
| bool skipChecking = false, | |
| }) 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()); | |
| final app = Directory(fileSystem.path.join(tempDir.path, 'dependency_checker_app')); | |
| if (versions.compileSdkVersion != null) { | |
| final appGradleBuild = File( | |
| fileSystem.path.join(app.path, 'android', 'app', 'build.gradle.kts'), | |
| ); | |
| final String appBuildContent = appGradleBuild.readAsStringSync().replaceFirst( | |
| flutterCompileSdkString, | |
| versions.compileSdkVersion!, | |
| ); | |
| appGradleBuild.writeAsStringSync(appBuildContent); | |
| } | |
| // Modify gradle version to passed in version. | |
| final gradleWrapperProperties = File( | |
| fileSystem.path.join(app.path, 'android', 'gradle', 'wrapper', 'gradle-wrapper.properties'), | |
| ); | |
| final String propertyContent = gradleWrapperPropertiesFileContent.replaceFirst( | |
| gradleReplacementString, | |
| versions.gradleVersion, | |
| ); | |
| await gradleWrapperProperties.writeAsString(propertyContent, flush: true); | |
| final gradleSettings = File(fileSystem.path.join(app.path, 'android', 'settings.gradle')); | |
| final String settingsContent = gradleSettingsFileContent | |
| .replaceFirst(agpReplacementString, versions.agpVersion) | |
| .replaceFirst(kgpReplacementString, versions.kotlinVersion); | |
| await gradleSettings.writeAsString(settingsContent, flush: true); | |
| // Ensure that gradle files exists from templates. | |
| result = await processManager.run(<String>[ | |
| flutterBin, | |
| 'build', | |
| 'apk', | |
| '--debug', | |
| if (skipChecking) '--android-skip-build-dependency-validation', | |
| ], workingDirectory: app.path); | |
| return result; | |
| } |