Fully support Dart-only mobile and macOS plugins (#96183)
This commit is contained in:
parent
f18d4d03d3
commit
2df7dca858
@ -152,14 +152,28 @@ Future<void> main() async {
|
||||
await flutter('clean');
|
||||
});
|
||||
|
||||
// Make a fake Dart-only plugin, since there are no existing examples.
|
||||
section('Create local plugin');
|
||||
|
||||
const String dartPluginName = 'dartplugin';
|
||||
await _createFakeDartPlugin(dartPluginName, tempDir);
|
||||
|
||||
section('Add plugins');
|
||||
|
||||
final File pubspec = File(path.join(projectDir.path, 'pubspec.yaml'));
|
||||
String content = await pubspec.readAsString();
|
||||
content = content.replaceFirst(
|
||||
'\ndependencies:\n',
|
||||
// One dynamic framework, one static framework, and one that does not support iOS.
|
||||
'\ndependencies:\n device_info: 2.0.3\n google_sign_in: 4.5.1\n android_alarm_manager: 0.4.5+11\n',
|
||||
// One dynamic framework, one static framework, one Dart-only,
|
||||
// and one that does not support iOS.
|
||||
'''
|
||||
dependencies:
|
||||
device_info: 2.0.3
|
||||
google_sign_in: 4.5.1
|
||||
android_alarm_manager: 0.4.5+11
|
||||
$dartPluginName:
|
||||
path: ../$dartPluginName
|
||||
''',
|
||||
);
|
||||
await pubspec.writeAsString(content, flush: true);
|
||||
await inDirectory(projectDir, () async {
|
||||
@ -191,7 +205,8 @@ Future<void> main() async {
|
||||
|| !podfileLockOutput.contains(':path: Flutter/FlutterPluginRegistrant')
|
||||
|| !podfileLockOutput.contains(':path: ".symlinks/plugins/device_info/ios"')
|
||||
|| !podfileLockOutput.contains(':path: ".symlinks/plugins/google_sign_in/ios"')
|
||||
|| podfileLockOutput.contains('android_alarm_manager')) {
|
||||
|| podfileLockOutput.contains('android_alarm_manager')
|
||||
|| podfileLockOutput.contains(dartPluginName)) {
|
||||
print(podfileLockOutput);
|
||||
return TaskResult.failure('Building ephemeral host app Podfile.lock does not contain expected pods');
|
||||
}
|
||||
@ -205,6 +220,9 @@ Future<void> main() async {
|
||||
// Android-only, no embedded framework.
|
||||
checkDirectoryNotExists(path.join(ephemeralIOSHostApp.path, 'Frameworks', 'android_alarm_manager.framework'));
|
||||
|
||||
// Dart-only, no embedded framework.
|
||||
checkDirectoryNotExists(path.join(ephemeralIOSHostApp.path, 'Frameworks', '$dartPluginName.framework'));
|
||||
|
||||
section('Clean and pub get module');
|
||||
|
||||
await inDirectory(projectDir, () async {
|
||||
@ -243,7 +261,8 @@ Future<void> main() async {
|
||||
|| !hostPodfileLockOutput.contains(':path: "../hello/.ios/Flutter/FlutterPluginRegistrant"')
|
||||
|| !hostPodfileLockOutput.contains(':path: "../hello/.ios/.symlinks/plugins/device_info/ios"')
|
||||
|| !hostPodfileLockOutput.contains(':path: "../hello/.ios/.symlinks/plugins/google_sign_in/ios"')
|
||||
|| hostPodfileLockOutput.contains('android_alarm_manager')) {
|
||||
|| hostPodfileLockOutput.contains('android_alarm_manager')
|
||||
|| hostPodfileLockOutput.contains(dartPluginName)) {
|
||||
print(hostPodfileLockOutput);
|
||||
throw TaskResult.failure('Building host app Podfile.lock does not contain expected pods');
|
||||
}
|
||||
@ -501,3 +520,46 @@ Future<bool> _isAppAotBuild(Directory app) async {
|
||||
|
||||
return symbolTable.contains('kDartIsolateSnapshotInstructions');
|
||||
}
|
||||
|
||||
Future<void> _createFakeDartPlugin(String name, Directory parent) async {
|
||||
// Start from a standard plugin template.
|
||||
await inDirectory(parent, () async {
|
||||
await flutter(
|
||||
'create',
|
||||
options: <String>[
|
||||
'--org',
|
||||
'io.flutter.devicelab',
|
||||
'--template=plugin',
|
||||
'--platforms=ios',
|
||||
name,
|
||||
],
|
||||
);
|
||||
});
|
||||
|
||||
final String pluginDir = path.join(parent.path, name);
|
||||
|
||||
// Convert the metadata to Dart-only.
|
||||
final String dartPluginClass = 'DartClassFor$name';
|
||||
final File pubspec = File(path.join(pluginDir, 'pubspec.yaml'));
|
||||
String content = await pubspec.readAsString();
|
||||
content = content.replaceAll(
|
||||
RegExp(r' pluginClass: .*?\n'),
|
||||
' dartPluginClass: $dartPluginClass\n',
|
||||
);
|
||||
await pubspec.writeAsString(content, flush: true);
|
||||
|
||||
// Add the Dart registration hook that the build will generate a call to.
|
||||
final File dartCode = File(path.join(pluginDir, 'lib', '$name.dart'));
|
||||
content = await dartCode.readAsString();
|
||||
content = '''
|
||||
$content
|
||||
|
||||
class $dartPluginClass {
|
||||
static void registerWith() {}
|
||||
}
|
||||
''';
|
||||
await dartCode.writeAsString(content, flush: true);
|
||||
|
||||
// Remove the native plugin code.
|
||||
await Directory(path.join(pluginDir, 'ios')).delete(recursive: true);
|
||||
}
|
||||
|
@ -16,5 +16,7 @@ Future<void> main() async {
|
||||
<String, String>{'ENABLE_ANDROID_EMBEDDING_V2': 'true'}),
|
||||
PluginTest('apk', <String>['-a', 'kotlin', '--platforms=android'], pluginCreateEnvironment:
|
||||
<String, String>{'ENABLE_ANDROID_EMBEDDING_V2': 'true'}),
|
||||
// Test that Dart-only plugins are supported.
|
||||
PluginTest('apk', <String>['--platforms=android'], dartOnlyPlugin: true),
|
||||
]));
|
||||
}
|
||||
|
@ -10,5 +10,8 @@ Future<void> main() async {
|
||||
PluginTest('ios', <String>['-i', 'objc', '--platforms=ios']),
|
||||
PluginTest('ios', <String>['-i', 'swift', '--platforms=ios']),
|
||||
PluginTest('macos', <String>['--platforms=macos']),
|
||||
// Test that Dart-only plugins are supported.
|
||||
PluginTest('ios', <String>['--platforms=ios'], dartOnlyPlugin: true),
|
||||
PluginTest('macos', <String>['--platforms=macos'], dartOnlyPlugin: true),
|
||||
]));
|
||||
}
|
||||
|
@ -26,12 +26,19 @@ TaskFunction combine(List<TaskFunction> tasks) {
|
||||
/// Defines task that creates new Flutter project, adds a local and remote
|
||||
/// plugin, and then builds the specified [buildTarget].
|
||||
class PluginTest {
|
||||
PluginTest(this.buildTarget, this.options, { this.pluginCreateEnvironment, this.appCreateEnvironment });
|
||||
PluginTest(
|
||||
this.buildTarget,
|
||||
this.options, {
|
||||
this.pluginCreateEnvironment,
|
||||
this.appCreateEnvironment,
|
||||
this.dartOnlyPlugin = false,
|
||||
});
|
||||
|
||||
final String buildTarget;
|
||||
final List<String> options;
|
||||
final Map<String, String>? pluginCreateEnvironment;
|
||||
final Map<String, String>? appCreateEnvironment;
|
||||
final bool dartOnlyPlugin;
|
||||
|
||||
Future<TaskResult> call() async {
|
||||
final Directory tempDir =
|
||||
@ -41,6 +48,9 @@ class PluginTest {
|
||||
final _FlutterProject plugin = await _FlutterProject.create(
|
||||
tempDir, options, buildTarget,
|
||||
name: 'plugintest', template: 'plugin', environment: pluginCreateEnvironment);
|
||||
if (dartOnlyPlugin) {
|
||||
await plugin.convertDefaultPluginToDartPlugin();
|
||||
}
|
||||
section('Test plugin');
|
||||
await plugin.test();
|
||||
section('Create Flutter app');
|
||||
@ -52,7 +62,7 @@ class PluginTest {
|
||||
pluginPath: path.join('..', 'plugintest'));
|
||||
await app.addPlugin('path_provider');
|
||||
section('Build app');
|
||||
await app.build(buildTarget);
|
||||
await app.build(buildTarget, validateNativeBuildProject: !dartOnlyPlugin);
|
||||
section('Test app');
|
||||
await app.test();
|
||||
} finally {
|
||||
@ -76,8 +86,10 @@ class _FlutterProject {
|
||||
|
||||
String get rootPath => path.join(parent.path, name);
|
||||
|
||||
File get pubspecFile => File(path.join(rootPath, 'pubspec.yaml'));
|
||||
|
||||
Future<void> addPlugin(String plugin, {String? pluginPath}) async {
|
||||
final File pubspec = File(path.join(rootPath, 'pubspec.yaml'));
|
||||
final File pubspec = pubspecFile;
|
||||
String content = await pubspec.readAsString();
|
||||
final String dependency =
|
||||
pluginPath != null ? '$plugin:\n path: $pluginPath' : '$plugin:';
|
||||
@ -88,6 +100,47 @@ class _FlutterProject {
|
||||
await pubspec.writeAsString(content, flush: true);
|
||||
}
|
||||
|
||||
/// Converts a plugin created from the standard template to a Dart-only
|
||||
/// plugin.
|
||||
Future<void> convertDefaultPluginToDartPlugin() async {
|
||||
final String dartPluginClass = 'DartClassFor$name';
|
||||
// Convert the metadata.
|
||||
final File pubspec = pubspecFile;
|
||||
String content = await pubspec.readAsString();
|
||||
content = content.replaceAll(
|
||||
RegExp(r' pluginClass: .*?\n'),
|
||||
' dartPluginClass: $dartPluginClass\n',
|
||||
);
|
||||
await pubspec.writeAsString(content, flush: true);
|
||||
|
||||
// Add the Dart registration hook that the build will generate a call to.
|
||||
final File dartCode = File(path.join(rootPath, 'lib', '$name.dart'));
|
||||
content = await dartCode.readAsString();
|
||||
content = '''
|
||||
$content
|
||||
|
||||
class $dartPluginClass {
|
||||
static void registerWith() {}
|
||||
}
|
||||
''';
|
||||
await dartCode.writeAsString(content, flush: true);
|
||||
|
||||
// Remove any native plugin code.
|
||||
const List<String> platforms = <String>[
|
||||
'android',
|
||||
'ios',
|
||||
'linux',
|
||||
'macos',
|
||||
'windows',
|
||||
];
|
||||
for (final String platform in platforms) {
|
||||
final Directory platformDir = Directory(path.join(rootPath, platform));
|
||||
if (platformDir.existsSync()) {
|
||||
await platformDir.delete(recursive: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> test() async {
|
||||
await inDirectory(Directory(rootPath), () async {
|
||||
await flutter('test');
|
||||
@ -147,7 +200,7 @@ class _FlutterProject {
|
||||
podspec.writeAsStringSync(podspecContent, flush: true);
|
||||
}
|
||||
|
||||
Future<void> build(String target) async {
|
||||
Future<void> build(String target, {bool validateNativeBuildProject = true}) async {
|
||||
await inDirectory(Directory(rootPath), () async {
|
||||
final String buildOutput = await evalFlutter('build', options: <String>[
|
||||
target,
|
||||
@ -167,28 +220,30 @@ class _FlutterProject {
|
||||
throw TaskResult.failure('Minimum plugin version warning present');
|
||||
}
|
||||
|
||||
final File podsProject = File(path.join(rootPath, target, 'Pods', 'Pods.xcodeproj', 'project.pbxproj'));
|
||||
if (!podsProject.existsSync()) {
|
||||
throw TaskResult.failure('Xcode Pods project file missing at ${podsProject.path}');
|
||||
}
|
||||
|
||||
final String podsProjectContent = podsProject.readAsStringSync();
|
||||
if (target == 'ios') {
|
||||
// Plugins with versions lower than the app version should not have IPHONEOS_DEPLOYMENT_TARGET set.
|
||||
// The plugintest plugin target should not have IPHONEOS_DEPLOYMENT_TARGET set since it has been lowered
|
||||
// in _reduceDarwinPluginMinimumVersion to 7, which is below the target version of 9.
|
||||
if (podsProjectContent.contains('IPHONEOS_DEPLOYMENT_TARGET = 7')) {
|
||||
throw TaskResult.failure('Plugin build setting IPHONEOS_DEPLOYMENT_TARGET not removed');
|
||||
if (validateNativeBuildProject) {
|
||||
final File podsProject = File(path.join(rootPath, target, 'Pods', 'Pods.xcodeproj', 'project.pbxproj'));
|
||||
if (!podsProject.existsSync()) {
|
||||
throw TaskResult.failure('Xcode Pods project file missing at ${podsProject.path}');
|
||||
}
|
||||
if (!podsProjectContent.contains(r'"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "$(inherited) i386";')) {
|
||||
throw TaskResult.failure(r'EXCLUDED_ARCHS is not "$(inherited) i386"');
|
||||
}
|
||||
}
|
||||
|
||||
// Same for macOS deployment target, but 10.8.
|
||||
// The plugintest target should not have MACOSX_DEPLOYMENT_TARGET set.
|
||||
if (target == 'macos' && podsProjectContent.contains('MACOSX_DEPLOYMENT_TARGET = 10.8')) {
|
||||
throw TaskResult.failure('Plugin build setting MACOSX_DEPLOYMENT_TARGET not removed');
|
||||
final String podsProjectContent = podsProject.readAsStringSync();
|
||||
if (target == 'ios') {
|
||||
// Plugins with versions lower than the app version should not have IPHONEOS_DEPLOYMENT_TARGET set.
|
||||
// The plugintest plugin target should not have IPHONEOS_DEPLOYMENT_TARGET set since it has been lowered
|
||||
// in _reduceDarwinPluginMinimumVersion to 7, which is below the target version of 9.
|
||||
if (podsProjectContent.contains('IPHONEOS_DEPLOYMENT_TARGET = 7')) {
|
||||
throw TaskResult.failure('Plugin build setting IPHONEOS_DEPLOYMENT_TARGET not removed');
|
||||
}
|
||||
if (!podsProjectContent.contains(r'"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "$(inherited) i386";')) {
|
||||
throw TaskResult.failure(r'EXCLUDED_ARCHS is not "$(inherited) i386"');
|
||||
}
|
||||
}
|
||||
|
||||
// Same for macOS deployment target, but 10.8.
|
||||
// The plugintest target should not have MACOSX_DEPLOYMENT_TARGET set.
|
||||
if (target == 'macos' && podsProjectContent.contains('MACOSX_DEPLOYMENT_TARGET = 10.8')) {
|
||||
throw TaskResult.failure('Plugin build setting MACOSX_DEPLOYMENT_TARGET not removed');
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -255,7 +255,8 @@ def flutter_install_plugin_pods(application_path = nil, relative_symlink_dir, pl
|
||||
plugin_pods.each do |plugin_hash|
|
||||
plugin_name = plugin_hash['name']
|
||||
plugin_path = plugin_hash['path']
|
||||
if (plugin_name && plugin_path)
|
||||
has_native_build = plugin_hash.fetch('native_build', true)
|
||||
if (plugin_name && plugin_path && has_native_build)
|
||||
symlink = File.join(symlink_plugins_dir, plugin_name)
|
||||
File.symlink(plugin_path, symlink)
|
||||
|
||||
|
@ -23,6 +23,12 @@ assert object.plugins.android instanceof List
|
||||
object.plugins.android.each { androidPlugin ->
|
||||
assert androidPlugin.name instanceof String
|
||||
assert androidPlugin.path instanceof String
|
||||
// Skip plugins that have no native build (such as a Dart-only implementation
|
||||
// of a federated plugin).
|
||||
def needsBuild = androidPlugin.containsKey('native_build') ? androidPlugin['native_build'] : true
|
||||
if (!needsBuild) {
|
||||
return
|
||||
}
|
||||
def pluginDirectory = new File(androidPlugin.path, 'android')
|
||||
assert pluginDirectory.exists()
|
||||
include ":${androidPlugin.name}"
|
||||
|
@ -20,6 +20,12 @@ if (pluginsFile.exists()) {
|
||||
object.plugins.android.each { androidPlugin ->
|
||||
assert androidPlugin.name instanceof String
|
||||
assert androidPlugin.path instanceof String
|
||||
// Skip plugins that have no native build (such as a Dart-only
|
||||
// implementation of a federated plugin).
|
||||
def needsBuild = androidPlugin.containsKey('native_build') ? androidPlugin['native_build'] : true
|
||||
if (!needsBuild) {
|
||||
return
|
||||
}
|
||||
def pluginDirectory = new File(androidPlugin.path, 'android')
|
||||
assert pluginDirectory.exists()
|
||||
include ":${androidPlugin.name}"
|
||||
|
@ -98,6 +98,7 @@ const String _kFlutterPluginsPluginListKey = 'plugins';
|
||||
const String _kFlutterPluginsNameKey = 'name';
|
||||
const String _kFlutterPluginsPathKey = 'path';
|
||||
const String _kFlutterPluginsDependenciesKey = 'dependencies';
|
||||
const String _kFlutterPluginsHasNativeBuildKey = 'native_build';
|
||||
|
||||
/// Filters [plugins] to those supported by [platformKey].
|
||||
List<Map<String, Object>> _filterPluginsByPlatform(List<Plugin> plugins, String platformKey) {
|
||||
@ -108,9 +109,13 @@ List<Map<String, Object>> _filterPluginsByPlatform(List<Plugin> plugins, String
|
||||
final Set<String> pluginNames = platformPlugins.map((Plugin plugin) => plugin.name).toSet();
|
||||
final List<Map<String, Object>> pluginInfo = <Map<String, Object>>[];
|
||||
for (final Plugin plugin in platformPlugins) {
|
||||
// This is guaranteed to be non-null due to the `where` filter above.
|
||||
final PluginPlatform platformPlugin = plugin.platforms[platformKey]!;
|
||||
pluginInfo.add(<String, Object>{
|
||||
_kFlutterPluginsNameKey: plugin.name,
|
||||
_kFlutterPluginsPathKey: globals.fsUtils.escapePath(plugin.path),
|
||||
if (platformPlugin is NativeOrDartPlugin)
|
||||
_kFlutterPluginsHasNativeBuildKey: (platformPlugin as NativeOrDartPlugin).isNative(),
|
||||
_kFlutterPluginsDependenciesKey: <String>[...plugin.dependencies.where(pluginNames.contains)],
|
||||
});
|
||||
}
|
||||
@ -130,7 +135,8 @@ List<Map<String, Object>> _filterPluginsByPlatform(List<Plugin> plugins, String
|
||||
/// "dependencies": [
|
||||
/// "plugin-a",
|
||||
/// "plugin-b"
|
||||
/// ]
|
||||
/// ],
|
||||
/// "native_build": true
|
||||
/// }
|
||||
/// ],
|
||||
/// "android": [],
|
||||
|
@ -81,7 +81,8 @@ def install_flutter_plugin_pods(flutter_application_path)
|
||||
plugin_pods.each do |plugin_hash|
|
||||
plugin_name = plugin_hash['name']
|
||||
plugin_path = plugin_hash['path']
|
||||
if (plugin_name && plugin_path)
|
||||
has_native_build = plugin_hash.fetch('native_build', true)
|
||||
if (plugin_name && plugin_path && has_native_build)
|
||||
symlink = File.join(symlinks_dir, plugin_name)
|
||||
FileUtils.rm_f(symlink)
|
||||
File.symlink(plugin_path, symlink)
|
||||
|
@ -30,6 +30,46 @@ import '../src/context.dart';
|
||||
import '../src/fakes.dart' hide FakeOperatingSystemUtils;
|
||||
import '../src/pubspec_schema.dart';
|
||||
|
||||
/// Information for a platform entry in the 'platforms' section of a plugin's
|
||||
/// pubspec.yaml.
|
||||
class _PluginPlatformInfo {
|
||||
const _PluginPlatformInfo({
|
||||
this.pluginClass,
|
||||
this.dartPluginClass,
|
||||
this.androidPackage,
|
||||
this.fileName
|
||||
}) : assert(pluginClass != null || dartPluginClass != null),
|
||||
assert(androidPackage == null || pluginClass != null);
|
||||
|
||||
/// The pluginClass entry, if any.
|
||||
final String pluginClass;
|
||||
|
||||
/// The dartPluginClass entry, if any.
|
||||
final String dartPluginClass;
|
||||
|
||||
/// The package entry for an Android plugin implementation using pluginClass.
|
||||
final String androidPackage;
|
||||
|
||||
/// The fileName entry for a web plugin implementation.
|
||||
final String fileName;
|
||||
|
||||
/// Returns the body of a platform section for a plugin's pubspec, properly
|
||||
/// indented.
|
||||
String get indentedPubspecSection {
|
||||
const String indentation = ' ';
|
||||
return <String>[
|
||||
if (pluginClass != null)
|
||||
'${indentation}pluginClass: $pluginClass',
|
||||
if (dartPluginClass != null)
|
||||
'${indentation}dartPluginClass: $dartPluginClass',
|
||||
if (androidPackage != null)
|
||||
'${indentation}package: $androidPackage',
|
||||
if (fileName != null)
|
||||
'${indentation}fileName: $fileName',
|
||||
].join('\n');
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
group('plugins', () {
|
||||
FileSystem fs;
|
||||
@ -331,7 +371,7 @@ flutter:
|
||||
);
|
||||
}
|
||||
|
||||
Directory createPluginWithDependencies({
|
||||
Directory createLegacyPluginWithDependencies({
|
||||
@required String name,
|
||||
@required List<String> dependencies,
|
||||
}) {
|
||||
@ -347,6 +387,44 @@ flutter:
|
||||
plugin:
|
||||
androidPackage: plugin2
|
||||
pluginClass: UseNewEmbedding
|
||||
dependencies:
|
||||
''');
|
||||
for (final String dependency in dependencies) {
|
||||
pluginDirectory
|
||||
.childFile('pubspec.yaml')
|
||||
.writeAsStringSync(' $dependency:\n', mode: FileMode.append);
|
||||
}
|
||||
flutterProject.directory
|
||||
.childFile('.packages')
|
||||
.writeAsStringSync(
|
||||
'$name:${pluginDirectory.childDirectory('lib').uri.toString()}\n',
|
||||
mode: FileMode.append,
|
||||
);
|
||||
return pluginDirectory;
|
||||
}
|
||||
|
||||
Directory createPlugin({
|
||||
@required String name,
|
||||
@required Map<String, _PluginPlatformInfo> platforms,
|
||||
List<String> dependencies = const <String>[],
|
||||
}) {
|
||||
assert(name != null);
|
||||
assert(dependencies != null);
|
||||
|
||||
final Iterable<String> platformSections = platforms.entries.map((MapEntry<String, _PluginPlatformInfo> entry) => '''
|
||||
${entry.key}:
|
||||
${entry.value.indentedPubspecSection}
|
||||
''');
|
||||
final Directory pluginDirectory = fs.systemTempDirectory.createTempSync('flutter_plugin.');
|
||||
pluginDirectory
|
||||
.childFile('pubspec.yaml')
|
||||
.writeAsStringSync('''
|
||||
name: $name
|
||||
flutter:
|
||||
plugin:
|
||||
platforms:
|
||||
${platformSections.join('\n')}
|
||||
|
||||
dependencies:
|
||||
''');
|
||||
for (final String dependency in dependencies) {
|
||||
@ -420,9 +498,9 @@ dependencies:
|
||||
testUsingContext(
|
||||
'Refreshing the plugin list modifies .flutter-plugins '
|
||||
'and .flutter-plugins-dependencies when there are plugins', () async {
|
||||
final Directory pluginA = createPluginWithDependencies(name: 'plugin-a', dependencies: const <String>['plugin-b', 'plugin-c', 'random-package']);
|
||||
final Directory pluginB = createPluginWithDependencies(name: 'plugin-b', dependencies: const <String>['plugin-c']);
|
||||
final Directory pluginC = createPluginWithDependencies(name: 'plugin-c', dependencies: const <String>[]);
|
||||
final Directory pluginA = createLegacyPluginWithDependencies(name: 'plugin-a', dependencies: const <String>['plugin-b', 'plugin-c', 'random-package']);
|
||||
final Directory pluginB = createLegacyPluginWithDependencies(name: 'plugin-b', dependencies: const <String>['plugin-c']);
|
||||
final Directory pluginC = createLegacyPluginWithDependencies(name: 'plugin-c', dependencies: const <String>[]);
|
||||
iosProject.testExists = true;
|
||||
|
||||
final DateTime dateCreated = DateTime(1970);
|
||||
@ -449,22 +527,25 @@ dependencies:
|
||||
<String, dynamic> {
|
||||
'name': 'plugin-a',
|
||||
'path': '${pluginA.path}/',
|
||||
'native_build': true,
|
||||
'dependencies': <String>[
|
||||
'plugin-b',
|
||||
'plugin-c'
|
||||
]
|
||||
],
|
||||
},
|
||||
<String, dynamic> {
|
||||
'name': 'plugin-b',
|
||||
'path': '${pluginB.path}/',
|
||||
'native_build': true,
|
||||
'dependencies': <String>[
|
||||
'plugin-c'
|
||||
]
|
||||
],
|
||||
},
|
||||
<String, dynamic> {
|
||||
'name': 'plugin-c',
|
||||
'path': '${pluginC.path}/',
|
||||
'dependencies': <String>[]
|
||||
'native_build': true,
|
||||
'dependencies': <String>[],
|
||||
},
|
||||
];
|
||||
expect(plugins['ios'], expectedPlugins);
|
||||
@ -514,6 +595,51 @@ dependencies:
|
||||
FlutterVersion: () => flutterVersion
|
||||
});
|
||||
|
||||
testUsingContext(
|
||||
'.flutter-plugins-dependencies indicates native build inclusion', () async {
|
||||
createPlugin(
|
||||
name: 'plugin-a',
|
||||
platforms: const <String, _PluginPlatformInfo>{
|
||||
// Native-only; should include native build.
|
||||
'android': _PluginPlatformInfo(pluginClass: 'Foo', androidPackage: 'bar.foo'),
|
||||
// Hybrid native and Dart; should include native build.
|
||||
'ios': _PluginPlatformInfo(pluginClass: 'Foo', dartPluginClass: 'Bar'),
|
||||
// Web; should not have the native build key at all since it doesn't apply.
|
||||
'web': _PluginPlatformInfo(pluginClass: 'Foo', fileName: 'lib/foo.dart'),
|
||||
// Dart-only; should not include native build.
|
||||
'windows': _PluginPlatformInfo(dartPluginClass: 'Foo'),
|
||||
});
|
||||
iosProject.testExists = true;
|
||||
|
||||
final DateTime dateCreated = DateTime(1970);
|
||||
systemClock.currentTime = dateCreated;
|
||||
|
||||
await refreshPluginsList(flutterProject);
|
||||
|
||||
expect(flutterProject.flutterPluginsDependenciesFile.existsSync(), true);
|
||||
final String pluginsString = flutterProject.flutterPluginsDependenciesFile.readAsStringSync();
|
||||
final Map<String, dynamic> jsonContent = json.decode(pluginsString) as Map<String, dynamic>;
|
||||
final Map<String, dynamic> plugins = jsonContent['plugins'] as Map<String, dynamic>;
|
||||
|
||||
// Extracts the native_build key (if any) from the first plugin for the
|
||||
// given platform.
|
||||
bool getNativeBuildValue(String platform) {
|
||||
final List<Map<String, dynamic>> platformPlugins = (plugins[platform]
|
||||
as List<dynamic>).cast<Map<String, dynamic>>();
|
||||
expect(platformPlugins.length, 1);
|
||||
return platformPlugins[0]['native_build'] as bool;
|
||||
}
|
||||
expect(getNativeBuildValue('android'), true);
|
||||
expect(getNativeBuildValue('ios'), true);
|
||||
expect(getNativeBuildValue('web'), null);
|
||||
expect(getNativeBuildValue('windows'), false);
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => fs,
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
SystemClock: () => systemClock,
|
||||
FlutterVersion: () => flutterVersion
|
||||
});
|
||||
|
||||
testUsingContext('Changes to the plugin list invalidates the Cocoapod lockfiles', () async {
|
||||
simulatePodInstallRun(iosProject);
|
||||
simulatePodInstallRun(macosProject);
|
||||
|
Loading…
x
Reference in New Issue
Block a user