Visitar URL original
[AGP 9.1.0 Migration #4] Replace the plugin build-type copy with init… · flutter/flutter@20c1a4c · GitHub
Skip to content

Commit 20c1a4c

Browse files
[AGP 9.1.0 Migration #4] Replace the plugin build-type copy with initWith on the public DSL
- PluginHandler no longer imports com.android.build.gradle.internal.dsl.BuildType. The build-type copy block that shared live legacy BuildType instances (addAll) for app-type plugin projects and hand-copied two properties for library plugin projects is replaced by a single initWith-based copy on the public DSL containers: missing build types are created on the plugin project with initWith(appBuildType) (which carries matchingFallbacks), and isDebuggable is additionally copied when both sides are application build types. Library build types cannot receive app-specific properties through the public DSL - this is a documented behavior change of the migration. - Production sources are now free of com.android.build.gradle.internal imports; InternalAgpApiImportTest locks that in. - PluginHandlerTest: replaced legacy mock-only copy tests with tests that run configurePlugins and assert the initWith copy for both library and app plugin projects, including the custom-debuggable-build-type -> debug engine artifact mapping and skipping pre-existing build types. - Updated documentation with P3 pre-spike and finalizeDsl fallback details.
1 parent e5ef1d0 commit 20c1a4c

5 files changed

Lines changed: 216 additions & 152 deletions

File tree

‎docs/platforms/android/Migrating-Flutter-Gradle-Plugin-to-AGP-public-API.md‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,14 @@ projects. That opt-out dies with AGP 10.
8484
signal (the library-plugin build-type copy in `PluginHandler`). This
8585
preserves add-to-app custom-debuggable matching (a host `staging`
8686
debuggable build type maps to debug engine artifacts).
87+
6. **P3 pre-spike / PR 4 (afterEvaluate DSL mutation under newDsl).** The planned scratch-app
88+
spike (AGP 9.1 + `newDsl=true` + custom build type, verifying that build-type
89+
creation from `pluginProject.afterEvaluate` still works) could not run in the
90+
implementation sandbox (no AGP artifact access). The `initWith` copy landed on the
91+
primary approach; the `android_plugin_example_app_build` integration test and a
92+
custom-build-type scratch build must confirm it in CI. Documented fallback if
93+
`afterEvaluate` mutation is rejected under newDsl: perform the copy in
94+
`androidComponents.finalizeDsl` on the plugin project instead.
8795

8896
## Replacement map
8997

‎packages/flutter_tools/gradle/src/main/kotlin/plugins/PluginHandler.kt‎

Lines changed: 33 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,14 @@
44

55
package com.flutter.gradle.plugins
66

7+
import com.android.build.api.dsl.ApplicationBuildType
78
import com.flutter.gradle.CompileSdkVersion
89
import com.flutter.gradle.FlutterExtension
910
import com.flutter.gradle.FlutterPluginUtils
1011
import com.flutter.gradle.FlutterPluginUtils.addApiDependencies
1112
import com.flutter.gradle.FlutterPluginUtils.buildModeFor
1213
import com.flutter.gradle.FlutterPluginUtils.getAndroidExtension
1314
import com.flutter.gradle.FlutterPluginUtils.getCompileSdkFromProject
14-
import com.flutter.gradle.FlutterPluginUtils.getLegacyAndroidExtension
15-
import com.flutter.gradle.FlutterPluginUtils.isBuiltAsApp
1615
import com.flutter.gradle.FlutterPluginUtils.supportsBuildMode
1716
import com.flutter.gradle.NativePluginLoaderReflectionBridge
1817
import org.gradle.api.Project
@@ -132,12 +131,41 @@ class PluginHandler(
132131
)
133132
}
134133

134+
copyAppBuildTypesToPlugin(project, pluginProject)
135+
135136
getAndroidExtension(project).buildTypes.forEach { buildType ->
136137
addEmbeddingDependencyToPlugin(project, pluginProject, buildType, engineVersion)
137138
}
138139
}
139140
}
140141

142+
private fun copyAppBuildTypesToPlugin(
143+
project: Project,
144+
pluginProject: Project
145+
) {
146+
if (!pluginProject.hasProperty("android")) {
147+
return
148+
}
149+
150+
// Copy the app project's build types onto the plugin project so that its variants
151+
// resolve. These are `initWith` copies, not live aliases: `initWith` copies the
152+
// properties both build types understand (matchingFallbacks included), and
153+
// app-specific properties are additionally copied when both sides are application
154+
// build types. Library build types cannot receive app-specific properties (such
155+
// as isDebuggable) through the public DSL.
156+
val pluginProjectBuildTypes = getAndroidExtension(pluginProject).buildTypes
157+
getAndroidExtension(project).buildTypes.forEach { appBuildType ->
158+
if (pluginProjectBuildTypes.findByName(appBuildType.name) == null) {
159+
pluginProjectBuildTypes.create(appBuildType.name) {
160+
initWith(appBuildType)
161+
if (this is ApplicationBuildType && appBuildType is ApplicationBuildType) {
162+
isDebuggable = appBuildType.isDebuggable
163+
}
164+
}
165+
}
166+
}
167+
}
168+
141169
private fun addEmbeddingDependencyToPlugin(
142170
project: Project,
143171
pluginProject: Project,
@@ -157,33 +185,6 @@ class PluginHandler(
157185
return
158186
}
159187

160-
// Copy build types from the app to the plugin.
161-
// This allows to build apps with plugins and custom build types or flavors.
162-
// However, only copy if the plugin is also an app project, since library projects
163-
// cannot have applicationIdSuffix and other app-specific properties.
164-
if (isBuiltAsApp(pluginProject)) {
165-
getLegacyAndroidExtension(project).buildTypes.forEach { appBuildType ->
166-
val pluginBuildTypes = getLegacyAndroidExtension(pluginProject).buildTypes
167-
if (pluginBuildTypes.findByName(appBuildType.name) == null) {
168-
pluginBuildTypes.create(appBuildType.name) {
169-
initWith(appBuildType)
170-
}
171-
}
172-
}
173-
} else {
174-
// For library projects, create compatible build types without app-specific properties
175-
getLegacyAndroidExtension(project).buildTypes.forEach { appBuildType ->
176-
if (getLegacyAndroidExtension(pluginProject).buildTypes.findByName(appBuildType.name) == null) {
177-
getLegacyAndroidExtension(pluginProject).buildTypes.create(appBuildType.name) {
178-
// Copy library-compatible properties only
179-
isDebuggable = appBuildType.isDebuggable
180-
isMinifyEnabled = appBuildType.isMinifyEnabled
181-
// Note: applicationIdSuffix and other app-specific properties are intentionally not copied
182-
}
183-
}
184-
}
185-
}
186-
187188
// The embedding is API dependency of the plugin, so the AGP is able to desugar
188189
// default method implementations when the interface is implemented by a plugin.
189190
//
@@ -218,6 +219,9 @@ class PluginHandler(
218219
}
219220
val pluginProject: Project = project.rootProject.findProject(":$pluginName") ?: return
220221

222+
// Warning: Iterating buildTypes causes the project-level 'implementation' dependency to be added multiple times.
223+
// When an app defines 3 build types (debug, profile, release), this registers 3
224+
// separate afterEvaluate actions adding the same dependencyProject.
221225
getAndroidExtension(project).buildTypes.forEach { buildType ->
222226
val flutterBuildMode: String = buildModeFor(buildType)
223227
if (flutterBuildMode == "release" && (pluginObject["dev_dependency"] as? Boolean == true)) {
@@ -235,7 +239,6 @@ class PluginHandler(
235239
val dependencyProject =
236240
project.rootProject.findProject(":$pluginDependencyName") ?: return@innerForEach
237241
pluginProject.afterEvaluate {
238-
// this.dependencies.add("implementation", dependencyProject)
239242
pluginProject.dependencies.add("implementation", dependencyProject)
240243
}
241244
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// Copyright 2014 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
package com.flutter.gradle
6+
7+
import java.io.File
8+
import kotlin.test.Test
9+
import kotlin.test.assertTrue
10+
11+
/**
12+
* Guards the AGP public-API migration (https://github.com/flutter/flutter/issues/180137):
13+
* production sources must not use AGP internals. AGP 10 removes access to internals
14+
* entirely, and the Flutter Gradle Plugin will compile against the `gradle-api` artifact,
15+
* where they do not exist. Test sources may still reference internal types until the
16+
* dependency swap.
17+
*/
18+
class InternalAgpApiImportTest {
19+
@Test
20+
fun `main sources do not import AGP internals`() {
21+
val workingDir = File(".")
22+
val mainSources =
23+
listOf(
24+
File("src/main"),
25+
File("packages/flutter_tools/gradle/src/main")
26+
).firstOrNull { it.isDirectory }
27+
assertTrue(
28+
mainSources != null,
29+
"Expected to find src/main relative to test working directory (${workingDir.absolutePath})."
30+
)
31+
val internalApiPattern = Regex("""\bcom\.android\.build\.gradle\.internal\b|\bcom\.android\.builder\.internal\b""")
32+
val offendingLines =
33+
mainSources
34+
.walkTopDown()
35+
.filter { it.isFile && it.extension in setOf("kt", "java", "groovy", "gradle") }
36+
.flatMap { file ->
37+
file.readLines().mapIndexedNotNull { index, line ->
38+
if (internalApiPattern.containsMatchIn(line)) {
39+
"${file.path}:${index + 1}: ${line.trim()}"
40+
} else {
41+
null
42+
}
43+
}
44+
}.toList()
45+
assertTrue(
46+
offendingLines.isEmpty(),
47+
"AGP internal APIs must not be used in production sources; they are removed in " +
48+
"AGP 10. Use the public com.android.build.api surface (see " +
49+
"docs/platforms/android/Migrating-Flutter-Gradle-Plugin-to-AGP-public-API.md).\n" +
50+
offendingLines.joinToString("\n")
51+
)
52+
}
53+
}

0 commit comments

Comments
 (0)