• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2018 The Chromium 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
5import 'package:meta/meta.dart';
6
7import '../application_package.dart';
8import '../base/io.dart';
9import '../base/os.dart';
10import '../base/platform.dart';
11import '../base/process_manager.dart';
12import '../build_info.dart';
13import '../desktop.dart';
14import '../device.dart';
15import '../globals.dart';
16import '../project.dart';
17import '../protocol_discovery.dart';
18import 'application_package.dart';
19import 'build_windows.dart';
20import 'windows_workflow.dart';
21
22/// A device that represents a desktop Windows target.
23class WindowsDevice extends Device {
24  WindowsDevice() : super(
25      'Windows',
26      category: Category.desktop,
27      platformType: PlatformType.windows,
28      ephemeral: false,
29  );
30
31  @override
32  void clearLogs() { }
33
34  @override
35  DeviceLogReader getLogReader({ ApplicationPackage app }) {
36    return _logReader;
37  }
38  final DesktopLogReader _logReader = DesktopLogReader();
39
40  // Since the host and target devices are the same, no work needs to be done
41  // to install the application.
42  @override
43  Future<bool> installApp(ApplicationPackage app) async => true;
44
45  // Since the host and target devices are the same, no work needs to be done
46  // to install the application.
47  @override
48  Future<bool> isAppInstalled(ApplicationPackage app) async => true;
49
50  // Since the host and target devices are the same, no work needs to be done
51  // to install the application.
52  @override
53  Future<bool> isLatestBuildInstalled(ApplicationPackage app) async => true;
54
55  @override
56  Future<bool> get isLocalEmulator async => false;
57
58  @override
59  Future<String> get emulatorId async => null;
60
61  @override
62  bool isSupported() => true;
63
64  @override
65  String get name => 'Windows';
66
67  @override
68  DevicePortForwarder get portForwarder => const NoOpDevicePortForwarder();
69
70  @override
71  Future<String> get sdkNameAndVersion async => os.name;
72
73  @override
74  Future<LaunchResult> startApp(
75    covariant WindowsApp package, {
76    String mainPath,
77    String route,
78    DebuggingOptions debuggingOptions,
79    Map<String, dynamic> platformArgs,
80    bool prebuiltApplication = false,
81    bool usesTerminalUi = true,
82    bool ipv6 = false,
83  }) async {
84    if (!prebuiltApplication) {
85      await buildWindows(
86        FlutterProject.current().windows,
87        debuggingOptions.buildInfo,
88        target: mainPath,
89      );
90    }
91    await stopApp(package);
92    final Process process = await processManager.start(<String>[
93      package.executable(debuggingOptions?.buildInfo?.mode)
94    ]);
95    if (debuggingOptions?.buildInfo?.isRelease == true) {
96      return LaunchResult.succeeded();
97    }
98    _logReader.initializeProcess(process);
99    final ProtocolDiscovery observatoryDiscovery = ProtocolDiscovery.observatory(_logReader);
100    try {
101      final Uri observatoryUri = await observatoryDiscovery.uri;
102      return LaunchResult.succeeded(observatoryUri: observatoryUri);
103    } catch (error) {
104      printError('Error waiting for a debug connection: $error');
105      return LaunchResult.failed();
106    } finally {
107      await observatoryDiscovery.cancel();
108    }
109  }
110
111  @override
112  Future<bool> stopApp(covariant WindowsApp app) async {
113    // Assume debug for now.
114    final List<String> process = runningProcess(app.executable(BuildMode.debug));
115    if (process == null) {
116      return false;
117    }
118    final ProcessResult result = await processManager.run(<String>['Taskkill', '/PID', process.first, '/F']);
119    return result.exitCode == 0;
120  }
121
122  @override
123  Future<TargetPlatform> get targetPlatform async => TargetPlatform.windows_x64;
124
125  // Since the host and target devices are the same, no work needs to be done
126  // to uninstall the application.
127  @override
128  Future<bool> uninstallApp(ApplicationPackage app) async => true;
129
130  @override
131  bool isSupportedForProject(FlutterProject flutterProject) {
132    return flutterProject.windows.existsSync();
133  }
134}
135
136class WindowsDevices extends PollingDeviceDiscovery {
137  WindowsDevices() : super('windows devices');
138
139  @override
140  bool get supportsPlatform => platform.isWindows;
141
142  @override
143  bool get canListAnything => windowsWorkflow.canListDevices;
144
145  @override
146  Future<List<Device>> pollingGetDevices() async {
147    if (!canListAnything) {
148      return const <Device>[];
149    }
150    return <Device>[
151      WindowsDevice(),
152    ];
153  }
154
155  @override
156  Future<List<String>> getDiagnostics() async => const <String>[];
157}
158
159final RegExp _whitespace = RegExp(r'\s+');
160
161/// Returns the running process matching `process` name.
162///
163/// This list contains the process name and id.
164@visibleForTesting
165List<String> runningProcess(String processName) {
166  // TODO(jonahwilliams): find a way to do this without powershell.
167  final ProcessResult result = processManager.runSync(<String>['powershell', '-script="Get-CimInstance Win32_Process"']);
168  if (result.exitCode != 0) {
169    return null;
170  }
171  for (String rawProcess in result.stdout.split('\n')) {
172    final String process = rawProcess.trim();
173    if (!process.contains(processName)) {
174      continue;
175    }
176    final List<String> parts = process.split(_whitespace);
177
178    final String processPid = parts[0];
179    final String currentRunningProcessPid = pid.toString();
180    // Don't kill the flutter tool process
181    if (processPid == currentRunningProcessPid) {
182      continue;
183    }
184    final List<String> data = <String>[
185      processPid, // ID
186      parts[1], // Name
187    ];
188    return data;
189  }
190  return null;
191}
192