Unverified Commit 6b191841 authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

Allow filtering devices to only those supported by current project (#31446)

parent 5ec33654
......@@ -535,6 +535,11 @@ class AndroidDevice extends Device {
await runCheckedAsync(adbCommandForDevice(<String>['pull', remotePath, outputFile.path]));
await runCheckedAsync(adbCommandForDevice(<String>['shell', 'rm', remotePath]));
}
@override
bool isSupportedForProject(FlutterProject flutterProject) {
return flutterProject.android.existsSync();
}
}
Map<String, String> parseAdbDeviceProperties(String str) {
......
......@@ -243,6 +243,7 @@ class AttachCommand extends FlutterCommand {
final bool useHot = getBuildInfo().isDebug;
final FlutterDevice flutterDevice = await FlutterDevice.create(
device,
flutterProject: flutterProject,
trackWidgetCreation: argResults['track-widget-creation'],
dillOutputPath: argResults['output-dill'],
fileSystemRoots: argResults['filesystem-root'],
......
......@@ -19,6 +19,7 @@ import '../convert.dart';
import '../device.dart';
import '../emulator.dart';
import '../globals.dart';
import '../project.dart';
import '../resident_runner.dart';
import '../run_cold.dart';
import '../run_hot.dart';
......@@ -344,9 +345,11 @@ class AppDomain extends Domain {
// We change the current working directory for the duration of the `start` command.
final Directory cwd = fs.currentDirectory;
fs.currentDirectory = fs.directory(projectDirectory);
final FlutterProject flutterProject = await FlutterProject.current();
final FlutterDevice flutterDevice = await FlutterDevice.create(
device,
flutterProject: flutterProject,
trackWidgetCreation: trackWidgetCreation,
dillOutputPath: dillOutputPath,
viewFilter: isolateFilter,
......
......@@ -13,6 +13,7 @@ import '../cache.dart';
import '../device.dart';
import '../globals.dart';
import '../ios/mac.dart';
import '../project.dart';
import '../resident_runner.dart';
import '../run_cold.dart';
import '../run_hot.dart';
......@@ -364,9 +365,11 @@ class RunCommand extends RunCommandBase {
expFlags = argResults[FlutterOptions.kEnableExperiment];
}
final List<FlutterDevice> flutterDevices = <FlutterDevice>[];
final FlutterProject flutterProject = await FlutterProject.current();
for (Device device in devices) {
final FlutterDevice flutterDevice = await FlutterDevice.create(
device,
flutterProject: flutterProject,
trackWidgetCreation: argResults['track-widget-creation'],
dillOutputPath: argResults['output-dill'],
fileSystemRoots: argResults['filesystem-root'],
......
......@@ -19,6 +19,7 @@ import 'ios/devices.dart';
import 'ios/simulators.dart';
import 'linux/linux_device.dart';
import 'macos/macos_device.dart';
import 'project.dart';
import 'tester/flutter_tester.dart';
import 'web/web_device.dart';
import 'windows/windows_device.dart';
......@@ -235,6 +236,9 @@ abstract class Device {
}
}
/// Whether the device is supported for the current project directory.
bool isSupportedForProject(FlutterProject flutterProject);
/// Check if a version of the given app is already installed
Future<bool> isAppInstalled(ApplicationPackage app);
......
......@@ -17,6 +17,7 @@ import '../base/time.dart';
import '../build_info.dart';
import '../device.dart';
import '../globals.dart';
import '../project.dart';
import '../vmservice.dart';
import 'fuchsia_sdk.dart';
......@@ -302,6 +303,9 @@ class FuchsiaDevice extends Device {
}
FuchsiaIsolateDiscoveryProtocol getIsolateDiscoveryProtocol(String isolateName) => FuchsiaIsolateDiscoveryProtocol(this, isolateName);
@override
bool isSupportedForProject(FlutterProject flutterProject) => true;
}
class FuchsiaIsolateDiscoveryProtocol {
......
......@@ -17,6 +17,7 @@ import '../build_info.dart';
import '../convert.dart';
import '../device.dart';
import '../globals.dart';
import '../project.dart';
import '../protocol_discovery.dart';
import 'code_signing.dart';
import 'ios_workflow.dart';
......@@ -401,6 +402,11 @@ class IOSDevice extends Device {
Future<void> takeScreenshot(File outputFile) async {
await iMobileDevice.takeScreenshot(outputFile);
}
@override
bool isSupportedForProject(FlutterProject flutterProject) {
return flutterProject.ios.existsSync();
}
}
/// Decodes a vis-encoded syslog string to a UTF-8 representation.
......
......@@ -19,6 +19,7 @@ import '../bundle.dart' as bundle;
import '../convert.dart';
import '../device.dart';
import '../globals.dart';
import '../project.dart';
import '../protocol_discovery.dart';
import 'ios_workflow.dart';
import 'mac.dart';
......@@ -473,6 +474,11 @@ class IOSSimulator extends Device {
Future<void> takeScreenshot(File outputFile) {
return SimControl.instance.takeScreenshot(id, outputFile.path);
}
@override
bool isSupportedForProject(FlutterProject flutterProject) {
return flutterProject.ios.existsSync();
}
}
/// Launches the device log reader process on the host.
......
......@@ -108,6 +108,11 @@ class LinuxDevice extends Device {
@override
Future<bool> uninstallApp(ApplicationPackage app) async => true;
@override
bool isSupportedForProject(FlutterProject flutterProject) {
return flutterProject.linux.existsSync();
}
// Track the last built mode from startApp.
BuildMode _lastBuiltMode;
}
......
......@@ -117,6 +117,11 @@ class MacOSDevice extends Device {
// to uninstall the application.
@override
Future<bool> uninstallApp(ApplicationPackage app) async => true;
@override
bool isSupportedForProject(FlutterProject flutterProject) {
return flutterProject.macos.existsSync();
}
}
class MacOSDevices extends PollingDeviceDiscovery {
......
......@@ -208,8 +208,6 @@ class IosProject {
Directory get _ephemeralDirectory => parent.directory.childDirectory('.ios');
Directory get _editableDirectory => parent.directory.childDirectory('ios');
bool existsSync() => parent.isModule || _editableDirectory.existsSync();
/// This parent folder of `Runner.xcodeproj`.
Directory get hostAppRoot {
if (!isModule || _editableDirectory.existsSync())
......@@ -264,6 +262,11 @@ class IosProject {
/// Xcode workspace shared workspace settings file for the host app.
File get xcodeWorkspaceSharedSettings => xcodeWorkspaceSharedData.childFile('WorkspaceSettings.xcsettings');
/// Whether the current flutter project has an iOS subproject.
bool existsSync() {
return parent.isModule || _editableDirectory.existsSync();
}
/// The product bundle identifier of the host app, or null if not set or if
/// iOS tooling needed to read it is not installed.
String get productBundleIdentifier {
......@@ -391,8 +394,6 @@ class AndroidProject {
return _ephemeralDirectory;
}
bool existsSync() => parent.isModule || _flutterLibGradleRoot.existsSync();
/// The Gradle root directory of the Android wrapping of Flutter and plugins.
/// This is the same as [hostAppGradleRoot] except when the project is
/// a Flutter module with an editable host app.
......@@ -420,6 +421,11 @@ class AndroidProject {
return fs.directory(fs.path.join(hostAppGradleRoot.path, 'app', 'build', 'outputs', 'bundle'));
}
/// Whether the current flutter project has an Android sub-project.
bool existsSync() {
return parent.isModule || _editableHostAppDirectory.existsSync();
}
bool get isUsingGradle {
return hostAppGradleRoot.childFile('build.gradle').existsSync();
}
......@@ -498,7 +504,10 @@ class WebProject {
final FlutterProject parent;
bool existsSync() => parent.directory.childDirectory('web').existsSync();
/// Whether this flutter project has a web sub-project.
bool existsSync() {
return parent.directory.childDirectory('web').existsSync();
}
Future<void> ensureReadyForPlatformSpecificTooling() async {
/// Generate index.html in build/web. Eventually we could support
......@@ -590,4 +599,4 @@ class LinuxProject {
/// The Linux project makefile.
File get makeFile => editableHostAppDirectory.childFile('Makefile');
}
\ No newline at end of file
}
......@@ -52,6 +52,7 @@ class FlutterDevice {
/// Create a [FlutterDevice] with optional code generation enabled.
static Future<FlutterDevice> create(
Device device, {
@required FlutterProject flutterProject,
@required bool trackWidgetCreation,
String dillOutputPath,
List<String> fileSystemRoots,
......@@ -64,7 +65,6 @@ class FlutterDevice {
@required BuildMode buildMode,
}) async {
ResidentCompiler generator;
final FlutterProject flutterProject = await FlutterProject.current();
if (flutterProject.hasBuilders) {
generator = await CodeGeneratingResidentCompiler.create(
flutterProject: flutterProject,
......
......@@ -527,6 +527,13 @@ abstract class FlutterCommand extends Command<void> {
}
devices = devices.where((Device device) => device.isSupported()).toList();
// If the user has not specified all devices and has multiple connected
// then filter then list by those supported in the current project. If
// this ends up with a single device we can proceed as normal.
if (devices.length > 1 && !deviceManager.hasSpecifiedAllDevices && !deviceManager.hasSpecifiedDeviceId) {
final FlutterProject flutterProject = await FlutterProject.current();
devices.removeWhere((Device device) => !device.isSupportedForProject(flutterProject));
}
if (devices.isEmpty) {
printStatus(userMessages.flutterNoSupportedDevices);
......
......@@ -18,6 +18,7 @@ import '../convert.dart';
import '../dart/package_map.dart';
import '../device.dart';
import '../globals.dart';
import '../project.dart';
import '../protocol_discovery.dart';
import '../version.dart';
......@@ -192,6 +193,9 @@ class FlutterTesterDevice extends Device {
@override
Future<bool> uninstallApp(ApplicationPackage app) async => true;
@override
bool isSupportedForProject(FlutterProject flutterProject) => true;
}
class FlutterTesterDevices extends PollingDeviceDiscovery {
......
......@@ -166,6 +166,11 @@ class WebDevice extends Device {
await request.response.addStream(file.openRead());
await request.response.close();
}
@override
bool isSupportedForProject(FlutterProject flutterProject) {
return flutterProject.web.existsSync();
}
}
class WebDevices extends PollingDeviceDiscovery {
......
......@@ -114,6 +114,11 @@ class WindowsDevice extends Device {
// to uninstall the application.
@override
Future<bool> uninstallApp(ApplicationPackage app) async => true;
@override
bool isSupportedForProject(FlutterProject flutterProject) {
return flutterProject.windows.existsSync();
}
}
class WindowsDevices extends PollingDeviceDiscovery {
......
......@@ -10,6 +10,7 @@ import 'package:flutter_tools/src/android/android_sdk.dart';
import 'package:flutter_tools/src/base/config.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/project.dart';
import 'package:mockito/mockito.dart';
import 'package:process/process.dart';
......@@ -157,6 +158,44 @@ Use the 'android' tool to install them:
ProcessManager: () => mockProcessManager,
});
});
testUsingContext('isSupportedForProject is true on module project', () async {
fs.file('pubspec.yaml')
..createSync()
..writeAsStringSync(r'''
name: example
flutter:
module: {}
''');
fs.file('.packages').createSync();
final FlutterProject flutterProject = await FlutterProject.current();
expect(AndroidDevice('test').isSupportedForProject(flutterProject), true);
}, overrides: <Type, Generator>{
FileSystem: () => MemoryFileSystem(),
});
testUsingContext('isSupportedForProject is true with editable host app', () async {
fs.file('pubspec.yaml').createSync();
fs.file('.packages').createSync();
fs.directory('android').createSync();
final FlutterProject flutterProject = await FlutterProject.current();
expect(AndroidDevice('test').isSupportedForProject(flutterProject), true);
}, overrides: <Type, Generator>{
FileSystem: () => MemoryFileSystem(),
});
testUsingContext('isSupportedForProject is false with no host app and no module', () async {
fs.file('pubspec.yaml').createSync();
fs.file('.packages').createSync();
final FlutterProject flutterProject = await FlutterProject.current();
expect(AndroidDevice('test').isSupportedForProject(flutterProject), false);
}, overrides: <Type, Generator>{
FileSystem: () => MemoryFileSystem(),
});
}
class MockProcessManager extends Mock implements ProcessManager {}
......
......@@ -53,6 +53,7 @@ void main() {
expect(device.supportsHotReload, true);
expect(device.supportsHotRestart, false);
expect(device.supportsStopApp, false);
expect(device.isSupportedForProject(null), true);
expect(await device.stopApp(null), false);
});
});
......
......@@ -5,12 +5,14 @@
import 'dart:async';
import 'package:file/file.dart';
import 'package:file/memory.dart';
import 'package:flutter_tools/src/application_package.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/device.dart';
import 'package:flutter_tools/src/ios/devices.dart';
import 'package:flutter_tools/src/ios/mac.dart';
import 'package:flutter_tools/src/project.dart';
import 'package:mockito/mockito.dart';
import 'package:platform/platform.dart';
import 'package:process/process.dart';
......@@ -183,4 +185,42 @@ f577a7903cc54959be2e34bc4f7f80b7009efcf4
IMobileDevice: () => mockIMobileDevice,
});
});
testUsingContext('IOSDevice.isSupportedForProject is true on module project', () async {
fs.file('pubspec.yaml')
..createSync()
..writeAsStringSync(r'''
name: example
flutter:
module: {}
''');
fs.file('.packages').createSync();
final FlutterProject flutterProject = await FlutterProject.current();
expect(IOSDevice('test').isSupportedForProject(flutterProject), true);
}, overrides: <Type, Generator>{
FileSystem: () => MemoryFileSystem(),
});
testUsingContext('IOSDevice.isSupportedForProject is true with editable host app', () async {
fs.file('pubspec.yaml').createSync();
fs.file('.packages').createSync();
fs.directory('ios').createSync();
final FlutterProject flutterProject = await FlutterProject.current();
expect(IOSDevice('test').isSupportedForProject(flutterProject), true);
}, overrides: <Type, Generator>{
FileSystem: () => MemoryFileSystem(),
});
testUsingContext('IOSDevice.isSupportedForProject is false with no host app and no module', () async {
fs.file('pubspec.yaml').createSync();
fs.file('.packages').createSync();
final FlutterProject flutterProject = await FlutterProject.current();
expect(IOSDevice('test').isSupportedForProject(flutterProject), false);
}, overrides: <Type, Generator>{
FileSystem: () => MemoryFileSystem(),
});
}
......@@ -6,11 +6,13 @@ import 'dart:async';
import 'dart:io' show ProcessResult, Process;
import 'package:file/file.dart';
import 'package:file/memory.dart';
import 'package:flutter_tools/src/device.dart';
import 'package:flutter_tools/src/application_package.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/ios/mac.dart';
import 'package:flutter_tools/src/ios/simulators.dart';
import 'package:flutter_tools/src/project.dart';
import 'package:mockito/mockito.dart';
import 'package:platform/platform.dart';
import 'package:process/process.dart';
......@@ -414,4 +416,42 @@ void main() {
SimControl: () => simControl,
});
});
testUsingContext('IOSDevice.isSupportedForProject is true on module project', () async {
fs.file('pubspec.yaml')
..createSync()
..writeAsStringSync(r'''
name: example
flutter:
module: {}
''');
fs.file('.packages').createSync();
final FlutterProject flutterProject = await FlutterProject.current();
expect(IOSSimulator('test').isSupportedForProject(flutterProject), true);
}, overrides: <Type, Generator>{
FileSystem: () => MemoryFileSystem(),
});
testUsingContext('IOSDevice.isSupportedForProject is true with editable host app', () async {
fs.file('pubspec.yaml').createSync();
fs.file('.packages').createSync();
fs.directory('ios').createSync();
final FlutterProject flutterProject = await FlutterProject.current();
expect(IOSSimulator('test').isSupportedForProject(flutterProject), true);
}, overrides: <Type, Generator>{
FileSystem: () => MemoryFileSystem(),
});
testUsingContext('IOSDevice.isSupportedForProject is false with no host app and no module', () async {
fs.file('pubspec.yaml').createSync();
fs.file('.packages').createSync();
final FlutterProject flutterProject = await FlutterProject.current();
expect(IOSSimulator('test').isSupportedForProject(flutterProject), false);
}, overrides: <Type, Generator>{
FileSystem: () => MemoryFileSystem(),
});
}
......@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:file/memory.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/base/platform.dart';
......@@ -9,6 +10,7 @@ import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/linux/application_package.dart';
import 'package:flutter_tools/src/linux/linux_device.dart';
import 'package:flutter_tools/src/device.dart';
import 'package:flutter_tools/src/project.dart';
import 'package:mockito/mockito.dart';
import 'package:process/process.dart';
......@@ -59,6 +61,27 @@ void main() {
Platform: () => notLinux,
});
});
testUsingContext('LinuxDevice.isSupportedForProject is true with editable host app', () async {
fs.file('pubspec.yaml').createSync();
fs.file('.packages').createSync();
fs.directory('linux').createSync();
final FlutterProject flutterProject = await FlutterProject.current();
expect(LinuxDevice().isSupportedForProject(flutterProject), true);
}, overrides: <Type, Generator>{
FileSystem: () => MemoryFileSystem(),
});
testUsingContext('LinuxDevice.isSupportedForProject is false with no host app', () async {
fs.file('pubspec.yaml').createSync();
fs.file('.packages').createSync();
final FlutterProject flutterProject = await FlutterProject.current();
expect(LinuxDevice().isSupportedForProject(flutterProject), false);
}, overrides: <Type, Generator>{
FileSystem: () => MemoryFileSystem(),
});
}
class MockPlatform extends Mock implements Platform {}
......
......@@ -4,7 +4,9 @@
import 'dart:convert';
import 'package:file/memory.dart';
import 'package:flutter_tools/src/base/context.dart';
import 'package:flutter_tools/src/project.dart';
import 'package:mockito/mockito.dart';
import 'package:process/process.dart';
......@@ -106,6 +108,27 @@ tester 17193 0.0 0.2 4791128 37820 ?? S 2:27PM 0:00.09 /Applica
}, overrides: <Type, Generator>{
Platform: () => notMac,
});
testUsingContext('isSupportedForProject is true with editable host app', () async {
fs.file('pubspec.yaml').createSync();
fs.file('.packages').createSync();
fs.directory('macos').createSync();
final FlutterProject flutterProject = await FlutterProject.current();
expect(MacOSDevice().isSupportedForProject(flutterProject), true);
}, overrides: <Type, Generator>{
FileSystem: () => MemoryFileSystem(),
});
testUsingContext('isSupportedForProject is false with no host app', () async {
fs.file('pubspec.yaml').createSync();
fs.file('.packages').createSync();
final FlutterProject flutterProject = await FlutterProject.current();
expect(MacOSDevice().isSupportedForProject(flutterProject), false);
}, overrides: <Type, Generator>{
FileSystem: () => MemoryFileSystem(),
});
});
}
......
......@@ -379,6 +379,9 @@ class MockAndroidDevice extends Mock implements AndroidDevice {
@override
bool isSupported() => true;
@override
bool isSupportedForProject(FlutterProject flutterProject) => true;
}
class MockIOSDevice extends Mock implements IOSDevice {
......@@ -387,6 +390,9 @@ class MockIOSDevice extends Mock implements IOSDevice {
@override
bool isSupported() => true;
@override
bool isSupportedForProject(FlutterProject flutterProject) => true;
}
class MockIOSSimulator extends Mock implements IOSSimulator {
......@@ -395,6 +401,9 @@ class MockIOSSimulator extends Mock implements IOSSimulator {
@override
bool isSupported() => true;
@override
bool isSupportedForProject(FlutterProject flutterProject) => true;
}
class MockDeviceLogReader extends DeviceLogReader {
......
......@@ -2,10 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:file/memory.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/project.dart';
import 'package:flutter_tools/src/windows/application_package.dart';
import 'package:flutter_tools/src/windows/windows_device.dart';
import 'package:flutter_tools/src/device.dart';
......@@ -58,6 +60,27 @@ void main() {
}, overrides: <Type, Generator>{
Platform: () => notWindows,
});
testUsingContext('isSupportedForProject is true with editable host app', () async {
fs.file('pubspec.yaml').createSync();
fs.file('.packages').createSync();
fs.directory('windows').createSync();
final FlutterProject flutterProject = await FlutterProject.current();
expect(WindowsDevice().isSupportedForProject(flutterProject), true);
}, overrides: <Type, Generator>{
FileSystem: () => MemoryFileSystem(),
});
testUsingContext('isSupportedForProject is false with no host app', () async {
fs.file('pubspec.yaml').createSync();
fs.file('.packages').createSync();
final FlutterProject flutterProject = await FlutterProject.current();
expect(WindowsDevice().isSupportedForProject(flutterProject), false);
}, overrides: <Type, Generator>{
FileSystem: () => MemoryFileSystem(),
});
});
}
......@@ -71,4 +94,4 @@ class MockProcessManager extends Mock implements ProcessManager {}
class MockProcess extends Mock implements Process {}
class MockProcessResult extends Mock implements ProcessResult {}
\ No newline at end of file
class MockProcessResult extends Mock implements ProcessResult {}
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