Allow iOS and macOS plugins to share darwin directory (#115337)
This commit is contained in:
parent
a02b9d2bff
commit
eaaacdcba9
19
.ci.yaml
19
.ci.yaml
@ -2944,6 +2944,25 @@ targets:
|
|||||||
- bin/**
|
- bin/**
|
||||||
- .ci.yaml
|
- .ci.yaml
|
||||||
|
|
||||||
|
- name: Mac plugin_test_macos
|
||||||
|
bringup: true
|
||||||
|
recipe: devicelab/devicelab_drone
|
||||||
|
timeout: 60
|
||||||
|
properties:
|
||||||
|
dependencies: >-
|
||||||
|
[
|
||||||
|
{"dependency": "xcode", "version": "14a5294e"},
|
||||||
|
{"dependency": "gems", "version": "v3.3.14"}
|
||||||
|
]
|
||||||
|
tags: >
|
||||||
|
["devicelab", "hostonly", "mac"]
|
||||||
|
task_name: plugin_test_macos
|
||||||
|
runIf:
|
||||||
|
- dev/**
|
||||||
|
- packages/flutter_tools/**
|
||||||
|
- bin/**
|
||||||
|
- .ci.yaml
|
||||||
|
|
||||||
- name: Mac_x64 tool_host_cross_arch_tests
|
- name: Mac_x64 tool_host_cross_arch_tests
|
||||||
recipe: flutter/flutter_drone
|
recipe: flutter/flutter_drone
|
||||||
timeout: 60
|
timeout: 60
|
||||||
|
@ -248,6 +248,7 @@
|
|||||||
/dev/devicelab/bin/tasks/platform_view_win_desktop__start_up.dart @yaakovschectman @flutter/desktop
|
/dev/devicelab/bin/tasks/platform_view_win_desktop__start_up.dart @yaakovschectman @flutter/desktop
|
||||||
/dev/devicelab/bin/tasks/plugin_lint_mac.dart @stuartmorgan @flutter/plugin
|
/dev/devicelab/bin/tasks/plugin_lint_mac.dart @stuartmorgan @flutter/plugin
|
||||||
/dev/devicelab/bin/tasks/plugin_test_ios.dart @jmagman @flutter/ios
|
/dev/devicelab/bin/tasks/plugin_test_ios.dart @jmagman @flutter/ios
|
||||||
|
/dev/devicelab/bin/tasks/plugin_test_macos.dart @jmagman @flutter/desktop
|
||||||
/dev/devicelab/bin/tasks/plugin_test.dart @stuartmorgan @flutter/plugin
|
/dev/devicelab/bin/tasks/plugin_test.dart @stuartmorgan @flutter/plugin
|
||||||
/dev/devicelab/bin/tasks/run_debug_test_android.dart @zanderso @flutter/tool
|
/dev/devicelab/bin/tasks/run_debug_test_android.dart @zanderso @flutter/tool
|
||||||
/dev/devicelab/bin/tasks/run_debug_test_macos.dart @cbracken @flutter/tool
|
/dev/devicelab/bin/tasks/run_debug_test_macos.dart @cbracken @flutter/tool
|
||||||
|
@ -9,12 +9,11 @@ Future<void> main() async {
|
|||||||
await task(combine(<TaskFunction>[
|
await task(combine(<TaskFunction>[
|
||||||
PluginTest('ios', <String>['-i', 'objc', '--platforms=ios']).call,
|
PluginTest('ios', <String>['-i', 'objc', '--platforms=ios']).call,
|
||||||
PluginTest('ios', <String>['-i', 'swift', '--platforms=ios']).call,
|
PluginTest('ios', <String>['-i', 'swift', '--platforms=ios']).call,
|
||||||
PluginTest('macos', <String>['--platforms=macos']).call,
|
|
||||||
// Test that Dart-only plugins are supported.
|
// Test that Dart-only plugins are supported.
|
||||||
PluginTest('ios', <String>['--platforms=ios'], dartOnlyPlugin: true).call,
|
PluginTest('ios', <String>['--platforms=ios'], dartOnlyPlugin: true).call,
|
||||||
PluginTest('macos', <String>['--platforms=macos'], dartOnlyPlugin: true).call,
|
// Test that shared darwin directories are supported.
|
||||||
|
PluginTest('ios', <String>['--platforms=ios,macos'], sharedDarwinSource: true).call,
|
||||||
// Test that FFI plugins are supported.
|
// Test that FFI plugins are supported.
|
||||||
PluginTest('ios', <String>['--platforms=ios'], template: 'plugin_ffi').call,
|
PluginTest('ios', <String>['--platforms=ios'], template: 'plugin_ffi').call,
|
||||||
PluginTest('macos', <String>['--platforms=macos'], template: 'plugin_ffi').call,
|
|
||||||
]));
|
]));
|
||||||
}
|
}
|
||||||
|
18
dev/devicelab/bin/tasks/plugin_test_macos.dart
Normal file
18
dev/devicelab/bin/tasks/plugin_test_macos.dart
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'package:flutter_devicelab/framework/framework.dart';
|
||||||
|
import 'package:flutter_devicelab/tasks/plugin_tests.dart';
|
||||||
|
|
||||||
|
Future<void> main() async {
|
||||||
|
await task(combine(<TaskFunction>[
|
||||||
|
PluginTest('macos', <String>['--platforms=macos']).call,
|
||||||
|
// Test that Dart-only plugins are supported.
|
||||||
|
PluginTest('macos', <String>['--platforms=macos'], dartOnlyPlugin: true).call,
|
||||||
|
// Test that shared darwin directories are supported.
|
||||||
|
PluginTest('macos', <String>['--platforms=ios,macos'], sharedDarwinSource: true).call,
|
||||||
|
// Test that FFI plugins are supported.
|
||||||
|
PluginTest('macos', <String>['--platforms=macos'], template: 'plugin_ffi').call,
|
||||||
|
]));
|
||||||
|
}
|
@ -33,6 +33,7 @@ class PluginTest {
|
|||||||
this.pluginCreateEnvironment,
|
this.pluginCreateEnvironment,
|
||||||
this.appCreateEnvironment,
|
this.appCreateEnvironment,
|
||||||
this.dartOnlyPlugin = false,
|
this.dartOnlyPlugin = false,
|
||||||
|
this.sharedDarwinSource = false,
|
||||||
this.template = 'plugin',
|
this.template = 'plugin',
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -41,6 +42,7 @@ class PluginTest {
|
|||||||
final Map<String, String>? pluginCreateEnvironment;
|
final Map<String, String>? pluginCreateEnvironment;
|
||||||
final Map<String, String>? appCreateEnvironment;
|
final Map<String, String>? appCreateEnvironment;
|
||||||
final bool dartOnlyPlugin;
|
final bool dartOnlyPlugin;
|
||||||
|
final bool sharedDarwinSource;
|
||||||
final String template;
|
final String template;
|
||||||
|
|
||||||
Future<TaskResult> call() async {
|
Future<TaskResult> call() async {
|
||||||
@ -58,6 +60,9 @@ class PluginTest {
|
|||||||
if (dartOnlyPlugin) {
|
if (dartOnlyPlugin) {
|
||||||
await plugin.convertDefaultPluginToDartPlugin();
|
await plugin.convertDefaultPluginToDartPlugin();
|
||||||
}
|
}
|
||||||
|
if (sharedDarwinSource) {
|
||||||
|
await plugin.convertDefaultPluginToSharedDarwinPlugin();
|
||||||
|
}
|
||||||
section('Test plugin');
|
section('Test plugin');
|
||||||
if (runFlutterTest) {
|
if (runFlutterTest) {
|
||||||
await plugin.runFlutterTest();
|
await plugin.runFlutterTest();
|
||||||
@ -159,6 +164,83 @@ class $dartPluginClass {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Converts an iOS/macOS plugin created from the standard template to a shared
|
||||||
|
/// darwin directory plugin.
|
||||||
|
Future<void> convertDefaultPluginToSharedDarwinPlugin() async {
|
||||||
|
// Convert the metadata.
|
||||||
|
final File pubspec = pubspecFile;
|
||||||
|
String pubspecContent = await pubspec.readAsString();
|
||||||
|
const String originalIOSKey = '\n ios:\n';
|
||||||
|
const String originalMacOSKey = '\n macos:\n';
|
||||||
|
if (!pubspecContent.contains(originalIOSKey) || !pubspecContent.contains(originalMacOSKey)) {
|
||||||
|
print(pubspecContent);
|
||||||
|
throw TaskResult.failure('Missing expected darwin platform plugin keys');
|
||||||
|
}
|
||||||
|
pubspecContent = pubspecContent.replaceAll(
|
||||||
|
originalIOSKey,
|
||||||
|
'$originalIOSKey sharedDarwinSource: true\n'
|
||||||
|
);
|
||||||
|
pubspecContent = pubspecContent.replaceAll(
|
||||||
|
originalMacOSKey,
|
||||||
|
'$originalMacOSKey sharedDarwinSource: true\n'
|
||||||
|
);
|
||||||
|
await pubspec.writeAsString(pubspecContent, flush: true);
|
||||||
|
|
||||||
|
// Copy ios to darwin, and delete macos.
|
||||||
|
final Directory iosDir = Directory(path.join(rootPath, 'ios'));
|
||||||
|
final Directory darwinDir = Directory(path.join(rootPath, 'darwin'));
|
||||||
|
recursiveCopy(iosDir, darwinDir);
|
||||||
|
|
||||||
|
await iosDir.delete(recursive: true);
|
||||||
|
await Directory(path.join(rootPath, 'macos')).delete(recursive: true);
|
||||||
|
|
||||||
|
final File podspec = File(path.join(darwinDir.path, '$name.podspec'));
|
||||||
|
String podspecContent = await podspec.readAsString();
|
||||||
|
if (!podspecContent.contains('s.platform =')) {
|
||||||
|
print(podspecContent);
|
||||||
|
throw TaskResult.failure('Missing expected podspec platform');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove "s.platform = :ios" to work on all platforms, including macOS.
|
||||||
|
podspecContent = podspecContent.replaceFirst(RegExp(r'.*s\.platform.*'), '');
|
||||||
|
podspecContent = podspecContent.replaceFirst("s.dependency 'Flutter'", "s.ios.dependency 'Flutter'\ns.osx.dependency 'FlutterMacOS'");
|
||||||
|
|
||||||
|
await podspec.writeAsString(podspecContent, flush: true);
|
||||||
|
|
||||||
|
// Make PlugintestPlugin.swift compile on iOS and macOS with target conditionals.
|
||||||
|
final String pluginClass = '${name[0].toUpperCase()}${name.substring(1)}Plugin';
|
||||||
|
print('pluginClass: $pluginClass');
|
||||||
|
final File pluginRegister = File(path.join(darwinDir.path, 'Classes', '$pluginClass.swift'));
|
||||||
|
final String pluginRegisterContent = '''
|
||||||
|
#if os(macOS)
|
||||||
|
import FlutterMacOS
|
||||||
|
#elseif os(iOS)
|
||||||
|
import Flutter
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public class $pluginClass: NSObject, FlutterPlugin {
|
||||||
|
public static func register(with registrar: FlutterPluginRegistrar) {
|
||||||
|
#if os(macOS)
|
||||||
|
let channel = FlutterMethodChannel(name: "$name", binaryMessenger: registrar.messenger)
|
||||||
|
#elseif os(iOS)
|
||||||
|
let channel = FlutterMethodChannel(name: "$name", binaryMessenger: registrar.messenger())
|
||||||
|
#endif
|
||||||
|
let instance = $pluginClass()
|
||||||
|
registrar.addMethodCallDelegate(instance, channel: channel)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||||
|
#if os(macOS)
|
||||||
|
result("macOS " + ProcessInfo.processInfo.operatingSystemVersionString)
|
||||||
|
#elseif os(iOS)
|
||||||
|
result("iOS " + UIDevice.current.systemVersion)
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
''';
|
||||||
|
await pluginRegister.writeAsString(pluginRegisterContent, flush: true);
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> runFlutterTest() async {
|
Future<void> runFlutterTest() async {
|
||||||
await inDirectory(Directory(rootPath), () async {
|
await inDirectory(Directory(rootPath), () async {
|
||||||
await flutter('test');
|
await flutter('test');
|
||||||
|
@ -266,6 +266,11 @@ def flutter_install_plugin_pods(application_path = nil, relative_symlink_dir, pl
|
|||||||
plugin_name = plugin_hash['name']
|
plugin_name = plugin_hash['name']
|
||||||
plugin_path = plugin_hash['path']
|
plugin_path = plugin_hash['path']
|
||||||
has_native_build = plugin_hash.fetch('native_build', true)
|
has_native_build = plugin_hash.fetch('native_build', true)
|
||||||
|
|
||||||
|
# iOS and macOS code can be shared in "darwin" directory, otherwise
|
||||||
|
# respectively in "ios" or "macos" directories.
|
||||||
|
shared_darwin_source = plugin_hash.fetch('shared_darwin_source', false)
|
||||||
|
platform_directory = shared_darwin_source ? 'darwin' : platform
|
||||||
next unless plugin_name && plugin_path && has_native_build
|
next unless plugin_name && plugin_path && has_native_build
|
||||||
symlink = File.join(symlink_plugins_dir, plugin_name)
|
symlink = File.join(symlink_plugins_dir, plugin_name)
|
||||||
File.symlink(plugin_path, symlink)
|
File.symlink(plugin_path, symlink)
|
||||||
@ -273,7 +278,7 @@ def flutter_install_plugin_pods(application_path = nil, relative_symlink_dir, pl
|
|||||||
# Keep pod path relative so it can be checked into Podfile.lock.
|
# Keep pod path relative so it can be checked into Podfile.lock.
|
||||||
relative = flutter_relative_path_from_podfile(symlink)
|
relative = flutter_relative_path_from_podfile(symlink)
|
||||||
|
|
||||||
pod plugin_name, path: File.join(relative, platform)
|
pod plugin_name, path: File.join(relative, platform_directory)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -288,7 +293,7 @@ def flutter_parse_plugins_file(file, platform)
|
|||||||
|
|
||||||
# dependencies_hash.dig('plugins', 'ios') not available until Ruby 2.3
|
# dependencies_hash.dig('plugins', 'ios') not available until Ruby 2.3
|
||||||
return [] unless dependencies_hash.has_key?('plugins')
|
return [] unless dependencies_hash.has_key?('plugins')
|
||||||
return [] unless dependencies_hash['plugins'].has_key?('ios')
|
return [] unless dependencies_hash['plugins'].has_key?(platform)
|
||||||
dependencies_hash['plugins'][platform] || []
|
dependencies_hash['plugins'][platform] || []
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -104,6 +104,7 @@ const String _kFlutterPluginsNameKey = 'name';
|
|||||||
const String _kFlutterPluginsPathKey = 'path';
|
const String _kFlutterPluginsPathKey = 'path';
|
||||||
const String _kFlutterPluginsDependenciesKey = 'dependencies';
|
const String _kFlutterPluginsDependenciesKey = 'dependencies';
|
||||||
const String _kFlutterPluginsHasNativeBuildKey = 'native_build';
|
const String _kFlutterPluginsHasNativeBuildKey = 'native_build';
|
||||||
|
const String _kFlutterPluginsSharedDarwinSource = 'shared_darwin_source';
|
||||||
|
|
||||||
/// Filters [plugins] to those supported by [platformKey].
|
/// Filters [plugins] to those supported by [platformKey].
|
||||||
List<Map<String, Object>> _filterPluginsByPlatform(List<Plugin> plugins, String platformKey) {
|
List<Map<String, Object>> _filterPluginsByPlatform(List<Plugin> plugins, String platformKey) {
|
||||||
@ -119,6 +120,8 @@ List<Map<String, Object>> _filterPluginsByPlatform(List<Plugin> plugins, String
|
|||||||
pluginInfo.add(<String, Object>{
|
pluginInfo.add(<String, Object>{
|
||||||
_kFlutterPluginsNameKey: plugin.name,
|
_kFlutterPluginsNameKey: plugin.name,
|
||||||
_kFlutterPluginsPathKey: globals.fsUtils.escapePath(plugin.path),
|
_kFlutterPluginsPathKey: globals.fsUtils.escapePath(plugin.path),
|
||||||
|
if (platformPlugin is DarwinPlugin && (platformPlugin as DarwinPlugin).sharedDarwinSource)
|
||||||
|
_kFlutterPluginsSharedDarwinSource: (platformPlugin as DarwinPlugin).sharedDarwinSource,
|
||||||
if (platformPlugin is NativeOrDartPlugin)
|
if (platformPlugin is NativeOrDartPlugin)
|
||||||
_kFlutterPluginsHasNativeBuildKey: (platformPlugin as NativeOrDartPlugin).hasMethodChannel() || (platformPlugin as NativeOrDartPlugin).hasFfi(),
|
_kFlutterPluginsHasNativeBuildKey: (platformPlugin as NativeOrDartPlugin).hasMethodChannel() || (platformPlugin as NativeOrDartPlugin).hasFfi(),
|
||||||
_kFlutterPluginsDependenciesKey: <String>[...plugin.dependencies.where(pluginNames.contains)],
|
_kFlutterPluginsDependenciesKey: <String>[...plugin.dependencies.where(pluginNames.contains)],
|
||||||
|
@ -19,6 +19,10 @@ const String kFfiPlugin = 'ffiPlugin';
|
|||||||
// Constant for 'defaultPackage' key in plugin maps.
|
// Constant for 'defaultPackage' key in plugin maps.
|
||||||
const String kDefaultPackage = 'default_package';
|
const String kDefaultPackage = 'default_package';
|
||||||
|
|
||||||
|
/// Constant for 'sharedDarwinSource' key in plugin maps.
|
||||||
|
/// Can be set for iOS and macOS plugins.
|
||||||
|
const String kSharedDarwinSource = 'sharedDarwinSource';
|
||||||
|
|
||||||
/// Constant for 'supportedVariants' key in plugin maps.
|
/// Constant for 'supportedVariants' key in plugin maps.
|
||||||
const String kSupportedVariants = 'supportedVariants';
|
const String kSupportedVariants = 'supportedVariants';
|
||||||
|
|
||||||
@ -52,6 +56,11 @@ abstract class NativeOrDartPlugin {
|
|||||||
bool hasMethodChannel();
|
bool hasMethodChannel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abstract class DarwinPlugin {
|
||||||
|
/// Indicates the iOS and macOS native code is shareable the subdirectory "darwin",
|
||||||
|
bool get sharedDarwinSource;
|
||||||
|
}
|
||||||
|
|
||||||
/// Contains parameters to template an Android plugin.
|
/// Contains parameters to template an Android plugin.
|
||||||
///
|
///
|
||||||
/// The [name] of the plugin is required. Additionally, either:
|
/// The [name] of the plugin is required. Additionally, either:
|
||||||
@ -227,7 +236,7 @@ class AndroidPlugin extends PluginPlatform implements NativeOrDartPlugin {
|
|||||||
/// - the [dartPluginClass] that will be the entry point for the plugin's
|
/// - the [dartPluginClass] that will be the entry point for the plugin's
|
||||||
/// Dart code
|
/// Dart code
|
||||||
/// is required.
|
/// is required.
|
||||||
class IOSPlugin extends PluginPlatform implements NativeOrDartPlugin {
|
class IOSPlugin extends PluginPlatform implements NativeOrDartPlugin, DarwinPlugin {
|
||||||
const IOSPlugin({
|
const IOSPlugin({
|
||||||
required this.name,
|
required this.name,
|
||||||
required this.classPrefix,
|
required this.classPrefix,
|
||||||
@ -235,7 +244,9 @@ class IOSPlugin extends PluginPlatform implements NativeOrDartPlugin {
|
|||||||
this.dartPluginClass,
|
this.dartPluginClass,
|
||||||
bool? ffiPlugin,
|
bool? ffiPlugin,
|
||||||
this.defaultPackage,
|
this.defaultPackage,
|
||||||
}) : ffiPlugin = ffiPlugin ?? false;
|
bool? sharedDarwinSource,
|
||||||
|
}) : ffiPlugin = ffiPlugin ?? false,
|
||||||
|
sharedDarwinSource = sharedDarwinSource ?? false;
|
||||||
|
|
||||||
factory IOSPlugin.fromYaml(String name, YamlMap yaml) {
|
factory IOSPlugin.fromYaml(String name, YamlMap yaml) {
|
||||||
assert(validate(yaml)); // TODO(zanderso): https://github.com/flutter/flutter/issues/67241
|
assert(validate(yaml)); // TODO(zanderso): https://github.com/flutter/flutter/issues/67241
|
||||||
@ -246,6 +257,7 @@ class IOSPlugin extends PluginPlatform implements NativeOrDartPlugin {
|
|||||||
dartPluginClass: yaml[kDartPluginClass] as String?,
|
dartPluginClass: yaml[kDartPluginClass] as String?,
|
||||||
ffiPlugin: yaml[kFfiPlugin] as bool?,
|
ffiPlugin: yaml[kFfiPlugin] as bool?,
|
||||||
defaultPackage: yaml[kDefaultPackage] as String?,
|
defaultPackage: yaml[kDefaultPackage] as String?,
|
||||||
|
sharedDarwinSource: yaml[kSharedDarwinSource] as bool?,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -256,6 +268,7 @@ class IOSPlugin extends PluginPlatform implements NativeOrDartPlugin {
|
|||||||
return yaml[kPluginClass] is String ||
|
return yaml[kPluginClass] is String ||
|
||||||
yaml[kDartPluginClass] is String ||
|
yaml[kDartPluginClass] is String ||
|
||||||
yaml[kFfiPlugin] == true ||
|
yaml[kFfiPlugin] == true ||
|
||||||
|
yaml[kSharedDarwinSource] == true ||
|
||||||
yaml[kDefaultPackage] is String;
|
yaml[kDefaultPackage] is String;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -271,6 +284,11 @@ class IOSPlugin extends PluginPlatform implements NativeOrDartPlugin {
|
|||||||
final bool ffiPlugin;
|
final bool ffiPlugin;
|
||||||
final String? defaultPackage;
|
final String? defaultPackage;
|
||||||
|
|
||||||
|
/// Indicates the iOS native code is shareable with macOS in
|
||||||
|
/// the subdirectory "darwin", otherwise in the subdirectory "ios".
|
||||||
|
@override
|
||||||
|
final bool sharedDarwinSource;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool hasMethodChannel() => pluginClass != null;
|
bool hasMethodChannel() => pluginClass != null;
|
||||||
|
|
||||||
@ -288,6 +306,7 @@ class IOSPlugin extends PluginPlatform implements NativeOrDartPlugin {
|
|||||||
if (pluginClass != null) 'class': pluginClass,
|
if (pluginClass != null) 'class': pluginClass,
|
||||||
if (dartPluginClass != null) kDartPluginClass : dartPluginClass,
|
if (dartPluginClass != null) kDartPluginClass : dartPluginClass,
|
||||||
if (ffiPlugin) kFfiPlugin: true,
|
if (ffiPlugin) kFfiPlugin: true,
|
||||||
|
if (sharedDarwinSource) kSharedDarwinSource: true,
|
||||||
if (defaultPackage != null) kDefaultPackage : defaultPackage,
|
if (defaultPackage != null) kDefaultPackage : defaultPackage,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -298,14 +317,16 @@ class IOSPlugin extends PluginPlatform implements NativeOrDartPlugin {
|
|||||||
/// The [name] of the plugin is required. Either [dartPluginClass] or
|
/// The [name] of the plugin is required. Either [dartPluginClass] or
|
||||||
/// [pluginClass] or [ffiPlugin] are required.
|
/// [pluginClass] or [ffiPlugin] are required.
|
||||||
/// [pluginClass] will be the entry point to the plugin's native code.
|
/// [pluginClass] will be the entry point to the plugin's native code.
|
||||||
class MacOSPlugin extends PluginPlatform implements NativeOrDartPlugin {
|
class MacOSPlugin extends PluginPlatform implements NativeOrDartPlugin, DarwinPlugin {
|
||||||
const MacOSPlugin({
|
const MacOSPlugin({
|
||||||
required this.name,
|
required this.name,
|
||||||
this.pluginClass,
|
this.pluginClass,
|
||||||
this.dartPluginClass,
|
this.dartPluginClass,
|
||||||
bool? ffiPlugin,
|
bool? ffiPlugin,
|
||||||
this.defaultPackage,
|
this.defaultPackage,
|
||||||
}) : ffiPlugin = ffiPlugin ?? false;
|
bool? sharedDarwinSource,
|
||||||
|
}) : ffiPlugin = ffiPlugin ?? false,
|
||||||
|
sharedDarwinSource = sharedDarwinSource ?? false;
|
||||||
|
|
||||||
factory MacOSPlugin.fromYaml(String name, YamlMap yaml) {
|
factory MacOSPlugin.fromYaml(String name, YamlMap yaml) {
|
||||||
assert(validate(yaml));
|
assert(validate(yaml));
|
||||||
@ -320,6 +341,7 @@ class MacOSPlugin extends PluginPlatform implements NativeOrDartPlugin {
|
|||||||
dartPluginClass: yaml[kDartPluginClass] as String?,
|
dartPluginClass: yaml[kDartPluginClass] as String?,
|
||||||
ffiPlugin: yaml[kFfiPlugin] as bool?,
|
ffiPlugin: yaml[kFfiPlugin] as bool?,
|
||||||
defaultPackage: yaml[kDefaultPackage] as String?,
|
defaultPackage: yaml[kDefaultPackage] as String?,
|
||||||
|
sharedDarwinSource: yaml[kSharedDarwinSource] as bool?,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -330,6 +352,7 @@ class MacOSPlugin extends PluginPlatform implements NativeOrDartPlugin {
|
|||||||
return yaml[kPluginClass] is String ||
|
return yaml[kPluginClass] is String ||
|
||||||
yaml[kDartPluginClass] is String ||
|
yaml[kDartPluginClass] is String ||
|
||||||
yaml[kFfiPlugin] == true ||
|
yaml[kFfiPlugin] == true ||
|
||||||
|
yaml[kSharedDarwinSource] == true ||
|
||||||
yaml[kDefaultPackage] is String;
|
yaml[kDefaultPackage] is String;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -341,6 +364,11 @@ class MacOSPlugin extends PluginPlatform implements NativeOrDartPlugin {
|
|||||||
final bool ffiPlugin;
|
final bool ffiPlugin;
|
||||||
final String? defaultPackage;
|
final String? defaultPackage;
|
||||||
|
|
||||||
|
/// Indicates the macOS native code is shareable with iOS in
|
||||||
|
/// the subdirectory "darwin", otherwise in the subdirectory "macos".
|
||||||
|
@override
|
||||||
|
final bool sharedDarwinSource;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool hasMethodChannel() => pluginClass != null;
|
bool hasMethodChannel() => pluginClass != null;
|
||||||
|
|
||||||
@ -357,6 +385,7 @@ class MacOSPlugin extends PluginPlatform implements NativeOrDartPlugin {
|
|||||||
if (pluginClass != null) 'class': pluginClass,
|
if (pluginClass != null) 'class': pluginClass,
|
||||||
if (dartPluginClass != null) kDartPluginClass: dartPluginClass,
|
if (dartPluginClass != null) kDartPluginClass: dartPluginClass,
|
||||||
if (ffiPlugin) kFfiPlugin: true,
|
if (ffiPlugin) kFfiPlugin: true,
|
||||||
|
if (sharedDarwinSource) kSharedDarwinSource: true,
|
||||||
if (defaultPackage != null) kDefaultPackage: defaultPackage,
|
if (defaultPackage != null) kDefaultPackage: defaultPackage,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,7 @@ void main() {
|
|||||||
|
|
||||||
expect(iosPlugin.pluginClass, 'SamplePlugin');
|
expect(iosPlugin.pluginClass, 'SamplePlugin');
|
||||||
expect(iosPlugin.classPrefix, 'FLT');
|
expect(iosPlugin.classPrefix, 'FLT');
|
||||||
|
expect(iosPlugin.sharedDarwinSource, isFalse);
|
||||||
expect(androidPlugin.pluginClass, 'SamplePlugin');
|
expect(androidPlugin.pluginClass, 'SamplePlugin');
|
||||||
expect(androidPlugin.package, 'com.flutter.dev');
|
expect(androidPlugin.package, 'com.flutter.dev');
|
||||||
});
|
});
|
||||||
@ -47,10 +48,12 @@ void main() {
|
|||||||
' pluginClass: ASamplePlugin\n'
|
' pluginClass: ASamplePlugin\n'
|
||||||
' ios:\n'
|
' ios:\n'
|
||||||
' pluginClass: ISamplePlugin\n'
|
' pluginClass: ISamplePlugin\n'
|
||||||
|
' sharedDarwinSource: true\n'
|
||||||
' linux:\n'
|
' linux:\n'
|
||||||
' pluginClass: LSamplePlugin\n'
|
' pluginClass: LSamplePlugin\n'
|
||||||
' macos:\n'
|
' macos:\n'
|
||||||
' pluginClass: MSamplePlugin\n'
|
' pluginClass: MSamplePlugin\n'
|
||||||
|
' sharedDarwinSource: true\n'
|
||||||
' web:\n'
|
' web:\n'
|
||||||
' pluginClass: WebSamplePlugin\n'
|
' pluginClass: WebSamplePlugin\n'
|
||||||
' fileName: web_plugin.dart\n'
|
' fileName: web_plugin.dart\n'
|
||||||
@ -76,10 +79,12 @@ void main() {
|
|||||||
|
|
||||||
expect(iosPlugin.pluginClass, 'ISamplePlugin');
|
expect(iosPlugin.pluginClass, 'ISamplePlugin');
|
||||||
expect(iosPlugin.classPrefix, '');
|
expect(iosPlugin.classPrefix, '');
|
||||||
|
expect(iosPlugin.sharedDarwinSource, isTrue);
|
||||||
expect(androidPlugin.pluginClass, 'ASamplePlugin');
|
expect(androidPlugin.pluginClass, 'ASamplePlugin');
|
||||||
expect(androidPlugin.package, 'com.flutter.dev');
|
expect(androidPlugin.package, 'com.flutter.dev');
|
||||||
expect(linuxPlugin.pluginClass, 'LSamplePlugin');
|
expect(linuxPlugin.pluginClass, 'LSamplePlugin');
|
||||||
expect(macOSPlugin.pluginClass, 'MSamplePlugin');
|
expect(macOSPlugin.pluginClass, 'MSamplePlugin');
|
||||||
|
expect(macOSPlugin.sharedDarwinSource, isTrue);
|
||||||
expect(webPlugin.pluginClass, 'WebSamplePlugin');
|
expect(webPlugin.pluginClass, 'WebSamplePlugin');
|
||||||
expect(webPlugin.fileName, 'web_plugin.dart');
|
expect(webPlugin.fileName, 'web_plugin.dart');
|
||||||
expect(windowsPlugin.pluginClass, 'WinSamplePlugin');
|
expect(windowsPlugin.pluginClass, 'WinSamplePlugin');
|
||||||
@ -124,10 +129,12 @@ void main() {
|
|||||||
|
|
||||||
expect(iosPlugin.pluginClass, 'ISamplePlugin');
|
expect(iosPlugin.pluginClass, 'ISamplePlugin');
|
||||||
expect(iosPlugin.classPrefix, '');
|
expect(iosPlugin.classPrefix, '');
|
||||||
|
expect(iosPlugin.sharedDarwinSource, isFalse);
|
||||||
expect(androidPlugin.pluginClass, 'ASamplePlugin');
|
expect(androidPlugin.pluginClass, 'ASamplePlugin');
|
||||||
expect(androidPlugin.package, 'com.flutter.dev');
|
expect(androidPlugin.package, 'com.flutter.dev');
|
||||||
expect(linuxPlugin.pluginClass, 'LSamplePlugin');
|
expect(linuxPlugin.pluginClass, 'LSamplePlugin');
|
||||||
expect(macOSPlugin.pluginClass, 'MSamplePlugin');
|
expect(macOSPlugin.pluginClass, 'MSamplePlugin');
|
||||||
|
expect(macOSPlugin.sharedDarwinSource, isFalse);
|
||||||
expect(webPlugin.pluginClass, 'WebSamplePlugin');
|
expect(webPlugin.pluginClass, 'WebSamplePlugin');
|
||||||
expect(webPlugin.fileName, 'web_plugin.dart');
|
expect(webPlugin.fileName, 'web_plugin.dart');
|
||||||
expect(windowsPlugin.pluginClass, 'WinSamplePlugin');
|
expect(windowsPlugin.pluginClass, 'WinSamplePlugin');
|
||||||
|
@ -34,6 +34,7 @@ class _PluginPlatformInfo {
|
|||||||
this.pluginClass,
|
this.pluginClass,
|
||||||
this.dartPluginClass,
|
this.dartPluginClass,
|
||||||
this.androidPackage,
|
this.androidPackage,
|
||||||
|
this.sharedDarwinSource = false,
|
||||||
this.fileName
|
this.fileName
|
||||||
}) : assert(pluginClass != null || dartPluginClass != null),
|
}) : assert(pluginClass != null || dartPluginClass != null),
|
||||||
assert(androidPackage == null || pluginClass != null);
|
assert(androidPackage == null || pluginClass != null);
|
||||||
@ -47,6 +48,8 @@ class _PluginPlatformInfo {
|
|||||||
/// The package entry for an Android plugin implementation using pluginClass.
|
/// The package entry for an Android plugin implementation using pluginClass.
|
||||||
final String? androidPackage;
|
final String? androidPackage;
|
||||||
|
|
||||||
|
final bool sharedDarwinSource;
|
||||||
|
|
||||||
/// The fileName entry for a web plugin implementation.
|
/// The fileName entry for a web plugin implementation.
|
||||||
final String? fileName;
|
final String? fileName;
|
||||||
|
|
||||||
@ -61,6 +64,8 @@ class _PluginPlatformInfo {
|
|||||||
'${indentation}dartPluginClass: $dartPluginClass',
|
'${indentation}dartPluginClass: $dartPluginClass',
|
||||||
if (androidPackage != null)
|
if (androidPackage != null)
|
||||||
'${indentation}package: $androidPackage',
|
'${indentation}package: $androidPackage',
|
||||||
|
if (sharedDarwinSource)
|
||||||
|
'${indentation}sharedDarwinSource: true',
|
||||||
if (fileName != null)
|
if (fileName != null)
|
||||||
'${indentation}fileName: $fileName',
|
'${indentation}fileName: $fileName',
|
||||||
].join('\n');
|
].join('\n');
|
||||||
@ -595,14 +600,14 @@ dependencies:
|
|||||||
});
|
});
|
||||||
|
|
||||||
testUsingContext(
|
testUsingContext(
|
||||||
'.flutter-plugins-dependencies indicates native build inclusion', () async {
|
'.flutter-plugins-dependencies contains plugin platform info', () async {
|
||||||
createPlugin(
|
createPlugin(
|
||||||
name: 'plugin-a',
|
name: 'plugin-a',
|
||||||
platforms: const <String, _PluginPlatformInfo>{
|
platforms: const <String, _PluginPlatformInfo>{
|
||||||
// Native-only; should include native build.
|
// Native-only; should include native build.
|
||||||
'android': _PluginPlatformInfo(pluginClass: 'Foo', androidPackage: 'bar.foo'),
|
'android': _PluginPlatformInfo(pluginClass: 'Foo', androidPackage: 'bar.foo'),
|
||||||
// Hybrid native and Dart; should include native build.
|
// Hybrid native and Dart; should include native build.
|
||||||
'ios': _PluginPlatformInfo(pluginClass: 'Foo', dartPluginClass: 'Bar'),
|
'ios': _PluginPlatformInfo(pluginClass: 'Foo', dartPluginClass: 'Bar', sharedDarwinSource: true),
|
||||||
// Web; should not have the native build key at all since it doesn't apply.
|
// Web; should not have the native build key at all since it doesn't apply.
|
||||||
'web': _PluginPlatformInfo(pluginClass: 'Foo', fileName: 'lib/foo.dart'),
|
'web': _PluginPlatformInfo(pluginClass: 'Foo', fileName: 'lib/foo.dart'),
|
||||||
// Dart-only; should not include native build.
|
// Dart-only; should not include native build.
|
||||||
@ -618,20 +623,45 @@ dependencies:
|
|||||||
expect(flutterProject.flutterPluginsDependenciesFile.existsSync(), true);
|
expect(flutterProject.flutterPluginsDependenciesFile.existsSync(), true);
|
||||||
final String pluginsString = flutterProject.flutterPluginsDependenciesFile.readAsStringSync();
|
final String pluginsString = flutterProject.flutterPluginsDependenciesFile.readAsStringSync();
|
||||||
final Map<String, dynamic> jsonContent = json.decode(pluginsString) as Map<String, dynamic>;
|
final Map<String, dynamic> jsonContent = json.decode(pluginsString) as Map<String, dynamic>;
|
||||||
final Map<String, dynamic>? plugins = jsonContent['plugins'] as Map<String, dynamic>?;
|
final Map<String, dynamic>? actualPlugins = jsonContent['plugins'] as Map<String, dynamic>?;
|
||||||
|
|
||||||
// Extracts the native_build key (if any) from the first plugin for the
|
final Map<String, Object> expectedPlugins = <String, Object>{
|
||||||
// given platform.
|
'ios': <Map<String, Object>>[
|
||||||
bool? getNativeBuildValue(String platform) {
|
<String, Object>{
|
||||||
final List<Map<String, dynamic>> platformPlugins = (plugins![platform]
|
'name': 'plugin-a',
|
||||||
as List<dynamic>).cast<Map<String, dynamic>>();
|
'path': '/.tmp_rand0/flutter_plugin.rand0/',
|
||||||
expect(platformPlugins.length, 1);
|
'shared_darwin_source': true,
|
||||||
return platformPlugins[0]['native_build'] as bool?;
|
'native_build': true,
|
||||||
}
|
'dependencies': <String>[]
|
||||||
expect(getNativeBuildValue('android'), true);
|
}
|
||||||
expect(getNativeBuildValue('ios'), true);
|
],
|
||||||
expect(getNativeBuildValue('web'), null);
|
'android': <Map<String, Object>>[
|
||||||
expect(getNativeBuildValue('windows'), false);
|
<String, Object>{
|
||||||
|
'name': 'plugin-a',
|
||||||
|
'path': '/.tmp_rand0/flutter_plugin.rand0/',
|
||||||
|
'native_build': true,
|
||||||
|
'dependencies': <String>[]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'macos': <Map<String, Object>>[],
|
||||||
|
'linux': <Map<String, Object>>[],
|
||||||
|
'windows': <Map<String, Object>>[
|
||||||
|
<String, Object>{
|
||||||
|
'name': 'plugin-a',
|
||||||
|
'path': '/.tmp_rand0/flutter_plugin.rand0/',
|
||||||
|
'native_build': false,
|
||||||
|
'dependencies': <String>[]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'web': <Map<String, Object>>[
|
||||||
|
<String, Object>{
|
||||||
|
'name': 'plugin-a',
|
||||||
|
'path': '/.tmp_rand0/flutter_plugin.rand0/',
|
||||||
|
'dependencies': <String>[]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
expect(actualPlugins, expectedPlugins);
|
||||||
}, overrides: <Type, Generator>{
|
}, overrides: <Type, Generator>{
|
||||||
FileSystem: () => fs,
|
FileSystem: () => fs,
|
||||||
ProcessManager: () => FakeProcessManager.any(),
|
ProcessManager: () => FakeProcessManager.any(),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user