mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
Convert base application name handling to kotlin source (start of FGP kt conversion) (#155963)
Wires up a new gradle subproject defining kotlin classes to be used by the FGP, so that we can incrementally move the entire plugin to be written in kotlin source. Starts by moving a piece of kotlin script. ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [ ] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [ ] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md --------- Co-authored-by: Gray Mackall <mackall@google.com>
This commit is contained in:
parent
ee8aab77fe
commit
6e8d80743d
@ -2,14 +2,33 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
|
||||
plugins {
|
||||
`java-gradle-plugin`
|
||||
groovy
|
||||
`kotlin-dsl`
|
||||
kotlin("jvm") version "1.9.20"
|
||||
}
|
||||
|
||||
group = "dev.flutter.plugin"
|
||||
version = "1.0.0"
|
||||
|
||||
// Optional: enable stricter validation, to ensure Gradle configuration is correct
|
||||
tasks.validatePlugins {
|
||||
enableStricterValidation.set(true)
|
||||
}
|
||||
|
||||
// We need to compile Kotlin first so we can call it from Groovy. See https://stackoverflow.com/q/36214437/7009800
|
||||
tasks.withType<GroovyCompile> {
|
||||
dependsOn(tasks.compileKotlin)
|
||||
classpath += files(tasks.compileKotlin.get().destinationDirectory)
|
||||
}
|
||||
|
||||
tasks.classes {
|
||||
dependsOn(tasks.compileGroovy)
|
||||
}
|
||||
|
||||
gradlePlugin {
|
||||
plugins {
|
||||
// The "flutterPlugin" name isn't used anywhere.
|
||||
@ -25,10 +44,28 @@ gradlePlugin {
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType<JavaCompile> {
|
||||
options.release.set(11)
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
kotlin {
|
||||
compilerOptions {
|
||||
jvmTarget.set(JvmTarget.JVM_11)
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// When bumping, also update:
|
||||
// * 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 in the buildscript block in packages/flutter_tools/gradle/src/main/kotlin_scripts/dependency_version_checker.gradle.kts
|
||||
// * AGP version constants in packages/flutter_tools/lib/src/android/gradle_utils.dart
|
||||
compileOnly("com.android.tools.build:gradle:7.3.0")
|
||||
compileOnly("com.android.tools.build:gradle:8.7.3")
|
||||
|
||||
testImplementation(kotlin("test"))
|
||||
testImplementation("com.android.tools.build:gradle:8.7.3")
|
||||
testImplementation("org.mockito:mockito-core:4.8.0")
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
import com.android.build.OutputFile
|
||||
import com.flutter.gradle.BaseApplicationNameHandler
|
||||
import groovy.json.JsonGenerator
|
||||
import groovy.xml.QName
|
||||
import java.nio.file.Paths
|
||||
@ -299,7 +300,7 @@ class FlutterPlugin implements Plugin<Project> {
|
||||
if (!shouldSkipDependencyChecks) {
|
||||
try {
|
||||
final String dependencyCheckerPluginPath = Paths.get(flutterRoot.absolutePath,
|
||||
"packages", "flutter_tools", "gradle", "src", "main", "kotlin",
|
||||
"packages", "flutter_tools", "gradle", "src", "main", "kotlin_scripts",
|
||||
"dependency_version_checker.gradle.kts")
|
||||
project.apply from: dependencyCheckerPluginPath
|
||||
} catch (Exception e) {
|
||||
@ -317,8 +318,8 @@ class FlutterPlugin implements Plugin<Project> {
|
||||
}
|
||||
}
|
||||
|
||||
// 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")
|
||||
// Use Kotlin source to handle baseApplicationName logic due to Groovy dynamic dispatch bug.
|
||||
BaseApplicationNameHandler.setBaseName(project)
|
||||
|
||||
String flutterProguardRules = Paths.get(flutterRoot.absolutePath, "packages", "flutter_tools",
|
||||
"gradle", "flutter_proguard_rules.pro")
|
||||
|
@ -0,0 +1,28 @@
|
||||
package com.flutter.gradle
|
||||
|
||||
import com.android.build.api.dsl.ApplicationExtension
|
||||
import org.gradle.api.Project
|
||||
|
||||
// TODO(gmackall): maybe migrate this to a package-level function when FGP conversion is done.
|
||||
object BaseApplicationNameHandler {
|
||||
internal const val DEFAULT_BASE_APPLICATION_NAME: String = "android.app.Application"
|
||||
|
||||
internal const val GRADLE_BASE_APPLICATION_NAME_PROPERTY: String = "base-application-name"
|
||||
|
||||
@JvmStatic fun setBaseName(project: Project) {
|
||||
// Only set the base application name for apps, skip otherwise (LibraryExtension, DynamicFeatureExtension).
|
||||
val androidComponentsExtension: ApplicationExtension =
|
||||
project.extensions.findByType(ApplicationExtension::class.java) ?: return
|
||||
|
||||
// Setting to android.app.Application is the same as omitting the attribute.
|
||||
var baseApplicationName: String = DEFAULT_BASE_APPLICATION_NAME
|
||||
|
||||
// Respect this property if it set by the Flutter tool.
|
||||
if (project.hasProperty(GRADLE_BASE_APPLICATION_NAME_PROPERTY)) {
|
||||
baseApplicationName = project.property(GRADLE_BASE_APPLICATION_NAME_PROPERTY).toString()
|
||||
}
|
||||
|
||||
androidComponentsExtension.defaultConfig.manifestPlaceholders["applicationName"] =
|
||||
baseApplicationName
|
||||
}
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
// 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.
|
||||
|
||||
apply<FlutterPluginKts>()
|
||||
|
||||
class FlutterPluginKts : Plugin<Project> {
|
||||
override fun apply(project: Project) {
|
||||
// Use withGroovyBuilder and getProperty() to access Groovy metaprogramming.
|
||||
project.withGroovyBuilder {
|
||||
getProperty("android").withGroovyBuilder {
|
||||
getProperty("defaultConfig").withGroovyBuilder {
|
||||
var baseApplicationName: String = "android.app.Application"
|
||||
if (project.hasProperty("base-application-name")) {
|
||||
baseApplicationName = project.property("base-application-name").toString()
|
||||
}
|
||||
// Setting to android.app.Application is the same as omitting the attribute.
|
||||
getProperty("manifestPlaceholders").withGroovyBuilder {
|
||||
setProperty("applicationName", baseApplicationName)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
package com.flutter.gradle
|
||||
import com.android.build.api.dsl.ApplicationDefaultConfig
|
||||
import com.android.build.api.dsl.ApplicationExtension
|
||||
import com.flutter.gradle.BaseApplicationNameHandler.GRADLE_BASE_APPLICATION_NAME_PROPERTY
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.plugins.ExtensionContainer
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.mockito.Mockito
|
||||
import kotlin.test.Test
|
||||
|
||||
class BaseApplicationNameHandlerTest {
|
||||
@Test
|
||||
fun `setBaseName respects Flutter tool property`() {
|
||||
val baseApplicationNamePassedByFlutterTool = "toolSetBaseApplicationName"
|
||||
|
||||
// Set up mocks.
|
||||
val mockProject: Project = Mockito.mock(Project::class.java)
|
||||
val mockAndroidComponentsExtension: ApplicationExtension = Mockito.mock(ApplicationExtension::class.java)
|
||||
val mockExtensionContainer: ExtensionContainer = Mockito.mock(ExtensionContainer::class.java)
|
||||
val mockDefaultConfig = Mockito.mock(ApplicationDefaultConfig::class.java)
|
||||
val mockManifestPlaceholders = HashMap<String, Any>()
|
||||
|
||||
Mockito.`when`(mockProject.hasProperty(GRADLE_BASE_APPLICATION_NAME_PROPERTY)).thenReturn(true)
|
||||
Mockito.`when`(mockProject.property(GRADLE_BASE_APPLICATION_NAME_PROPERTY)).thenReturn(baseApplicationNamePassedByFlutterTool)
|
||||
|
||||
Mockito.`when`(mockProject.extensions).thenReturn(mockExtensionContainer)
|
||||
Mockito.`when`(mockExtensionContainer.findByType(ApplicationExtension::class.java)).thenReturn(mockAndroidComponentsExtension)
|
||||
Mockito.`when`(mockAndroidComponentsExtension.defaultConfig).thenReturn(mockDefaultConfig)
|
||||
Mockito.`when`(mockDefaultConfig.manifestPlaceholders).thenReturn(mockManifestPlaceholders)
|
||||
|
||||
// Call the base name handler.
|
||||
BaseApplicationNameHandler.setBaseName(mockProject)
|
||||
|
||||
// Make sure we set the value passed by the tool.
|
||||
assertEquals(mockManifestPlaceholders["applicationName"], baseApplicationNamePassedByFlutterTool)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `setBaseName defaults to correct value`() {
|
||||
// Set up mocks.
|
||||
val mockProject: Project = Mockito.mock(Project::class.java)
|
||||
val mockAndroidComponentsExtension: ApplicationExtension = Mockito.mock(ApplicationExtension::class.java)
|
||||
val mockExtensionContainer: ExtensionContainer = Mockito.mock(ExtensionContainer::class.java)
|
||||
val mockDefaultConfig = Mockito.mock(ApplicationDefaultConfig::class.java)
|
||||
val mockManifestPlaceholders = HashMap<String, Any>()
|
||||
|
||||
Mockito.`when`(mockProject.hasProperty(GRADLE_BASE_APPLICATION_NAME_PROPERTY)).thenReturn(false)
|
||||
|
||||
Mockito.`when`(mockProject.extensions).thenReturn(mockExtensionContainer)
|
||||
Mockito.`when`(mockExtensionContainer.findByType(ApplicationExtension::class.java)).thenReturn(mockAndroidComponentsExtension)
|
||||
Mockito.`when`(mockAndroidComponentsExtension.defaultConfig).thenReturn(mockDefaultConfig)
|
||||
Mockito.`when`(mockDefaultConfig.manifestPlaceholders).thenReturn(mockManifestPlaceholders)
|
||||
|
||||
// Call the base name handler.
|
||||
BaseApplicationNameHandler.setBaseName(mockProject)
|
||||
|
||||
// Make sure we default to the correct value.
|
||||
assertEquals(mockManifestPlaceholders["applicationName"], BaseApplicationNameHandler.DEFAULT_BASE_APPLICATION_NAME)
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
// 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/base/file_system.dart';
|
||||
import 'package:flutter_tools/src/base/process.dart';
|
||||
import 'package:flutter_tools/src/globals.dart' as globals;
|
||||
|
||||
import '../src/common.dart';
|
||||
import '../src/context.dart';
|
||||
import 'test_utils.dart';
|
||||
|
||||
void main() {
|
||||
testUsingContext('Flutter Gradle Plugin unit tests pass', () async {
|
||||
final String gradleFileName = Platform.isWindows ? 'gradlew.bat' : 'gradlew';
|
||||
final String gradleExecutable = Platform.isWindows ? '.\\$gradleFileName' : './$gradleFileName';
|
||||
final Directory flutterGradlePluginDirectory = fileSystem
|
||||
.directory(getFlutterRoot())
|
||||
.childDirectory('packages')
|
||||
.childDirectory('flutter_tools')
|
||||
.childDirectory('gradle');
|
||||
globals.gradleUtils?.injectGradleWrapperIfNeeded(flutterGradlePluginDirectory);
|
||||
makeExecutable(flutterGradlePluginDirectory.childFile(gradleFileName));
|
||||
final RunResult runResult = await globals.processUtils.run(<String>[
|
||||
gradleExecutable,
|
||||
'test',
|
||||
], workingDirectory: flutterGradlePluginDirectory.path);
|
||||
expect(runResult.exitCode, 0);
|
||||
});
|
||||
}
|
||||
|
||||
void makeExecutable(File file) {
|
||||
if (Platform.isWindows) {
|
||||
// no op.
|
||||
return;
|
||||
}
|
||||
|
||||
final ProcessResult result = processManager.runSync(<String>['chmod', '+x', file.path]);
|
||||
expect(result, const ProcessResultMatcher());
|
||||
}
|
Loading…
Reference in New Issue
Block a user