Unverified Commit b19b744b authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

[flutter_tools] check for Runner.sln when parsing for plugins (#57392)

Throw a toolExit if the windows plugin logic runs on an invalid windows project. Update the supported project check to validate the existence of a Runner.sln file
parent 6294dd59
...@@ -1084,7 +1084,7 @@ Future<void> injectPlugins(FlutterProject project, {bool checkProjects = false}) ...@@ -1084,7 +1084,7 @@ Future<void> injectPlugins(FlutterProject project, {bool checkProjects = false})
if (featureFlags.isWindowsEnabled && project.windows.existsSync()) { if (featureFlags.isWindowsEnabled && project.windows.existsSync()) {
await _writeWindowsPluginFiles(project, plugins); await _writeWindowsPluginFiles(project, plugins);
final List<Plugin>nativePlugins = _filterNativePlugins(plugins, WindowsPlugin.kConfigKey); final List<Plugin> nativePlugins = _filterNativePlugins(plugins, WindowsPlugin.kConfigKey);
await VisualStudioSolutionUtils(project: project.windows, fileSystem: globals.fs).updatePlugins(nativePlugins); await VisualStudioSolutionUtils(project: project.windows, fileSystem: globals.fs).updatePlugins(nativePlugins);
} }
for (final XcodeBasedProject subproject in <XcodeBasedProject>[project.ios, project.macos]) { for (final XcodeBasedProject subproject in <XcodeBasedProject>[project.ios, project.macos]) {
......
...@@ -1006,7 +1006,7 @@ class WindowsProject extends FlutterProjectPlatform { ...@@ -1006,7 +1006,7 @@ class WindowsProject extends FlutterProjectPlatform {
String get pluginConfigKey => WindowsPlugin.kConfigKey; String get pluginConfigKey => WindowsPlugin.kConfigKey;
@override @override
bool existsSync() => _editableDirectory.existsSync(); bool existsSync() => _editableDirectory.existsSync() && solutionFile.existsSync();
Directory get _editableDirectory => project.directory.childDirectory('windows'); Directory get _editableDirectory => project.directory.childDirectory('windows');
......
...@@ -66,6 +66,12 @@ class VisualStudioSolutionUtils { ...@@ -66,6 +66,12 @@ class VisualStudioSolutionUtils {
/// dependencies to include [plugins], removing any previous plugins from the /// dependencies to include [plugins], removing any previous plugins from the
/// solution. /// solution.
Future<void> updatePlugins(List<Plugin> plugins) async { Future<void> updatePlugins(List<Plugin> plugins) async {
if (!_project.solutionFile.existsSync()) {
throwToolExit(
'Attempted to update Windows plugins on a project that does not '
'support Windows.',
);
}
final String solutionContent = await _project.solutionFile.readAsString(); final String solutionContent = await _project.solutionFile.readAsString();
// Map of GUID to name for the current plugin list. // Map of GUID to name for the current plugin list.
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
// found in the LICENSE file. // found in the LICENSE file.
import 'package:file/memory.dart'; import 'package:file/memory.dart';
import 'package:file_testing/file_testing.dart';
import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/convert.dart'; import 'package:flutter_tools/src/convert.dart';
import 'package:flutter_tools/src/plugins.dart'; import 'package:flutter_tools/src/plugins.dart';
...@@ -26,15 +27,15 @@ void main() { ...@@ -26,15 +27,15 @@ void main() {
const String pluginCGuid = '8E010714-28FF-416A-BC6F-9CDE336A02A7'; const String pluginCGuid = '8E010714-28FF-416A-BC6F-9CDE336A02A7';
const String pluginDGuid = '304F1860-7E8B-4C99-8E1D-F5E55259F5C3'; const String pluginDGuid = '304F1860-7E8B-4C99-8E1D-F5E55259F5C3';
FileSystem fs; FileSystem fileSystem;
MockWindowsProject project; MockWindowsProject project;
setUp(() async { setUp(() async {
fs = MemoryFileSystem(style: FileSystemStyle.windows); fileSystem = MemoryFileSystem.test(style: FileSystemStyle.windows);
project = MockWindowsProject(); project = MockWindowsProject();
when(project.pluginConfigKey).thenReturn('windows'); when(project.pluginConfigKey).thenReturn('windows');
final Directory windowsManagedDirectory = fs.directory('C:').childDirectory('windows').childDirectory('flutter'); final Directory windowsManagedDirectory = fileSystem.directory('C:').childDirectory('windows').childDirectory('flutter');
when(project.solutionFile).thenReturn(windowsManagedDirectory.parent.childFile('Runner.sln')); when(project.solutionFile).thenReturn(windowsManagedDirectory.parent.childFile('Runner.sln'));
when(project.vcprojFile).thenReturn(windowsManagedDirectory.parent.childFile('Runner.vcxproj')); when(project.vcprojFile).thenReturn(windowsManagedDirectory.parent.childFile('Runner.vcxproj'));
when(project.pluginSymlinkDirectory).thenReturn(windowsManagedDirectory.childDirectory('ephemeral').childDirectory('.plugin_symlinks')); when(project.pluginSymlinkDirectory).thenReturn(windowsManagedDirectory.childDirectory('ephemeral').childDirectory('.plugin_symlinks'));
...@@ -239,13 +240,13 @@ EndGlobal'''); ...@@ -239,13 +240,13 @@ EndGlobal''');
final List<Plugin> plugins = <Plugin>[ final List<Plugin> plugins = <Plugin>[
getMockPlugin('plugin_a', pluginAGuid), getMockPlugin('plugin_a', pluginAGuid),
]; ];
await VisualStudioSolutionUtils(project: project, fileSystem: fs).updatePlugins(plugins); await VisualStudioSolutionUtils(project: project, fileSystem: fileSystem).updatePlugins(plugins);
final String newSolutionContents = project.solutionFile.readAsStringSync(); final String newSolutionContents = project.solutionFile.readAsStringSync();
// Check for: // Check for:
// - Plugin project. // - Plugin project.
final String pluginSubpath = fs.path.join('Flutter', 'ephemeral', '.plugin_symlinks', 'plugin_a', 'windows', 'plugin.vcxproj'); final String pluginSubpath = fileSystem.path.join('Flutter', 'ephemeral', '.plugin_symlinks', 'plugin_a', 'windows', 'plugin.vcxproj');
expect(newSolutionContents, contains('Project("{$solutionTypeGuidVcxproj}") = "plugin_a", "$pluginSubpath", "{$pluginAGuid}"')); expect(newSolutionContents, contains('Project("{$solutionTypeGuidVcxproj}") = "plugin_a", "$pluginSubpath", "{$pluginAGuid}"'));
// - A dependency on the plugin project (from the Runner). // - A dependency on the plugin project (from the Runner).
expect(newSolutionContents, contains('{$pluginAGuid} = {$pluginAGuid}')); expect(newSolutionContents, contains('{$pluginAGuid} = {$pluginAGuid}'));
...@@ -268,7 +269,7 @@ EndGlobal'''); ...@@ -268,7 +269,7 @@ EndGlobal''');
getMockPlugin('plugin_a', pluginAGuid), getMockPlugin('plugin_a', pluginAGuid),
getMockPlugin('plugin_c', pluginCGuid), getMockPlugin('plugin_c', pluginCGuid),
]; ];
await VisualStudioSolutionUtils(project: project, fileSystem: fs).updatePlugins(plugins); await VisualStudioSolutionUtils(project: project, fileSystem: fileSystem).updatePlugins(plugins);
final String newSolutionContents = project.solutionFile.readAsStringSync(); final String newSolutionContents = project.solutionFile.readAsStringSync();
...@@ -289,7 +290,7 @@ EndGlobal'''); ...@@ -289,7 +290,7 @@ EndGlobal''');
final List<Plugin> plugins = <Plugin>[ final List<Plugin> plugins = <Plugin>[
]; ];
await VisualStudioSolutionUtils(project: project, fileSystem: fs).updatePlugins( plugins); await VisualStudioSolutionUtils(project: project, fileSystem: fileSystem).updatePlugins( plugins);
final String newSolutionContents = project.solutionFile.readAsStringSync(); final String newSolutionContents = project.solutionFile.readAsStringSync();
...@@ -309,7 +310,7 @@ EndGlobal'''); ...@@ -309,7 +310,7 @@ EndGlobal''');
getMockPlugin('plugin_c', pluginCGuid), getMockPlugin('plugin_c', pluginCGuid),
getMockPlugin('plugin_d', pluginDGuid), getMockPlugin('plugin_d', pluginDGuid),
]; ];
await VisualStudioSolutionUtils(project: project, fileSystem: fs).updatePlugins(plugins); await VisualStudioSolutionUtils(project: project, fileSystem: fileSystem).updatePlugins(plugins);
final String newSolutionContents = project.solutionFile.readAsStringSync(); final String newSolutionContents = project.solutionFile.readAsStringSync();
...@@ -326,7 +327,7 @@ EndGlobal'''); ...@@ -326,7 +327,7 @@ EndGlobal''');
// All the plugin D values should be added: // All the plugin D values should be added:
// - Plugin project. // - Plugin project.
final String pluginSubpath = fs.path.join('Flutter', 'ephemeral', '.plugin_symlinks', 'plugin_d', 'windows', 'plugin.vcxproj'); final String pluginSubpath = fileSystem.path.join('Flutter', 'ephemeral', '.plugin_symlinks', 'plugin_d', 'windows', 'plugin.vcxproj');
expect(newSolutionContents, contains('Project("{$solutionTypeGuidVcxproj}") = "plugin_d", "$pluginSubpath", "{$pluginDGuid}"')); expect(newSolutionContents, contains('Project("{$solutionTypeGuidVcxproj}") = "plugin_d", "$pluginSubpath", "{$pluginDGuid}"'));
// - A dependency on the plugin project (from the Runner). // - A dependency on the plugin project (from the Runner).
expect(newSolutionContents, contains('{$pluginDGuid} = {$pluginDGuid}')); expect(newSolutionContents, contains('{$pluginDGuid} = {$pluginDGuid}'));
...@@ -350,7 +351,7 @@ EndGlobal'''); ...@@ -350,7 +351,7 @@ EndGlobal''');
getMockPlugin('plugin_c', pluginCGuid), getMockPlugin('plugin_c', pluginCGuid),
getMockPlugin('plugin_d', pluginDGuid), getMockPlugin('plugin_d', pluginDGuid),
]; ];
await VisualStudioSolutionUtils(project: project, fileSystem: fs).updatePlugins(plugins); await VisualStudioSolutionUtils(project: project, fileSystem: fileSystem).updatePlugins(plugins);
final String newSolutionContents = project.solutionFile.readAsStringSync(); final String newSolutionContents = project.solutionFile.readAsStringSync();
// There should only be: // There should only be:
...@@ -375,7 +376,7 @@ EndGlobal'''); ...@@ -375,7 +376,7 @@ EndGlobal''');
getMockPlugin('plugin_c', pluginCGuid), getMockPlugin('plugin_c', pluginCGuid),
getMockPlugin('plugin_d', pluginDGuid), getMockPlugin('plugin_d', pluginDGuid),
]; ];
await VisualStudioSolutionUtils(project: project, fileSystem: fs).updatePlugins(plugins); await VisualStudioSolutionUtils(project: project, fileSystem: fileSystem).updatePlugins(plugins);
final String newSolutionContents = project.solutionFile.readAsStringSync(); final String newSolutionContents = project.solutionFile.readAsStringSync();
// Plugin A should still be before Flutter Build in the Runner dependencies. // Plugin A should still be before Flutter Build in the Runner dependencies.
...@@ -393,7 +394,7 @@ EndGlobal'''); ...@@ -393,7 +394,7 @@ EndGlobal''');
writeSolutionWithPlugins(); writeSolutionWithPlugins();
final List<Plugin> plugins = <Plugin>[]; final List<Plugin> plugins = <Plugin>[];
await VisualStudioSolutionUtils(project: project, fileSystem: fs).updatePlugins(plugins); await VisualStudioSolutionUtils(project: project, fileSystem: fileSystem).updatePlugins(plugins);
// Visual Studio expects sln files to start with a BOM. // Visual Studio expects sln files to start with a BOM.
final List<int> solutionStartingBytes = project.solutionFile.readAsBytesSync().take(3).toList(); final List<int> solutionStartingBytes = project.solutionFile.readAsBytesSync().take(3).toList();
...@@ -408,7 +409,7 @@ EndGlobal'''); ...@@ -408,7 +409,7 @@ EndGlobal''');
getMockPlugin('plugin_a', pluginAGuid), getMockPlugin('plugin_a', pluginAGuid),
getMockPlugin('plugin_b', pluginBGuid), getMockPlugin('plugin_b', pluginBGuid),
]; ];
await VisualStudioSolutionUtils(project: project, fileSystem: fs).updatePlugins(plugins); await VisualStudioSolutionUtils(project: project, fileSystem: fileSystem).updatePlugins(plugins);
final String newSolutionContents = project.solutionFile.readAsStringSync(); final String newSolutionContents = project.solutionFile.readAsStringSync();
// Project, EndProject, Global, and EndGlobal should be at the start of // Project, EndProject, Global, and EndGlobal should be at the start of
...@@ -431,9 +432,21 @@ EndGlobal'''); ...@@ -431,9 +432,21 @@ EndGlobal''');
final List<Plugin> plugins = <Plugin>[ final List<Plugin> plugins = <Plugin>[
getMockPlugin('plugin_a', pluginAGuid, createProject: false), getMockPlugin('plugin_a', pluginAGuid, createProject: false),
]; ];
expect(() => VisualStudioSolutionUtils(project: project, fileSystem: fs).updatePlugins(plugins), expect(() => VisualStudioSolutionUtils(project: project, fileSystem: fileSystem).updatePlugins(plugins),
throwsToolExit()); throwsToolExit());
}); });
test('A Windows project with a missing Runner.sln file throws a ToolExit', () async {
final MockWindowsProject windowsProject = MockWindowsProject();
final File file = fileSystem.file('does_not_exist');
expect(file, isNot(exists));
when(windowsProject.solutionFile).thenReturn(file);
expect(() async => await VisualStudioSolutionUtils(project: project, fileSystem: fileSystem)
.updatePlugins(<Plugin>[]), throwsToolExit());
});
}); });
} }
......
...@@ -66,6 +66,7 @@ void main() { ...@@ -66,6 +66,7 @@ void main() {
globals.fs.file('pubspec.yaml').createSync(); globals.fs.file('pubspec.yaml').createSync();
globals.fs.file('.packages').createSync(); globals.fs.file('.packages').createSync();
globals.fs.directory('windows').createSync(); globals.fs.directory('windows').createSync();
globals.fs.file(globals.fs.path.join('windows', 'Runner.sln')).createSync();
final FlutterProject flutterProject = FlutterProject.current(); final FlutterProject flutterProject = FlutterProject.current();
expect(WindowsDevice().isSupportedForProject(flutterProject), true); expect(WindowsDevice().isSupportedForProject(flutterProject), true);
...@@ -85,6 +86,18 @@ void main() { ...@@ -85,6 +86,18 @@ void main() {
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => FakeProcessManager.any(),
}); });
testUsingContext('isSupportedForProject is false with no Runner.sln', () async {
globals.fs.file('pubspec.yaml').createSync();
globals.fs.file('.packages').createSync();
globals.fs.directory('windows').createSync();
final FlutterProject flutterProject = FlutterProject.current();
expect(WindowsDevice().isSupportedForProject(flutterProject), false);
}, overrides: <Type, Generator>{
FileSystem: () => MemoryFileSystem(),
ProcessManager: () => FakeProcessManager.any(),
});
testUsingContext('executablePathForDevice uses the correct package executable', () async { testUsingContext('executablePathForDevice uses the correct package executable', () async {
final MockWindowsApp mockApp = MockWindowsApp(); final MockWindowsApp mockApp = MockWindowsApp();
const String debugPath = 'debug/executable'; const String debugPath = 'debug/executable';
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment