Skip to content

Commit b7d4806

Browse files
author
Emmanuel Garcia
authored
Implement dartPluginClass support for plugins (flutter#74469)
1 parent d9fca66 commit b7d4806

21 files changed

Lines changed: 1466 additions & 21 deletions
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Copyright 2014 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'package:flutter_devicelab/tasks/dart_plugin_registry_tests.dart';
6+
import 'package:flutter_devicelab/framework/framework.dart';
7+
8+
Future<void> main() async {
9+
await task(dartPluginRegistryTest());
10+
}
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
// Copyright 2014 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'dart:async';
6+
import 'dart:convert';
7+
import 'dart:io';
8+
9+
import 'package:path/path.dart' as path;
10+
import 'package:flutter_devicelab/framework/framework.dart';
11+
import 'package:flutter_devicelab/framework/task_result.dart';
12+
import 'package:flutter_devicelab/framework/utils.dart';
13+
14+
TaskFunction dartPluginRegistryTest({
15+
String deviceIdOverride,
16+
Map<String, String> environment,
17+
}) {
18+
final Directory tempDir = Directory.systemTemp
19+
.createTempSync('flutter_devicelab_dart_plugin_test.');
20+
return () async {
21+
try {
22+
section('Create implementation plugin');
23+
await inDirectory(tempDir, () async {
24+
await flutter(
25+
'create',
26+
options: <String>[
27+
'--template=plugin',
28+
'--org',
29+
'io.flutter.devicelab',
30+
'--platforms',
31+
'macos',
32+
'plugin_platform_implementation',
33+
],
34+
environment: environment,
35+
);
36+
});
37+
38+
final File pluginMain = File(path.join(
39+
tempDir.absolute.path,
40+
'plugin_platform_implementation',
41+
'lib',
42+
'plugin_platform_implementation.dart',
43+
));
44+
if (!pluginMain.existsSync()) {
45+
return TaskResult.failure('${pluginMain.path} does not exist');
46+
}
47+
48+
// Patch plugin main dart file.
49+
await pluginMain.writeAsString('''
50+
class PluginPlatformInterfaceMacOS {
51+
static void registerWith() {
52+
print('PluginPlatformInterfaceMacOS.registerWith() was called');
53+
}
54+
}
55+
''', flush: true);
56+
57+
// Patch plugin main pubspec file.
58+
final File pluginImplPubspec = File(path.join(
59+
tempDir.absolute.path,
60+
'plugin_platform_implementation',
61+
'pubspec.yaml',
62+
));
63+
String pluginImplPubspecContent = await pluginImplPubspec.readAsString();
64+
pluginImplPubspecContent = pluginImplPubspecContent.replaceFirst(
65+
' pluginClass: PluginPlatformImplementationPlugin',
66+
' pluginClass: PluginPlatformImplementationPlugin\n'
67+
' dartPluginClass: PluginPlatformInterfaceMacOS\n',
68+
);
69+
pluginImplPubspecContent = pluginImplPubspecContent.replaceFirst(
70+
' platforms:\n',
71+
' implements: plugin_platform_interface\n'
72+
' platforms:\n');
73+
await pluginImplPubspec.writeAsString(pluginImplPubspecContent,
74+
flush: true);
75+
76+
section('Create interface plugin');
77+
await inDirectory(tempDir, () async {
78+
await flutter(
79+
'create',
80+
options: <String>[
81+
'--template=plugin',
82+
'--org',
83+
'io.flutter.devicelab',
84+
'--platforms',
85+
'macos',
86+
'plugin_platform_interface',
87+
],
88+
environment: environment,
89+
);
90+
});
91+
final File pluginInterfacePubspec = File(path.join(
92+
tempDir.absolute.path,
93+
'plugin_platform_interface',
94+
'pubspec.yaml',
95+
));
96+
String pluginInterfacePubspecContent =
97+
await pluginInterfacePubspec.readAsString();
98+
pluginInterfacePubspecContent =
99+
pluginInterfacePubspecContent.replaceFirst(
100+
' pluginClass: PluginPlatformInterfacePlugin',
101+
' default_package: plugin_platform_implementation\n');
102+
pluginInterfacePubspecContent =
103+
pluginInterfacePubspecContent.replaceFirst(
104+
'dependencies:',
105+
'dependencies:\n'
106+
' plugin_platform_implementation:\n'
107+
' path: ../plugin_platform_implementation\n');
108+
await pluginInterfacePubspec.writeAsString(pluginInterfacePubspecContent,
109+
flush: true);
110+
111+
section('Create app');
112+
113+
await inDirectory(tempDir, () async {
114+
await flutter(
115+
'create',
116+
options: <String>[
117+
'--template=app',
118+
'--org',
119+
'io.flutter.devicelab',
120+
'--platforms',
121+
'macos',
122+
'app',
123+
],
124+
environment: environment,
125+
);
126+
});
127+
128+
final File appPubspec = File(path.join(
129+
tempDir.absolute.path,
130+
'app',
131+
'pubspec.yaml',
132+
));
133+
String appPubspecContent = await appPubspec.readAsString();
134+
appPubspecContent = appPubspecContent.replaceFirst(
135+
'dependencies:',
136+
'dependencies:\n'
137+
' plugin_platform_interface:\n'
138+
' path: ../plugin_platform_interface\n');
139+
await appPubspec.writeAsString(appPubspecContent, flush: true);
140+
141+
section('Flutter run for macos');
142+
143+
await inDirectory(path.join(tempDir.path, 'app'), () async {
144+
final Process run = await startProcess(
145+
path.join(flutterDirectory.path, 'bin', 'flutter'),
146+
flutterCommandArgs('run', <String>['-d', 'macos', '-v']),
147+
environment: null,
148+
);
149+
Completer<void> registryExecutedCompleter = Completer<void>();
150+
final StreamSubscription<void> subscription = run.stdout
151+
.transform<String>(utf8.decoder)
152+
.transform<String>(const LineSplitter())
153+
.listen((String line) {
154+
if (line.contains(
155+
'PluginPlatformInterfaceMacOS.registerWith() was called')) {
156+
registryExecutedCompleter.complete();
157+
}
158+
print('stdout: $line');
159+
});
160+
161+
section('Wait for registry execution');
162+
await registryExecutedCompleter.future
163+
.timeout(const Duration(minutes: 1));
164+
165+
// Hot restart.
166+
run.stdin.write('R');
167+
registryExecutedCompleter = Completer<void>();
168+
169+
section('Wait for registry execution after hot restart');
170+
await registryExecutedCompleter.future
171+
.timeout(const Duration(minutes: 1));
172+
173+
subscription.cancel();
174+
run.kill();
175+
});
176+
return TaskResult.success(null);
177+
} finally {
178+
rmTree(tempDir);
179+
}
180+
};
181+
}

packages/flutter_tools/lib/src/build_system/build_system.dart

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,7 @@ class Environment {
308308
@required Artifacts artifacts,
309309
@required ProcessManager processManager,
310310
@required String engineVersion,
311+
@required bool generateDartPluginRegistry,
311312
Directory buildDir,
312313
Map<String, String> defines = const <String, String>{},
313314
Map<String, String> inputs = const <String, String>{},
@@ -347,6 +348,7 @@ class Environment {
347348
processManager: processManager,
348349
engineVersion: engineVersion,
349350
inputs: inputs,
351+
generateDartPluginRegistry: generateDartPluginRegistry,
350352
);
351353
}
352354

@@ -363,6 +365,7 @@ class Environment {
363365
Map<String, String> defines = const <String, String>{},
364366
Map<String, String> inputs = const <String, String>{},
365367
String engineVersion,
368+
bool generateDartPluginRegistry = false,
366369
@required FileSystem fileSystem,
367370
@required Logger logger,
368371
@required Artifacts artifacts,
@@ -381,6 +384,7 @@ class Environment {
381384
artifacts: artifacts,
382385
processManager: processManager,
383386
engineVersion: engineVersion,
387+
generateDartPluginRegistry: generateDartPluginRegistry,
384388
);
385389
}
386390

@@ -398,6 +402,7 @@ class Environment {
398402
@required this.artifacts,
399403
@required this.engineVersion,
400404
@required this.inputs,
405+
@required this.generateDartPluginRegistry,
401406
});
402407

403408
/// The [Source] value which is substituted with the path to [projectDir].
@@ -475,6 +480,11 @@ class Environment {
475480

476481
/// The version of the current engine, or `null` if built with a local engine.
477482
final String engineVersion;
483+
484+
/// Whether to generate the Dart plugin registry.
485+
/// When [true], the main entrypoint is wrapped and the wrapper becomes
486+
/// the new entrypoint.
487+
final bool generateDartPluginRegistry;
478488
}
479489

480490
/// The result information from the build system.

packages/flutter_tools/lib/src/build_system/targets/common.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,8 @@ class KernelSnapshot extends Target {
279279
fileSystemScheme: fileSystemScheme,
280280
dartDefines: decodeDartDefines(environment.defines, kDartDefines),
281281
packageConfig: packageConfig,
282+
buildDir: environment.buildDir,
283+
generateDartPluginRegistry: environment.generateDartPluginRegistry,
282284
);
283285
if (output == null || output.errorCount != 0) {
284286
throw Exception();

packages/flutter_tools/lib/src/bundle.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ Future<void> buildWithAssemble({
160160
fileSystem: globals.fs,
161161
logger: globals.logger,
162162
processManager: globals.processManager,
163+
generateDartPluginRegistry: true,
163164
);
164165
final Target target = buildMode == BuildMode.debug
165166
? const CopyFlutterBundle()

packages/flutter_tools/lib/src/commands/assemble.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,8 @@ class AssembleCommand extends FlutterCommand {
195195
processManager: globals.processManager,
196196
engineVersion: globals.artifacts.isLocalEngine
197197
? null
198-
: globals.flutterVersion.engineRevision
198+
: globals.flutterVersion.engineRevision,
199+
generateDartPluginRegistry: true,
199200
);
200201
return result;
201202
}

packages/flutter_tools/lib/src/commands/build_ios_framework.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,7 @@ end
385385
engineVersion: globals.artifacts.isLocalEngine
386386
? null
387387
: globals.flutterVersion.engineRevision,
388+
generateDartPluginRegistry: true,
388389
);
389390
Target target;
390391
// Always build debug for simulator.

packages/flutter_tools/lib/src/commands/packages.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ class PackagesGetCommand extends FlutterCommand {
119119
outputDir: globals.fs.directory(getBuildDirectory()),
120120
processManager: globals.processManager,
121121
projectDir: flutterProject.directory,
122+
generateDartPluginRegistry: true,
122123
);
123124

124125
await generateLocalizationsSyntheticPackage(
@@ -324,6 +325,7 @@ class PackagesInteractiveGetCommand extends FlutterCommand {
324325
outputDir: globals.fs.directory(getBuildDirectory()),
325326
processManager: globals.processManager,
326327
projectDir: flutterProject.directory,
328+
generateDartPluginRegistry: true,
327329
);
328330

329331
await generateLocalizationsSyntheticPackage(

packages/flutter_tools/lib/src/compile.dart

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ import 'base/logger.dart';
1919
import 'base/platform.dart';
2020
import 'build_info.dart';
2121
import 'convert.dart';
22+
import 'plugins.dart';
23+
import 'project.dart';
2224

2325
/// The target model describes the set of core libraries that are available within
2426
/// the SDK.
@@ -209,6 +211,8 @@ class KernelCompiler {
209211
String fileSystemScheme,
210212
String initializeFromDill,
211213
String platformDill,
214+
Directory buildDir,
215+
bool generateDartPluginRegistry = false,
212216
@required String packagesPath,
213217
@required BuildMode buildMode,
214218
@required bool trackWidgetCreation,
@@ -227,14 +231,30 @@ class KernelCompiler {
227231
throwToolExit('Unable to find Dart binary at $engineDartPath');
228232
}
229233
String mainUri;
230-
final Uri mainFileUri = _fileSystem.file(mainPath).uri;
234+
final File mainFile = _fileSystem.file(mainPath);
235+
final Uri mainFileUri = mainFile.uri;
231236
if (packagesPath != null) {
232237
mainUri = packageConfig.toPackageUri(mainFileUri)?.toString();
233238
}
234239
mainUri ??= toMultiRootPath(mainFileUri, _fileSystemScheme, _fileSystemRoots, _fileSystem.path.separator == r'\');
235240
if (outputFilePath != null && !_fileSystem.isFileSync(outputFilePath)) {
236241
_fileSystem.file(outputFilePath).createSync(recursive: true);
237242
}
243+
if (buildDir != null && generateDartPluginRegistry) {
244+
// `generated_main.dart` is under `.dart_tools/flutter_build/`,
245+
// so the resident compiler can find it.
246+
final File newMainDart = buildDir.parent.childFile('generated_main.dart');
247+
if (await generateMainDartWithPluginRegistrant(
248+
FlutterProject.current(),
249+
packageConfig,
250+
mainUri,
251+
newMainDart,
252+
mainFile,
253+
)) {
254+
mainUri = newMainDart.path;
255+
}
256+
}
257+
238258
final List<String> command = <String>[
239259
engineDartPath,
240260
'--disable-dart-dev',
@@ -579,7 +599,6 @@ class DefaultResidentCompiler implements ResidentCompiler {
579599
if (!_controller.hasListener) {
580600
_controller.stream.listen(_handleCompilationRequest);
581601
}
582-
583602
final Completer<CompilerOutput> completer = Completer<CompilerOutput>();
584603
_controller.add(
585604
_RecompileRequest(completer, mainUri, invalidatedFiles, outputPath, packageConfig, suppressErrors)

packages/flutter_tools/lib/src/devfs.dart

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,20 @@ class DevFS {
518518
// dill files that depend on the invalidated files.
519519
_logger.printTrace('Compiling dart to kernel with ${invalidatedFiles.length} updated files');
520520

521+
// `generated_main.dart` contains the Dart plugin registry.
522+
if (projectRootPath != null) {
523+
final File generatedMainDart = _fileSystem.file(
524+
_fileSystem.path.join(
525+
projectRootPath,
526+
'.dart_tool',
527+
'flutter_build',
528+
'generated_main.dart',
529+
),
530+
);
531+
if (generatedMainDart != null && generatedMainDart.existsSync()) {
532+
mainUri = generatedMainDart.uri;
533+
}
534+
}
521535
// Await the compiler response after checking if the bundle is updated. This allows the file
522536
// stating to be done while waiting for the frontend_server response.
523537
final Future<CompilerOutput> pendingCompilerOutput = generator.recompile(

0 commit comments

Comments
 (0)