mirror of
https://github.com/flutter/flutter
synced 2024-09-12 21:01:59 +00:00
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:
parent
8d928947ca
commit
9c6fcdac6f
|
@ -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');
|
||||
|
|
Loading…
Reference in a new issue