mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Reland the Dart plugin registry (#79669)
This commit is contained in:
parent
9b0fd4230c
commit
b0a63c4ffe
10
dev/devicelab/bin/tasks/dart_plugin_registry_test.dart
Normal file
10
dev/devicelab/bin/tasks/dart_plugin_registry_test.dart
Normal file
@ -0,0 +1,10 @@
|
||||
// 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/tasks/dart_plugin_registry_tests.dart';
|
||||
import 'package:flutter_devicelab/framework/framework.dart';
|
||||
|
||||
Future<void> main() async {
|
||||
await task(dartPluginRegistryTest());
|
||||
}
|
||||
179
dev/devicelab/lib/tasks/dart_plugin_registry_tests.dart
Normal file
179
dev/devicelab/lib/tasks/dart_plugin_registry_tests.dart
Normal file
@ -0,0 +1,179 @@
|
||||
// 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 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:flutter_devicelab/framework/framework.dart';
|
||||
import 'package:flutter_devicelab/framework/task_result.dart';
|
||||
import 'package:flutter_devicelab/framework/utils.dart';
|
||||
|
||||
TaskFunction dartPluginRegistryTest({
|
||||
String deviceIdOverride,
|
||||
Map<String, String> environment,
|
||||
}) {
|
||||
final Directory tempDir = Directory.systemTemp
|
||||
.createTempSync('flutter_devicelab_dart_plugin_test.');
|
||||
return () async {
|
||||
try {
|
||||
section('Create implementation plugin');
|
||||
await inDirectory(tempDir, () async {
|
||||
await flutter(
|
||||
'create',
|
||||
options: <String>[
|
||||
'--template=plugin',
|
||||
'--org',
|
||||
'io.flutter.devicelab',
|
||||
'--platforms',
|
||||
'macos',
|
||||
'plugin_platform_implementation',
|
||||
],
|
||||
environment: environment,
|
||||
);
|
||||
});
|
||||
|
||||
final File pluginMain = File(path.join(
|
||||
tempDir.absolute.path,
|
||||
'plugin_platform_implementation',
|
||||
'lib',
|
||||
'plugin_platform_implementation.dart',
|
||||
));
|
||||
if (!pluginMain.existsSync()) {
|
||||
return TaskResult.failure('${pluginMain.path} does not exist');
|
||||
}
|
||||
|
||||
// Patch plugin main dart file.
|
||||
await pluginMain.writeAsString('''
|
||||
class PluginPlatformInterfaceMacOS {
|
||||
static void registerWith() {
|
||||
print('PluginPlatformInterfaceMacOS.registerWith() was called');
|
||||
}
|
||||
}
|
||||
''', flush: true);
|
||||
|
||||
// Patch plugin main pubspec file.
|
||||
final File pluginImplPubspec = File(path.join(
|
||||
tempDir.absolute.path,
|
||||
'plugin_platform_implementation',
|
||||
'pubspec.yaml',
|
||||
));
|
||||
String pluginImplPubspecContent = await pluginImplPubspec.readAsString();
|
||||
pluginImplPubspecContent = pluginImplPubspecContent.replaceFirst(
|
||||
' pluginClass: PluginPlatformImplementationPlugin',
|
||||
' pluginClass: PluginPlatformImplementationPlugin\n'
|
||||
' dartPluginClass: PluginPlatformInterfaceMacOS\n',
|
||||
);
|
||||
pluginImplPubspecContent = pluginImplPubspecContent.replaceFirst(
|
||||
' platforms:\n',
|
||||
' implements: plugin_platform_interface\n'
|
||||
' platforms:\n');
|
||||
await pluginImplPubspec.writeAsString(pluginImplPubspecContent,
|
||||
flush: true);
|
||||
|
||||
section('Create interface plugin');
|
||||
await inDirectory(tempDir, () async {
|
||||
await flutter(
|
||||
'create',
|
||||
options: <String>[
|
||||
'--template=plugin',
|
||||
'--org',
|
||||
'io.flutter.devicelab',
|
||||
'--platforms',
|
||||
'macos',
|
||||
'plugin_platform_interface',
|
||||
],
|
||||
environment: environment,
|
||||
);
|
||||
});
|
||||
final File pluginInterfacePubspec = File(path.join(
|
||||
tempDir.absolute.path,
|
||||
'plugin_platform_interface',
|
||||
'pubspec.yaml',
|
||||
));
|
||||
String pluginInterfacePubspecContent =
|
||||
await pluginInterfacePubspec.readAsString();
|
||||
pluginInterfacePubspecContent =
|
||||
pluginInterfacePubspecContent.replaceFirst(
|
||||
' pluginClass: PluginPlatformInterfacePlugin',
|
||||
' default_package: plugin_platform_implementation\n');
|
||||
pluginInterfacePubspecContent =
|
||||
pluginInterfacePubspecContent.replaceFirst(
|
||||
'dependencies:',
|
||||
'dependencies:\n'
|
||||
' plugin_platform_implementation:\n'
|
||||
' path: ../plugin_platform_implementation\n');
|
||||
await pluginInterfacePubspec.writeAsString(pluginInterfacePubspecContent,
|
||||
flush: true);
|
||||
|
||||
section('Create app');
|
||||
|
||||
await inDirectory(tempDir, () async {
|
||||
await flutter(
|
||||
'create',
|
||||
options: <String>[
|
||||
'--template=app',
|
||||
'--org',
|
||||
'io.flutter.devicelab',
|
||||
'--platforms',
|
||||
'macos',
|
||||
'app',
|
||||
],
|
||||
environment: environment,
|
||||
);
|
||||
});
|
||||
|
||||
final File appPubspec = File(path.join(
|
||||
tempDir.absolute.path,
|
||||
'app',
|
||||
'pubspec.yaml',
|
||||
));
|
||||
String appPubspecContent = await appPubspec.readAsString();
|
||||
appPubspecContent = appPubspecContent.replaceFirst(
|
||||
'dependencies:',
|
||||
'dependencies:\n'
|
||||
' plugin_platform_interface:\n'
|
||||
' path: ../plugin_platform_interface\n');
|
||||
await appPubspec.writeAsString(appPubspecContent, flush: true);
|
||||
|
||||
section('Flutter run for macos');
|
||||
|
||||
await inDirectory(path.join(tempDir.path, 'app'), () async {
|
||||
final Process run = await startProcess(
|
||||
path.join(flutterDirectory.path, 'bin', 'flutter'),
|
||||
flutterCommandArgs('run', <String>['-d', 'macos', '-v']),
|
||||
environment: null,
|
||||
);
|
||||
Completer<void> registryExecutedCompleter = Completer<void>();
|
||||
final StreamSubscription<void> subscription = run.stdout
|
||||
.transform<String>(utf8.decoder)
|
||||
.transform<String>(const LineSplitter())
|
||||
.listen((String line) {
|
||||
if (line.contains(
|
||||
'PluginPlatformInterfaceMacOS.registerWith() was called')) {
|
||||
registryExecutedCompleter.complete();
|
||||
}
|
||||
print('stdout: $line');
|
||||
});
|
||||
|
||||
section('Wait for registry execution');
|
||||
await registryExecutedCompleter.future;
|
||||
|
||||
// Hot restart.
|
||||
run.stdin.write('R');
|
||||
registryExecutedCompleter = Completer<void>();
|
||||
|
||||
section('Wait for registry execution after hot restart');
|
||||
await registryExecutedCompleter.future;
|
||||
|
||||
subscription.cancel();
|
||||
run.kill();
|
||||
});
|
||||
return TaskResult.success(null);
|
||||
} finally {
|
||||
rmTree(tempDir);
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -332,6 +332,7 @@ class Environment {
|
||||
@required ProcessManager processManager,
|
||||
@required Platform platform,
|
||||
@required String engineVersion,
|
||||
@required bool generateDartPluginRegistry,
|
||||
Directory buildDir,
|
||||
Map<String, String> defines = const <String, String>{},
|
||||
Map<String, String> inputs = const <String, String>{},
|
||||
@ -372,6 +373,7 @@ class Environment {
|
||||
platform: platform,
|
||||
engineVersion: engineVersion,
|
||||
inputs: inputs,
|
||||
generateDartPluginRegistry: generateDartPluginRegistry,
|
||||
);
|
||||
}
|
||||
|
||||
@ -389,6 +391,7 @@ class Environment {
|
||||
Map<String, String> inputs = const <String, String>{},
|
||||
String engineVersion,
|
||||
Platform platform,
|
||||
bool generateDartPluginRegistry = false,
|
||||
@required FileSystem fileSystem,
|
||||
@required Logger logger,
|
||||
@required Artifacts artifacts,
|
||||
@ -408,6 +411,7 @@ class Environment {
|
||||
processManager: processManager,
|
||||
platform: platform ?? FakePlatform(),
|
||||
engineVersion: engineVersion,
|
||||
generateDartPluginRegistry: generateDartPluginRegistry,
|
||||
);
|
||||
}
|
||||
|
||||
@ -426,6 +430,7 @@ class Environment {
|
||||
@required this.artifacts,
|
||||
@required this.engineVersion,
|
||||
@required this.inputs,
|
||||
@required this.generateDartPluginRegistry,
|
||||
});
|
||||
|
||||
/// The [Source] value which is substituted with the path to [projectDir].
|
||||
@ -505,6 +510,11 @@ class Environment {
|
||||
|
||||
/// The version of the current engine, or `null` if built with a local engine.
|
||||
final String engineVersion;
|
||||
|
||||
/// Whether to generate the Dart plugin registry.
|
||||
/// When [true], the main entrypoint is wrapped and the wrapper becomes
|
||||
/// the new entrypoint.
|
||||
final bool generateDartPluginRegistry;
|
||||
}
|
||||
|
||||
/// The result information from the build system.
|
||||
|
||||
@ -17,6 +17,7 @@ import '../build_system.dart';
|
||||
import '../depfile.dart';
|
||||
import '../exceptions.dart';
|
||||
import 'assets.dart';
|
||||
import 'dart_plugin_registrant.dart';
|
||||
import 'icon_tree_shaker.dart';
|
||||
import 'localizations.dart';
|
||||
|
||||
@ -209,6 +210,7 @@ class KernelSnapshot extends Target {
|
||||
@override
|
||||
List<Target> get dependencies => const <Target>[
|
||||
GenerateLocalizationsTarget(),
|
||||
DartPluginRegistrantTarget(),
|
||||
];
|
||||
|
||||
@override
|
||||
@ -286,6 +288,8 @@ class KernelSnapshot extends Target {
|
||||
fileSystemScheme: fileSystemScheme,
|
||||
dartDefines: decodeDartDefines(environment.defines, kDartDefines),
|
||||
packageConfig: packageConfig,
|
||||
buildDir: environment.buildDir,
|
||||
checkDartPluginRegistry: environment.generateDartPluginRegistry,
|
||||
);
|
||||
if (output == null || output.errorCount != 0) {
|
||||
throw Exception();
|
||||
|
||||
@ -0,0 +1,88 @@
|
||||
// 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.
|
||||
|
||||
// @dart = 2.8
|
||||
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:package_config/package_config.dart';
|
||||
|
||||
import '../../base/file_system.dart';
|
||||
import '../../dart/package_map.dart';
|
||||
import '../../flutter_plugins.dart';
|
||||
import '../../project.dart';
|
||||
import '../build_system.dart';
|
||||
import 'common.dart';
|
||||
|
||||
/// Generates a new `./dart_tool/flutter_build/generated_main.dart`
|
||||
/// based on the current dependency map in `pubspec.lock`.
|
||||
class DartPluginRegistrantTarget extends Target {
|
||||
/// Construct a [DartPluginRegistrantTarget].
|
||||
const DartPluginRegistrantTarget() : _project = null;
|
||||
|
||||
/// Construct a [DartPluginRegistrantTarget].
|
||||
///
|
||||
/// If `project` is unset, a [FlutterProject] based on environment is used.
|
||||
@visibleForTesting
|
||||
factory DartPluginRegistrantTarget.test(FlutterProject project) {
|
||||
return DartPluginRegistrantTarget._(project);
|
||||
}
|
||||
|
||||
DartPluginRegistrantTarget._(this._project);
|
||||
|
||||
final FlutterProject _project;
|
||||
|
||||
@override
|
||||
Future<void> build(Environment environment) async {
|
||||
assert(environment.generateDartPluginRegistry);
|
||||
final File packagesFile = environment.projectDir
|
||||
.childDirectory('.dart_tool')
|
||||
.childFile('package_config.json');
|
||||
final PackageConfig packageConfig = await loadPackageConfigWithLogging(
|
||||
packagesFile,
|
||||
logger: environment.logger,
|
||||
);
|
||||
final String targetFile = environment.defines[kTargetFile] ??
|
||||
environment.fileSystem.path.join('lib', 'main.dart');
|
||||
final File mainFile = environment.fileSystem.file(targetFile);
|
||||
final Uri mainFileUri = mainFile.uri;
|
||||
assert(packagesFile.path != null);
|
||||
final String mainUri = packageConfig.toPackageUri(mainFileUri)?.toString();
|
||||
final File newMainDart = environment.projectDir
|
||||
.childDirectory('.dart_tool')
|
||||
.childDirectory('flutter_build')
|
||||
.childFile('generated_main.dart');
|
||||
await generateMainDartWithPluginRegistrant(
|
||||
_project ?? FlutterProject.fromDirectory(environment.projectDir),
|
||||
packageConfig,
|
||||
mainUri,
|
||||
newMainDart,
|
||||
mainFile,
|
||||
throwOnPluginPubspecError: false,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
bool canSkip(Environment environment) {
|
||||
return !environment.generateDartPluginRegistry;
|
||||
}
|
||||
|
||||
@override
|
||||
List<Target> get dependencies => <Target>[];
|
||||
|
||||
@override
|
||||
List<Source> get inputs => <Source>[
|
||||
const Source.pattern('{PROJECT_DIR}/.dart_tool/package_config_subset'),
|
||||
];
|
||||
|
||||
@override
|
||||
String get name => 'gen_dart_plugin_registrant';
|
||||
|
||||
@override
|
||||
List<Source> get outputs => <Source>[
|
||||
const Source.pattern(
|
||||
'{PROJECT_DIR}/.dart_tool/flutter_build/generated_main.dart',
|
||||
optional: true,
|
||||
),
|
||||
];
|
||||
}
|
||||
@ -137,6 +137,7 @@ class BundleBuilder {
|
||||
logger: globals.logger,
|
||||
processManager: globals.processManager,
|
||||
platform: globals.platform,
|
||||
generateDartPluginRegistry: true,
|
||||
);
|
||||
final Target target = buildInfo.mode == BuildMode.debug
|
||||
? const CopyFlutterBundle()
|
||||
|
||||
@ -246,7 +246,8 @@ class AssembleCommand extends FlutterCommand {
|
||||
platform: globals.platform,
|
||||
engineVersion: globals.artifacts.isLocalEngine
|
||||
? null
|
||||
: globals.flutterVersion.engineRevision
|
||||
: globals.flutterVersion.engineRevision,
|
||||
generateDartPluginRegistry: true,
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -385,6 +385,7 @@ end
|
||||
engineVersion: globals.artifacts.isLocalEngine
|
||||
? null
|
||||
: globals.flutterVersion.engineRevision,
|
||||
generateDartPluginRegistry: true,
|
||||
);
|
||||
Target target;
|
||||
// Always build debug for simulator.
|
||||
|
||||
@ -120,6 +120,7 @@ class PackagesGetCommand extends FlutterCommand {
|
||||
processManager: globals.processManager,
|
||||
platform: globals.platform,
|
||||
projectDir: flutterProject.directory,
|
||||
generateDartPluginRegistry: true,
|
||||
);
|
||||
|
||||
await generateLocalizationsSyntheticPackage(
|
||||
@ -325,6 +326,7 @@ class PackagesInteractiveGetCommand extends FlutterCommand {
|
||||
processManager: globals.processManager,
|
||||
platform: globals.platform,
|
||||
projectDir: flutterProject.directory,
|
||||
generateDartPluginRegistry: true,
|
||||
);
|
||||
|
||||
await generateLocalizationsSyntheticPackage(
|
||||
|
||||
@ -229,6 +229,8 @@ class KernelCompiler {
|
||||
String fileSystemScheme,
|
||||
String initializeFromDill,
|
||||
String platformDill,
|
||||
Directory buildDir,
|
||||
bool checkDartPluginRegistry = false,
|
||||
@required String packagesPath,
|
||||
@required BuildMode buildMode,
|
||||
@required bool trackWidgetCreation,
|
||||
@ -247,7 +249,8 @@ class KernelCompiler {
|
||||
throwToolExit('Unable to find Dart binary at $engineDartPath');
|
||||
}
|
||||
String mainUri;
|
||||
final Uri mainFileUri = _fileSystem.file(mainPath).uri;
|
||||
final File mainFile = _fileSystem.file(mainPath);
|
||||
final Uri mainFileUri = mainFile.uri;
|
||||
if (packagesPath != null) {
|
||||
mainUri = packageConfig.toPackageUri(mainFileUri)?.toString();
|
||||
}
|
||||
@ -255,6 +258,15 @@ class KernelCompiler {
|
||||
if (outputFilePath != null && !_fileSystem.isFileSync(outputFilePath)) {
|
||||
_fileSystem.file(outputFilePath).createSync(recursive: true);
|
||||
}
|
||||
if (buildDir != null && checkDartPluginRegistry) {
|
||||
// Check if there's a Dart plugin registrant.
|
||||
// This is contained in the file `generated_main.dart` under `.dart_tools/flutter_build/`.
|
||||
final File newMainDart = buildDir.parent.childFile('generated_main.dart');
|
||||
if (newMainDart.existsSync()) {
|
||||
mainUri = newMainDart.path;
|
||||
}
|
||||
}
|
||||
|
||||
final List<String> command = <String>[
|
||||
engineDartPath,
|
||||
'--disable-dart-dev',
|
||||
@ -457,6 +469,8 @@ abstract class ResidentCompiler {
|
||||
List<Uri> invalidatedFiles, {
|
||||
@required String outputPath,
|
||||
@required PackageConfig packageConfig,
|
||||
@required String projectRootPath,
|
||||
@required FileSystem fs,
|
||||
bool suppressErrors = false,
|
||||
});
|
||||
|
||||
@ -596,12 +610,27 @@ class DefaultResidentCompiler implements ResidentCompiler {
|
||||
@required String outputPath,
|
||||
@required PackageConfig packageConfig,
|
||||
bool suppressErrors = false,
|
||||
String projectRootPath,
|
||||
FileSystem fs,
|
||||
}) async {
|
||||
assert(outputPath != null);
|
||||
if (!_controller.hasListener) {
|
||||
_controller.stream.listen(_handleCompilationRequest);
|
||||
}
|
||||
|
||||
// `generated_main.dart` contains the Dart plugin registry.
|
||||
if (projectRootPath != null && fs != null) {
|
||||
final File generatedMainDart = fs.file(
|
||||
fs.path.join(
|
||||
projectRootPath,
|
||||
'.dart_tool',
|
||||
'flutter_build',
|
||||
'generated_main.dart',
|
||||
),
|
||||
);
|
||||
if (generatedMainDart != null && generatedMainDart.existsSync()) {
|
||||
mainUri = generatedMainDart.uri;
|
||||
}
|
||||
}
|
||||
final Completer<CompilerOutput> completer = Completer<CompilerOutput>();
|
||||
_controller.add(
|
||||
_RecompileRequest(completer, mainUri, invalidatedFiles, outputPath, packageConfig, suppressErrors)
|
||||
|
||||
@ -524,6 +524,8 @@ class DevFS {
|
||||
mainUri,
|
||||
invalidatedFiles,
|
||||
outputPath: dillOutputPath,
|
||||
fs: _fileSystem,
|
||||
projectRootPath: projectRootPath,
|
||||
packageConfig: packageConfig,
|
||||
);
|
||||
if (bundle != null) {
|
||||
|
||||
@ -75,6 +75,13 @@ class FlutterManifest {
|
||||
/// The string value of the top-level `name` property in the `pubspec.yaml` file.
|
||||
String get appName => _descriptor['name'] as String? ?? '';
|
||||
|
||||
/// Contains the name of the dependencies.
|
||||
/// These are the keys specified in the `dependency` map.
|
||||
Set<String> get dependencies {
|
||||
final YamlMap? dependencies = _descriptor['dependencies'] as YamlMap?;
|
||||
return dependencies != null ? <String>{...dependencies.keys.cast<String>()} : <String>{};
|
||||
}
|
||||
|
||||
// Flag to avoid printing multiple invalid version messages.
|
||||
bool _hasShowInvalidVersionMsg = false;
|
||||
|
||||
|
||||
@ -17,7 +17,9 @@ import 'base/os.dart';
|
||||
import 'base/platform.dart';
|
||||
import 'base/template.dart';
|
||||
import 'base/version.dart';
|
||||
import 'cache.dart';
|
||||
import 'convert.dart';
|
||||
import 'dart/language_version.dart';
|
||||
import 'dart/package_map.dart';
|
||||
import 'features.dart';
|
||||
import 'globals.dart' as globals;
|
||||
@ -32,15 +34,16 @@ void _renderTemplateToFile(String template, dynamic context, File file, Template
|
||||
file.writeAsStringSync(renderedTemplate);
|
||||
}
|
||||
|
||||
Plugin _pluginFromPackage(String name, Uri packageRoot) {
|
||||
final String pubspecPath = globals.fs.path.fromUri(packageRoot.resolve('pubspec.yaml'));
|
||||
if (!globals.fs.isFileSync(pubspecPath)) {
|
||||
Plugin _pluginFromPackage(String name, Uri packageRoot, Set<String> appDependencies, {FileSystem fileSystem}) {
|
||||
final FileSystem fs = fileSystem ?? globals.fs;
|
||||
final String pubspecPath = fs.path.fromUri(packageRoot.resolve('pubspec.yaml'));
|
||||
if (!fs.isFileSync(pubspecPath)) {
|
||||
return null;
|
||||
}
|
||||
dynamic pubspec;
|
||||
|
||||
try {
|
||||
pubspec = loadYaml(globals.fs.file(pubspecPath).readAsStringSync());
|
||||
pubspec = loadYaml(fs.file(pubspecPath).readAsStringSync());
|
||||
} on YamlException catch (err) {
|
||||
globals.printTrace('Failed to parse plugin manifest for $name: $err');
|
||||
// Do nothing, potentially not a plugin.
|
||||
@ -52,7 +55,7 @@ Plugin _pluginFromPackage(String name, Uri packageRoot) {
|
||||
if (flutterConfig == null || !(flutterConfig.containsKey('plugin') as bool)) {
|
||||
return null;
|
||||
}
|
||||
final String packageRootPath = globals.fs.path.fromUri(packageRoot);
|
||||
final String packageRootPath = fs.path.fromUri(packageRoot);
|
||||
final YamlMap dependencies = pubspec['dependencies'] as YamlMap;
|
||||
globals.printTrace('Found plugin $name at $packageRootPath');
|
||||
return Plugin.fromYaml(
|
||||
@ -60,24 +63,31 @@ Plugin _pluginFromPackage(String name, Uri packageRoot) {
|
||||
packageRootPath,
|
||||
flutterConfig['plugin'] as YamlMap,
|
||||
dependencies == null ? <String>[] : <String>[...dependencies.keys.cast<String>()],
|
||||
fileSystem: globals.fs,
|
||||
fileSystem: fs,
|
||||
appDependencies: appDependencies,
|
||||
);
|
||||
}
|
||||
|
||||
Future<List<Plugin>> findPlugins(FlutterProject project, { bool throwOnError = true}) async {
|
||||
final List<Plugin> plugins = <Plugin>[];
|
||||
final String packagesFile = globals.fs.path.join(
|
||||
final FileSystem fs = project.directory.fileSystem;
|
||||
final String packagesFile = fs.path.join(
|
||||
project.directory.path,
|
||||
'.packages',
|
||||
);
|
||||
final PackageConfig packageConfig = await loadPackageConfigWithLogging(
|
||||
globals.fs.file(packagesFile),
|
||||
fs.file(packagesFile),
|
||||
logger: globals.logger,
|
||||
throwOnError: throwOnError,
|
||||
);
|
||||
for (final Package package in packageConfig.packages) {
|
||||
final Uri packageRoot = package.packageUriRoot.resolve('..');
|
||||
final Plugin plugin = _pluginFromPackage(package.name, packageRoot);
|
||||
final Plugin plugin = _pluginFromPackage(
|
||||
package.name,
|
||||
packageRoot,
|
||||
project.manifest.dependencies,
|
||||
fileSystem: fs
|
||||
);
|
||||
if (plugin != null) {
|
||||
plugins.add(plugin);
|
||||
}
|
||||
@ -619,6 +629,74 @@ foreach(plugin ${FLUTTER_PLUGIN_LIST})
|
||||
endforeach(plugin)
|
||||
''';
|
||||
|
||||
const String _dartPluginRegisterWith = r'''
|
||||
try {
|
||||
{{dartClass}}.registerWith();
|
||||
} catch (err) {
|
||||
print(
|
||||
'`{{pluginName}}` threw an error: $err. '
|
||||
'The app may not function as expected until you remove this plugin from pubspec.yaml'
|
||||
);
|
||||
rethrow;
|
||||
}
|
||||
''';
|
||||
|
||||
// TODO(egarciad): Evaluate merging the web and desktop plugin registry templates.
|
||||
// https://github.com/flutter/flutter/issues/80406
|
||||
const String _dartPluginRegistryForDesktopTemplate = '''
|
||||
//
|
||||
// Generated file. Do not edit.
|
||||
// This file is generated from template in file `flutter_tools/lib/src/flutter_plugins.dart`.
|
||||
//
|
||||
|
||||
// @dart = {{dartLanguageVersion}}
|
||||
|
||||
import '{{mainEntrypoint}}' as entrypoint;
|
||||
import 'dart:io'; // flutter_ignore: dart_io_import.
|
||||
{{#linux}}
|
||||
import 'package:{{pluginName}}/{{pluginName}}.dart';
|
||||
{{/linux}}
|
||||
{{#macos}}
|
||||
import 'package:{{pluginName}}/{{pluginName}}.dart';
|
||||
{{/macos}}
|
||||
{{#windows}}
|
||||
import 'package:{{pluginName}}/{{pluginName}}.dart';
|
||||
{{/windows}}
|
||||
|
||||
@pragma('vm:entry-point')
|
||||
class _PluginRegistrant {
|
||||
|
||||
@pragma('vm:entry-point')
|
||||
static void register() {
|
||||
if (Platform.isLinux) {
|
||||
{{#linux}}
|
||||
$_dartPluginRegisterWith
|
||||
{{/linux}}
|
||||
} else if (Platform.isMacOS) {
|
||||
{{#macos}}
|
||||
$_dartPluginRegisterWith
|
||||
{{/macos}}
|
||||
} else if (Platform.isWindows) {
|
||||
{{#windows}}
|
||||
$_dartPluginRegisterWith
|
||||
{{/windows}}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
typedef _UnaryFunction = dynamic Function(List<String> args);
|
||||
typedef _NullaryFunction = dynamic Function();
|
||||
|
||||
void main(List<String> args) {
|
||||
if (entrypoint.main is _UnaryFunction) {
|
||||
(entrypoint.main as _UnaryFunction)(args);
|
||||
} else {
|
||||
(entrypoint.main as _NullaryFunction)();
|
||||
}
|
||||
}
|
||||
''';
|
||||
|
||||
Future<void> _writeIOSPluginRegistrant(FlutterProject project, List<Plugin> plugins) async {
|
||||
final List<Map<String, dynamic>> iosPlugins = _extractPlatformMaps(plugins, IOSPlugin.kConfigKey);
|
||||
final Map<String, dynamic> context = <String, dynamic>{
|
||||
@ -979,3 +1057,180 @@ Future<void> injectPlugins(
|
||||
bool hasPlugins(FlutterProject project) {
|
||||
return _readFileContent(project.flutterPluginsFile) != null;
|
||||
}
|
||||
|
||||
/// Resolves the platform implementation for Dart-only plugins.
|
||||
///
|
||||
/// * If there are multiple direct pub dependencies on packages that implement the
|
||||
/// frontend plugin for the current platform, fail.
|
||||
/// * If there is a single direct dependency on a package that implements the
|
||||
/// frontend plugin for the target platform, this package is the selected implementation.
|
||||
/// * If there is no direct dependency on a package that implements the frontend
|
||||
/// plugin for the target platform, and the frontend plugin has a default implementation
|
||||
/// for the target platform the default implementation is selected.
|
||||
/// * Else fail.
|
||||
///
|
||||
/// For more details, https://flutter.dev/go/federated-plugins.
|
||||
List<PluginInterfaceResolution> resolvePlatformImplementation(
|
||||
List<Plugin> plugins, {
|
||||
bool throwOnPluginPubspecError = true,
|
||||
}) {
|
||||
final List<String> platforms = <String>[
|
||||
LinuxPlugin.kConfigKey,
|
||||
MacOSPlugin.kConfigKey,
|
||||
WindowsPlugin.kConfigKey,
|
||||
];
|
||||
final Map<String, PluginInterfaceResolution> directDependencyResolutions
|
||||
= <String, PluginInterfaceResolution>{};
|
||||
final Map<String, String> defaultImplementations = <String, String>{};
|
||||
bool didFindError = false;
|
||||
|
||||
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) {
|
||||
continue;
|
||||
}
|
||||
// The plugin doesn't implement an interface, verify that it has a default implementation.
|
||||
if (plugin.implementsPackage == null || plugin.implementsPackage.isEmpty) {
|
||||
final String defaultImplementation = plugin.defaultPackagePlatforms[platform];
|
||||
if (defaultImplementation == null) {
|
||||
if (throwOnPluginPubspecError) {
|
||||
globals.printError(
|
||||
'Plugin `${plugin.name}` 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'
|
||||
' $platform:\n'
|
||||
' $kDefaultPackage: <plugin-implementation>\n'
|
||||
'\n'
|
||||
'To implement an interface, use:\n'
|
||||
'flutter:\n'
|
||||
' plugin:\n'
|
||||
' implements: <plugin-interface>'
|
||||
'\n'
|
||||
);
|
||||
}
|
||||
didFindError = true;
|
||||
continue;
|
||||
}
|
||||
defaultImplementations['$platform/${plugin.name}'] = defaultImplementation;
|
||||
continue;
|
||||
}
|
||||
if (plugin.pluginDartClassPlatforms[platform] == null ||
|
||||
plugin.pluginDartClassPlatforms[platform] == 'none') {
|
||||
continue;
|
||||
}
|
||||
final String resolutionKey = '$platform/${plugin.implementsPackage}';
|
||||
if (directDependencyResolutions.containsKey(resolutionKey)) {
|
||||
final PluginInterfaceResolution currResolution = directDependencyResolutions[resolutionKey];
|
||||
if (currResolution != null && currResolution.plugin.isDirectDependency) {
|
||||
if (plugin.isDirectDependency) {
|
||||
if (throwOnPluginPubspecError) {
|
||||
globals.printError(
|
||||
'Plugin `${plugin.name}` implements an interface for `$platform`, which was already '
|
||||
'implemented by plugin `${currResolution.plugin.name}`.\n'
|
||||
'To fix this issue, remove either dependency from pubspec.yaml.'
|
||||
'\n\n'
|
||||
);
|
||||
}
|
||||
didFindError = true;
|
||||
}
|
||||
// Use the plugin implementation added by the user as a direct dependency.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
directDependencyResolutions[resolutionKey] = PluginInterfaceResolution(
|
||||
plugin: plugin,
|
||||
platform: platform,
|
||||
);
|
||||
}
|
||||
}
|
||||
if (didFindError && throwOnPluginPubspecError) {
|
||||
throwToolExit('Please resolve the errors');
|
||||
}
|
||||
final List<PluginInterfaceResolution> finalResolution = <PluginInterfaceResolution>[];
|
||||
for (final MapEntry<String, PluginInterfaceResolution> resolution in directDependencyResolutions.entries) {
|
||||
if (resolution.value.plugin.isDirectDependency) {
|
||||
finalResolution.add(resolution.value);
|
||||
} else if (defaultImplementations.containsKey(resolution.key)) {
|
||||
// Pick the default implementation.
|
||||
if (defaultImplementations[resolution.key] == resolution.value.plugin.name) {
|
||||
finalResolution.add(resolution.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return finalResolution;
|
||||
}
|
||||
|
||||
/// Generates the Dart plugin registrant, which allows to bind a platform
|
||||
/// implementation of a Dart only plugin to its interface.
|
||||
/// The new entrypoint wraps [currentMainUri], adds the [_PluginRegistrant] class,
|
||||
/// and writes the file to [newMainDart].
|
||||
///
|
||||
/// [mainFile] is the main entrypoint file. e.g. /<app>/lib/main.dart.
|
||||
///
|
||||
/// A successful run will create a new generate_main.dart file or update the existing file.
|
||||
/// Throws [ToolExit] if unable to generate the file.
|
||||
///
|
||||
/// This method also validates each plugin's pubspec.yaml, but errors are only
|
||||
/// reported if [throwOnPluginPubspecError] is [true].
|
||||
///
|
||||
/// For more details, see https://flutter.dev/go/federated-plugins.
|
||||
Future<void> generateMainDartWithPluginRegistrant(
|
||||
FlutterProject rootProject,
|
||||
PackageConfig packageConfig,
|
||||
String currentMainUri,
|
||||
File newMainDart,
|
||||
File mainFile, {
|
||||
bool throwOnPluginPubspecError,
|
||||
}) async {
|
||||
final List<Plugin> plugins = await findPlugins(rootProject);
|
||||
final List<PluginInterfaceResolution> resolutions = resolvePlatformImplementation(
|
||||
plugins,
|
||||
throwOnPluginPubspecError: throwOnPluginPubspecError ?? false,
|
||||
);
|
||||
final LanguageVersion entrypointVersion = determineLanguageVersion(
|
||||
mainFile,
|
||||
packageConfig.packageOf(mainFile.absolute.uri),
|
||||
Cache.flutterRoot,
|
||||
);
|
||||
final Map<String, dynamic> templateContext = <String, dynamic>{
|
||||
'mainEntrypoint': currentMainUri,
|
||||
'dartLanguageVersion': entrypointVersion.toString(),
|
||||
LinuxPlugin.kConfigKey: <dynamic>[],
|
||||
MacOSPlugin.kConfigKey: <dynamic>[],
|
||||
WindowsPlugin.kConfigKey: <dynamic>[],
|
||||
};
|
||||
if (resolutions.isEmpty) {
|
||||
try {
|
||||
if (newMainDart.existsSync()) {
|
||||
newMainDart.deleteSync();
|
||||
}
|
||||
} on FileSystemException catch (error) {
|
||||
globals.printError(
|
||||
'Unable to remove ${newMainDart.path}, received error: $error.\n'
|
||||
'You might need to run flutter clean.'
|
||||
);
|
||||
rethrow;
|
||||
}
|
||||
return;
|
||||
}
|
||||
for (final PluginInterfaceResolution resolution in resolutions) {
|
||||
assert(templateContext.containsKey(resolution.platform));
|
||||
(templateContext[resolution.platform] as List<dynamic>).add(resolution.toMap());
|
||||
}
|
||||
try {
|
||||
_renderTemplateToFile(
|
||||
_dartPluginRegistryForDesktopTemplate,
|
||||
templateContext,
|
||||
newMainDart,
|
||||
globals.templateRenderer,
|
||||
);
|
||||
} on FileSystemException catch (error) {
|
||||
globals.printError('Unable to write ${newMainDart.path}, received error: $error');
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
@ -835,6 +835,8 @@ class WebDevFS implements DevFS {
|
||||
invalidatedFiles,
|
||||
outputPath: dillOutputPath,
|
||||
packageConfig: packageConfig,
|
||||
projectRootPath: projectRootPath,
|
||||
fs: globals.fs,
|
||||
);
|
||||
if (compilerOutput == null || compilerOutput.errorCount > 0) {
|
||||
return UpdateFSReport(success: false);
|
||||
|
||||
@ -13,6 +13,9 @@ const String kPluginClass = 'pluginClass';
|
||||
/// Constant for 'pluginClass' key in plugin maps.
|
||||
const String kDartPluginClass = 'dartPluginClass';
|
||||
|
||||
// Constant for 'defaultPackage' key in plugin maps.
|
||||
const String kDefaultPackage = 'default_package';
|
||||
|
||||
/// Marker interface for all platform specific plugin config implementations.
|
||||
abstract class PluginPlatform {
|
||||
const PluginPlatform();
|
||||
@ -204,6 +207,7 @@ class MacOSPlugin extends PluginPlatform implements NativeOrDartPlugin {
|
||||
required this.name,
|
||||
this.pluginClass,
|
||||
this.dartPluginClass,
|
||||
this.defaultPackage,
|
||||
});
|
||||
|
||||
factory MacOSPlugin.fromYaml(String name, YamlMap yaml) {
|
||||
@ -217,6 +221,7 @@ class MacOSPlugin extends PluginPlatform implements NativeOrDartPlugin {
|
||||
name: name,
|
||||
pluginClass: pluginClass,
|
||||
dartPluginClass: yaml[kDartPluginClass] as String?,
|
||||
defaultPackage: yaml[kDefaultPackage] as String?,
|
||||
);
|
||||
}
|
||||
|
||||
@ -224,7 +229,9 @@ class MacOSPlugin extends PluginPlatform implements NativeOrDartPlugin {
|
||||
if (yaml == null) {
|
||||
return false;
|
||||
}
|
||||
return yaml[kPluginClass] is String || yaml[kDartPluginClass] is String;
|
||||
return yaml[kPluginClass] is String ||
|
||||
yaml[kDartPluginClass] is String ||
|
||||
yaml[kDefaultPackage] is String;
|
||||
}
|
||||
|
||||
static const String kConfigKey = 'macos';
|
||||
@ -232,6 +239,7 @@ class MacOSPlugin extends PluginPlatform implements NativeOrDartPlugin {
|
||||
final String name;
|
||||
final String? pluginClass;
|
||||
final String? dartPluginClass;
|
||||
final String? defaultPackage;
|
||||
|
||||
@override
|
||||
bool isNative() => pluginClass != null;
|
||||
@ -241,7 +249,8 @@ class MacOSPlugin extends PluginPlatform implements NativeOrDartPlugin {
|
||||
return <String, dynamic>{
|
||||
'name': name,
|
||||
if (pluginClass != null) 'class': pluginClass,
|
||||
if (dartPluginClass != null) 'dartPluginClass': dartPluginClass,
|
||||
if (dartPluginClass != null) kDartPluginClass : dartPluginClass,
|
||||
if (defaultPackage != null) kDefaultPackage : defaultPackage,
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -255,7 +264,8 @@ class WindowsPlugin extends PluginPlatform implements NativeOrDartPlugin{
|
||||
required this.name,
|
||||
this.pluginClass,
|
||||
this.dartPluginClass,
|
||||
}) : assert(pluginClass != null || dartPluginClass != null);
|
||||
this.defaultPackage,
|
||||
}) : assert(pluginClass != null || dartPluginClass != null || defaultPackage != null);
|
||||
|
||||
factory WindowsPlugin.fromYaml(String name, YamlMap yaml) {
|
||||
assert(validate(yaml));
|
||||
@ -268,6 +278,7 @@ class WindowsPlugin extends PluginPlatform implements NativeOrDartPlugin{
|
||||
name: name,
|
||||
pluginClass: pluginClass,
|
||||
dartPluginClass: yaml[kDartPluginClass] as String?,
|
||||
defaultPackage: yaml[kDefaultPackage] as String?,
|
||||
);
|
||||
}
|
||||
|
||||
@ -275,7 +286,9 @@ class WindowsPlugin extends PluginPlatform implements NativeOrDartPlugin{
|
||||
if (yaml == null) {
|
||||
return false;
|
||||
}
|
||||
return yaml[kDartPluginClass] is String || yaml[kPluginClass] is String;
|
||||
return yaml[kPluginClass] is String ||
|
||||
yaml[kDartPluginClass] is String ||
|
||||
yaml[kDefaultPackage] is String;
|
||||
}
|
||||
|
||||
static const String kConfigKey = 'windows';
|
||||
@ -283,6 +296,7 @@ class WindowsPlugin extends PluginPlatform implements NativeOrDartPlugin{
|
||||
final String name;
|
||||
final String? pluginClass;
|
||||
final String? dartPluginClass;
|
||||
final String? defaultPackage;
|
||||
|
||||
@override
|
||||
bool isNative() => pluginClass != null;
|
||||
@ -291,9 +305,10 @@ class WindowsPlugin extends PluginPlatform implements NativeOrDartPlugin{
|
||||
Map<String, dynamic> toMap() {
|
||||
return <String, dynamic>{
|
||||
'name': name,
|
||||
if (pluginClass != null) 'class': pluginClass,
|
||||
if (pluginClass != null) 'class': pluginClass!,
|
||||
if (pluginClass != null) 'filename': _filenameForCppClass(pluginClass!),
|
||||
if (dartPluginClass != null) 'dartPluginClass': dartPluginClass,
|
||||
if (dartPluginClass != null) kDartPluginClass: dartPluginClass!,
|
||||
if (defaultPackage != null) kDefaultPackage: defaultPackage!,
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -307,7 +322,8 @@ class LinuxPlugin extends PluginPlatform implements NativeOrDartPlugin {
|
||||
required this.name,
|
||||
this.pluginClass,
|
||||
this.dartPluginClass,
|
||||
}) : assert(pluginClass != null || dartPluginClass != null);
|
||||
this.defaultPackage,
|
||||
}) : assert(pluginClass != null || dartPluginClass != null || defaultPackage != null);
|
||||
|
||||
factory LinuxPlugin.fromYaml(String name, YamlMap yaml) {
|
||||
assert(validate(yaml));
|
||||
@ -320,6 +336,7 @@ class LinuxPlugin extends PluginPlatform implements NativeOrDartPlugin {
|
||||
name: name,
|
||||
pluginClass: pluginClass,
|
||||
dartPluginClass: yaml[kDartPluginClass] as String?,
|
||||
defaultPackage: yaml[kDefaultPackage] as String?,
|
||||
);
|
||||
}
|
||||
|
||||
@ -327,7 +344,9 @@ class LinuxPlugin extends PluginPlatform implements NativeOrDartPlugin {
|
||||
if (yaml == null) {
|
||||
return false;
|
||||
}
|
||||
return yaml[kPluginClass] is String || yaml[kDartPluginClass] is String;
|
||||
return yaml[kPluginClass] is String ||
|
||||
yaml[kDartPluginClass] is String ||
|
||||
yaml[kDefaultPackage] is String;
|
||||
}
|
||||
|
||||
static const String kConfigKey = 'linux';
|
||||
@ -335,6 +354,7 @@ class LinuxPlugin extends PluginPlatform implements NativeOrDartPlugin {
|
||||
final String name;
|
||||
final String? pluginClass;
|
||||
final String? dartPluginClass;
|
||||
final String? defaultPackage;
|
||||
|
||||
@override
|
||||
bool isNative() => pluginClass != null;
|
||||
@ -343,9 +363,10 @@ class LinuxPlugin extends PluginPlatform implements NativeOrDartPlugin {
|
||||
Map<String, dynamic> toMap() {
|
||||
return <String, dynamic>{
|
||||
'name': name,
|
||||
if (pluginClass != null) 'class': pluginClass,
|
||||
if (pluginClass != null) 'class': pluginClass!,
|
||||
if (pluginClass != null) 'filename': _filenameForCppClass(pluginClass!),
|
||||
if (dartPluginClass != null) 'dartPluginClass': dartPluginClass,
|
||||
if (dartPluginClass != null) kDartPluginClass: dartPluginClass!,
|
||||
if (defaultPackage != null) kDefaultPackage: defaultPackage!,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,11 +13,18 @@ class Plugin {
|
||||
required this.name,
|
||||
required this.path,
|
||||
required this.platforms,
|
||||
required this.defaultPackagePlatforms,
|
||||
required this.pluginDartClassPlatforms,
|
||||
required this.dependencies,
|
||||
required this.isDirectDependency,
|
||||
this.implementsPackage,
|
||||
}) : assert(name != null),
|
||||
assert(path != null),
|
||||
assert(platforms != null),
|
||||
assert(dependencies != null);
|
||||
assert(defaultPackagePlatforms != null),
|
||||
assert(pluginDartClassPlatforms != null),
|
||||
assert(dependencies != null),
|
||||
assert(isDirectDependency != null);
|
||||
|
||||
/// Parses [Plugin] specification from the provided pluginYaml.
|
||||
///
|
||||
@ -53,15 +60,30 @@ class Plugin {
|
||||
YamlMap? pluginYaml,
|
||||
List<String> dependencies, {
|
||||
required FileSystem fileSystem,
|
||||
Set<String>? appDependencies,
|
||||
}) {
|
||||
final List<String> errors = validatePluginYaml(pluginYaml);
|
||||
if (errors.isNotEmpty) {
|
||||
throwToolExit('Invalid plugin specification $name.\n${errors.join('\n')}');
|
||||
}
|
||||
if (pluginYaml != null && pluginYaml['platforms'] != null) {
|
||||
return Plugin._fromMultiPlatformYaml(name, path, pluginYaml, dependencies, fileSystem);
|
||||
return Plugin._fromMultiPlatformYaml(
|
||||
name,
|
||||
path,
|
||||
pluginYaml,
|
||||
dependencies,
|
||||
fileSystem,
|
||||
appDependencies != null && appDependencies.contains(name),
|
||||
);
|
||||
}
|
||||
return Plugin._fromLegacyYaml(name, path, pluginYaml, dependencies, fileSystem);
|
||||
return Plugin._fromLegacyYaml(
|
||||
name,
|
||||
path,
|
||||
pluginYaml,
|
||||
dependencies,
|
||||
fileSystem,
|
||||
appDependencies != null && appDependencies.contains(name),
|
||||
);
|
||||
}
|
||||
|
||||
factory Plugin._fromMultiPlatformYaml(
|
||||
@ -70,6 +92,7 @@ class Plugin {
|
||||
dynamic pluginYaml,
|
||||
List<String> dependencies,
|
||||
FileSystem fileSystem,
|
||||
bool isDirectDependency,
|
||||
) {
|
||||
assert (pluginYaml != null && pluginYaml['platforms'] != null,
|
||||
'Invalid multi-platform plugin specification $name.');
|
||||
@ -114,11 +137,47 @@ 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);
|
||||
|
||||
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,
|
||||
},
|
||||
dependencies: dependencies,
|
||||
isDirectDependency: isDirectDependency,
|
||||
implementsPackage: pluginYaml['implements'] != null ? pluginYaml['implements'] as String : '',
|
||||
);
|
||||
}
|
||||
|
||||
@ -128,6 +187,7 @@ class Plugin {
|
||||
dynamic pluginYaml,
|
||||
List<String> dependencies,
|
||||
FileSystem fileSystem,
|
||||
bool isDirectDependency,
|
||||
) {
|
||||
final Map<String, PluginPlatform> platforms = <String, PluginPlatform>{};
|
||||
final String pluginClass = pluginYaml['pluginClass'] as String;
|
||||
@ -155,7 +215,10 @@ class Plugin {
|
||||
name: name,
|
||||
path: path,
|
||||
platforms: platforms,
|
||||
defaultPackagePlatforms: <String, String>{},
|
||||
pluginDartClassPlatforms: <String, String>{},
|
||||
dependencies: dependencies,
|
||||
isDirectDependency: isDirectDependency,
|
||||
);
|
||||
}
|
||||
|
||||
@ -271,11 +334,41 @@ class Plugin {
|
||||
return errors;
|
||||
}
|
||||
|
||||
static bool _providesImplementationForPlatform(YamlMap platformsYaml, String platformKey) {
|
||||
static bool _supportsPlatform(YamlMap platformsYaml, String platformKey) {
|
||||
if (!platformsYaml.containsKey(platformKey)) {
|
||||
return false;
|
||||
}
|
||||
if ((platformsYaml[platformKey] as YamlMap).containsKey('default_package')) {
|
||||
if (platformsYaml[platformKey] is YamlMap) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static String? _getDefaultPackageForPlatform(YamlMap platformsYaml, String platformKey) {
|
||||
if (!_supportsPlatform(platformsYaml, platformKey)) {
|
||||
return null;
|
||||
}
|
||||
if ((platformsYaml[platformKey] as YamlMap).containsKey(kDefaultPackage)) {
|
||||
return (platformsYaml[platformKey] as YamlMap)[kDefaultPackage] as String;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static String? _getPluginDartClassForPlatform(YamlMap platformsYaml, String platformKey) {
|
||||
if (!_supportsPlatform(platformsYaml, platformKey)) {
|
||||
return null;
|
||||
}
|
||||
if ((platformsYaml[platformKey] as YamlMap).containsKey(kDartPluginClass)) {
|
||||
return (platformsYaml[platformKey] as YamlMap)[kDartPluginClass] as String;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static bool _providesImplementationForPlatform(YamlMap platformsYaml, String platformKey) {
|
||||
if (!_supportsPlatform(platformsYaml, platformKey)) {
|
||||
return false;
|
||||
}
|
||||
if ((platformsYaml[platformKey] as YamlMap).containsKey(kDefaultPackage)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@ -284,9 +377,45 @@ class Plugin {
|
||||
final String name;
|
||||
final String path;
|
||||
|
||||
/// The name of the interface package that this plugin implements.
|
||||
/// If [null], this plugin doesn't implement an interface.
|
||||
final String? implementsPackage;
|
||||
|
||||
/// The name of the packages this plugin depends on.
|
||||
final List<String> dependencies;
|
||||
|
||||
/// This is a mapping from platform config key to the plugin platform spec.
|
||||
final Map<String, PluginPlatform> platforms;
|
||||
|
||||
/// This is a mapping from platform config key to the default package implementation.
|
||||
final Map<String, String> defaultPackagePlatforms;
|
||||
|
||||
/// This is a mapping from platform config key to the plugin class for the given platform.
|
||||
final Map<String, String> pluginDartClassPlatforms;
|
||||
|
||||
/// Whether this plugin is a direct dependency of the app.
|
||||
/// If [false], the plugin is a dependency of another plugin.
|
||||
final bool isDirectDependency;
|
||||
}
|
||||
|
||||
/// Metadata associated with the resolution of a platform interface of a plugin.
|
||||
class PluginInterfaceResolution {
|
||||
PluginInterfaceResolution({
|
||||
required this.plugin,
|
||||
required this.platform,
|
||||
}) : assert(plugin != null),
|
||||
assert(platform != null);
|
||||
|
||||
/// The plugin.
|
||||
final Plugin plugin;
|
||||
// The name of the platform that this plugin implements.
|
||||
final String platform;
|
||||
|
||||
Map<String, String> toMap() {
|
||||
return <String, String> {
|
||||
'pluginName': plugin.name,
|
||||
'platform': platform,
|
||||
'dartClass': plugin.pluginDartClassPlatforms[platform] ?? '',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -26,6 +26,7 @@ import 'base/terminal.dart';
|
||||
import 'base/utils.dart';
|
||||
import 'build_info.dart';
|
||||
import 'build_system/build_system.dart';
|
||||
import 'build_system/targets/dart_plugin_registrant.dart';
|
||||
import 'build_system/targets/localizations.dart';
|
||||
import 'bundle.dart';
|
||||
import 'cache.dart';
|
||||
@ -1211,9 +1212,16 @@ abstract class ResidentRunner extends ResidentHandlers {
|
||||
processManager: globals.processManager,
|
||||
platform: globals.platform,
|
||||
projectDir: globals.fs.currentDirectory,
|
||||
generateDartPluginRegistry: true,
|
||||
);
|
||||
_lastBuild = await globals.buildSystem.buildIncremental(
|
||||
|
||||
final CompositeTarget compositeTarget = CompositeTarget(<Target>[
|
||||
const GenerateLocalizationsTarget(),
|
||||
const DartPluginRegistrantTarget(),
|
||||
]);
|
||||
|
||||
_lastBuild = await globals.buildSystem.buildIncremental(
|
||||
compositeTarget,
|
||||
_environment,
|
||||
_lastBuild,
|
||||
);
|
||||
|
||||
@ -26,6 +26,7 @@ import 'devfs.dart';
|
||||
import 'device.dart';
|
||||
import 'features.dart';
|
||||
import 'globals_null_migrated.dart' as globals;
|
||||
import 'project.dart';
|
||||
import 'reporting/reporting.dart';
|
||||
import 'resident_devtools_handler.dart';
|
||||
import 'resident_runner.dart';
|
||||
@ -314,6 +315,7 @@ class HotRunner extends ResidentRunner {
|
||||
bool enableDevTools = false,
|
||||
String route,
|
||||
}) async {
|
||||
final File mainFile = globals.fs.file(mainPath);
|
||||
firstBuildTime = DateTime.now();
|
||||
|
||||
final List<Future<bool>> startupTasks = <Future<bool>>[];
|
||||
@ -326,7 +328,7 @@ class HotRunner extends ResidentRunner {
|
||||
if (device.generator != null) {
|
||||
startupTasks.add(
|
||||
device.generator.recompile(
|
||||
globals.fs.file(mainPath).uri,
|
||||
mainFile.uri,
|
||||
<Uri>[],
|
||||
// When running without a provided applicationBinary, the tool will
|
||||
// simultaneously run the initial frontend_server compilation and
|
||||
@ -338,6 +340,8 @@ class HotRunner extends ResidentRunner {
|
||||
trackWidgetCreation: debuggingOptions.buildInfo.trackWidgetCreation,
|
||||
),
|
||||
packageConfig: debuggingOptions.buildInfo.packageConfig,
|
||||
projectRootPath: FlutterProject.current().directory.absolute.path,
|
||||
fs: globals.fs,
|
||||
).then((CompilerOutput output) => output?.errorCount == 0)
|
||||
);
|
||||
}
|
||||
|
||||
@ -1176,6 +1176,7 @@ abstract class FlutterCommand extends Command<void> {
|
||||
processManager: globals.processManager,
|
||||
platform: globals.platform,
|
||||
projectDir: project.directory,
|
||||
generateDartPluginRegistry: true,
|
||||
);
|
||||
|
||||
await generateLocalizationsSyntheticPackage(
|
||||
|
||||
@ -140,6 +140,8 @@ class TestCompiler {
|
||||
<Uri>[request.mainUri],
|
||||
outputPath: outputDill.path,
|
||||
packageConfig: buildInfo.packageConfig,
|
||||
projectRootPath: flutterProject.directory.absolute.path,
|
||||
fs: globals.fs,
|
||||
);
|
||||
final String outputPath = compilerOutput?.outputFilename;
|
||||
|
||||
|
||||
@ -138,6 +138,8 @@ class WebTestCompiler {
|
||||
<Uri>[],
|
||||
outputPath: outputDirectory.childFile('out').path,
|
||||
packageConfig: buildInfo.packageConfig,
|
||||
fs: _fileSystem,
|
||||
projectRootPath: projectDirectory.absolute.path,
|
||||
);
|
||||
if (output.errorCount > 0) {
|
||||
throwToolExit('Failed to compile');
|
||||
|
||||
@ -71,6 +71,9 @@ Future<void> buildWeb(
|
||||
? null
|
||||
: globals.flutterVersion.engineRevision,
|
||||
flutterRootDir: globals.fs.directory(Cache.flutterRoot),
|
||||
// Web uses a different Dart plugin registry.
|
||||
// https://github.com/flutter/flutter/issues/80406
|
||||
generateDartPluginRegistry: false,
|
||||
));
|
||||
if (!result.success) {
|
||||
for (final ExceptionMeasurement measurement in result.exceptions.values) {
|
||||
|
||||
@ -38,6 +38,7 @@ void main() {
|
||||
processManager: globals.processManager,
|
||||
platform: FakePlatform(),
|
||||
engineVersion: 'invalidEngineVersion',
|
||||
generateDartPluginRegistry: false,
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -0,0 +1,292 @@
|
||||
// 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.
|
||||
|
||||
// @dart = 2.8
|
||||
|
||||
import 'package:file/memory.dart';
|
||||
import 'package:flutter_tools/src/base/file_system.dart';
|
||||
import 'package:flutter_tools/src/base/logger.dart';
|
||||
import 'package:flutter_tools/src/build_system/build_system.dart';
|
||||
import 'package:flutter_tools/src/build_system/targets/common.dart';
|
||||
import 'package:flutter_tools/src/build_system/targets/dart_plugin_registrant.dart';
|
||||
import 'package:flutter_tools/src/project.dart';
|
||||
|
||||
import '../../../src/common.dart';
|
||||
import '../../../src/context.dart';
|
||||
|
||||
const String _kEmptyPubspecFile = '''
|
||||
name: app_without_plugins
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
''';
|
||||
|
||||
const String _kEmptyPackageJson = '''
|
||||
{
|
||||
"configVersion": 2,
|
||||
"packages": [
|
||||
]
|
||||
}
|
||||
''';
|
||||
|
||||
const String _kSamplePackageJson = '''
|
||||
{
|
||||
"configVersion": 2,
|
||||
"packages": [
|
||||
{
|
||||
"name": "path_provider_linux",
|
||||
"rootUri": "/path_provider_linux",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "2.12"
|
||||
},
|
||||
{
|
||||
"name": "path_provider_example",
|
||||
"rootUri": "../",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "2.12"
|
||||
}
|
||||
]
|
||||
}
|
||||
''';
|
||||
|
||||
const String _kSamplePackagesFile = '''
|
||||
path_provider_linux:/path_provider_linux/lib/
|
||||
path_provider_example:lib/
|
||||
''';
|
||||
|
||||
const String _kSamplePubspecFile = '''
|
||||
name: path_provider_example
|
||||
description: Demonstrates how to use the path_provider plugin.
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
path_provider_linux: 1.0.0
|
||||
''';
|
||||
|
||||
const String _kLinuxRegistrant =
|
||||
'//\n'
|
||||
'// Generated file. Do not edit.\n'
|
||||
'// This file is generated from template in file `flutter_tools/lib/src/flutter_plugins.dart`.\n'
|
||||
'//\n'
|
||||
'\n'
|
||||
'// @dart = 2.12\n'
|
||||
'\n'
|
||||
'import \'package:path_provider_example/main.dart\' as entrypoint;\n'
|
||||
'import \'dart:io\'; // flutter_ignore: dart_io_import.\n'
|
||||
'import \'package:path_provider_linux/path_provider_linux.dart\';\n'
|
||||
'\n'
|
||||
'@pragma(\'vm:entry-point\')\n'
|
||||
'class _PluginRegistrant {\n'
|
||||
'\n'
|
||||
' @pragma(\'vm:entry-point\')\n'
|
||||
' static void register() {\n'
|
||||
' if (Platform.isLinux) {\n'
|
||||
' try {\n'
|
||||
' PathProviderLinux.registerWith();\n'
|
||||
' } catch (err) {\n'
|
||||
' print(\n'
|
||||
' \'`path_provider_linux` 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.isMacOS) {\n'
|
||||
' } else if (Platform.isWindows) {\n'
|
||||
' }\n'
|
||||
' }\n'
|
||||
'\n'
|
||||
'}\n'
|
||||
'\n'
|
||||
'typedef _UnaryFunction = dynamic Function(List<String> args);\n'
|
||||
'typedef _NullaryFunction = dynamic Function();\n'
|
||||
'\n'
|
||||
'void main(List<String> args) {\n'
|
||||
' if (entrypoint.main is _UnaryFunction) {\n'
|
||||
' (entrypoint.main as _UnaryFunction)(args);\n'
|
||||
' } else {\n'
|
||||
' (entrypoint.main as _NullaryFunction)();\n'
|
||||
' }\n'
|
||||
'}\n'
|
||||
'';
|
||||
|
||||
const String _kSamplePluginPubspec = '''
|
||||
name: path_provider_linux
|
||||
description: linux implementation of the path_provider plugin
|
||||
// version: 2.0.1
|
||||
// homepage: https://github.com/flutter/plugins/tree/master/packages/path_provider/path_provider_linux
|
||||
|
||||
flutter:
|
||||
plugin:
|
||||
implements: path_provider
|
||||
platforms:
|
||||
linux:
|
||||
dartPluginClass: PathProviderLinux
|
||||
pluginClass: none
|
||||
|
||||
environment:
|
||||
sdk: ">=2.12.0-259.9.beta <3.0.0"
|
||||
flutter: ">=1.20.0"
|
||||
''';
|
||||
|
||||
void main() {
|
||||
|
||||
group('Dart plugin registrant' , () {
|
||||
final FileSystem fileSystem = MemoryFileSystem.test();
|
||||
|
||||
testWithoutContext('skipped based on environment.generateDartPluginRegistry',
|
||||
() async {
|
||||
final Environment environment = Environment.test(
|
||||
fileSystem.currentDirectory,
|
||||
artifacts: null,
|
||||
fileSystem: fileSystem,
|
||||
logger: BufferLogger.test(),
|
||||
processManager: FakeProcessManager.any(),
|
||||
generateDartPluginRegistry: false);
|
||||
|
||||
expect(const DartPluginRegistrantTarget().canSkip(environment), true);
|
||||
|
||||
final Environment environment2 = Environment.test(
|
||||
fileSystem.currentDirectory,
|
||||
artifacts: null,
|
||||
fileSystem: fileSystem,
|
||||
logger: BufferLogger.test(),
|
||||
processManager: FakeProcessManager.any(),
|
||||
generateDartPluginRegistry: true);
|
||||
|
||||
expect(const DartPluginRegistrantTarget().canSkip(environment2), false);
|
||||
});
|
||||
|
||||
testUsingContext("doesn't generate generated_main.dart if there aren't Dart plugins", () async {
|
||||
final Environment environment = Environment.test(
|
||||
fileSystem.currentDirectory,
|
||||
projectDir: fileSystem.directory('project')..createSync(),
|
||||
artifacts: null,
|
||||
fileSystem: fileSystem,
|
||||
logger: BufferLogger.test(),
|
||||
processManager: FakeProcessManager.any(),
|
||||
generateDartPluginRegistry: true);
|
||||
|
||||
final File config = environment.projectDir
|
||||
.childDirectory('.dart_tool')
|
||||
.childFile('package_config.json');
|
||||
config.createSync(recursive: true);
|
||||
config.writeAsStringSync(_kSamplePackageJson);
|
||||
|
||||
final File pubspec = environment.projectDir.childFile('pubspec.yaml');
|
||||
pubspec.createSync();
|
||||
|
||||
final File packages = environment.projectDir.childFile('.packages');
|
||||
packages.createSync();
|
||||
|
||||
final File generatedMain = environment.projectDir
|
||||
.childDirectory('.dart_tool')
|
||||
.childDirectory('flutter_build')
|
||||
.childFile('generated_main.dart');
|
||||
|
||||
final FlutterProject testProject = FlutterProject.fromDirectoryTest(environment.projectDir);
|
||||
await DartPluginRegistrantTarget.test(testProject).build(environment);
|
||||
|
||||
expect(generatedMain.existsSync(), isFalse);
|
||||
});
|
||||
|
||||
testUsingContext('regenerates generated_main.dart', () async {
|
||||
final Directory projectDir = fileSystem.directory('project')..createSync();
|
||||
final Environment environment = Environment.test(
|
||||
fileSystem.currentDirectory,
|
||||
projectDir: projectDir,
|
||||
artifacts: null,
|
||||
fileSystem: fileSystem,
|
||||
logger: BufferLogger.test(),
|
||||
processManager: FakeProcessManager.any(),
|
||||
defines: <String, String>{
|
||||
kTargetFile: projectDir.childDirectory('lib').childFile('main.dart').absolute.path,
|
||||
},
|
||||
generateDartPluginRegistry: true);
|
||||
|
||||
final File config = projectDir
|
||||
.childDirectory('.dart_tool')
|
||||
.childFile('package_config.json');
|
||||
config.createSync(recursive: true);
|
||||
config.writeAsStringSync(_kSamplePackageJson);
|
||||
|
||||
final File pubspec = projectDir.childFile('pubspec.yaml');
|
||||
pubspec.createSync();
|
||||
pubspec.writeAsStringSync(_kSamplePubspecFile);
|
||||
|
||||
final File packages = projectDir.childFile('.packages');
|
||||
packages.createSync();
|
||||
packages.writeAsStringSync(_kSamplePackagesFile);
|
||||
|
||||
final File generatedMain = projectDir
|
||||
.childDirectory('.dart_tool')
|
||||
.childDirectory('flutter_build')
|
||||
.childFile('generated_main.dart');
|
||||
generatedMain.createSync(recursive: true);
|
||||
|
||||
final File mainEntrypoint = projectDir.childDirectory('lib').childFile('main.dart');
|
||||
mainEntrypoint.createSync(recursive: true);
|
||||
|
||||
final File pluginPubspec = environment.fileSystem.currentDirectory.childDirectory('path_provider_linux').childFile('pubspec.yaml');
|
||||
pluginPubspec.createSync(recursive: true);
|
||||
pluginPubspec.writeAsStringSync(_kSamplePluginPubspec);
|
||||
|
||||
final FlutterProject testProject = FlutterProject.fromDirectoryTest(environment.projectDir);
|
||||
await DartPluginRegistrantTarget.test(testProject).build(environment);
|
||||
|
||||
final String mainContent = generatedMain.readAsStringSync();
|
||||
expect(mainContent, equals(_kLinuxRegistrant));
|
||||
});
|
||||
|
||||
testUsingContext('removes generated_main.dart if plugins are removed from pubspec.yaml', () async {
|
||||
final Environment environment = Environment.test(
|
||||
fileSystem.currentDirectory,
|
||||
projectDir: fileSystem.directory('project')..createSync(),
|
||||
artifacts: null,
|
||||
fileSystem: fileSystem,
|
||||
logger: BufferLogger.test(),
|
||||
processManager: FakeProcessManager.any(),
|
||||
generateDartPluginRegistry: true);
|
||||
final File config = environment.projectDir
|
||||
.childDirectory('.dart_tool')
|
||||
.childFile('package_config.json');
|
||||
config.createSync(recursive: true);
|
||||
config.writeAsStringSync(_kSamplePackageJson);
|
||||
|
||||
final File pubspec = environment.projectDir.childFile('pubspec.yaml');
|
||||
pubspec.createSync();
|
||||
pubspec.writeAsStringSync(_kSamplePubspecFile);
|
||||
|
||||
final File packages = environment.projectDir.childFile('.packages');
|
||||
packages.createSync();
|
||||
packages.writeAsStringSync(_kSamplePackagesFile);
|
||||
|
||||
final File generatedMain = environment.projectDir
|
||||
.childDirectory('.dart_tool')
|
||||
.childDirectory('flutter_build')
|
||||
.childFile('generated_main.dart');
|
||||
|
||||
final File pluginPubspec = environment.fileSystem.currentDirectory
|
||||
.childDirectory('path_provider_linux')
|
||||
.childFile('pubspec.yaml');
|
||||
|
||||
pluginPubspec.createSync(recursive: true);
|
||||
pluginPubspec.writeAsStringSync(_kSamplePluginPubspec);
|
||||
|
||||
final FlutterProject testProject = FlutterProject.fromDirectoryTest(environment.projectDir);
|
||||
await DartPluginRegistrantTarget.test(testProject).build(environment);
|
||||
expect(generatedMain.existsSync(), isTrue);
|
||||
|
||||
// Simulate a user removing everything from pubspec.yaml.
|
||||
pubspec.writeAsStringSync(_kEmptyPubspecFile);
|
||||
packages.writeAsStringSync(_kEmptyPackageJson);
|
||||
config.writeAsStringSync(_kEmptyPackageJson);
|
||||
|
||||
await DartPluginRegistrantTarget.test(testProject).build(environment);
|
||||
expect(generatedMain.existsSync(), isFalse);
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -8,6 +8,7 @@ import 'dart:async';
|
||||
|
||||
import 'package:file/memory.dart';
|
||||
import 'package:flutter_tools/src/artifacts.dart';
|
||||
import 'package:flutter_tools/src/base/file_system.dart';
|
||||
import 'package:flutter_tools/src/base/logger.dart';
|
||||
import 'package:flutter_tools/src/build_info.dart';
|
||||
import 'package:flutter_tools/src/compile.dart';
|
||||
@ -367,4 +368,60 @@ void main() {
|
||||
|
||||
expect((await output).outputFilename, '');
|
||||
});
|
||||
|
||||
testWithoutContext('KernelCompiler uses generated entrypoint', () async {
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
final StdoutHandler stdoutHandler = StdoutHandler(logger: logger, fileSystem: MemoryFileSystem.test());
|
||||
final Completer<void> completer = Completer<void>();
|
||||
final MemoryFileSystem fs = MemoryFileSystem.test();
|
||||
final KernelCompiler kernelCompiler = KernelCompiler(
|
||||
artifacts: Artifacts.test(),
|
||||
fileSystem: fs,
|
||||
fileSystemRoots: <String>[
|
||||
'/foo/bar/fizz',
|
||||
],
|
||||
fileSystemScheme: 'scheme',
|
||||
logger: logger,
|
||||
processManager: FakeProcessManager.list(<FakeCommand>[
|
||||
FakeCommand(command: const <String>[
|
||||
'HostArtifact.engineDartBinary',
|
||||
'--disable-dart-dev',
|
||||
'Artifact.frontendServerSnapshotForEngineDartSdk',
|
||||
'--sdk-root',
|
||||
'/path/to/sdkroot/',
|
||||
'--target=flutter',
|
||||
'--no-print-incremental-dependencies',
|
||||
'-Ddart.vm.profile=false',
|
||||
'-Ddart.vm.product=false',
|
||||
'--enable-asserts',
|
||||
'--no-link-platform',
|
||||
'--packages',
|
||||
'.packages',
|
||||
'.dart_tools/flutter_build/generated_main.dart',
|
||||
], completer: completer),
|
||||
]),
|
||||
stdoutHandler: stdoutHandler,
|
||||
);
|
||||
|
||||
final Directory buildDir = fs.directory('.dart_tools')
|
||||
.childDirectory('flutter_build')
|
||||
.childDirectory('test');
|
||||
|
||||
buildDir.parent.childFile('generated_main.dart').createSync(recursive: true);
|
||||
|
||||
final Future<CompilerOutput> output = kernelCompiler.compile(sdkRoot: '/path/to/sdkroot',
|
||||
mainPath: '/foo/bar/fizz/main.dart',
|
||||
buildMode: BuildMode.debug,
|
||||
trackWidgetCreation: false,
|
||||
dartDefines: const <String>[],
|
||||
packageConfig: PackageConfig.empty,
|
||||
packagesPath: '.packages',
|
||||
buildDir: buildDir,
|
||||
checkDartPluginRegistry: true,
|
||||
);
|
||||
|
||||
stdoutHandler.compilerOutput.complete(const CompilerOutput('', 0, <Uri>[]));
|
||||
completer.complete();
|
||||
await output;
|
||||
});
|
||||
}
|
||||
|
||||
@ -95,6 +95,8 @@ void main() {
|
||||
null, /* invalidatedFiles */
|
||||
outputPath: '/build/',
|
||||
packageConfig: PackageConfig.empty,
|
||||
projectRootPath: '',
|
||||
fs: fileSystem,
|
||||
).then((CompilerOutput output) {
|
||||
expect(frontendServerStdIn.getAndClear(),
|
||||
'compile file:///path/to/main.dart\n');
|
||||
@ -137,6 +139,8 @@ void main() {
|
||||
null, /* invalidatedFiles */
|
||||
outputPath: '/build/',
|
||||
packageConfig: PackageConfig.empty,
|
||||
projectRootPath: '',
|
||||
fs: MemoryFileSystem(),
|
||||
).then((CompilerOutput outputCompile) {
|
||||
expect(testLogger.errorText,
|
||||
equals('line1\nline2\n'));
|
||||
|
||||
@ -92,6 +92,8 @@ void main() {
|
||||
null /* invalidatedFiles */,
|
||||
outputPath: '/build/',
|
||||
packageConfig: PackageConfig.empty,
|
||||
fs: MemoryFileSystem(),
|
||||
projectRootPath: '',
|
||||
);
|
||||
expect(frontendServerStdIn.getAndClear(), 'compile /path/to/main.dart\n');
|
||||
expect(testLogger.errorText, equals('line1\nline2\n'));
|
||||
@ -117,6 +119,8 @@ void main() {
|
||||
null /* invalidatedFiles */,
|
||||
outputPath: '/build/',
|
||||
packageConfig: PackageConfig.empty,
|
||||
fs: MemoryFileSystem(),
|
||||
projectRootPath: '',
|
||||
);
|
||||
expect(frontendServerStdIn.getAndClear(), 'compile scheme:///main.dart\n');
|
||||
expect(testLogger.errorText, equals('line1\nline2\n'));
|
||||
@ -135,6 +139,8 @@ void main() {
|
||||
null, /* invalidatedFiles */
|
||||
outputPath: '/build/',
|
||||
packageConfig: PackageConfig.empty,
|
||||
fs: MemoryFileSystem(),
|
||||
projectRootPath: '',
|
||||
)), throwsToolExit());
|
||||
});
|
||||
|
||||
@ -150,6 +156,8 @@ void main() {
|
||||
null, /* invalidatedFiles */
|
||||
outputPath: '/build/',
|
||||
packageConfig: PackageConfig.empty,
|
||||
fs: MemoryFileSystem(),
|
||||
projectRootPath: '',
|
||||
)), throwsToolExit(message: 'the Dart compiler exited unexpectedly.'));
|
||||
});
|
||||
|
||||
@ -167,6 +175,8 @@ void main() {
|
||||
null, /* invalidatedFiles */
|
||||
outputPath: '/build/',
|
||||
packageConfig: PackageConfig.empty,
|
||||
projectRootPath: '',
|
||||
fs: MemoryFileSystem(),
|
||||
);
|
||||
expect(frontendServerStdIn.getAndClear(), 'compile /path/to/main.dart\n');
|
||||
|
||||
@ -213,6 +223,8 @@ void main() {
|
||||
null, /* invalidatedFiles */
|
||||
outputPath: '/build/',
|
||||
packageConfig: PackageConfig.empty,
|
||||
fs: MemoryFileSystem(),
|
||||
projectRootPath: '',
|
||||
);
|
||||
expect(frontendServerStdIn.getAndClear(), 'compile scheme:///main.dart\n');
|
||||
|
||||
@ -274,6 +286,8 @@ void main() {
|
||||
null, /* invalidatedFiles */
|
||||
outputPath: '/build/',
|
||||
packageConfig: PackageConfig.empty,
|
||||
fs: MemoryFileSystem(),
|
||||
projectRootPath: '',
|
||||
);
|
||||
expect(frontendServerStdIn.getAndClear(), 'compile scheme:///main.dart\n');
|
||||
|
||||
@ -323,6 +337,8 @@ void main() {
|
||||
<Uri>[],
|
||||
outputPath: '/build/',
|
||||
packageConfig: PackageConfig.empty,
|
||||
fs: MemoryFileSystem(),
|
||||
projectRootPath: '',
|
||||
);
|
||||
expect(frontendServerStdIn.getAndClear(), 'compile /path/to/main.dart\n');
|
||||
|
||||
@ -359,6 +375,8 @@ void main() {
|
||||
null /* invalidatedFiles */,
|
||||
outputPath: '/build/',
|
||||
packageConfig: PackageConfig.empty,
|
||||
fs: MemoryFileSystem(),
|
||||
projectRootPath: '',
|
||||
);
|
||||
expect(frontendServerStdIn.getAndClear(), 'compile /path/to/main.dart\n');
|
||||
|
||||
@ -398,6 +416,8 @@ Future<void> _recompile(
|
||||
outputPath: '/build/',
|
||||
packageConfig: PackageConfig.empty,
|
||||
suppressErrors: suppressErrors,
|
||||
fs: MemoryFileSystem(),
|
||||
projectRootPath: '',
|
||||
);
|
||||
|
||||
// Put content into the output stream after generator.recompile gets
|
||||
|
||||
954
packages/flutter_tools/test/general.shard/dart_plugin_test.dart
Normal file
954
packages/flutter_tools/test/general.shard/dart_plugin_test.dart
Normal file
@ -0,0 +1,954 @@
|
||||
// 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.
|
||||
|
||||
// @dart = 2.8
|
||||
|
||||
import 'package:file/file.dart';
|
||||
import 'package:file/memory.dart';
|
||||
import 'package:flutter_tools/src/dart/package_map.dart';
|
||||
import 'package:flutter_tools/src/flutter_manifest.dart';
|
||||
import 'package:flutter_tools/src/flutter_plugins.dart';
|
||||
import 'package:flutter_tools/src/globals_null_migrated.dart' as globals;
|
||||
import 'package:flutter_tools/src/plugins.dart';
|
||||
import 'package:flutter_tools/src/project.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:package_config/package_config.dart';
|
||||
import 'package:yaml/yaml.dart';
|
||||
|
||||
import '../src/common.dart';
|
||||
import '../src/context.dart';
|
||||
|
||||
void main() {
|
||||
group('Dart plugin registrant', () {
|
||||
FileSystem fs;
|
||||
MockFlutterProject flutterProject;
|
||||
MockFlutterManifest flutterManifest;
|
||||
|
||||
setUp(() async {
|
||||
fs = MemoryFileSystem.test();
|
||||
|
||||
flutterProject = MockFlutterProject();
|
||||
|
||||
flutterManifest = MockFlutterManifest();
|
||||
when(flutterManifest.dependencies).thenReturn(<String>{});
|
||||
|
||||
when(flutterProject.manifest).thenReturn(flutterManifest);
|
||||
when(flutterProject.directory).thenReturn(fs.systemTempDirectory.childDirectory('app'));
|
||||
|
||||
when(flutterProject.flutterPluginsFile).thenReturn(flutterProject.directory.childFile('.flutter-plugins'));
|
||||
when(flutterProject.flutterPluginsDependenciesFile).thenReturn(flutterProject.directory.childFile('.flutter-plugins-dependencies'));
|
||||
|
||||
flutterProject.directory.childFile('.packages').createSync(recursive: true);
|
||||
});
|
||||
|
||||
group('resolvePlatformImplementation', () {
|
||||
testWithoutContext('selects implementation from direct dependency', () async {
|
||||
final Set<String> directDependencies = <String>{
|
||||
'url_launcher_linux',
|
||||
'url_launcher_macos',
|
||||
};
|
||||
final List<PluginInterfaceResolution> resolutions = resolvePlatformImplementation(<Plugin>[
|
||||
Plugin.fromYaml(
|
||||
'url_launcher_linux',
|
||||
'',
|
||||
YamlMap.wrap(<String, dynamic>{
|
||||
'implements': 'url_launcher',
|
||||
'platforms': <String, dynamic>{
|
||||
'linux': <String, dynamic>{
|
||||
'dartPluginClass': 'UrlLauncherPluginLinux',
|
||||
},
|
||||
},
|
||||
}),
|
||||
<String>[],
|
||||
fileSystem: fs,
|
||||
appDependencies: directDependencies,
|
||||
),
|
||||
Plugin.fromYaml(
|
||||
'url_launcher_macos',
|
||||
'',
|
||||
YamlMap.wrap(<String, dynamic>{
|
||||
'implements': 'url_launcher',
|
||||
'platforms': <String, dynamic>{
|
||||
'macos': <String, dynamic>{
|
||||
'dartPluginClass': 'UrlLauncherPluginMacOS',
|
||||
},
|
||||
},
|
||||
}),
|
||||
<String>[],
|
||||
fileSystem: fs,
|
||||
appDependencies: directDependencies,
|
||||
),
|
||||
Plugin.fromYaml(
|
||||
'undirect_dependency_plugin',
|
||||
'',
|
||||
YamlMap.wrap(<String, dynamic>{
|
||||
'implements': 'url_launcher',
|
||||
'platforms': <String, dynamic>{
|
||||
'windows': <String, dynamic>{
|
||||
'dartPluginClass': 'UrlLauncherPluginWindows',
|
||||
},
|
||||
},
|
||||
}),
|
||||
<String>[],
|
||||
fileSystem: fs,
|
||||
appDependencies: directDependencies,
|
||||
),
|
||||
]);
|
||||
|
||||
resolvePlatformImplementation(<Plugin>[
|
||||
Plugin.fromYaml(
|
||||
'url_launcher_macos',
|
||||
'',
|
||||
YamlMap.wrap(<String, dynamic>{
|
||||
'implements': 'url_launcher',
|
||||
'platforms': <String, dynamic>{
|
||||
'macos': <String, dynamic>{
|
||||
'dartPluginClass': 'UrlLauncherPluginMacOS',
|
||||
},
|
||||
},
|
||||
}),
|
||||
<String>[],
|
||||
fileSystem: fs,
|
||||
appDependencies: directDependencies,
|
||||
),
|
||||
]);
|
||||
|
||||
expect(resolutions.length, equals(2));
|
||||
expect(resolutions[0].toMap(), equals(
|
||||
<String, String>{
|
||||
'pluginName': 'url_launcher_linux',
|
||||
'dartClass': 'UrlLauncherPluginLinux',
|
||||
'platform': 'linux',
|
||||
})
|
||||
);
|
||||
expect(resolutions[1].toMap(), equals(
|
||||
<String, String>{
|
||||
'pluginName': 'url_launcher_macos',
|
||||
'dartClass': 'UrlLauncherPluginMacOS',
|
||||
'platform': 'macos',
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
testWithoutContext('selects default implementation', () 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>{
|
||||
'default_package': 'url_launcher_linux',
|
||||
},
|
||||
},
|
||||
}),
|
||||
<String>[],
|
||||
fileSystem: fs,
|
||||
appDependencies: directDependencies,
|
||||
),
|
||||
Plugin.fromYaml(
|
||||
'url_launcher_linux',
|
||||
'',
|
||||
YamlMap.wrap(<String, dynamic>{
|
||||
'implements': 'url_launcher',
|
||||
'platforms': <String, dynamic>{
|
||||
'linux': <String, dynamic>{
|
||||
'dartPluginClass': 'UrlLauncherPluginLinux',
|
||||
},
|
||||
},
|
||||
}),
|
||||
<String>[],
|
||||
fileSystem: fs,
|
||||
appDependencies: directDependencies,
|
||||
),
|
||||
]);
|
||||
expect(resolutions.length, equals(1));
|
||||
expect(resolutions[0].toMap(), equals(
|
||||
<String, String>{
|
||||
'pluginName': 'url_launcher_linux',
|
||||
'dartClass': 'UrlLauncherPluginLinux',
|
||||
'platform': 'linux',
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
testWithoutContext('selects default implementation if interface is direct dependency', () async {
|
||||
final Set<String> directDependencies = <String>{'url_launcher'};
|
||||
|
||||
final List<PluginInterfaceResolution> resolutions = resolvePlatformImplementation(<Plugin>[
|
||||
Plugin.fromYaml(
|
||||
'url_launcher',
|
||||
'',
|
||||
YamlMap.wrap(<String, dynamic>{
|
||||
'platforms': <String, dynamic>{
|
||||
'linux': <String, dynamic>{
|
||||
'default_package': 'url_launcher_linux',
|
||||
},
|
||||
},
|
||||
}),
|
||||
<String>[],
|
||||
fileSystem: fs,
|
||||
appDependencies: directDependencies,
|
||||
),
|
||||
Plugin.fromYaml(
|
||||
'url_launcher_linux',
|
||||
'',
|
||||
YamlMap.wrap(<String, dynamic>{
|
||||
'implements': 'url_launcher',
|
||||
'platforms': <String, dynamic>{
|
||||
'linux': <String, dynamic>{
|
||||
'dartPluginClass': 'UrlLauncherPluginLinux',
|
||||
},
|
||||
},
|
||||
}),
|
||||
<String>[],
|
||||
fileSystem: fs,
|
||||
appDependencies: directDependencies,
|
||||
),
|
||||
]);
|
||||
expect(resolutions.length, equals(1));
|
||||
expect(resolutions[0].toMap(), equals(
|
||||
<String, String>{
|
||||
'pluginName': 'url_launcher_linux',
|
||||
'dartClass': 'UrlLauncherPluginLinux',
|
||||
'platform': 'linux',
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
testWithoutContext('selects user selected implementation despites default implementation', () async {
|
||||
final Set<String> directDependencies = <String>{
|
||||
'user_selected_url_launcher_implementation',
|
||||
'url_launcher',
|
||||
};
|
||||
|
||||
final List<PluginInterfaceResolution> resolutions = resolvePlatformImplementation(<Plugin>[
|
||||
Plugin.fromYaml(
|
||||
'url_launcher',
|
||||
'',
|
||||
YamlMap.wrap(<String, dynamic>{
|
||||
'platforms': <String, dynamic>{
|
||||
'linux': <String, dynamic>{
|
||||
'default_package': 'url_launcher_linux',
|
||||
},
|
||||
},
|
||||
}),
|
||||
<String>[],
|
||||
fileSystem: fs,
|
||||
appDependencies: directDependencies,
|
||||
),
|
||||
Plugin.fromYaml(
|
||||
'url_launcher_linux',
|
||||
'',
|
||||
YamlMap.wrap(<String, dynamic>{
|
||||
'implements': 'url_launcher',
|
||||
'platforms': <String, dynamic>{
|
||||
'linux': <String, dynamic>{
|
||||
'dartPluginClass': 'UrlLauncherPluginLinux',
|
||||
},
|
||||
},
|
||||
}),
|
||||
<String>[],
|
||||
fileSystem: fs,
|
||||
appDependencies: directDependencies,
|
||||
),
|
||||
Plugin.fromYaml(
|
||||
'user_selected_url_launcher_implementation',
|
||||
'',
|
||||
YamlMap.wrap(<String, dynamic>{
|
||||
'implements': 'url_launcher',
|
||||
'platforms': <String, dynamic>{
|
||||
'linux': <String, dynamic>{
|
||||
'dartPluginClass': 'UrlLauncherPluginLinux',
|
||||
},
|
||||
},
|
||||
}),
|
||||
<String>[],
|
||||
fileSystem: fs,
|
||||
appDependencies: directDependencies,
|
||||
),
|
||||
]);
|
||||
expect(resolutions.length, equals(1));
|
||||
expect(resolutions[0].toMap(), equals(
|
||||
<String, String>{
|
||||
'pluginName': 'user_selected_url_launcher_implementation',
|
||||
'dartClass': 'UrlLauncherPluginLinux',
|
||||
'platform': 'linux',
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
testWithoutContext('selects user selected implementation despites default implementation', () async {
|
||||
final Set<String> directDependencies = <String>{
|
||||
'user_selected_url_launcher_implementation',
|
||||
'url_launcher',
|
||||
};
|
||||
|
||||
final List<PluginInterfaceResolution> resolutions = resolvePlatformImplementation(<Plugin>[
|
||||
Plugin.fromYaml(
|
||||
'url_launcher',
|
||||
'',
|
||||
YamlMap.wrap(<String, dynamic>{
|
||||
'platforms': <String, dynamic>{
|
||||
'linux': <String, dynamic>{
|
||||
'default_package': 'url_launcher_linux',
|
||||
},
|
||||
},
|
||||
}),
|
||||
<String>[],
|
||||
fileSystem: fs,
|
||||
appDependencies: directDependencies,
|
||||
),
|
||||
Plugin.fromYaml(
|
||||
'url_launcher_linux',
|
||||
'',
|
||||
YamlMap.wrap(<String, dynamic>{
|
||||
'implements': 'url_launcher',
|
||||
'platforms': <String, dynamic>{
|
||||
'linux': <String, dynamic>{
|
||||
'dartPluginClass': 'UrlLauncherPluginLinux',
|
||||
},
|
||||
},
|
||||
}),
|
||||
<String>[],
|
||||
fileSystem: fs,
|
||||
appDependencies: directDependencies,
|
||||
),
|
||||
Plugin.fromYaml(
|
||||
'user_selected_url_launcher_implementation',
|
||||
'',
|
||||
YamlMap.wrap(<String, dynamic>{
|
||||
'implements': 'url_launcher',
|
||||
'platforms': <String, dynamic>{
|
||||
'linux': <String, dynamic>{
|
||||
'dartPluginClass': 'UrlLauncherPluginLinux',
|
||||
},
|
||||
},
|
||||
}),
|
||||
<String>[],
|
||||
fileSystem: fs,
|
||||
appDependencies: directDependencies,
|
||||
),
|
||||
]);
|
||||
expect(resolutions.length, equals(1));
|
||||
expect(resolutions[0].toMap(), equals(
|
||||
<String, String>{
|
||||
'pluginName': 'user_selected_url_launcher_implementation',
|
||||
'dartClass': 'UrlLauncherPluginLinux',
|
||||
'platform': 'linux',
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
testUsingContext('provides error when user selected multiple implementations', () async {
|
||||
final Set<String> directDependencies = <String>{
|
||||
'url_launcher_linux_1',
|
||||
'url_launcher_linux_2',
|
||||
};
|
||||
expect(() {
|
||||
resolvePlatformImplementation(<Plugin>[
|
||||
Plugin.fromYaml(
|
||||
'url_launcher_linux_1',
|
||||
'',
|
||||
YamlMap.wrap(<String, dynamic>{
|
||||
'implements': 'url_launcher',
|
||||
'platforms': <String, dynamic>{
|
||||
'linux': <String, dynamic>{
|
||||
'dartPluginClass': 'UrlLauncherPluginLinux',
|
||||
},
|
||||
},
|
||||
}),
|
||||
<String>[],
|
||||
fileSystem: fs,
|
||||
appDependencies: directDependencies,
|
||||
),
|
||||
Plugin.fromYaml(
|
||||
'url_launcher_linux_2',
|
||||
'',
|
||||
YamlMap.wrap(<String, dynamic>{
|
||||
'implements': 'url_launcher',
|
||||
'platforms': <String, dynamic>{
|
||||
'linux': <String, dynamic>{
|
||||
'dartPluginClass': 'UrlLauncherPluginLinux',
|
||||
},
|
||||
},
|
||||
}),
|
||||
<String>[],
|
||||
fileSystem: fs,
|
||||
appDependencies: directDependencies,
|
||||
),
|
||||
]);
|
||||
|
||||
expect(
|
||||
testLogger.errorText,
|
||||
'Plugin `url_launcher_linux_2` implements an interface for `linux`, which was already implemented by plugin `url_launcher_linux_1`.\n'
|
||||
'To fix this issue, remove either dependency from pubspec.yaml.'
|
||||
'\n\n'
|
||||
);
|
||||
},
|
||||
throwsToolExit(
|
||||
message: 'Please resolve the errors',
|
||||
));
|
||||
});
|
||||
|
||||
testUsingContext('provides all errors when user selected multiple implementations', () async {
|
||||
final Set<String> directDependencies = <String>{
|
||||
'url_launcher_linux_1',
|
||||
'url_launcher_linux_2',
|
||||
};
|
||||
expect(() {
|
||||
resolvePlatformImplementation(<Plugin>[
|
||||
Plugin.fromYaml(
|
||||
'url_launcher_linux_1',
|
||||
'',
|
||||
YamlMap.wrap(<String, dynamic>{
|
||||
'implements': 'url_launcher',
|
||||
'platforms': <String, dynamic>{
|
||||
'linux': <String, dynamic>{
|
||||
'dartPluginClass': 'UrlLauncherPluginLinux',
|
||||
},
|
||||
},
|
||||
}),
|
||||
<String>[],
|
||||
fileSystem: fs,
|
||||
appDependencies: directDependencies,
|
||||
),
|
||||
Plugin.fromYaml(
|
||||
'url_launcher_linux_2',
|
||||
'',
|
||||
YamlMap.wrap(<String, dynamic>{
|
||||
'implements': 'url_launcher',
|
||||
'platforms': <String, dynamic>{
|
||||
'linux': <String, dynamic>{
|
||||
'dartPluginClass': 'UrlLauncherPluginLinux',
|
||||
},
|
||||
},
|
||||
}),
|
||||
<String>[],
|
||||
fileSystem: fs,
|
||||
appDependencies: directDependencies,
|
||||
),
|
||||
]);
|
||||
|
||||
expect(
|
||||
testLogger.errorText,
|
||||
'Plugin `url_launcher_linux_2` implements an interface for `linux`, which was already implemented by plugin `url_launcher_linux_1`.\n'
|
||||
'To fix this issue, remove either dependency from pubspec.yaml.'
|
||||
'\n\n'
|
||||
);
|
||||
},
|
||||
throwsToolExit(
|
||||
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', () {
|
||||
|
||||
void createFakeDartPlugins(
|
||||
FlutterProject flutterProject,
|
||||
FlutterManifest flutterManifest,
|
||||
FileSystem fs,
|
||||
Map<String, String> plugins,
|
||||
) {
|
||||
final Directory fakePubCache = fs.systemTempDirectory.childDirectory('cache');
|
||||
final File packagesFile = flutterProject.directory
|
||||
.childFile('.packages')
|
||||
..createSync(recursive: true);
|
||||
|
||||
for (final MapEntry<String, String> entry in plugins.entries) {
|
||||
final String name = fs.path.basename(entry.key);
|
||||
final Directory pluginDirectory = fakePubCache.childDirectory(name);
|
||||
packagesFile.writeAsStringSync(
|
||||
'$name:file://${pluginDirectory.childFile('lib').uri}\n',
|
||||
mode: FileMode.writeOnlyAppend);
|
||||
pluginDirectory.childFile('pubspec.yaml')
|
||||
..createSync(recursive: true)
|
||||
..writeAsStringSync(entry.value);
|
||||
}
|
||||
when(flutterManifest.dependencies).thenReturn(<String>{...plugins.keys});
|
||||
}
|
||||
|
||||
testUsingContext('Generates new entrypoint', () async {
|
||||
when(flutterProject.isModule).thenReturn(false);
|
||||
|
||||
createFakeDartPlugins(
|
||||
flutterProject,
|
||||
flutterManifest,
|
||||
fs,
|
||||
<String, String>{
|
||||
'url_launcher_macos': '''
|
||||
flutter:
|
||||
plugin:
|
||||
implements: url_launcher
|
||||
platforms:
|
||||
macos:
|
||||
dartPluginClass: MacOSPlugin
|
||||
''',
|
||||
'url_launcher_linux': '''
|
||||
flutter:
|
||||
plugin:
|
||||
implements: url_launcher
|
||||
platforms:
|
||||
linux:
|
||||
dartPluginClass: LinuxPlugin
|
||||
''',
|
||||
'url_launcher_windows': '''
|
||||
flutter:
|
||||
plugin:
|
||||
implements: url_launcher
|
||||
platforms:
|
||||
windows:
|
||||
dartPluginClass: WindowsPlugin
|
||||
''',
|
||||
'awesome_macos': '''
|
||||
flutter:
|
||||
plugin:
|
||||
implements: awesome
|
||||
platforms:
|
||||
macos:
|
||||
dartPluginClass: AwesomeMacOS
|
||||
'''
|
||||
});
|
||||
|
||||
final Directory libDir = flutterProject.directory.childDirectory('lib');
|
||||
libDir.createSync(recursive: true);
|
||||
|
||||
final File mainFile = libDir.childFile('main.dart');
|
||||
mainFile.writeAsStringSync('''
|
||||
// @dart = 2.8
|
||||
void main() {
|
||||
}
|
||||
''');
|
||||
final File generatedMainFile = flutterProject.directory.childFile('generated_main.dart');
|
||||
final PackageConfig packageConfig = await loadPackageConfigWithLogging(
|
||||
flutterProject.directory.childDirectory('.dart_tool').childFile('package_config.json'),
|
||||
logger: globals.logger,
|
||||
throwOnError: false,
|
||||
);
|
||||
await generateMainDartWithPluginRegistrant(
|
||||
flutterProject,
|
||||
packageConfig,
|
||||
'package:app/main.dart',
|
||||
generatedMainFile,
|
||||
mainFile,
|
||||
throwOnPluginPubspecError: true,
|
||||
);
|
||||
expect(generatedMainFile.readAsStringSync(),
|
||||
'//\n'
|
||||
'// Generated file. Do not edit.\n'
|
||||
'// This file is generated from template in file `flutter_tools/lib/src/flutter_plugins.dart`.\n'
|
||||
'//\n'
|
||||
'\n'
|
||||
'// @dart = 2.8\n'
|
||||
'\n'
|
||||
'import \'package:app/main.dart\' as entrypoint;\n'
|
||||
'import \'dart:io\'; // flutter_ignore: dart_io_import.\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'
|
||||
'import \'package:url_launcher_windows/url_launcher_windows.dart\';\n'
|
||||
'\n'
|
||||
'@pragma(\'vm:entry-point\')\n'
|
||||
'class _PluginRegistrant {\n'
|
||||
'\n'
|
||||
' @pragma(\'vm:entry-point\')\n'
|
||||
' static void register() {\n'
|
||||
' if (Platform.isLinux) {\n'
|
||||
' try {\n'
|
||||
' LinuxPlugin.registerWith();\n'
|
||||
' } catch (err) {\n'
|
||||
' print(\n'
|
||||
' \'`url_launcher_linux` 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.isMacOS) {\n'
|
||||
' try {\n'
|
||||
' AwesomeMacOS.registerWith();\n'
|
||||
' } catch (err) {\n'
|
||||
' print(\n'
|
||||
' \'`awesome_macos` 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'
|
||||
' try {\n'
|
||||
' MacOSPlugin.registerWith();\n'
|
||||
' } catch (err) {\n'
|
||||
' print(\n'
|
||||
' \'`url_launcher_macos` 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.isWindows) {\n'
|
||||
' try {\n'
|
||||
' WindowsPlugin.registerWith();\n'
|
||||
' } catch (err) {\n'
|
||||
' print(\n'
|
||||
' \'`url_launcher_windows` 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'
|
||||
' }\n'
|
||||
' }\n'
|
||||
'\n'
|
||||
'}\n'
|
||||
'\n'
|
||||
'typedef _UnaryFunction = dynamic Function(List<String> args);\n'
|
||||
'typedef _NullaryFunction = dynamic Function();\n'
|
||||
'\n'
|
||||
'void main(List<String> args) {\n'
|
||||
' if (entrypoint.main is _UnaryFunction) {\n'
|
||||
' (entrypoint.main as _UnaryFunction)(args);\n'
|
||||
' } else {\n'
|
||||
' (entrypoint.main as _NullaryFunction)();\n'
|
||||
' }\n'
|
||||
'}\n'
|
||||
'',
|
||||
);
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => fs,
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
});
|
||||
|
||||
testUsingContext('Plugin without platform support throws tool exit', () async {
|
||||
when(flutterProject.isModule).thenReturn(false);
|
||||
|
||||
createFakeDartPlugins(
|
||||
flutterProject,
|
||||
flutterManifest,
|
||||
fs,
|
||||
<String, String>{
|
||||
'url_launcher_macos': '''
|
||||
flutter:
|
||||
plugin:
|
||||
implements: url_launcher
|
||||
platforms:
|
||||
macos:
|
||||
invalid:
|
||||
'''
|
||||
});
|
||||
|
||||
final Directory libDir = flutterProject.directory.childDirectory('lib');
|
||||
libDir.createSync(recursive: true);
|
||||
|
||||
final File mainFile = libDir.childFile('main.dart')..writeAsStringSync('');
|
||||
final File generatedMainFile = flutterProject.directory.childFile('generated_main.dart');
|
||||
final PackageConfig packageConfig = await loadPackageConfigWithLogging(
|
||||
flutterProject.directory.childDirectory('.dart_tool').childFile('package_config.json'),
|
||||
logger: globals.logger,
|
||||
throwOnError: false,
|
||||
);
|
||||
await expectLater(
|
||||
generateMainDartWithPluginRegistrant(
|
||||
flutterProject,
|
||||
packageConfig,
|
||||
'package:app/main.dart',
|
||||
generatedMainFile,
|
||||
mainFile,
|
||||
throwOnPluginPubspecError: true,
|
||||
), throwsToolExit(message:
|
||||
'Invalid plugin specification url_launcher_macos.\n'
|
||||
'Invalid "macos" plugin specification.'
|
||||
),
|
||||
);
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => fs,
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
});
|
||||
|
||||
testUsingContext('Plugin with platform support without dart plugin class throws tool exit', () async {
|
||||
when(flutterProject.isModule).thenReturn(false);
|
||||
|
||||
createFakeDartPlugins(
|
||||
flutterProject,
|
||||
flutterManifest,
|
||||
fs,
|
||||
<String, String>{
|
||||
'url_launcher_macos': '''
|
||||
flutter:
|
||||
plugin:
|
||||
implements: url_launcher
|
||||
'''
|
||||
});
|
||||
|
||||
final Directory libDir = flutterProject.directory.childDirectory('lib');
|
||||
libDir.createSync(recursive: true);
|
||||
|
||||
final File mainFile = libDir.childFile('main.dart')..writeAsStringSync('');
|
||||
final File generatedMainFile = flutterProject.directory.childFile('generated_main.dart');
|
||||
final PackageConfig packageConfig = await loadPackageConfigWithLogging(
|
||||
flutterProject.directory.childDirectory('.dart_tool').childFile('package_config.json'),
|
||||
logger: globals.logger,
|
||||
throwOnError: false,
|
||||
);
|
||||
await expectLater(
|
||||
generateMainDartWithPluginRegistrant(
|
||||
flutterProject,
|
||||
packageConfig,
|
||||
'package:app/main.dart',
|
||||
generatedMainFile,
|
||||
mainFile,
|
||||
throwOnPluginPubspecError: true,
|
||||
), throwsToolExit(message:
|
||||
'Invalid plugin specification url_launcher_macos.\n'
|
||||
'Cannot find the `flutter.plugin.platforms` key in the `pubspec.yaml` file. '
|
||||
'An instruction to format the `pubspec.yaml` can be found here: '
|
||||
'https://flutter.dev/docs/development/packages-and-plugins/developing-packages#plugin-platforms'
|
||||
),
|
||||
);
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => fs,
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
});
|
||||
|
||||
testUsingContext('Does not show error messages if throwOnPluginPubspecError is false', () async {
|
||||
final Set<String> directDependencies = <String>{
|
||||
'url_launcher_windows',
|
||||
};
|
||||
resolvePlatformImplementation(<Plugin>[
|
||||
Plugin.fromYaml(
|
||||
'url_launcher_windows',
|
||||
'',
|
||||
YamlMap.wrap(<String, dynamic>{
|
||||
'platforms': <String, dynamic>{
|
||||
'windows': <String, dynamic>{
|
||||
'dartPluginClass': 'UrlLauncherPluginWindows',
|
||||
},
|
||||
},
|
||||
}),
|
||||
<String>[],
|
||||
fileSystem: fs,
|
||||
appDependencies: directDependencies,
|
||||
),
|
||||
],
|
||||
throwOnPluginPubspecError: false,
|
||||
);
|
||||
expect(testLogger.errorText, '');
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => fs,
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
});
|
||||
|
||||
testUsingContext('Does not create new entrypoint if there are no platform resolutions', () async {
|
||||
when(flutterProject.isModule).thenReturn(false);
|
||||
when(flutterManifest.dependencies).thenReturn(<String>{});
|
||||
|
||||
final Directory libDir = flutterProject.directory.childDirectory('lib');
|
||||
libDir.createSync(recursive: true);
|
||||
|
||||
final File mainFile = libDir.childFile('main.dart')..writeAsStringSync('');
|
||||
final File generatedMainFile = flutterProject.directory.childFile('generated_main.dart');
|
||||
final PackageConfig packageConfig = await loadPackageConfigWithLogging(
|
||||
flutterProject.directory.childDirectory('.dart_tool').childFile('package_config.json'),
|
||||
logger: globals.logger,
|
||||
throwOnError: false,
|
||||
);
|
||||
await generateMainDartWithPluginRegistrant(
|
||||
flutterProject,
|
||||
packageConfig,
|
||||
'package:app/main.dart',
|
||||
generatedMainFile,
|
||||
mainFile,
|
||||
throwOnPluginPubspecError: true,
|
||||
);
|
||||
expect(generatedMainFile.existsSync(), isFalse);
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => fs,
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
});
|
||||
|
||||
testUsingContext('Deletes new entrypoint if there are no platform resolutions', () async {
|
||||
when(flutterProject.isModule).thenReturn(false);
|
||||
|
||||
createFakeDartPlugins(
|
||||
flutterProject,
|
||||
flutterManifest,
|
||||
fs,
|
||||
<String, String>{
|
||||
'url_launcher_macos': '''
|
||||
flutter:
|
||||
plugin:
|
||||
implements: url_launcher
|
||||
platforms:
|
||||
macos:
|
||||
dartPluginClass: MacOSPlugin
|
||||
'''
|
||||
});
|
||||
|
||||
final Directory libDir = flutterProject.directory.childDirectory('lib');
|
||||
libDir.createSync(recursive: true);
|
||||
|
||||
final File mainFile = libDir.childFile('main.dart')..writeAsStringSync('');
|
||||
final File generatedMainFile = flutterProject.directory.childFile('generated_main.dart');
|
||||
final PackageConfig packageConfig = await loadPackageConfigWithLogging(
|
||||
flutterProject.directory.childDirectory('.dart_tool').childFile('package_config.json'),
|
||||
logger: globals.logger,
|
||||
throwOnError: false,
|
||||
);
|
||||
await generateMainDartWithPluginRegistrant(
|
||||
flutterProject,
|
||||
packageConfig,
|
||||
'package:app/main.dart',
|
||||
generatedMainFile,
|
||||
mainFile,
|
||||
throwOnPluginPubspecError: true,
|
||||
);
|
||||
expect(generatedMainFile.existsSync(), isTrue);
|
||||
|
||||
// No plugins.
|
||||
createFakeDartPlugins(
|
||||
flutterProject,
|
||||
flutterManifest,
|
||||
fs,
|
||||
<String, String>{});
|
||||
|
||||
await generateMainDartWithPluginRegistrant(
|
||||
flutterProject,
|
||||
packageConfig,
|
||||
'package:app/main.dart',
|
||||
generatedMainFile,
|
||||
mainFile,
|
||||
throwOnPluginPubspecError: true,
|
||||
);
|
||||
expect(generatedMainFile.existsSync(), isFalse);
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => fs,
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
class MockFlutterManifest extends Mock implements FlutterManifest {}
|
||||
class MockFlutterProject extends Mock implements FlutterProject {}
|
||||
@ -170,6 +170,8 @@ void main() {
|
||||
any,
|
||||
outputPath: anyNamed('outputPath'),
|
||||
packageConfig: anyNamed('packageConfig'),
|
||||
projectRootPath: anyNamed('projectRootPath'),
|
||||
fs: anyNamed('fs'),
|
||||
)).thenAnswer((Invocation invocation) async {
|
||||
fileSystem.file('lib/foo.dill')
|
||||
..createSync(recursive: true)
|
||||
@ -237,6 +239,8 @@ void main() {
|
||||
any,
|
||||
outputPath: anyNamed('outputPath'),
|
||||
packageConfig: anyNamed('packageConfig'),
|
||||
projectRootPath: anyNamed('projectRootPath'),
|
||||
fs: anyNamed('fs'),
|
||||
)).thenAnswer((Invocation invocation) async {
|
||||
return const CompilerOutput('lib/foo.dill', 2, <Uri>[]);
|
||||
});
|
||||
@ -281,6 +285,8 @@ void main() {
|
||||
any,
|
||||
outputPath: anyNamed('outputPath'),
|
||||
packageConfig: anyNamed('packageConfig'),
|
||||
projectRootPath: anyNamed('projectRootPath'),
|
||||
fs: anyNamed('fs'),
|
||||
)).thenAnswer((Invocation invocation) async {
|
||||
fileSystem.file('example').createSync();
|
||||
return const CompilerOutput('lib/foo.txt.dill', 0, <Uri>[]);
|
||||
@ -327,6 +333,8 @@ void main() {
|
||||
any,
|
||||
outputPath: anyNamed('outputPath'),
|
||||
packageConfig: anyNamed('packageConfig'),
|
||||
projectRootPath: anyNamed('projectRootPath'),
|
||||
fs: anyNamed('fs'),
|
||||
)).thenAnswer((Invocation invocation) async {
|
||||
fileSystem.file('lib/foo.txt.dill').createSync(recursive: true);
|
||||
return const CompilerOutput('lib/foo.txt.dill', 0, <Uri>[]);
|
||||
@ -379,6 +387,8 @@ void main() {
|
||||
any,
|
||||
outputPath: anyNamed('outputPath'),
|
||||
packageConfig: anyNamed('packageConfig'),
|
||||
projectRootPath: anyNamed('projectRootPath'),
|
||||
fs: anyNamed('fs'),
|
||||
)).thenAnswer((Invocation invocation) async {
|
||||
fileSystem.file('example').createSync();
|
||||
return const CompilerOutput('lib/foo.txt.dill', 0, <Uri>[]);
|
||||
|
||||
@ -1444,6 +1444,19 @@ flutter:
|
||||
expect(deferredComponents[0].assets[1].path, 'path/to/asset2.jpg');
|
||||
expect(deferredComponents[0].assets[2].path, 'path/to/asset3.jpg');
|
||||
});
|
||||
|
||||
testWithoutContext('FlutterManifest can parse empty dependencies', () async {
|
||||
const String manifest = '''
|
||||
name: test
|
||||
''';
|
||||
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
||||
manifest,
|
||||
logger: BufferLogger.test(),
|
||||
);
|
||||
|
||||
expect(flutterManifest, isNotNull);
|
||||
expect(flutterManifest!.dependencies, isEmpty);
|
||||
});
|
||||
}
|
||||
|
||||
Matcher matchesManifest({
|
||||
|
||||
@ -218,6 +218,39 @@ void main() {
|
||||
);
|
||||
|
||||
expect(plugin.platforms, <String, PluginPlatform>{});
|
||||
expect(plugin.defaultPackagePlatforms, <String, String>{
|
||||
'linux': 'sample_package_linux',
|
||||
'macos': 'sample_package_macos',
|
||||
'windows': 'sample_package_windows',
|
||||
});
|
||||
expect(plugin.pluginDartClassPlatforms, <String, String>{});
|
||||
});
|
||||
|
||||
testWithoutContext('Desktop plugin parsing allows a dartPluginClass field', () {
|
||||
final FileSystem fileSystem = MemoryFileSystem.test();
|
||||
const String pluginYamlRaw =
|
||||
'platforms:\n'
|
||||
' linux:\n'
|
||||
' dartPluginClass: LinuxClass\n'
|
||||
' macos:\n'
|
||||
' dartPluginClass: MacOSClass\n'
|
||||
' windows:\n'
|
||||
' dartPluginClass: WindowsClass\n';
|
||||
|
||||
final YamlMap pluginYaml = loadYaml(pluginYamlRaw) as YamlMap;
|
||||
final Plugin plugin = Plugin.fromYaml(
|
||||
_kTestPluginName,
|
||||
_kTestPluginPath,
|
||||
pluginYaml,
|
||||
const <String>[],
|
||||
fileSystem: fileSystem,
|
||||
);
|
||||
|
||||
expect(plugin.pluginDartClassPlatforms, <String, String>{
|
||||
'linux': 'LinuxClass',
|
||||
'macos': 'MacOSClass',
|
||||
'windows': 'WindowsClass',
|
||||
});
|
||||
});
|
||||
|
||||
testWithoutContext('Plugin parsing throws a fatal error on an empty plugin', () {
|
||||
|
||||
@ -14,6 +14,7 @@ import 'package:flutter_tools/src/base/platform.dart';
|
||||
import 'package:flutter_tools/src/base/time.dart';
|
||||
import 'package:flutter_tools/src/base/utils.dart';
|
||||
import 'package:flutter_tools/src/features.dart';
|
||||
import 'package:flutter_tools/src/flutter_manifest.dart';
|
||||
import 'package:flutter_tools/src/flutter_plugins.dart';
|
||||
import 'package:flutter_tools/src/globals_null_migrated.dart' as globals;
|
||||
import 'package:flutter_tools/src/ios/xcodeproj.dart';
|
||||
@ -33,6 +34,7 @@ void main() {
|
||||
group('plugins', () {
|
||||
FileSystem fs;
|
||||
MockFlutterProject flutterProject;
|
||||
MockFlutterManifest flutterManifest;
|
||||
MockIosProject iosProject;
|
||||
MockMacOSProject macosProject;
|
||||
MockAndroidProject androidProject;
|
||||
@ -49,6 +51,12 @@ void main() {
|
||||
// Adds basic properties to the flutterProject and its subprojects.
|
||||
void setUpProject(FileSystem fileSystem) {
|
||||
flutterProject = MockFlutterProject();
|
||||
|
||||
flutterManifest = MockFlutterManifest();
|
||||
when(flutterManifest.dependencies).thenReturn(<String>{});
|
||||
|
||||
when(flutterProject.manifest).thenReturn(flutterManifest);
|
||||
|
||||
when(flutterProject.directory).thenReturn(fileSystem.systemTempDirectory.childDirectory('app'));
|
||||
// TODO(franciscojma): Remove logic for .flutter-plugins once it's deprecated.
|
||||
when(flutterProject.flutterPluginsFile).thenReturn(flutterProject.directory.childFile('.flutter-plugins'));
|
||||
@ -1412,6 +1420,7 @@ flutter:
|
||||
}
|
||||
|
||||
class MockAndroidProject extends Mock implements AndroidProject {}
|
||||
class MockFlutterManifest extends Mock implements FlutterManifest {}
|
||||
class MockFlutterProject extends Mock implements FlutterProject {}
|
||||
class MockIosProject extends Mock implements IosProject {}
|
||||
class MockMacOSProject extends Mock implements MacOSProject {}
|
||||
|
||||
@ -110,6 +110,17 @@ void main() {
|
||||
);
|
||||
});
|
||||
|
||||
_testInMemory('reads dependencies from pubspec.yaml', () async {
|
||||
final Directory directory = globals.fs.directory('myproject');
|
||||
directory.childFile('pubspec.yaml')
|
||||
..createSync(recursive: true)
|
||||
..writeAsStringSync(validPubspecWithDependencies);
|
||||
expect(
|
||||
FlutterProject.fromDirectory(directory).manifest.dependencies,
|
||||
<String>{'plugin_a', 'plugin_b'},
|
||||
);
|
||||
});
|
||||
|
||||
_testInMemory('sets up location', () async {
|
||||
final Directory directory = globals.fs.directory('myproject');
|
||||
expect(
|
||||
@ -920,6 +931,16 @@ name: hello
|
||||
flutter:
|
||||
''';
|
||||
|
||||
String get validPubspecWithDependencies => '''
|
||||
name: hello
|
||||
flutter:
|
||||
|
||||
dependencies:
|
||||
plugin_a:
|
||||
plugin_b:
|
||||
''';
|
||||
|
||||
|
||||
String get invalidPubspec => '''
|
||||
name: hello
|
||||
flutter:
|
||||
|
||||
@ -271,6 +271,8 @@ void main() {
|
||||
outputPath: anyNamed('outputPath'),
|
||||
packageConfig: anyNamed('packageConfig'),
|
||||
suppressErrors: true,
|
||||
projectRootPath: anyNamed('projectRootPath'),
|
||||
fs: anyNamed('fs'),
|
||||
)).thenAnswer((Invocation invocation) async {
|
||||
return const CompilerOutput('foo', 0 ,<Uri>[]);
|
||||
});
|
||||
@ -288,6 +290,8 @@ void main() {
|
||||
outputPath: anyNamed('outputPath'),
|
||||
packageConfig: anyNamed('packageConfig'),
|
||||
suppressErrors: true,
|
||||
projectRootPath: anyNamed('projectRootPath'),
|
||||
fs: anyNamed('fs'),
|
||||
)).called(1);
|
||||
expect(fakeVmServiceHost.hasRemainingExpectations, false);
|
||||
}), overrides: <Type, Generator>{
|
||||
@ -316,6 +320,8 @@ void main() {
|
||||
outputPath: anyNamed('outputPath'),
|
||||
packageConfig: anyNamed('packageConfig'),
|
||||
suppressErrors: true,
|
||||
projectRootPath: anyNamed('projectRootPath'),
|
||||
fs: anyNamed('fs'),
|
||||
)).thenAnswer((Invocation invocation) async {
|
||||
return const CompilerOutput('foo', 1 ,<Uri>[]);
|
||||
});
|
||||
@ -408,6 +414,8 @@ void main() {
|
||||
any,
|
||||
outputPath: anyNamed('outputPath'),
|
||||
packageConfig: anyNamed('packageConfig'),
|
||||
projectRootPath: anyNamed('projectRootPath'),
|
||||
fs: anyNamed('fs'),
|
||||
suppressErrors: false,
|
||||
)).thenAnswer((Invocation invocation) async {
|
||||
return const CompilerOutput('foo', 0, <Uri>[]);
|
||||
@ -426,6 +434,8 @@ void main() {
|
||||
outputPath: anyNamed('outputPath'),
|
||||
packageConfig: anyNamed('packageConfig'),
|
||||
suppressErrors: false,
|
||||
projectRootPath: anyNamed('projectRootPath'),
|
||||
fs: anyNamed('fs'),
|
||||
)).called(1);
|
||||
expect(fakeVmServiceHost.hasRemainingExpectations, false);
|
||||
}), overrides: <Type, Generator>{
|
||||
@ -1438,6 +1448,26 @@ void main() {
|
||||
globals.fs.file('l10n.yaml').createSync();
|
||||
globals.fs.file('pubspec.yaml').writeAsStringSync('flutter:\n generate: true\n');
|
||||
|
||||
// Create necessary files for [DartPluginRegistrantTarget]
|
||||
final File packageConfig = globals.fs.directory('.dart_tool')
|
||||
.childFile('package_config.json');
|
||||
packageConfig.createSync(recursive: true);
|
||||
packageConfig.writeAsStringSync('''
|
||||
{
|
||||
"configVersion": 2,
|
||||
"packages": [
|
||||
{
|
||||
"name": "path_provider_linux",
|
||||
"rootUri": "../../../path_provider_linux",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "2.12"
|
||||
}
|
||||
]
|
||||
}
|
||||
''');
|
||||
// Start from an empty generated_main.dart file.
|
||||
globals.fs.directory('.dart_tool').childDirectory('flutter_build').childFile('generated_main.dart').createSync(recursive: true);
|
||||
|
||||
await residentRunner.runSourceGenerators();
|
||||
|
||||
expect(testLogger.errorText, isEmpty);
|
||||
@ -2515,6 +2545,8 @@ class FakeResidentCompiler extends Fake implements ResidentCompiler {
|
||||
List<Uri> invalidatedFiles, {
|
||||
@required String outputPath,
|
||||
@required PackageConfig packageConfig,
|
||||
@required String projectRootPath,
|
||||
@required FileSystem fs,
|
||||
bool suppressErrors = false,
|
||||
}) async {
|
||||
return const CompilerOutput('foo.dill', 0, <Uri>[]);
|
||||
|
||||
@ -55,6 +55,8 @@ void main() {
|
||||
<Uri>[Uri.parse('test/foo.dart')],
|
||||
outputPath: testCompiler.outputDill.path,
|
||||
packageConfig: anyNamed('packageConfig'),
|
||||
projectRootPath: anyNamed('projectRootPath'),
|
||||
fs: anyNamed('fs'),
|
||||
)).thenAnswer((Invocation invocation) async {
|
||||
fileSystem.file('abc.dill').createSync();
|
||||
return const CompilerOutput('abc.dill', 0, <Uri>[]);
|
||||
@ -80,6 +82,8 @@ void main() {
|
||||
<Uri>[Uri.parse('test/foo.dart')],
|
||||
outputPath: testCompiler.outputDill.path,
|
||||
packageConfig: anyNamed('packageConfig'),
|
||||
projectRootPath: anyNamed('projectRootPath'),
|
||||
fs: anyNamed('fs'),
|
||||
)).thenAnswer((Invocation invocation) async {
|
||||
fileSystem.file('abc.dill').createSync();
|
||||
return const CompilerOutput('abc.dill', 1, <Uri>[]);
|
||||
|
||||
@ -605,6 +605,8 @@ void main() {
|
||||
any,
|
||||
outputPath: anyNamed('outputPath'),
|
||||
packageConfig: anyNamed('packageConfig'),
|
||||
projectRootPath: anyNamed('projectRootPath'),
|
||||
fs: anyNamed('fs'),
|
||||
)).thenAnswer((Invocation invocation) async {
|
||||
return const CompilerOutput('a', 0, <Uri>[]);
|
||||
});
|
||||
@ -723,6 +725,8 @@ void main() {
|
||||
any,
|
||||
outputPath: anyNamed('outputPath'),
|
||||
packageConfig: anyNamed('packageConfig'),
|
||||
projectRootPath: anyNamed('projectRootPath'),
|
||||
fs: anyNamed('fs'),
|
||||
)).thenAnswer((Invocation invocation) async {
|
||||
return const CompilerOutput('a', 0, <Uri>[]);
|
||||
});
|
||||
@ -838,6 +842,8 @@ void main() {
|
||||
any,
|
||||
outputPath: anyNamed('outputPath'),
|
||||
packageConfig: anyNamed('packageConfig'),
|
||||
projectRootPath: anyNamed('projectRootPath'),
|
||||
fs: anyNamed('fs'),
|
||||
)).thenAnswer((Invocation invocation) async {
|
||||
return const CompilerOutput('a', 0, <Uri>[]);
|
||||
});
|
||||
@ -883,6 +889,8 @@ void main() {
|
||||
any,
|
||||
outputPath: anyNamed('outputPath'),
|
||||
packageConfig: anyNamed('packageConfig'),
|
||||
projectRootPath: anyNamed('projectRootPath'),
|
||||
fs: anyNamed('fs'),
|
||||
)).thenAnswer((Invocation invocation) async {
|
||||
return const CompilerOutput('a', 0, <Uri>[]);
|
||||
});
|
||||
@ -936,6 +944,8 @@ void main() {
|
||||
any,
|
||||
outputPath: anyNamed('outputPath'),
|
||||
packageConfig: anyNamed('packageConfig'),
|
||||
projectRootPath: anyNamed('projectRootPath'),
|
||||
fs: anyNamed('fs'),
|
||||
)).thenAnswer((Invocation invocation) async {
|
||||
return const CompilerOutput('a', 0, <Uri>[]);
|
||||
});
|
||||
@ -1056,6 +1066,8 @@ void main() {
|
||||
any,
|
||||
outputPath: anyNamed('outputPath'),
|
||||
packageConfig: anyNamed('packageConfig'),
|
||||
projectRootPath: anyNamed('projectRootPath'),
|
||||
fs: anyNamed('fs'),
|
||||
)).thenAnswer((Invocation invocation) async {
|
||||
return const CompilerOutput('a', 0, <Uri>[]);
|
||||
});
|
||||
|
||||
@ -44,8 +44,11 @@ void main() {
|
||||
Plugin(
|
||||
name: 'test',
|
||||
path: 'foo',
|
||||
defaultPackagePlatforms: const <String, String>{},
|
||||
pluginDartClassPlatforms: const <String, String>{},
|
||||
platforms: const <String, PluginPlatform>{WindowsPlugin.kConfigKey: WindowsPlugin(name: 'test', pluginClass: 'Foo')},
|
||||
dependencies: <String>[],
|
||||
isDirectDependency: true,
|
||||
),
|
||||
], renderer);
|
||||
|
||||
@ -67,8 +70,11 @@ void main() {
|
||||
Plugin(
|
||||
name: 'test',
|
||||
path: 'foo',
|
||||
defaultPackagePlatforms: const <String, String>{},
|
||||
pluginDartClassPlatforms: const <String, String>{},
|
||||
platforms: const <String, PluginPlatform>{WindowsPlugin.kConfigKey: WindowsPlugin(name: 'test', pluginClass: 'Foo')},
|
||||
dependencies: <String>[],
|
||||
isDirectDependency: true,
|
||||
),
|
||||
], renderer);
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user