refactor: Perform plugin resolution per platform (#144506)

Part of #137040 and #80374

`possibleResolutions` and `possibleResolutions` used a key containing the platform and the plugin name via `getResolutionKey`. The global list is not necessary and also confusing. It can be avoided by handling the resolution of plugins per platform. This helps to have a more self-contained plugin resolution. This also is necessary to reuse the plugin resolution logic (per platform) for #137040.
This commit is contained in:
August 2024-04-05 02:19:12 +02:00 committed by GitHub
parent 8d928947ca
commit 9c6fcdac6f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -1137,15 +1137,14 @@ List<PluginInterfaceResolution> resolvePlatformImplementation(
MacOSPlugin.kConfigKey,
WindowsPlugin.kConfigKey,
];
final Map<String, List<PluginInterfaceResolution>> possibleResolutions =
<String, List<PluginInterfaceResolution>>{};
final Map<String, String> defaultImplementations = <String, String>{};
// Generates a key for the maps above.
String getResolutionKey({required String platform, required String packageName}) {
return '$packageName:$platform';
}
final List<PluginInterfaceResolution> finalResolution = <PluginInterfaceResolution>[];
bool hasResolutionError = false;
for (final String platformKey in platformKeys) {
// Key: the plugin name
final Map<String, List<Plugin>> possibleResolutions = <String, List<Plugin>>{};
final Map<String, String> defaultImplementations = <String, String>{};
for (final Plugin plugin in plugins) {
final String? defaultImplementation = plugin.defaultPackagePlatforms[platformKey];
if (plugin.platforms[platformKey] == null && defaultImplementation == null) {
@ -1160,9 +1159,8 @@ List<PluginInterfaceResolution> resolvePlatformImplementation(
// Skip native inline PluginPlatform implementation
continue;
}
final String defaultImplementationKey = getResolutionKey(platform: platformKey, packageName: plugin.name);
if (defaultImplementation != null) {
defaultImplementations[defaultImplementationKey] = defaultImplementation;
defaultImplementations[plugin.name] = defaultImplementation;
continue;
} else {
// An app-facing package (i.e., one with no 'implements') with an
@ -1182,7 +1180,7 @@ List<PluginInterfaceResolution> resolvePlatformImplementation(
minFlutterVersion.compareTo(semver.Version(2, 11, 0)) >= 0;
if (!isDesktop || hasMinVersionForImplementsRequirement) {
implementsPackage = plugin.name;
defaultImplementations[defaultImplementationKey] = plugin.name;
defaultImplementations[plugin.name] = plugin.name;
} else {
// If it doesn't meet any of the conditions, it isn't eligible for
// auto-registration.
@ -1198,63 +1196,63 @@ List<PluginInterfaceResolution> resolvePlatformImplementation(
// If it hasn't been skipped, it's a candidate for auto-registration, so
// add it as a possible resolution.
final String resolutionKey = getResolutionKey(platform: platformKey, packageName: implementsPackage);
possibleResolutions.putIfAbsent(resolutionKey, () => <PluginInterfaceResolution>[]);
possibleResolutions[resolutionKey]!.add(PluginInterfaceResolution(
plugin: plugin,
platform: platformKey,
));
possibleResolutions.putIfAbsent(implementsPackage, () => <Plugin>[]);
possibleResolutions[implementsPackage]!.add(plugin);
}
}
// Now resolve all the possible resolutions to a single option for each
// plugin, or throw if that's not possible.
bool hasResolutionError = false;
final List<PluginInterfaceResolution> finalResolution = <PluginInterfaceResolution>[];
for (final MapEntry<String, List<PluginInterfaceResolution>> entry in possibleResolutions.entries) {
final List<PluginInterfaceResolution> candidates = entry.value;
// If there's only one candidate, use it.
if (candidates.length == 1) {
finalResolution.add(candidates.first);
continue;
}
// Next, try direct dependencies of the resolving application.
final Iterable<PluginInterfaceResolution> directDependencies = candidates.where((PluginInterfaceResolution r) {
return r.plugin.isDirectDependency;
});
if (directDependencies.isNotEmpty) {
if (directDependencies.length > 1) {
final List<Plugin> pluginResolution = <Plugin>[];
// Resolve all the possible resolutions to a single option for each plugin, or throw if that's not possible.
for (final MapEntry<String, List<Plugin>> entry in possibleResolutions.entries) {
final List<Plugin> candidates = entry.value;
// If there's only one candidate, use it.
if (candidates.length == 1) {
pluginResolution.add(candidates.first);
continue;
}
// Next, try direct dependencies of the resolving application.
final Iterable<Plugin> directDependencies = candidates.where((Plugin plugin) {
return plugin.isDirectDependency;
});
if (directDependencies.isNotEmpty) {
if (directDependencies.length > 1) {
globals.printError(
'Plugin ${entry.key}:$platformKey has conflicting direct dependency implementations:\n'
'${directDependencies.map((Plugin plugin) => ' ${plugin.name}\n').join()}'
'To fix this issue, remove all but one of these dependencies from pubspec.yaml.\n'
);
hasResolutionError = true;
} else {
pluginResolution.add(directDependencies.first);
}
continue;
}
// Next, defer to the default implementation if there is one.
final String? defaultPackageName = defaultImplementations[entry.key];
if (defaultPackageName != null) {
final int defaultIndex = candidates
.indexWhere((Plugin plugin) => plugin.name == defaultPackageName);
if (defaultIndex != -1) {
pluginResolution.add(candidates[defaultIndex]);
continue;
}
}
// Otherwise, require an explicit choice.
if (candidates.length > 1) {
globals.printError(
'Plugin ${entry.key} has conflicting direct dependency implementations:\n'
'${directDependencies.map((PluginInterfaceResolution r) => ' ${r.plugin.name}\n').join()}'
'To fix this issue, remove all but one of these dependencies from pubspec.yaml.\n'
'Plugin ${entry.key}:$platformKey has multiple possible implementations:\n'
'${candidates.map((Plugin plugin) => ' ${plugin.name}\n').join()}'
'To fix this issue, add one of these dependencies to pubspec.yaml.\n'
);
hasResolutionError = true;
} else {
finalResolution.add(directDependencies.first);
}
continue;
}
// Next, defer to the default implementation if there is one.
final String? defaultPackageName = defaultImplementations[entry.key];
if (defaultPackageName != null) {
final int defaultIndex = candidates
.indexWhere((PluginInterfaceResolution r) => r.plugin.name == defaultPackageName);
if (defaultIndex != -1) {
finalResolution.add(candidates[defaultIndex]);
continue;
}
}
// Otherwise, require an explicit choice.
if (candidates.length > 1) {
globals.printError(
'Plugin ${entry.key} has multiple possible implementations:\n'
'${candidates.map((PluginInterfaceResolution r) => ' ${r.plugin.name}\n').join()}'
'To fix this issue, add one of these dependencies to pubspec.yaml.\n'
);
hasResolutionError = true;
continue;
}
finalResolution.addAll(
pluginResolution.map((Plugin plugin) =>
PluginInterfaceResolution(plugin: plugin, platform: platformKey)),
);
}
if (hasResolutionError) {
throwToolExit('Please resolve the plugin implementation selection errors');