Unverified Commit eaaacdcb authored by Jenn Magder's avatar Jenn Magder Committed by GitHub

Allow iOS and macOS plugins to share darwin directory (#115337)

parent a02b9d2b
...@@ -2944,6 +2944,25 @@ targets: ...@@ -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 @@ ...@@ -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 { ...@@ -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,
])); ]));
} }
// 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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 ...@@ -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 ...@@ -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) ...@@ -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'; ...@@ -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 ...@@ -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'; ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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() { ...@@ -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() { ...@@ -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() { ...@@ -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() { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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: ...@@ -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: ...@@ -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(),
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment