Skip to content

Commit d550ba5

Browse files
[flutter_tools] toolexit when using plugins with preview device (flutter#136936)
Part of flutter#130277 Without this, if a user runs an app that has plugins that call method channels with the `preview` device, the app will build successfully, however, they will get a runtime error when their dart code tries to call the method channel that does not exist in the native build (which was pre-built and thus does not include the plugin code). This change adds a validation when injecting plugins that will tool exit if the device-id is `preview` and their project contains plugins with method channels.
1 parent 5ebca79 commit d550ba5

File tree

7 files changed

+76
-9
lines changed

7 files changed

+76
-9
lines changed

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5-
6-
75
import 'dart:async';
86

97
import 'package:meta/meta.dart';

packages/flutter_tools/lib/src/dart/pub.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ abstract class Pub {
150150
String? flutterRootOverride,
151151
bool checkUpToDate = false,
152152
bool shouldSkipThirdPartyGenerator = true,
153-
PubOutputMode outputMode = PubOutputMode.all
153+
PubOutputMode outputMode = PubOutputMode.all,
154154
});
155155

156156
/// Runs pub in 'batch' mode.
@@ -255,7 +255,7 @@ class _DefaultPub implements Pub {
255255
String? flutterRootOverride,
256256
bool checkUpToDate = false,
257257
bool shouldSkipThirdPartyGenerator = true,
258-
PubOutputMode outputMode = PubOutputMode.all
258+
PubOutputMode outputMode = PubOutputMode.all,
259259
}) async {
260260
final String directory = project.directory.path;
261261
final File packageConfigFile = project.packageConfigFile;

packages/flutter_tools/lib/src/flutter_plugins.dart

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -920,8 +920,29 @@ List<Plugin> _filterPluginsByVariant(List<Plugin> plugins, String platformKey, P
920920
}
921921

922922
@visibleForTesting
923-
Future<void> writeWindowsPluginFiles(FlutterProject project, List<Plugin> plugins, TemplateRenderer templateRenderer) async {
923+
Future<void> writeWindowsPluginFiles(
924+
FlutterProject project,
925+
List<Plugin> plugins,
926+
TemplateRenderer templateRenderer, {
927+
Iterable<String>? allowedPlugins,
928+
}) async {
924929
final List<Plugin> methodChannelPlugins = _filterMethodChannelPlugins(plugins, WindowsPlugin.kConfigKey);
930+
if (allowedPlugins != null) {
931+
final List<Plugin> disallowedPlugins = methodChannelPlugins
932+
.toList()
933+
..removeWhere((Plugin plugin) => allowedPlugins.contains(plugin.name));
934+
if (disallowedPlugins.isNotEmpty) {
935+
final StringBuffer buffer = StringBuffer();
936+
buffer.writeln('The Flutter Preview device does not support the following plugins from your pubspec.yaml:');
937+
buffer.writeln();
938+
buffer.writeln(disallowedPlugins.map((Plugin p) => p.name).toList().toString());
939+
buffer.writeln();
940+
buffer.writeln('In order to build a Flutter app with plugins, you must use another target platform,');
941+
buffer.writeln('such as Windows. Type `flutter doctor` into your terminal to see which target platforms');
942+
buffer.writeln('are ready to be used, and how to get required dependencies for other platforms.');
943+
throwToolExit(buffer.toString());
944+
}
945+
}
925946
final List<Plugin> win32Plugins = _filterPluginsByVariant(methodChannelPlugins, WindowsPlugin.kConfigKey, PluginPlatformVariant.win32);
926947
final List<Map<String, Object?>> windowsMethodChannelPlugins = _extractPlatformMaps(win32Plugins, WindowsPlugin.kConfigKey);
927948
final List<Plugin> ffiPlugins = _filterFfiPlugins(plugins, WindowsPlugin.kConfigKey)..removeWhere(methodChannelPlugins.contains);
@@ -1156,6 +1177,7 @@ Future<void> injectPlugins(
11561177
bool linuxPlatform = false,
11571178
bool macOSPlatform = false,
11581179
bool windowsPlatform = false,
1180+
Iterable<String>? allowedPlugins,
11591181
}) async {
11601182
final List<Plugin> plugins = await findPlugins(project);
11611183
// Sort the plugins by name to keep ordering stable in generated files.
@@ -1173,7 +1195,7 @@ Future<void> injectPlugins(
11731195
await _writeMacOSPluginRegistrant(project, plugins);
11741196
}
11751197
if (windowsPlatform) {
1176-
await writeWindowsPluginFiles(project, plugins, globals.templateRenderer);
1198+
await writeWindowsPluginFiles(project, plugins, globals.templateRenderer, allowedPlugins: allowedPlugins);
11771199
}
11781200
if (!project.isModule) {
11791201
final List<XcodeBasedProject> darwinProjects = <XcodeBasedProject>[

packages/flutter_tools/lib/src/preview_device.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,11 @@ class PreviewDevice extends Device {
123123
final Artifacts _artifacts;
124124
final File _previewBinary;
125125

126+
/// The set of plugins that are allowed to be used by Preview users.
127+
///
128+
/// Currently no plugins are supported.
129+
static const List<String> supportedPubPlugins = <String>[];
130+
126131
@override
127132
void clearLogs() { }
128133

packages/flutter_tools/lib/src/project.dart

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,14 @@ class FlutterProject {
329329
/// registrants for app and module projects only.
330330
///
331331
/// Will not create project platform directories if they do not already exist.
332-
Future<void> regeneratePlatformSpecificTooling({DeprecationBehavior deprecationBehavior = DeprecationBehavior.none}) async {
332+
///
333+
/// If [allowedPlugins] is non-null, all plugins with method channels in the
334+
/// project's pubspec.yaml will be validated to be in that set, or else a
335+
/// [ToolExit] will be thrown.
336+
Future<void> regeneratePlatformSpecificTooling({
337+
DeprecationBehavior deprecationBehavior = DeprecationBehavior.none,
338+
Iterable<String>? allowedPlugins,
339+
}) async {
333340
return ensureReadyForPlatformSpecificTooling(
334341
androidPlatform: android.existsSync(),
335342
iosPlatform: ios.existsSync(),
@@ -340,6 +347,7 @@ class FlutterProject {
340347
windowsPlatform: featureFlags.isWindowsEnabled && windows.existsSync(),
341348
webPlatform: featureFlags.isWebEnabled && web.existsSync(),
342349
deprecationBehavior: deprecationBehavior,
350+
allowedPlugins: allowedPlugins,
343351
);
344352
}
345353

@@ -353,6 +361,7 @@ class FlutterProject {
353361
bool windowsPlatform = false,
354362
bool webPlatform = false,
355363
DeprecationBehavior deprecationBehavior = DeprecationBehavior.none,
364+
Iterable<String>? allowedPlugins,
356365
}) async {
357366
if (!directory.existsSync() || isPlugin) {
358367
return;
@@ -383,6 +392,7 @@ class FlutterProject {
383392
linuxPlatform: linuxPlatform,
384393
macOSPlatform: macOSPlatform,
385394
windowsPlatform: windowsPlatform,
395+
allowedPlugins: allowedPlugins,
386396
);
387397
}
388398

packages/flutter_tools/lib/src/runner/flutter_command.dart

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import '../dart/pub.dart';
2727
import '../device.dart';
2828
import '../features.dart';
2929
import '../globals.dart' as globals;
30+
import '../preview_device.dart';
3031
import '../project.dart';
3132
import '../reporting/reporting.dart';
3233
import '../web/compile.dart';
@@ -1694,7 +1695,14 @@ Run 'flutter -h' (or 'flutter <command> -h') for available flutter commands and
16941695
project: project,
16951696
checkUpToDate: cachePubGet,
16961697
);
1697-
await project.regeneratePlatformSpecificTooling();
1698+
1699+
// null implicitly means all plugins are allowed
1700+
List<String>? allowedPlugins;
1701+
if (stringArg(FlutterGlobalOptions.kDeviceIdOption, global: true) == 'preview') {
1702+
// The preview device does not currently support any plugins.
1703+
allowedPlugins = PreviewDevice.supportedPubPlugins;
1704+
}
1705+
await project.regeneratePlatformSpecificTooling(allowedPlugins: allowedPlugins);
16981706
if (reportNullSafety) {
16991707
await _sendNullSafetyAnalyticsEvents(project);
17001708
}

packages/flutter_tools/test/general.shard/plugins_test.dart

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import 'package:flutter_tools/src/flutter_plugins.dart';
1717
import 'package:flutter_tools/src/globals.dart' as globals;
1818
import 'package:flutter_tools/src/ios/xcodeproj.dart';
1919
import 'package:flutter_tools/src/plugins.dart';
20+
import 'package:flutter_tools/src/preview_device.dart';
2021
import 'package:flutter_tools/src/project.dart';
2122
import 'package:flutter_tools/src/version.dart';
2223
import 'package:test/fake.dart';
@@ -210,7 +211,7 @@ void main() {
210211
? fakePubCache.childDirectory(name)
211212
: fileSystem.directory(nameOrPath);
212213
packagesFile.writeAsStringSync(
213-
'$name:file://${pluginDirectory.childFile('lib').uri}\n',
214+
'$name:${pluginDirectory.childFile('lib').uri}\n',
214215
mode: FileMode.writeOnlyAppend);
215216
pluginDirectory.childFile('pubspec.yaml')
216217
..createSync(recursive: true)
@@ -1438,6 +1439,29 @@ flutter:
14381439
FileSystem: () => fsWindows,
14391440
ProcessManager: () => FakeProcessManager.any(),
14401441
});
1442+
1443+
testUsingContext('injectPlugins will validate if all plugins in the project are part of the passed allowedPlugins', () async {
1444+
// Re-run the setup using the Windows filesystem.
1445+
setUpProject(fsWindows);
1446+
createFakePlugins(fsWindows, const <String>['plugin_one', 'plugin_two']);
1447+
1448+
expect(
1449+
() => injectPlugins(
1450+
flutterProject,
1451+
linuxPlatform: true,
1452+
windowsPlatform: true,
1453+
allowedPlugins: PreviewDevice.supportedPubPlugins,
1454+
),
1455+
throwsToolExit(message: '''
1456+
The Flutter Preview device does not support the following plugins from your pubspec.yaml:
1457+
1458+
[plugin_one, plugin_two]
1459+
'''),
1460+
);
1461+
}, overrides: <Type, Generator>{
1462+
FileSystem: () => fsWindows,
1463+
ProcessManager: () => FakeProcessManager.empty(),
1464+
});
14411465
});
14421466

14431467
group('createPluginSymlinks', () {

0 commit comments

Comments
 (0)