-
Notifications
You must be signed in to change notification settings - Fork 523
Expand file tree
/
Copy pathxcode.dart
More file actions
159 lines (149 loc) Β· 5.21 KB
/
xcode.dart
File metadata and controls
159 lines (149 loc) Β· 5.21 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:convert';
import 'dart:io' as io;
import 'package:file/file.dart';
import 'core.dart';
import 'process_runner.dart';
const String _xcodeBuildCommand = 'xcodebuild';
const String _xcRunCommand = 'xcrun';
/// A utility class for interacting with the installed version of Xcode.
class Xcode {
/// Creates an instance that runs commands with the given [processRunner].
///
/// If [log] is true, commands run by this instance will long various status
/// messages.
Xcode({
this.processRunner = const ProcessRunner(),
this.log = false,
});
/// The [ProcessRunner] used to run commands. Overridable for testing.
final ProcessRunner processRunner;
/// Whether or not to log when running commands.
final bool log;
/// Runs an `xcodebuild` in [directory] with the given parameters.
Future<int> runXcodeBuild(
Directory directory, {
List<String> actions = const <String>['build'],
required String workspace,
required String scheme,
String? configuration,
List<String> extraFlags = const <String>[],
}) {
final List<String> args = <String>[
_xcodeBuildCommand,
...actions,
...<String>['-workspace', workspace],
...<String>['-scheme', scheme],
if (configuration != null) ...<String>['-configuration', configuration],
...extraFlags,
];
final String completeTestCommand = '$_xcRunCommand ${args.join(' ')}';
if (log) {
print(completeTestCommand);
}
return processRunner.runAndStream(_xcRunCommand, args,
workingDir: directory);
}
/// Returns true if [project], which should be an .xcodeproj directory,
/// contains a target called [target], false if it does not, and null if the
/// check fails (e.g., if [project] is not an Xcode project).
Future<bool?> projectHasTarget(Directory project, String target) async {
final io.ProcessResult result =
await processRunner.run(_xcRunCommand, <String>[
_xcodeBuildCommand,
'-list',
'-json',
'-project',
project.path,
]);
if (result.exitCode != 0) {
return null;
}
Map<String, dynamic>? projectInfo;
try {
projectInfo = (jsonDecode(result.stdout as String)
as Map<String, dynamic>)['project'] as Map<String, dynamic>?;
} on FormatException {
return null;
}
if (projectInfo == null) {
return null;
}
final List<String>? targets =
(projectInfo['targets'] as List<dynamic>?)?.cast<String>();
return targets?.contains(target) ?? false;
}
/// Returns the newest available simulator (highest OS version, with ties
/// broken in favor of newest device), if any.
Future<String?> findBestAvailableIphoneSimulator() async {
final List<String> findSimulatorsArguments = <String>[
'simctl',
'list',
'devices',
'runtimes',
'available',
'--json',
];
final String findSimulatorCompleteCommand =
'$_xcRunCommand ${findSimulatorsArguments.join(' ')}';
if (log) {
print('Looking for available simulators...');
print(findSimulatorCompleteCommand);
}
final io.ProcessResult findSimulatorsResult =
await processRunner.run(_xcRunCommand, findSimulatorsArguments);
if (findSimulatorsResult.exitCode != 0) {
if (log) {
printError(
'Error occurred while running "$findSimulatorCompleteCommand":\n'
'${findSimulatorsResult.stderr}');
}
return null;
}
final Map<String, dynamic> simulatorListJson =
jsonDecode(findSimulatorsResult.stdout as String)
as Map<String, dynamic>;
final List<Map<String, dynamic>> runtimes =
(simulatorListJson['runtimes'] as List<dynamic>)
.cast<Map<String, dynamic>>();
final Map<String, Object> devices =
(simulatorListJson['devices'] as Map<String, dynamic>)
.cast<String, Object>();
if (runtimes.isEmpty || devices.isEmpty) {
return null;
}
String? id;
// Looking for runtimes, trying to find one with highest OS version.
for (final Map<String, dynamic> rawRuntimeMap in runtimes.reversed) {
final Map<String, Object> runtimeMap =
rawRuntimeMap.cast<String, Object>();
if ((runtimeMap['name'] as String?)?.contains('iOS') != true) {
continue;
}
final String? runtimeID = runtimeMap['identifier'] as String?;
if (runtimeID == null) {
continue;
}
final List<Map<String, dynamic>>? devicesForRuntime =
(devices[runtimeID] as List<dynamic>?)?.cast<Map<String, dynamic>>();
if (devicesForRuntime == null || devicesForRuntime.isEmpty) {
continue;
}
// Looking for runtimes, trying to find latest version of device.
for (final Map<String, dynamic> rawDevice in devicesForRuntime.reversed) {
final Map<String, Object> device = rawDevice.cast<String, Object>();
id = device['udid'] as String?;
if (id == null) {
continue;
}
if (log) {
print('device selected: $device');
}
return id;
}
}
return null;
}
}