[Android] Actually remove dev dependencies from release builds (#161343)

Revises https://github.com/flutter/flutter/pull/158026 to fix
https://github.com/flutter/flutter/issues/160407.

Makes a number of fixes:

- Fixes Groovy logic that attempted to remove dev dependencies from
release builds to use `<buildType>Api` versus `api` and loop to
configure the project dependencies per build type
- Adds back dependency on embedding to plugin projects since this is
needed regardless (the plugin may not be included in a particularly
typed build but it still needs it for where it's included)
- Fixes integration test to throw and error upon failure, check right
APK for the release build it's testing, and configure the flag need for
`.flutter-plugin-dependencies` to mark plugins as dev dependencies as
expected
- Uses @matanlurey's
[patch](https://github.com/flutter/flutter/issues/160407#issuecomment-2547546038)
to remove dev dependency from the `GeneratedPluginRegistrant` in release
mode

## 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.
- [ ] All existing and new tests are passing.

<!-- 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
This commit is contained in:
Camille Simon 2025-01-14 13:36:52 -06:00 committed by GitHub
parent 0369b35640
commit 818133b8b0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 37 additions and 20 deletions

View File

@ -15,6 +15,9 @@ Future<void> main() async {
await task(() async { await task(() async {
try { try {
await runProjectTest((FlutterProject flutterProject) async { await runProjectTest((FlutterProject flutterProject) async {
// Enable plugins being marked as dev dependncies in the .flutter-plugins-dependencies file.
await flutter('config', options: <String>['--explicit-package-dependencies']);
// Create dev_dependency plugin to use for test. // Create dev_dependency plugin to use for test.
final Directory tempDir = Directory.systemTemp.createTempSync( final Directory tempDir = Directory.systemTemp.createTempSync(
'android_release_builds_exclude_dev_dependencies_test.', 'android_release_builds_exclude_dev_dependencies_test.',
@ -50,7 +53,7 @@ Future<void> main() async {
'app', 'app',
'outputs', 'outputs',
'flutter-apk', 'flutter-apk',
'app-debug.apk', 'app-$buildMode.apk',
), ),
); );
if (!apk.existsSync()) { if (!apk.existsSync()) {
@ -66,7 +69,7 @@ Future<void> main() async {
final bool apkIncludesDevDependencyAsExpected = final bool apkIncludesDevDependencyAsExpected =
isTestingReleaseMode ? !apkIncludesDevDependency : apkIncludesDevDependency; isTestingReleaseMode ? !apkIncludesDevDependency : apkIncludesDevDependency;
if (!apkIncludesDevDependencyAsExpected) { if (!apkIncludesDevDependencyAsExpected) {
return TaskResult.failure( throw TaskResult.failure(
'Expected to${isTestingReleaseMode ? ' not' : ''} find dev_dependency_plugin in APK built with debug mode but did${isTestingReleaseMode ? '' : ' not'}.', 'Expected to${isTestingReleaseMode ? ' not' : ''} find dev_dependency_plugin in APK built with debug mode but did${isTestingReleaseMode ? '' : ' not'}.',
); );
} }

View File

@ -738,13 +738,12 @@ class FlutterPlugin implements Plugin<Project> {
// compile/target/min sdk values. // compile/target/min sdk values.
pluginProject.extensions.create("flutter", FlutterExtension) pluginProject.extensions.create("flutter", FlutterExtension)
// Add plugin dependency to the app project. // Add plugin dependency to the app project. We only want to add dependency
project.android.buildTypes.each { buildType -> // for dev dependencies in non-release builds.
String flutterBuildMode = buildModeFor(buildType) project.afterEvaluate {
if (flutterBuildMode != "release" || !pluginObject.dev_dependency) { project.android.buildTypes.all { buildType ->
// Only add dependency on dev dependencies in non-release builds. if (!pluginObject.dev_dependency || buildType.name != 'release') {
project.dependencies { project.dependencies.add("${buildType.name}Api", pluginProject)
api(pluginProject)
} }
} }
} }
@ -760,12 +759,6 @@ class FlutterPlugin implements Plugin<Project> {
if (!pluginProject.hasProperty("android")) { if (!pluginProject.hasProperty("android")) {
return return
} }
if (flutterBuildMode == "release" && pluginObject.dev_dependency) {
// This plugin is a dev dependency and will not be included in
// the release build, so no need to add the embedding
// dependency to it.
return
}
// Copy build types from the app to the plugin. // Copy build types from the app to the plugin.
// This allows to build apps with plugins and custom build types or flavors. // This allows to build apps with plugins and custom build types or flavors.
pluginProject.android.buildTypes { pluginProject.android.buildTypes {

View File

@ -356,18 +356,27 @@ public final class GeneratedPluginRegistrant {
} }
'''; ''';
List<Map<String, Object?>> _extractPlatformMaps(List<Plugin> plugins, String type) { List<Map<String, Object?>> _extractPlatformMaps(Iterable<Plugin> plugins, String type) {
return <Map<String, Object?>>[ return <Map<String, Object?>>[
for (final Plugin plugin in plugins) for (final Plugin plugin in plugins)
if (plugin.platforms[type] case final PluginPlatform platformPlugin) platformPlugin.toMap(), if (plugin.platforms[type] case final PluginPlatform platformPlugin) platformPlugin.toMap(),
]; ];
} }
Future<void> _writeAndroidPluginRegistrant(FlutterProject project, List<Plugin> plugins) async { Future<void> _writeAndroidPluginRegistrant(
final List<Plugin> methodChannelPlugins = _filterMethodChannelPlugins( FlutterProject project,
List<Plugin> plugins, {
required bool releaseMode,
}) async {
Iterable<Plugin> methodChannelPlugins = _filterMethodChannelPlugins(
plugins, plugins,
AndroidPlugin.kConfigKey, AndroidPlugin.kConfigKey,
); );
// TODO(camsim99): Remove dev dependencies from release builds for all platforms. See https://github.com/flutter/flutter/issues/161348.
if (releaseMode) {
methodChannelPlugins = methodChannelPlugins.where((Plugin p) => !p.isDevDependency);
}
final List<Map<String, Object?>> androidPlugins = _extractPlatformMaps( final List<Map<String, Object?>> androidPlugins = _extractPlatformMaps(
methodChannelPlugins, methodChannelPlugins,
AndroidPlugin.kConfigKey, AndroidPlugin.kConfigKey,
@ -1214,6 +1223,7 @@ Future<void> injectPlugins(
bool windowsPlatform = false, bool windowsPlatform = false,
Iterable<String>? allowedPlugins, Iterable<String>? allowedPlugins,
DarwinDependencyManagement? darwinDependencyManagement, DarwinDependencyManagement? darwinDependencyManagement,
bool? releaseMode,
}) async { }) async {
final List<Plugin> plugins = await findPlugins(project); final List<Plugin> plugins = await findPlugins(project);
final Map<String, List<Plugin>> pluginsByPlatform = _resolvePluginImplementations( final Map<String, List<Plugin>> pluginsByPlatform = _resolvePluginImplementations(
@ -1222,7 +1232,11 @@ Future<void> injectPlugins(
); );
if (androidPlatform) { if (androidPlatform) {
await _writeAndroidPluginRegistrant(project, pluginsByPlatform[AndroidPlugin.kConfigKey]!); await _writeAndroidPluginRegistrant(
project,
pluginsByPlatform[AndroidPlugin.kConfigKey]!,
releaseMode: releaseMode ?? false,
);
} }
if (iosPlatform) { if (iosPlatform) {
await _writeIOSPluginRegistrant(project, pluginsByPlatform[IOSPlugin.kConfigKey]!); await _writeIOSPluginRegistrant(project, pluginsByPlatform[IOSPlugin.kConfigKey]!);

View File

@ -332,6 +332,7 @@ class FlutterProject {
Future<void> regeneratePlatformSpecificTooling({ Future<void> regeneratePlatformSpecificTooling({
DeprecationBehavior deprecationBehavior = DeprecationBehavior.none, DeprecationBehavior deprecationBehavior = DeprecationBehavior.none,
Iterable<String>? allowedPlugins, Iterable<String>? allowedPlugins,
bool? releaseMode,
}) async { }) async {
return ensureReadyForPlatformSpecificTooling( return ensureReadyForPlatformSpecificTooling(
androidPlatform: android.existsSync(), androidPlatform: android.existsSync(),
@ -344,6 +345,7 @@ class FlutterProject {
webPlatform: featureFlags.isWebEnabled && web.existsSync(), webPlatform: featureFlags.isWebEnabled && web.existsSync(),
deprecationBehavior: deprecationBehavior, deprecationBehavior: deprecationBehavior,
allowedPlugins: allowedPlugins, allowedPlugins: allowedPlugins,
releaseMode: releaseMode,
); );
} }
@ -358,6 +360,7 @@ class FlutterProject {
bool webPlatform = false, bool webPlatform = false,
DeprecationBehavior deprecationBehavior = DeprecationBehavior.none, DeprecationBehavior deprecationBehavior = DeprecationBehavior.none,
Iterable<String>? allowedPlugins, Iterable<String>? allowedPlugins,
bool? releaseMode,
}) async { }) async {
if (!directory.existsSync() || isPlugin) { if (!directory.existsSync() || isPlugin) {
return; return;
@ -389,6 +392,7 @@ class FlutterProject {
macOSPlatform: macOSPlatform, macOSPlatform: macOSPlatform,
windowsPlatform: windowsPlatform, windowsPlatform: windowsPlatform,
allowedPlugins: allowedPlugins, allowedPlugins: allowedPlugins,
releaseMode: releaseMode,
); );
} }

View File

@ -1907,7 +1907,10 @@ Run 'flutter -h' (or 'flutter <command> -h') for available flutter commands and
// The preview device does not currently support any plugins. // The preview device does not currently support any plugins.
allowedPlugins = PreviewDevice.supportedPubPlugins; allowedPlugins = PreviewDevice.supportedPubPlugins;
} }
await project.regeneratePlatformSpecificTooling(allowedPlugins: allowedPlugins); await project.regeneratePlatformSpecificTooling(
allowedPlugins: allowedPlugins,
releaseMode: featureFlags.isExplicitPackageDependenciesEnabled && getBuildMode().isRelease,
);
if (reportNullSafety) { if (reportNullSafety) {
await _sendNullSafetyAnalyticsEvents(project); await _sendNullSafetyAnalyticsEvents(project);
} }