Skip to content

Commit bd6f555

Browse files
mboetgerIvoneDjaja
authored andcommitted
Respect product flavor abiFilters by adding a disable-abi-filtering Android project flag. (flutter#177753)
This PR introduces a flag (`disable-abi-filtering`) that disables the FlutterPlugin's abiFiltering on buildTypes. This should only be used when the developers know what they are doing. Specifically this allows developers to set their own abiFilters in ProductFlavors or defaultConfig. Because Gradle has complex merging logic, there is a hierarchy of priority when calculating settings (like abiFilters). For example: defaultConfig < productFlavors < buildTypes when combining the three into the final build variant. If abiFilters are set in buildType, it will override abiFilters in productFlavors. When the FlutterPlugin executes, it cannot know about the developers build.gradle settings and therefore we cannot add logic that checks to see if abiFilters are set in productFlavors. Therefore, this flag gives developers an escape hatch so they can implement what they want. Fixes: flutter#175845 ## 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]. - [X] 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. - [X] All existing and new tests are passing.
1 parent 0e477e6 commit bd6f555

File tree

3 files changed

+86
-10
lines changed

3 files changed

+86
-10
lines changed

packages/flutter_tools/gradle/src/main/kotlin/FlutterPlugin.kt

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -166,13 +166,15 @@ class FlutterPlugin : Plugin<Project> {
166166
//
167167
// If the user has specified abiFilters in their build.gradle file, those
168168
// settings will take precedence over these defaults.
169-
FlutterPluginUtils.getAndroidExtension(project).buildTypes.forEach { buildType ->
170-
buildType.ndk.abiFilters.clear()
171-
FlutterPluginConstants.DEFAULT_PLATFORMS.forEach { platform ->
172-
val abiValue: String =
173-
FlutterPluginConstants.PLATFORM_ARCH_MAP[platform]
174-
?: throw GradleException("Invalid platform: $platform")
175-
buildType.ndk.abiFilters.add(abiValue)
169+
if (!FlutterPluginUtils.shouldProjectDisableAbiFiltering(project)) {
170+
FlutterPluginUtils.getAndroidExtension(project).buildTypes.forEach { buildType ->
171+
buildType.ndk.abiFilters.clear()
172+
FlutterPluginConstants.DEFAULT_PLATFORMS.forEach { platform ->
173+
val abiValue: String =
174+
FlutterPluginConstants.PLATFORM_ARCH_MAP[platform]
175+
?: throw GradleException("Invalid platform: $platform")
176+
buildType.ndk.abiFilters.add(abiValue)
177+
}
176178
}
177179
}
178180
}

packages/flutter_tools/gradle/src/main/kotlin/FlutterPluginUtils.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ object FlutterPluginUtils {
3636
internal const val PROP_TARGET = "target"
3737
internal const val PROP_LOCAL_ENGINE_BUILD_MODE = "local-engine-build-mode"
3838
internal const val PROP_TARGET_PLATFORM = "target-platform"
39+
internal const val PROP_DISABLE_ABI_FILTERING = "disable-abi-filtering"
3940

4041
// ----------------- Methods for string manipulation and comparison. -----------------
4142

@@ -203,6 +204,10 @@ object FlutterPluginUtils {
203204
@JvmName("isProjectVerbose")
204205
internal fun isProjectVerbose(project: Project): Boolean = project.findProperty(PROP_IS_VERBOSE)?.toString()?.toBoolean() ?: false
205206

207+
@JvmStatic
208+
@JvmName("shouldProjectDisableAbiFiltering")
209+
internal fun shouldProjectDisableAbiFiltering(project: Project): Boolean = project.hasProperty(PROP_DISABLE_ABI_FILTERING)
210+
206211
/**
207212
* TODO: Remove this AGP hack. https://github.com/flutter/flutter/issues/109560
208213
*

packages/flutter_tools/test/integration.shard/gradle_jni_packaging_test.dart

Lines changed: 72 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,68 @@ void main() {
7777
expect(_checkLibIsInApk(projectDir, 'lib/armeabi-v7a/libflutter.so'), false);
7878
expect(_checkLibIsInApk(projectDir, 'lib/x86/libflutter.so'), false);
7979
});
80+
81+
testWithoutContext(
82+
'abiFilters in product flavors provided by the user take precedence over the default',
83+
() async {
84+
final Directory projectDir = createProjectWithThirdpartyLib(tempDir);
85+
final String buildGradleContents = projectDir
86+
.childFile('android/app/build.gradle.kts')
87+
.readAsStringSync();
88+
89+
const productFlavorsBlock = '''
90+
flavorDimensions += listOf("device")
91+
productFlavors {
92+
create("arm64") {
93+
dimension = "device"
94+
ndk {
95+
abiFilters.clear()
96+
abiFilters.addAll(listOf("arm64-v8a"))
97+
}
98+
}
99+
create("armeabi") {
100+
dimension = "device"
101+
ndk {
102+
abiFilters.clear()
103+
abiFilters.addAll(listOf("armeabi-v7a"))
104+
}
105+
}
106+
}''';
107+
// Modify the project's build.gradle.kts file to include abiFilters for product flavors.
108+
final String updatedBuildGradleContents = buildGradleContents.replaceFirstMapped(
109+
RegExp(r'^(android\s*\{)', multiLine: true),
110+
(Match match) => '${match.group(1)!}\n$productFlavorsBlock',
111+
);
112+
projectDir
113+
.childFile('android/app/build.gradle.kts')
114+
.writeAsStringSync(updatedBuildGradleContents);
115+
116+
processManager.runSync(<String>[
117+
flutterBin,
118+
'build',
119+
'apk',
120+
'--release',
121+
'--flavor',
122+
'arm64',
123+
'-P',
124+
'disable-abi-filtering=true',
125+
], workingDirectory: projectDir.path);
126+
127+
expect(
128+
_checkLibIsInApk(projectDir, 'lib/arm64-v8a/libflutter.so', productFlavor: 'arm64'),
129+
true,
130+
);
131+
expect(
132+
_checkLibIsInApk(projectDir, 'lib/x86_64/libflutter.so', productFlavor: 'arm64'),
133+
false,
134+
);
135+
expect(
136+
_checkLibIsInApk(projectDir, 'lib/armeabi-v7a/libflutter.so', productFlavor: 'arm64'),
137+
false,
138+
);
139+
expect(_checkLibIsInApk(projectDir, 'lib/x86/libflutter.so', productFlavor: 'arm64'), false);
140+
},
141+
);
80142
}
81143

82144
Directory createProjectWithThirdpartyLib(Directory workingDir) {
@@ -118,6 +180,7 @@ bool _checkLibIsInApk(
118180
Directory appDir,
119181
String filename, {
120182
BuildMode buildMode = BuildMode.release,
183+
String productFlavor = '',
121184
}) {
122185
final File localPropertiesFile = appDir.childDirectory('android').childFile('local.properties');
123186
if (!localPropertiesFile.existsSync()) {
@@ -139,9 +202,15 @@ bool _checkLibIsInApk(
139202
.childFile(Platform.isWindows ? 'apkanalyzer.bat' : 'apkanalyzer')
140203
.path;
141204

142-
final File apkFile = appDir
143-
.childDirectory('build/app/outputs/apk/${buildMode.cliName}')
144-
.childFile('app-${buildMode.cliName}.apk');
205+
final apkName = (productFlavor.isEmpty)
206+
? 'app-${buildMode.cliName}.apk'
207+
: 'app-$productFlavor-${buildMode.cliName}.apk';
208+
209+
final String apkDir = (productFlavor.isEmpty)
210+
? buildMode.cliName
211+
: '$productFlavor/${buildMode.cliName}';
212+
213+
final File apkFile = appDir.childDirectory('build/app/outputs/apk/$apkDir').childFile(apkName);
145214

146215
if (!apkFile.existsSync()) {
147216
throw StateError('APK file not found at ${apkFile.path}');

0 commit comments

Comments
 (0)