Add dartPluginClass support for Android and iOS (#87991)
This commit is contained in:
parent
0b38b9cba3
commit
23cea26715
@ -61,17 +61,14 @@ class DartPluginRegistrantTarget extends Target {
|
||||
return true;
|
||||
}
|
||||
final String? platformName = environment.defines[kTargetPlatform];
|
||||
if (platformName == null) {
|
||||
return true;
|
||||
}
|
||||
final TargetPlatform? targetPlatform = getTargetPlatformForName(platformName);
|
||||
// TODO(egarciad): Support Android and iOS.
|
||||
// https://github.com/flutter/flutter/issues/52267
|
||||
return targetPlatform != TargetPlatform.darwin &&
|
||||
targetPlatform != TargetPlatform.linux_x64 &&
|
||||
targetPlatform != TargetPlatform.linux_arm64 &&
|
||||
targetPlatform != TargetPlatform.windows_x64 &&
|
||||
targetPlatform != TargetPlatform.windows_uwp_x64;
|
||||
final TargetPlatform? targetPlatform = platformName == null ? null
|
||||
: getTargetPlatformForName(platformName);
|
||||
// TODO(stuartmorgan): Investigate removing this check entirely; ideally the
|
||||
// source generation step shouldn't be platform dependent, and the generated
|
||||
// code should just do the right thing on every platform.
|
||||
return targetPlatform == TargetPlatform.fuchsia_arm64 ||
|
||||
targetPlatform == TargetPlatform.fuchsia_x64 ||
|
||||
targetPlatform == TargetPlatform.web_javascript;
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -353,8 +353,9 @@ AndroidEmbeddingVersion _getAndroidEmbeddingVersion(FlutterProject project) {
|
||||
}
|
||||
|
||||
Future<void> _writeAndroidPluginRegistrant(FlutterProject project, List<Plugin> plugins) async {
|
||||
final List<Plugin> nativePlugins = _filterNativePlugins(plugins, AndroidPlugin.kConfigKey);
|
||||
final List<Map<String, Object?>> androidPlugins =
|
||||
_extractPlatformMaps(plugins, AndroidPlugin.kConfigKey);
|
||||
_extractPlatformMaps(nativePlugins, AndroidPlugin.kConfigKey);
|
||||
|
||||
final Map<String, Object> templateContext = <String, Object>{
|
||||
'plugins': androidPlugins,
|
||||
@ -676,9 +677,9 @@ const String _dartPluginRegisterWith = r'''
|
||||
}
|
||||
''';
|
||||
|
||||
// TODO(egarciad): Evaluate merging the web and desktop plugin registry templates.
|
||||
// TODO(egarciad): Evaluate merging the web and non-web plugin registry templates.
|
||||
// https://github.com/flutter/flutter/issues/80406
|
||||
const String _dartPluginRegistryForDesktopTemplate = '''
|
||||
const String _dartPluginRegistryForNonWebTemplate = '''
|
||||
//
|
||||
// Generated file. Do not edit.
|
||||
// This file is generated from template in file `flutter_tools/lib/src/flutter_plugins.dart`.
|
||||
@ -688,6 +689,12 @@ const String _dartPluginRegistryForDesktopTemplate = '''
|
||||
|
||||
import '{{mainEntrypoint}}' as entrypoint;
|
||||
import 'dart:io'; // flutter_ignore: dart_io_import.
|
||||
{{#android}}
|
||||
import 'package:{{pluginName}}/{{pluginName}}.dart';
|
||||
{{/android}}
|
||||
{{#ios}}
|
||||
import 'package:{{pluginName}}/{{pluginName}}.dart';
|
||||
{{/ios}}
|
||||
{{#linux}}
|
||||
import 'package:{{pluginName}}/{{pluginName}}.dart';
|
||||
{{/linux}}
|
||||
@ -703,7 +710,15 @@ class _PluginRegistrant {
|
||||
|
||||
@pragma('vm:entry-point')
|
||||
static void register() {
|
||||
if (Platform.isLinux) {
|
||||
if (Platform.isAndroid) {
|
||||
{{#android}}
|
||||
$_dartPluginRegisterWith
|
||||
{{/android}}
|
||||
} else if (Platform.isIOS) {
|
||||
{{#ios}}
|
||||
$_dartPluginRegisterWith
|
||||
{{/ios}}
|
||||
} else if (Platform.isLinux) {
|
||||
{{#linux}}
|
||||
$_dartPluginRegisterWith
|
||||
{{/linux}}
|
||||
@ -733,7 +748,8 @@ void main(List<String> args) {
|
||||
''';
|
||||
|
||||
Future<void> _writeIOSPluginRegistrant(FlutterProject project, List<Plugin> plugins) async {
|
||||
final List<Map<String, Object?>> iosPlugins = _extractPlatformMaps(plugins, IOSPlugin.kConfigKey);
|
||||
final List<Plugin> nativePlugins = _filterNativePlugins(plugins, IOSPlugin.kConfigKey);
|
||||
final List<Map<String, Object?>> iosPlugins = _extractPlatformMaps(nativePlugins, IOSPlugin.kConfigKey);
|
||||
final Map<String, Object> context = <String, Object>{
|
||||
'os': 'ios',
|
||||
'deploymentTarget': '9.0',
|
||||
@ -1119,11 +1135,15 @@ bool hasPlugins(FlutterProject project) {
|
||||
/// * Else fail.
|
||||
///
|
||||
/// For more details, https://flutter.dev/go/federated-plugins.
|
||||
// TODO(stuartmorgan): Expand implementation to apply to all implementations,
|
||||
// not just Dart-only, per the federated plugin spec.
|
||||
List<PluginInterfaceResolution> resolvePlatformImplementation(
|
||||
List<Plugin> plugins, {
|
||||
bool throwOnPluginPubspecError = true,
|
||||
}) {
|
||||
final List<String> platforms = <String>[
|
||||
AndroidPlugin.kConfigKey,
|
||||
IOSPlugin.kConfigKey,
|
||||
LinuxPlugin.kConfigKey,
|
||||
MacOSPlugin.kConfigKey,
|
||||
WindowsPlugin.kConfigKey,
|
||||
@ -1135,20 +1155,28 @@ List<PluginInterfaceResolution> resolvePlatformImplementation(
|
||||
|
||||
for (final Plugin plugin in plugins) {
|
||||
for (final String platform in platforms) {
|
||||
// The plugin doesn't implement this platform.
|
||||
if (plugin.platforms[platform] == null &&
|
||||
plugin.defaultPackagePlatforms[platform] == null) {
|
||||
// The plugin doesn't implement this platform.
|
||||
continue;
|
||||
}
|
||||
// The plugin doesn't implement an interface, verify that it has a default implementation.
|
||||
final String? implementsPackage = plugin.implementsPackage;
|
||||
String? implementsPackage = plugin.implementsPackage;
|
||||
if (implementsPackage == null || implementsPackage.isEmpty) {
|
||||
final String? defaultImplementation = plugin.defaultPackagePlatforms[platform];
|
||||
if (defaultImplementation == null) {
|
||||
final bool hasInlineDartImplementation =
|
||||
plugin.pluginDartClassPlatforms[platform] != null;
|
||||
if (defaultImplementation == null && !hasInlineDartImplementation) {
|
||||
if (throwOnPluginPubspecError) {
|
||||
globals.printError(
|
||||
"Plugin `${plugin.name}` doesn't implement a plugin interface, nor sets "
|
||||
'a default implementation in pubspec.yaml.\n\n'
|
||||
"Plugin `${plugin.name}` doesn't implement a plugin interface, nor does "
|
||||
'it specify an implementation in pubspec.yaml.\n\n'
|
||||
'To set an inline implementation, use:\n'
|
||||
'flutter:\n'
|
||||
' plugin:\n'
|
||||
' platforms:\n'
|
||||
' $platform:\n'
|
||||
' $kDartPluginClass: <plugin-class>\n'
|
||||
'\n'
|
||||
'To set a default implementation, use:\n'
|
||||
'flutter:\n'
|
||||
' plugin:\n'
|
||||
@ -1166,8 +1194,18 @@ List<PluginInterfaceResolution> resolvePlatformImplementation(
|
||||
didFindError = true;
|
||||
continue;
|
||||
}
|
||||
defaultImplementations['$platform/${plugin.name}'] = defaultImplementation;
|
||||
continue;
|
||||
if (defaultImplementation != null) {
|
||||
defaultImplementations['$platform/${plugin.name}'] = defaultImplementation;
|
||||
continue;
|
||||
} else if (platform != 'linux' && platform != 'macos' && platform != 'windows') {
|
||||
// An interface package (i.e., one with no 'implements') with an
|
||||
// inline implementation is its own default implementation.
|
||||
// TODO(stuartmorgan): This should be true on desktop as well, but
|
||||
// enabling that would be a breaking change for most existing
|
||||
// Dart-only plugins. See https://github.com/flutter/flutter/issues/87862
|
||||
implementsPackage = plugin.name;
|
||||
defaultImplementations['$platform/${plugin.name}'] = plugin.name;
|
||||
}
|
||||
}
|
||||
if (plugin.pluginDartClassPlatforms[platform] == null ||
|
||||
plugin.pluginDartClassPlatforms[platform] == 'none') {
|
||||
@ -1249,6 +1287,8 @@ Future<void> generateMainDartWithPluginRegistrant(
|
||||
final Map<String, Object> templateContext = <String, Object>{
|
||||
'mainEntrypoint': currentMainUri,
|
||||
'dartLanguageVersion': entrypointVersion.toString(),
|
||||
AndroidPlugin.kConfigKey: <Object?>[],
|
||||
IOSPlugin.kConfigKey: <Object?>[],
|
||||
LinuxPlugin.kConfigKey: <Object?>[],
|
||||
MacOSPlugin.kConfigKey: <Object?>[],
|
||||
WindowsPlugin.kConfigKey: <Object?>[],
|
||||
@ -1274,7 +1314,7 @@ Future<void> generateMainDartWithPluginRegistrant(
|
||||
}
|
||||
try {
|
||||
_renderTemplateToFile(
|
||||
_dartPluginRegistryForDesktopTemplate,
|
||||
_dartPluginRegistryForNonWebTemplate,
|
||||
templateContext,
|
||||
newMainDart,
|
||||
globals.templateRenderer,
|
||||
|
@ -49,14 +49,22 @@ abstract class NativeOrDartPlugin {
|
||||
|
||||
/// Contains parameters to template an Android plugin.
|
||||
///
|
||||
/// The required fields include: [name] of the plugin, [package] of the plugin and
|
||||
/// the [pluginClass] that will be the entry point to the plugin's native code.
|
||||
class AndroidPlugin extends PluginPlatform {
|
||||
/// The [name] of the plugin is required. Additionally, either:
|
||||
/// - [defaultPackage], or
|
||||
/// - an implementation consisting of:
|
||||
/// - the [package] and [pluginClass] that will be the entry point to the
|
||||
/// plugin's native code, and/or
|
||||
/// - the [dartPluginClass] that will be the entry point for the plugin's
|
||||
/// Dart code
|
||||
/// is required.
|
||||
class AndroidPlugin extends PluginPlatform implements NativeOrDartPlugin {
|
||||
AndroidPlugin({
|
||||
required this.name,
|
||||
required this.package,
|
||||
required this.pluginClass,
|
||||
required this.pluginPath,
|
||||
this.package,
|
||||
this.pluginClass,
|
||||
this.dartPluginClass,
|
||||
this.defaultPackage,
|
||||
required FileSystem fileSystem,
|
||||
}) : _fileSystem = fileSystem;
|
||||
|
||||
@ -64,8 +72,10 @@ class AndroidPlugin extends PluginPlatform {
|
||||
assert(validate(yaml));
|
||||
return AndroidPlugin(
|
||||
name: name,
|
||||
package: yaml['package'] as String,
|
||||
pluginClass: yaml['pluginClass'] as String,
|
||||
package: yaml['package'] as String?,
|
||||
pluginClass: yaml[kPluginClass] as String?,
|
||||
dartPluginClass: yaml[kDartPluginClass] as String?,
|
||||
defaultPackage: yaml[kDefaultPackage] as String?,
|
||||
pluginPath: pluginPath,
|
||||
fileSystem: fileSystem,
|
||||
);
|
||||
@ -73,11 +83,16 @@ class AndroidPlugin extends PluginPlatform {
|
||||
|
||||
final FileSystem _fileSystem;
|
||||
|
||||
@override
|
||||
bool isNative() => pluginClass != null;
|
||||
|
||||
static bool validate(YamlMap yaml) {
|
||||
if (yaml == null) {
|
||||
return false;
|
||||
}
|
||||
return yaml['package'] is String && yaml['pluginClass'] is String;
|
||||
return (yaml['package'] is String && yaml['pluginClass'] is String)||
|
||||
yaml[kDartPluginClass] is String ||
|
||||
yaml[kDefaultPackage] is String;
|
||||
}
|
||||
|
||||
static const String kConfigKey = 'android';
|
||||
@ -86,10 +101,16 @@ class AndroidPlugin extends PluginPlatform {
|
||||
final String name;
|
||||
|
||||
/// The plugin package name defined in pubspec.yaml.
|
||||
final String package;
|
||||
final String? package;
|
||||
|
||||
/// The plugin main class defined in pubspec.yaml.
|
||||
final String pluginClass;
|
||||
/// The native plugin main class defined in pubspec.yaml, if any.
|
||||
final String? pluginClass;
|
||||
|
||||
/// The Dart plugin main class defined in pubspec.yaml, if any.
|
||||
final String? dartPluginClass;
|
||||
|
||||
/// The default implementation package defined in pubspec.yaml, if any.
|
||||
final String? defaultPackage;
|
||||
|
||||
/// The absolute path to the plugin in the pub cache.
|
||||
final String pluginPath;
|
||||
@ -98,8 +119,10 @@ class AndroidPlugin extends PluginPlatform {
|
||||
Map<String, dynamic> toMap() {
|
||||
return <String, dynamic>{
|
||||
'name': name,
|
||||
'package': package,
|
||||
'class': pluginClass,
|
||||
if (package != null) 'package': package,
|
||||
if (pluginClass != null) 'class': pluginClass,
|
||||
if (dartPluginClass != null) kDartPluginClass : dartPluginClass,
|
||||
if (defaultPackage != null) kDefaultPackage : defaultPackage,
|
||||
// Mustache doesn't support complex types.
|
||||
'supportsEmbeddingV1': _supportedEmbeddings.contains('1'),
|
||||
'supportsEmbeddingV2': _supportedEmbeddings.contains('2'),
|
||||
@ -119,6 +142,13 @@ class AndroidPlugin extends PluginPlatform {
|
||||
'main',
|
||||
);
|
||||
|
||||
final String? package = this.package;
|
||||
// Don't attempt to validate the native code if there isn't supposed to
|
||||
// be any.
|
||||
if (package == null) {
|
||||
return supportedEmbeddings;
|
||||
}
|
||||
|
||||
final List<String> mainClassCandidates = <String>[
|
||||
_fileSystem.path.join(
|
||||
baseMainPath,
|
||||
@ -170,13 +200,21 @@ class AndroidPlugin extends PluginPlatform {
|
||||
|
||||
/// Contains the parameters to template an iOS plugin.
|
||||
///
|
||||
/// The required fields include: [name] of the plugin, the [pluginClass] that
|
||||
/// will be the entry point to the plugin's native code.
|
||||
class IOSPlugin extends PluginPlatform {
|
||||
/// The [name] of the plugin is required. Additionally, either:
|
||||
/// - [defaultPackage], or
|
||||
/// - an implementation consisting of:
|
||||
/// - the [pluginClass] (with optional [classPrefix]) that will be the entry
|
||||
/// point to the plugin's native code, and/or
|
||||
/// - the [dartPluginClass] that will be the entry point for the plugin's
|
||||
/// Dart code
|
||||
/// is required.
|
||||
class IOSPlugin extends PluginPlatform implements NativeOrDartPlugin {
|
||||
const IOSPlugin({
|
||||
required this.name,
|
||||
required this.classPrefix,
|
||||
required this.pluginClass,
|
||||
this.pluginClass,
|
||||
this.dartPluginClass,
|
||||
this.defaultPackage,
|
||||
});
|
||||
|
||||
factory IOSPlugin.fromYaml(String name, YamlMap yaml) {
|
||||
@ -184,7 +222,9 @@ class IOSPlugin extends PluginPlatform {
|
||||
return IOSPlugin(
|
||||
name: name,
|
||||
classPrefix: '',
|
||||
pluginClass: yaml['pluginClass'] as String,
|
||||
pluginClass: yaml[kPluginClass] as String?,
|
||||
dartPluginClass: yaml[kDartPluginClass] as String?,
|
||||
defaultPackage: yaml[kDefaultPackage] as String?,
|
||||
);
|
||||
}
|
||||
|
||||
@ -192,7 +232,9 @@ class IOSPlugin extends PluginPlatform {
|
||||
if (yaml == null) {
|
||||
return false;
|
||||
}
|
||||
return yaml['pluginClass'] is String;
|
||||
return yaml[kPluginClass] is String ||
|
||||
yaml[kDartPluginClass] is String ||
|
||||
yaml[kDefaultPackage] is String;
|
||||
}
|
||||
|
||||
static const String kConfigKey = 'ios';
|
||||
@ -202,14 +244,21 @@ class IOSPlugin extends PluginPlatform {
|
||||
/// Note, this is here only for legacy reasons. Multi-platform format
|
||||
/// always sets it to empty String.
|
||||
final String classPrefix;
|
||||
final String pluginClass;
|
||||
final String? pluginClass;
|
||||
final String? dartPluginClass;
|
||||
final String? defaultPackage;
|
||||
|
||||
@override
|
||||
bool isNative() => pluginClass != null;
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toMap() {
|
||||
return <String, dynamic>{
|
||||
'name': name,
|
||||
'prefix': classPrefix,
|
||||
'class': pluginClass,
|
||||
if (pluginClass != null) 'class': pluginClass,
|
||||
if (dartPluginClass != null) kDartPluginClass : dartPluginClass,
|
||||
if (defaultPackage != null) kDefaultPackage : defaultPackage,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -136,44 +136,35 @@ class Plugin {
|
||||
WindowsPlugin.fromYaml(name, platformsYaml[WindowsPlugin.kConfigKey] as YamlMap);
|
||||
}
|
||||
|
||||
final String? defaultPackageForLinux =
|
||||
_getDefaultPackageForPlatform(platformsYaml, LinuxPlugin.kConfigKey);
|
||||
|
||||
final String? defaultPackageForMacOS =
|
||||
_getDefaultPackageForPlatform(platformsYaml, MacOSPlugin.kConfigKey);
|
||||
|
||||
final String? defaultPackageForWindows =
|
||||
_getDefaultPackageForPlatform(platformsYaml, WindowsPlugin.kConfigKey);
|
||||
|
||||
final String? defaultPluginDartClassForLinux =
|
||||
_getPluginDartClassForPlatform(platformsYaml, LinuxPlugin.kConfigKey);
|
||||
|
||||
final String? defaultPluginDartClassForMacOS =
|
||||
_getPluginDartClassForPlatform(platformsYaml, MacOSPlugin.kConfigKey);
|
||||
|
||||
final String? defaultPluginDartClassForWindows =
|
||||
_getPluginDartClassForPlatform(platformsYaml, WindowsPlugin.kConfigKey);
|
||||
// TODO(stuartmorgan): Consider merging web into this common handling; the
|
||||
// fact that its implementation of Dart-only plugins and default packages
|
||||
// are separate is legacy.
|
||||
final List<String> sharedHandlingPlatforms = <String>[
|
||||
AndroidPlugin.kConfigKey,
|
||||
IOSPlugin.kConfigKey,
|
||||
LinuxPlugin.kConfigKey,
|
||||
MacOSPlugin.kConfigKey,
|
||||
WindowsPlugin.kConfigKey,
|
||||
];
|
||||
final Map<String, String> defaultPackages = <String, String>{};
|
||||
final Map<String, String> dartPluginClasses = <String, String>{};
|
||||
for (final String platform in sharedHandlingPlatforms) {
|
||||
final String? defaultPackage = _getDefaultPackageForPlatform(platformsYaml, platform);
|
||||
if (defaultPackage != null) {
|
||||
defaultPackages[platform] = defaultPackage;
|
||||
}
|
||||
final String? dartClass = _getPluginDartClassForPlatform(platformsYaml, platform);
|
||||
if (dartClass != null) {
|
||||
dartPluginClasses[platform] = dartClass;
|
||||
}
|
||||
}
|
||||
|
||||
return Plugin(
|
||||
name: name,
|
||||
path: path,
|
||||
platforms: platforms,
|
||||
defaultPackagePlatforms: <String, String>{
|
||||
if (defaultPackageForLinux != null)
|
||||
LinuxPlugin.kConfigKey : defaultPackageForLinux,
|
||||
if (defaultPackageForMacOS != null)
|
||||
MacOSPlugin.kConfigKey : defaultPackageForMacOS,
|
||||
if (defaultPackageForWindows != null)
|
||||
WindowsPlugin.kConfigKey : defaultPackageForWindows,
|
||||
},
|
||||
pluginDartClassPlatforms: <String, String>{
|
||||
if (defaultPluginDartClassForLinux != null)
|
||||
LinuxPlugin.kConfigKey : defaultPluginDartClassForLinux,
|
||||
if (defaultPluginDartClassForMacOS != null)
|
||||
MacOSPlugin.kConfigKey : defaultPluginDartClassForMacOS,
|
||||
if (defaultPluginDartClassForWindows != null)
|
||||
WindowsPlugin.kConfigKey : defaultPluginDartClassForWindows,
|
||||
},
|
||||
defaultPackagePlatforms: defaultPackages,
|
||||
pluginDartClassPlatforms: dartPluginClasses,
|
||||
dependencies: dependencies,
|
||||
isDirectDependency: isDirectDependency,
|
||||
implementsPackage: pluginYaml['implements'] != null ? pluginYaml['implements'] as String : '',
|
||||
|
@ -26,6 +26,40 @@ void main() {
|
||||
));
|
||||
});
|
||||
|
||||
testWithoutContext('AndroidPlugin does not validate the main class for Dart-only plugins', () {
|
||||
final FileSystem fileSystem = MemoryFileSystem.test();
|
||||
final AndroidPlugin androidPlugin = AndroidPlugin(
|
||||
name: 'pluginA',
|
||||
dartPluginClass: 'PluginA',
|
||||
pluginPath: '.pub_cache/plugin_a',
|
||||
fileSystem: fileSystem,
|
||||
);
|
||||
|
||||
expect(androidPlugin.toMap(), <String, Object>{
|
||||
'name': 'pluginA',
|
||||
'dartPluginClass': 'PluginA',
|
||||
'supportsEmbeddingV1': false,
|
||||
'supportsEmbeddingV2': false,
|
||||
});
|
||||
});
|
||||
|
||||
testWithoutContext('AndroidPlugin does not validate the main class for default_package', () {
|
||||
final FileSystem fileSystem = MemoryFileSystem.test();
|
||||
final AndroidPlugin androidPlugin = AndroidPlugin(
|
||||
name: 'pluginA',
|
||||
defaultPackage: 'plugin_a_android',
|
||||
pluginPath: '.pub_cache/plugin_a',
|
||||
fileSystem: fileSystem,
|
||||
);
|
||||
|
||||
expect(androidPlugin.toMap(), <String, Object>{
|
||||
'name': 'pluginA',
|
||||
'default_package': 'plugin_a_android',
|
||||
'supportsEmbeddingV1': false,
|
||||
'supportsEmbeddingV2': false,
|
||||
});
|
||||
});
|
||||
|
||||
testWithoutContext('AndroidPlugin parses embedding version 2 from the Java search path', () {
|
||||
final FileSystem fileSystem = MemoryFileSystem.test();
|
||||
final AndroidPlugin androidPlugin = AndroidPlugin(
|
||||
|
@ -110,10 +110,7 @@ void main() {
|
||||
fileSystem: fileSystem,
|
||||
logger: BufferLogger.test(),
|
||||
processManager: FakeProcessManager.any(),
|
||||
generateDartPluginRegistry: false,
|
||||
defines: <String, String>{
|
||||
kTargetPlatform: 'darwin-x64',
|
||||
});
|
||||
generateDartPluginRegistry: false);
|
||||
|
||||
expect(const DartPluginRegistrantTarget().canSkip(environment), isTrue);
|
||||
|
||||
@ -123,14 +120,10 @@ void main() {
|
||||
fileSystem: fileSystem,
|
||||
logger: BufferLogger.test(),
|
||||
processManager: FakeProcessManager.any(),
|
||||
generateDartPluginRegistry: true,
|
||||
defines: <String, String>{
|
||||
kTargetPlatform: 'darwin-x64',
|
||||
});
|
||||
generateDartPluginRegistry: true);
|
||||
|
||||
expect(const DartPluginRegistrantTarget().canSkip(environment2), isFalse);
|
||||
});
|
||||
|
||||
testWithoutContext('skipped based on platform', () async {
|
||||
const Map<String, bool> canSkip = <String, bool>{
|
||||
'darwin-x64': false,
|
||||
@ -139,8 +132,8 @@ void main() {
|
||||
'windows-x64': false,
|
||||
'windows-uwp-x64': false,
|
||||
'web-javascript': true,
|
||||
'ios': true,
|
||||
'android': true,
|
||||
'ios': false,
|
||||
'android': false,
|
||||
'fuchsia-arm64': true,
|
||||
'fuchsia-x64': true,
|
||||
};
|
||||
@ -258,7 +251,9 @@ void main() {
|
||||
'\n'
|
||||
" @pragma('vm:entry-point')\n"
|
||||
' static void register() {\n'
|
||||
' if (Platform.isLinux) {\n'
|
||||
' if (Platform.isAndroid) {\n'
|
||||
' } else if (Platform.isIOS) {\n'
|
||||
' } else if (Platform.isLinux) {\n'
|
||||
' try {\n'
|
||||
' PathProviderLinux.registerWith();\n'
|
||||
' } catch (err) {\n'
|
||||
@ -395,7 +390,9 @@ void main() {
|
||||
'\n'
|
||||
" @pragma('vm:entry-point')\n"
|
||||
' static void register() {\n'
|
||||
' if (Platform.isLinux) {\n'
|
||||
' if (Platform.isAndroid) {\n'
|
||||
' } else if (Platform.isIOS) {\n'
|
||||
' } else if (Platform.isLinux) {\n'
|
||||
' try {\n'
|
||||
' PathProviderLinux.registerWith();\n'
|
||||
' } catch (err) {\n'
|
||||
|
@ -127,6 +127,75 @@ void main() {
|
||||
);
|
||||
});
|
||||
|
||||
testWithoutContext('selects inline implementation on mobile', () async {
|
||||
final Set<String> directDependencies = <String>{};
|
||||
|
||||
final List<PluginInterfaceResolution> resolutions = resolvePlatformImplementation(<Plugin>[
|
||||
Plugin.fromYaml(
|
||||
'url_launcher',
|
||||
'',
|
||||
YamlMap.wrap(<String, dynamic>{
|
||||
'platforms': <String, dynamic>{
|
||||
'android': <String, dynamic>{
|
||||
'dartPluginClass': 'UrlLauncherAndroid',
|
||||
},
|
||||
'ios': <String, dynamic>{
|
||||
'dartPluginClass': 'UrlLauncherIos',
|
||||
},
|
||||
},
|
||||
}),
|
||||
<String>[],
|
||||
fileSystem: fs,
|
||||
appDependencies: directDependencies,
|
||||
),
|
||||
]);
|
||||
expect(resolutions.length, equals(2));
|
||||
expect(resolutions[0].toMap(), equals(
|
||||
<String, String>{
|
||||
'pluginName': 'url_launcher',
|
||||
'dartClass': 'UrlLauncherAndroid',
|
||||
'platform': 'android',
|
||||
})
|
||||
);
|
||||
expect(resolutions[1].toMap(), equals(
|
||||
<String, String>{
|
||||
'pluginName': 'url_launcher',
|
||||
'dartClass': 'UrlLauncherIos',
|
||||
'platform': 'ios',
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
// See https://github.com/flutter/flutter/issues/87862 for why this is
|
||||
// currently asserted even though it's not the desired behavior long term.
|
||||
testWithoutContext('does not select inline implementation on desktop', () async {
|
||||
final Set<String> directDependencies = <String>{};
|
||||
|
||||
final List<PluginInterfaceResolution> resolutions = resolvePlatformImplementation(<Plugin>[
|
||||
Plugin.fromYaml(
|
||||
'url_launcher',
|
||||
'',
|
||||
YamlMap.wrap(<String, dynamic>{
|
||||
'platforms': <String, dynamic>{
|
||||
'linux': <String, dynamic>{
|
||||
'dartPluginClass': 'UrlLauncherLinux',
|
||||
},
|
||||
'macos': <String, dynamic>{
|
||||
'dartPluginClass': 'UrlLauncherMacOS',
|
||||
},
|
||||
'windows': <String, dynamic>{
|
||||
'dartPluginClass': 'UrlLauncherWindows',
|
||||
},
|
||||
},
|
||||
}),
|
||||
<String>[],
|
||||
fileSystem: fs,
|
||||
appDependencies: directDependencies,
|
||||
),
|
||||
]);
|
||||
expect(resolutions.length, equals(0));
|
||||
});
|
||||
|
||||
testWithoutContext('selects default implementation', () async {
|
||||
final Set<String> directDependencies = <String>{};
|
||||
|
||||
@ -440,123 +509,6 @@ void main() {
|
||||
message: 'Please resolve the errors',
|
||||
));
|
||||
});
|
||||
|
||||
testUsingContext('provides error when plugin pubspec.yaml doesn\'t have "implementation" nor "default_implementation"', () async {
|
||||
final Set<String> directDependencies = <String>{
|
||||
'url_launcher_linux_1',
|
||||
};
|
||||
expect(() {
|
||||
resolvePlatformImplementation(<Plugin>[
|
||||
Plugin.fromYaml(
|
||||
'url_launcher_linux_1',
|
||||
'',
|
||||
YamlMap.wrap(<String, dynamic>{
|
||||
'platforms': <String, dynamic>{
|
||||
'linux': <String, dynamic>{
|
||||
'dartPluginClass': 'UrlLauncherPluginLinux',
|
||||
},
|
||||
},
|
||||
}),
|
||||
<String>[],
|
||||
fileSystem: fs,
|
||||
appDependencies: directDependencies,
|
||||
),
|
||||
]);
|
||||
},
|
||||
throwsToolExit(
|
||||
message: 'Please resolve the errors'
|
||||
));
|
||||
expect(
|
||||
testLogger.errorText,
|
||||
"Plugin `url_launcher_linux_1` doesn't implement a plugin interface, "
|
||||
'nor sets a default implementation in pubspec.yaml.\n\n'
|
||||
'To set a default implementation, use:\n'
|
||||
'flutter:\n'
|
||||
' plugin:\n'
|
||||
' platforms:\n'
|
||||
' linux:\n'
|
||||
' default_package: <plugin-implementation>\n'
|
||||
'\n'
|
||||
'To implement an interface, use:\n'
|
||||
'flutter:\n'
|
||||
' plugin:\n'
|
||||
' implements: <plugin-interface>'
|
||||
'\n\n'
|
||||
);
|
||||
});
|
||||
|
||||
testUsingContext('provides all errors when plugin pubspec.yaml doesn\'t have "implementation" nor "default_implementation"', () async {
|
||||
final Set<String> directDependencies = <String>{
|
||||
'url_launcher_linux',
|
||||
'url_launcher_windows',
|
||||
};
|
||||
expect(() {
|
||||
resolvePlatformImplementation(<Plugin>[
|
||||
Plugin.fromYaml(
|
||||
'url_launcher_linux',
|
||||
'',
|
||||
YamlMap.wrap(<String, dynamic>{
|
||||
'platforms': <String, dynamic>{
|
||||
'linux': <String, dynamic>{
|
||||
'dartPluginClass': 'UrlLauncherPluginLinux',
|
||||
},
|
||||
},
|
||||
}),
|
||||
<String>[],
|
||||
fileSystem: fs,
|
||||
appDependencies: directDependencies,
|
||||
),
|
||||
Plugin.fromYaml(
|
||||
'url_launcher_windows',
|
||||
'',
|
||||
YamlMap.wrap(<String, dynamic>{
|
||||
'platforms': <String, dynamic>{
|
||||
'windows': <String, dynamic>{
|
||||
'dartPluginClass': 'UrlLauncherPluginWindows',
|
||||
},
|
||||
},
|
||||
}),
|
||||
<String>[],
|
||||
fileSystem: fs,
|
||||
appDependencies: directDependencies,
|
||||
),
|
||||
]);
|
||||
},
|
||||
throwsToolExit(
|
||||
message: 'Please resolve the errors'
|
||||
));
|
||||
expect(
|
||||
testLogger.errorText,
|
||||
"Plugin `url_launcher_linux` doesn't implement a plugin interface, "
|
||||
'nor sets a default implementation in pubspec.yaml.\n\n'
|
||||
'To set a default implementation, use:\n'
|
||||
'flutter:\n'
|
||||
' plugin:\n'
|
||||
' platforms:\n'
|
||||
' linux:\n'
|
||||
' default_package: <plugin-implementation>\n'
|
||||
'\n'
|
||||
'To implement an interface, use:\n'
|
||||
'flutter:\n'
|
||||
' plugin:\n'
|
||||
' implements: <plugin-interface>'
|
||||
'\n\n'
|
||||
"Plugin `url_launcher_windows` doesn't implement a plugin interface, "
|
||||
'nor sets a default implementation in pubspec.yaml.\n\n'
|
||||
'To set a default implementation, use:\n'
|
||||
'flutter:\n'
|
||||
' plugin:\n'
|
||||
' platforms:\n'
|
||||
' windows:\n'
|
||||
' default_package: <plugin-implementation>\n'
|
||||
'\n'
|
||||
'To implement an interface, use:\n'
|
||||
'flutter:\n'
|
||||
' plugin:\n'
|
||||
' implements: <plugin-interface>'
|
||||
'\n\n'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
group('generateMainDartWithPluginRegistrant', () {
|
||||
@ -568,6 +520,22 @@ void main() {
|
||||
flutterManifest,
|
||||
fs,
|
||||
<String, String>{
|
||||
'url_launcher_android': '''
|
||||
flutter:
|
||||
plugin:
|
||||
implements: url_launcher
|
||||
platforms:
|
||||
android:
|
||||
dartPluginClass: AndroidPlugin
|
||||
''',
|
||||
'url_launcher_ios': '''
|
||||
flutter:
|
||||
plugin:
|
||||
implements: url_launcher
|
||||
platforms:
|
||||
ios:
|
||||
dartPluginClass: IosPlugin
|
||||
''',
|
||||
'url_launcher_macos': '''
|
||||
flutter:
|
||||
plugin:
|
||||
@ -600,7 +568,7 @@ void main() {
|
||||
macos:
|
||||
dartPluginClass: AwesomeMacOS
|
||||
'''
|
||||
});
|
||||
});
|
||||
|
||||
final Directory libDir = flutterProject.directory.childDirectory('lib');
|
||||
libDir.createSync(recursive: true);
|
||||
@ -633,6 +601,8 @@ void main() {
|
||||
'\n'
|
||||
"import 'package:app/main.dart' as entrypoint;\n"
|
||||
"import 'dart:io'; // flutter_ignore: dart_io_import.\n"
|
||||
"import 'package:url_launcher_android/url_launcher_android.dart';\n"
|
||||
"import 'package:url_launcher_ios/url_launcher_ios.dart';\n"
|
||||
"import 'package:url_launcher_linux/url_launcher_linux.dart';\n"
|
||||
"import 'package:awesome_macos/awesome_macos.dart';\n"
|
||||
"import 'package:url_launcher_macos/url_launcher_macos.dart';\n"
|
||||
@ -643,7 +613,29 @@ void main() {
|
||||
'\n'
|
||||
" @pragma('vm:entry-point')\n"
|
||||
' static void register() {\n'
|
||||
' if (Platform.isLinux) {\n'
|
||||
' if (Platform.isAndroid) {\n'
|
||||
' try {\n'
|
||||
' AndroidPlugin.registerWith();\n'
|
||||
' } catch (err) {\n'
|
||||
' print(\n'
|
||||
" '`url_launcher_android` threw an error: \$err. '\n"
|
||||
" 'The app may not function as expected until you remove this plugin from pubspec.yaml'\n"
|
||||
' );\n'
|
||||
' rethrow;\n'
|
||||
' }\n'
|
||||
'\n'
|
||||
' } else if (Platform.isIOS) {\n'
|
||||
' try {\n'
|
||||
' IosPlugin.registerWith();\n'
|
||||
' } catch (err) {\n'
|
||||
' print(\n'
|
||||
" '`url_launcher_ios` threw an error: \$err. '\n"
|
||||
" 'The app may not function as expected until you remove this plugin from pubspec.yaml'\n"
|
||||
' );\n'
|
||||
' rethrow;\n'
|
||||
' }\n'
|
||||
'\n'
|
||||
' } else if (Platform.isLinux) {\n'
|
||||
' try {\n'
|
||||
' LinuxPlugin.registerWith();\n'
|
||||
' } catch (err) {\n'
|
||||
@ -715,7 +707,7 @@ void main() {
|
||||
flutterManifest,
|
||||
fs,
|
||||
<String, String>{
|
||||
'url_launcher_macos': '''
|
||||
'url_launcher_macos': '''
|
||||
flutter:
|
||||
plugin:
|
||||
implements: url_launcher
|
||||
@ -723,7 +715,7 @@ void main() {
|
||||
macos:
|
||||
invalid:
|
||||
'''
|
||||
});
|
||||
});
|
||||
|
||||
final Directory libDir = flutterProject.directory.childDirectory('lib');
|
||||
libDir.createSync(recursive: true);
|
||||
@ -759,12 +751,12 @@ void main() {
|
||||
flutterManifest,
|
||||
fs,
|
||||
<String, String>{
|
||||
'url_launcher_macos': '''
|
||||
'url_launcher_macos': '''
|
||||
flutter:
|
||||
plugin:
|
||||
implements: url_launcher
|
||||
'''
|
||||
});
|
||||
});
|
||||
|
||||
final Directory libDir = flutterProject.directory.childDirectory('lib');
|
||||
libDir.createSync(recursive: true);
|
||||
@ -855,7 +847,7 @@ void main() {
|
||||
flutterManifest,
|
||||
fs,
|
||||
<String, String>{
|
||||
'url_launcher_macos': '''
|
||||
'url_launcher_macos': '''
|
||||
flutter:
|
||||
plugin:
|
||||
implements: url_launcher
|
||||
@ -863,7 +855,7 @@ void main() {
|
||||
macos:
|
||||
dartPluginClass: MacOSPlugin
|
||||
'''
|
||||
});
|
||||
});
|
||||
|
||||
final Directory libDir = flutterProject.directory.childDirectory('lib');
|
||||
libDir.createSync(recursive: true);
|
||||
|
@ -31,12 +31,10 @@ void main() {
|
||||
|
||||
final AndroidPlugin androidPlugin = plugin.platforms[AndroidPlugin.kConfigKey]! as AndroidPlugin;
|
||||
final IOSPlugin iosPlugin = plugin.platforms[IOSPlugin.kConfigKey]! as IOSPlugin;
|
||||
final String androidPluginClass = androidPlugin.pluginClass;
|
||||
final String iosPluginClass = iosPlugin.pluginClass;
|
||||
|
||||
expect(iosPluginClass, 'SamplePlugin');
|
||||
expect(androidPluginClass, 'SamplePlugin');
|
||||
expect(iosPlugin.pluginClass, 'SamplePlugin');
|
||||
expect(iosPlugin.classPrefix, 'FLT');
|
||||
expect(androidPlugin.pluginClass, 'SamplePlugin');
|
||||
expect(androidPlugin.package, 'com.flutter.dev');
|
||||
});
|
||||
|
||||
@ -73,12 +71,10 @@ void main() {
|
||||
final MacOSPlugin macOSPlugin = plugin.platforms[MacOSPlugin.kConfigKey]! as MacOSPlugin;
|
||||
final WebPlugin webPlugin = plugin.platforms[WebPlugin.kConfigKey]! as WebPlugin;
|
||||
final WindowsPlugin windowsPlugin = plugin.platforms[WindowsPlugin.kConfigKey]! as WindowsPlugin;
|
||||
final String androidPluginClass = androidPlugin.pluginClass;
|
||||
final String iosPluginClass = iosPlugin.pluginClass;
|
||||
|
||||
expect(iosPluginClass, 'ISamplePlugin');
|
||||
expect(androidPluginClass, 'ASamplePlugin');
|
||||
expect(iosPlugin.pluginClass, 'ISamplePlugin');
|
||||
expect(iosPlugin.classPrefix, '');
|
||||
expect(androidPlugin.pluginClass, 'ASamplePlugin');
|
||||
expect(androidPlugin.package, 'com.flutter.dev');
|
||||
expect(linuxPlugin.pluginClass, 'LSamplePlugin');
|
||||
expect(macOSPlugin.pluginClass, 'MSamplePlugin');
|
||||
@ -122,12 +118,10 @@ void main() {
|
||||
final MacOSPlugin macOSPlugin = plugin.platforms[MacOSPlugin.kConfigKey]! as MacOSPlugin;
|
||||
final WebPlugin webPlugin = plugin.platforms[WebPlugin.kConfigKey]! as WebPlugin;
|
||||
final WindowsPlugin windowsPlugin = plugin.platforms[WindowsPlugin.kConfigKey]! as WindowsPlugin;
|
||||
final String androidPluginClass = androidPlugin.pluginClass;
|
||||
final String iosPluginClass = iosPlugin.pluginClass;
|
||||
|
||||
expect(iosPluginClass, 'ISamplePlugin');
|
||||
expect(androidPluginClass, 'ASamplePlugin');
|
||||
expect(iosPlugin.pluginClass, 'ISamplePlugin');
|
||||
expect(iosPlugin.classPrefix, '');
|
||||
expect(androidPlugin.pluginClass, 'ASamplePlugin');
|
||||
expect(androidPlugin.package, 'com.flutter.dev');
|
||||
expect(linuxPlugin.pluginClass, 'LSamplePlugin');
|
||||
expect(macOSPlugin.pluginClass, 'MSamplePlugin');
|
||||
@ -138,9 +132,12 @@ void main() {
|
||||
|
||||
testWithoutContext('Plugin parsing allows for Dart-only plugins without a pluginClass', () {
|
||||
final FileSystem fileSystem = MemoryFileSystem.test();
|
||||
/// This is currently supported only on macOS, linux, Windows.
|
||||
const String pluginYamlRaw = 'implements: same_plugin\n' // this should be ignored by the tool
|
||||
'platforms:\n'
|
||||
' android:\n'
|
||||
' dartPluginClass: ASamplePlugin\n'
|
||||
' ios:\n'
|
||||
' dartPluginClass: ISamplePlugin\n'
|
||||
' linux:\n'
|
||||
' dartPluginClass: LSamplePlugin\n'
|
||||
' macos:\n'
|
||||
@ -157,13 +154,19 @@ void main() {
|
||||
fileSystem: fileSystem,
|
||||
);
|
||||
|
||||
final AndroidPlugin androidPlugin = plugin.platforms[AndroidPlugin.kConfigKey]! as AndroidPlugin;
|
||||
final IOSPlugin iOSPlugin = plugin.platforms[IOSPlugin.kConfigKey]! as IOSPlugin;
|
||||
final LinuxPlugin linuxPlugin = plugin.platforms[LinuxPlugin.kConfigKey]! as LinuxPlugin;
|
||||
final MacOSPlugin macOSPlugin = plugin.platforms[MacOSPlugin.kConfigKey]! as MacOSPlugin;
|
||||
final WindowsPlugin windowsPlugin = plugin.platforms[WindowsPlugin.kConfigKey]! as WindowsPlugin;
|
||||
|
||||
expect(androidPlugin.pluginClass, isNull);
|
||||
expect(iOSPlugin.pluginClass, isNull);
|
||||
expect(linuxPlugin.pluginClass, isNull);
|
||||
expect(macOSPlugin.pluginClass, isNull);
|
||||
expect(windowsPlugin.pluginClass, isNull);
|
||||
expect(androidPlugin.dartPluginClass, 'ASamplePlugin');
|
||||
expect(iOSPlugin.dartPluginClass, 'ISamplePlugin');
|
||||
expect(linuxPlugin.dartPluginClass, 'LSamplePlugin');
|
||||
expect(macOSPlugin.dartPluginClass, 'MSamplePlugin');
|
||||
expect(windowsPlugin.dartPluginClass, 'WinSamplePlugin');
|
||||
@ -219,6 +222,8 @@ void main() {
|
||||
|
||||
expect(plugin.platforms, <String, PluginPlatform>{});
|
||||
expect(plugin.defaultPackagePlatforms, <String, String>{
|
||||
'android': 'sample_package_android',
|
||||
'ios': 'sample_package_ios',
|
||||
'linux': 'sample_package_linux',
|
||||
'macos': 'sample_package_macos',
|
||||
'windows': 'sample_package_windows',
|
||||
|
@ -966,6 +966,53 @@ web_plugin_with_nested:${webPluginWithNestedFile.childDirectory('lib').uri.toStr
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
});
|
||||
|
||||
testUsingContext('Injecting creates generated Android registrant, but does not include Dart-only plugins', () async {
|
||||
// Create a plugin without a pluginClass.
|
||||
final Directory pluginDirectory = createFakePlugin(fs);
|
||||
pluginDirectory.childFile('pubspec.yaml').writeAsStringSync('''
|
||||
flutter:
|
||||
plugin:
|
||||
platforms:
|
||||
android:
|
||||
dartPluginClass: SomePlugin
|
||||
''');
|
||||
|
||||
await injectPlugins(flutterProject, androidPlatform: true);
|
||||
|
||||
final File registrantFile = androidProject.pluginRegistrantHost
|
||||
.childDirectory(fs.path.join('src', 'main', 'java', 'io', 'flutter', 'plugins'))
|
||||
.childFile('GeneratedPluginRegistrant.java');
|
||||
|
||||
expect(registrantFile, exists);
|
||||
expect(registrantFile, isNot(contains('SomePlugin')));
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => fs,
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
});
|
||||
|
||||
testUsingContext('Injecting creates generated iOS registrant, but does not include Dart-only plugins', () async {
|
||||
flutterProject.isModule = true;
|
||||
// Create a plugin without a pluginClass.
|
||||
final Directory pluginDirectory = createFakePlugin(fs);
|
||||
pluginDirectory.childFile('pubspec.yaml').writeAsStringSync('''
|
||||
flutter:
|
||||
plugin:
|
||||
platforms:
|
||||
ios:
|
||||
dartPluginClass: SomePlugin
|
||||
''');
|
||||
|
||||
await injectPlugins(flutterProject, iosPlatform: true);
|
||||
|
||||
final File registrantFile = iosProject.pluginRegistrantImplementation;
|
||||
|
||||
expect(registrantFile, exists);
|
||||
expect(registrantFile, isNot(contains('SomePlugin')));
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => fs,
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
});
|
||||
|
||||
testUsingContext('Injecting creates generated macos registrant, but does not include Dart-only plugins', () async {
|
||||
flutterProject.isModule = true;
|
||||
// Create a plugin without a pluginClass.
|
||||
@ -1548,6 +1595,12 @@ class FakeIosProject extends Fake implements IosProject {
|
||||
@override
|
||||
Directory pluginRegistrantHost;
|
||||
|
||||
@override
|
||||
File get pluginRegistrantHeader => pluginRegistrantHost.childFile('GeneratedPluginRegistrant.h');
|
||||
|
||||
@override
|
||||
File get pluginRegistrantImplementation => pluginRegistrantHost.childFile('GeneratedPluginRegistrant.m');
|
||||
|
||||
@override
|
||||
File podfile;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user