From fd827e3a886cd1f7df4126fbf442dc5ffe860935 Mon Sep 17 00:00:00 2001 From: Bartek Pacia Date: Fri, 12 Jan 2024 19:18:32 +0100 Subject: [PATCH] Expose versionCode and versionName from local.properties in FlutterExtension (#141417) This PR has no issue. I got this cool idea and decided to quickly try it out, and it works. ### Summary This will allow Flutter Developers to have less code in their Android Gradle buildscripts. ```diff plugins { id "com.android.application" id "dev.flutter.flutter-gradle-plugin" id "kotlin-android" } -def localProperties = new Properties() -def localPropertiesFile = rootProject.file("local.properties") -if (localPropertiesFile.exists()) { - localPropertiesFile.withReader("UTF-8") { reader -> - localProperties.load(reader) - } -} - -def flutterVersionCode = localProperties.getProperty("flutter.versionCode") -if (flutterVersionCode == null) { - flutterVersionCode = "1" -} - -def flutterVersionName = localProperties.getProperty("flutter.versionName") -if (flutterVersionName == null) { - flutterVersionName = "1.0" -} - -def keystorePropertiesFile = rootProject.file("keystore.properties") -def keystoreProperties = new Properties() - keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) android { applicationId "pl.baftek.discoverrudy" minSdk 21 targetSdk 34 - versionCode flutterVersionCode.toInteger() - versionName flutterVersionName + versionCode flutter.versionCode() + versionName flutter.versionName() } ``` The boilerplate that loads 'local.properties' can live in Flutter Gradle Plugin. ### Concerns I was worried about lifecycle/ordering issues, so I tested it. To Flutter Gradle Plugin, I added: ```diff class FlutterPlugin implements Plugin { //... @Override void apply(Project project) { + project.logger.quiet("Start applying FGP") // ... } } ``` and to my `android/app/build.gradle` I added: ```diff android { + logger.quiet("Start evaluating android block") namespace "pl.bartekpacia.awesomeapp" compileSdk 34 defaultConfig { applicationId "pl.baftek.discoverrudy" minSdk 21 targetSdk 34 versionCode flutter.versionCode() versionName flutter.versionName() } ``` Gradle first applies the plugins (which sets versionCode and versionName on FlutterExtension), and then it executes the `android {}` extension block: ``` $ ./gradlew :app:assembleDebug > Configure project :app Start applying FGP Start evaluating android block BUILD SUCCESSFUL in 2s 383 actionable tasks: 10 executed, 373 up-to-date ``` So ordering is fine. --- examples/hello_world/android/app/build.gradle | 22 +------- examples/layers/android/app/build.gradle | 14 +---- .../gradle/src/main/groovy/flutter.groovy | 51 ++++++++++++++++++- 3 files changed, 54 insertions(+), 33 deletions(-) diff --git a/examples/hello_world/android/app/build.gradle b/examples/hello_world/android/app/build.gradle index 67db8d3fbe0e..5d571d1d78eb 100644 --- a/examples/hello_world/android/app/build.gradle +++ b/examples/hello_world/android/app/build.gradle @@ -7,24 +7,6 @@ plugins { id "dev.flutter.flutter-gradle-plugin" } -def localProperties = new Properties() -def localPropertiesFile = rootProject.file('local.properties') -if (localPropertiesFile.exists()) { - localPropertiesFile.withReader('UTF-8') { reader -> - localProperties.load(reader) - } -} - -def flutterVersionCode = localProperties.getProperty('flutter.versionCode') -if (flutterVersionCode == null) { - flutterVersionCode = '1' -} - -def flutterVersionName = localProperties.getProperty('flutter.versionName') -if (flutterVersionName == null) { - flutterVersionName = '1.0' -} - android { namespace "io.flutter.examples.hello_world" compileSdk flutter.compileSdkVersion @@ -38,8 +20,8 @@ android { applicationId "io.flutter.examples.hello_world" minSdkVersion flutter.minSdkVersion targetSdkVersion flutter.targetSdkVersion - versionCode flutterVersionCode.toInteger() - versionName flutterVersionName + versionCode flutter.versionCode() + versionName flutter.versionName() } buildTypes { diff --git a/examples/layers/android/app/build.gradle b/examples/layers/android/app/build.gradle index 322731cf699b..33437aaa29b1 100644 --- a/examples/layers/android/app/build.gradle +++ b/examples/layers/android/app/build.gradle @@ -15,16 +15,6 @@ if (flutterRoot == null) { throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") } -def flutterVersionCode = localProperties.getProperty('flutter.versionCode') -if (flutterVersionCode == null) { - flutterVersionCode = '1' -} - -def flutterVersionName = localProperties.getProperty('flutter.versionName') -if (flutterVersionName == null) { - flutterVersionName = '1.0' -} - apply plugin: 'com.android.application' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" @@ -41,8 +31,8 @@ android { applicationId "io.flutter.examples.layers" minSdkVersion flutter.minSdkVersion targetSdkVersion flutter.targetSdkVersion - versionCode flutterVersionCode.toInteger() - versionName flutterVersionName + versionCode flutter.versionCode() + versionName flutter.versionName() } buildTypes { diff --git a/packages/flutter_tools/gradle/src/main/groovy/flutter.groovy b/packages/flutter_tools/gradle/src/main/groovy/flutter.groovy index 772d31222362..375e293bd9db 100644 --- a/packages/flutter_tools/gradle/src/main/groovy/flutter.groovy +++ b/packages/flutter_tools/gradle/src/main/groovy/flutter.groovy @@ -69,6 +69,34 @@ class FlutterExtension { /** Allows to override the target file. Otherwise, the target is lib/main.dart. */ String target + + /** The versionCode that was read from app's local.properties. */ + String flutterVersionCode = null + + /** The versionName that was read from app's local.properties. */ + String flutterVersionName = null + + /** Returns flutterVersionCode as an integer with error handling. */ + Integer versionCode() { + if (flutterVersionCode == null) { + throw new GradleException("flutterVersionCode must not be null.") + } + + if (!flutterVersionCode.isNumber()) { + throw new GradleException("flutterVersionCode must be an integer.") + } + + return flutterVersionCode.toInteger() + } + + /** Returns flutterVersionName with error handling. */ + String versionName() { + if (flutterVersionName == null) { + throw new GradleException("flutterVersionName must not be null.") + } + + return flutterVersionName + } } // This buildscript block supplies dependencies for this file's own import @@ -226,7 +254,28 @@ class FlutterPlugin implements Plugin { // Load shared gradle functions project.apply from: Paths.get(flutterRoot.absolutePath, "packages", "flutter_tools", "gradle", "src", "main", "groovy", "native_plugin_loader.groovy") - project.extensions.create("flutter", FlutterExtension) + def extension = project.extensions.create("flutter", FlutterExtension) + def localProperties = new Properties() + def localPropertiesFile = rootProject.file("local.properties") + if (localPropertiesFile.exists()) { + localPropertiesFile.withReader("UTF-8") { reader -> + localProperties.load(reader) + } + } + + def flutterVersionCode = localProperties.getProperty("flutter.versionCode") + if (flutterVersionCode == null) { + flutterVersionCode = "1" + } + extension.flutterVersionCode = flutterVersionCode + + + def flutterVersionName = localProperties.getProperty("flutter.versionName") + if (flutterVersionName == null) { + flutterVersionName = "1.0" + } + extension.flutterVersionName = flutterVersionName + this.addFlutterTasks(project) // By default, assembling APKs generates fat APKs if multiple platforms are passed.