1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 | const path = require('path');
|
21 | const build = require('./build');
|
22 | const execa = require('execa');
|
23 | const { CordovaError, events } = require('cordova-common');
|
24 | const check_reqs = require('./check_reqs');
|
25 | const fs = require('fs-extra');
|
26 |
|
27 |
|
28 | module.exports.run = function (runOptions) {
|
29 | const projectPath = this.root;
|
30 |
|
31 |
|
32 | if (runOptions.device && runOptions.emulator) {
|
33 | return Promise.reject(new CordovaError('Only one of "device"/"emulator" options should be specified'));
|
34 | }
|
35 |
|
36 |
|
37 | if (runOptions.list) {
|
38 | if (runOptions.device) return module.exports.listDevices();
|
39 | if (runOptions.emulator) return module.exports.listEmulators();
|
40 |
|
41 | return module.exports.listDevices().then(() => module.exports.listEmulators());
|
42 | }
|
43 |
|
44 | let useDevice = !!runOptions.device;
|
45 | const configuration = runOptions.release ? 'Release' : 'Debug';
|
46 |
|
47 | return require('./listDevices').run()
|
48 | .then(devices => {
|
49 | if (devices.length > 0 && !(runOptions.emulator)) {
|
50 | useDevice = true;
|
51 |
|
52 |
|
53 | runOptions.device = true;
|
54 | return check_reqs.check_ios_deploy();
|
55 | }
|
56 | }).then(() => {
|
57 | if (!runOptions.nobuild) {
|
58 | return build.run(runOptions);
|
59 | } else {
|
60 | return Promise.resolve();
|
61 | }
|
62 | }).then(() => build.findXCodeProjectIn(projectPath))
|
63 | .then(projectName => {
|
64 | let appPath = path.join(projectPath, 'build', `${configuration}-iphonesimulator`, `${projectName}.app`);
|
65 | const buildOutputDir = path.join(projectPath, 'build', `${configuration}-iphoneos`);
|
66 |
|
67 |
|
68 |
|
69 | if (useDevice) {
|
70 | return module.exports.checkDeviceConnected()
|
71 | .then(() => {
|
72 |
|
73 | const ipafile = path.join(buildOutputDir, `${projectName}.ipa`);
|
74 |
|
75 |
|
76 | return execa('unzip', ['-o', '-qq', ipafile], { cwd: buildOutputDir, stdio: 'inherit' });
|
77 | })
|
78 | .then(() => {
|
79 |
|
80 | const appFileInflated = path.join(buildOutputDir, 'Payload', `${projectName}.app`);
|
81 | const appFile = path.join(buildOutputDir, `${projectName}.app`);
|
82 | const payloadFolder = path.join(buildOutputDir, 'Payload');
|
83 |
|
84 |
|
85 | fs.removeSync(appFile);
|
86 |
|
87 | fs.moveSync(appFileInflated, appFile);
|
88 |
|
89 | fs.removeSync(payloadFolder);
|
90 |
|
91 | return null;
|
92 | })
|
93 | .then(
|
94 | () => {
|
95 | appPath = path.join(projectPath, 'build', `${configuration}-iphoneos`, `${projectName}.app`);
|
96 | let extraArgs = [];
|
97 | if (runOptions.argv) {
|
98 |
|
99 | extraArgs = module.exports.filterSupportedArgs(runOptions.argv.slice(2));
|
100 | }
|
101 | return module.exports.deployToDevice(appPath, runOptions.target, extraArgs);
|
102 | },
|
103 |
|
104 | () => module.exports.deployToSim(appPath, runOptions.target)
|
105 | );
|
106 | } else {
|
107 | return module.exports.deployToSim(appPath, runOptions.target);
|
108 | }
|
109 | })
|
110 | .then(() => {});
|
111 | };
|
112 |
|
113 | module.exports.filterSupportedArgs = filterSupportedArgs;
|
114 | module.exports.checkDeviceConnected = checkDeviceConnected;
|
115 | module.exports.deployToDevice = deployToDevice;
|
116 | module.exports.deployToSim = deployToSim;
|
117 | module.exports.startSim = startSim;
|
118 | module.exports.listDevices = listDevices;
|
119 | module.exports.listEmulators = listEmulators;
|
120 |
|
121 |
|
122 |
|
123 |
|
124 |
|
125 |
|
126 | function filterSupportedArgs (args) {
|
127 | const filtered = [];
|
128 | const sargs = ['--device', '--emulator', '--nobuild', '--list', '--target', '--debug', '--release'];
|
129 | const re = new RegExp(sargs.join('|'));
|
130 |
|
131 | args.forEach(element => {
|
132 |
|
133 |
|
134 | if (element.search(re) === -1) {
|
135 | filtered.push(element);
|
136 | }
|
137 | }, this);
|
138 |
|
139 | return filtered;
|
140 | }
|
141 |
|
142 |
|
143 |
|
144 |
|
145 |
|
146 | function checkDeviceConnected () {
|
147 | return execa('ios-deploy', ['-c', '-t', '1'], { stdio: 'inherit' });
|
148 | }
|
149 |
|
150 |
|
151 |
|
152 |
|
153 |
|
154 |
|
155 |
|
156 | function deployToDevice (appPath, target, extraArgs) {
|
157 | events.emit('log', 'Deploying to device');
|
158 | const args = ['--justlaunch', '-d', '-b', appPath];
|
159 | if (target) {
|
160 | args.push('-i', target);
|
161 | } else {
|
162 | args.push('--no-wifi');
|
163 | }
|
164 | return execa('ios-deploy', args.concat(extraArgs), { stdio: 'inherit' });
|
165 | }
|
166 |
|
167 |
|
168 |
|
169 |
|
170 |
|
171 |
|
172 |
|
173 | async function deployToSim (appPath, target) {
|
174 | events.emit('log', 'Deploying to simulator');
|
175 |
|
176 | if (!target) {
|
177 |
|
178 | const emulators = await require('./listEmulatorImages').run();
|
179 | const iPhoneEmus = emulators.filter(emulator => emulator.startsWith('iPhone'));
|
180 | target = iPhoneEmus.concat(emulators)[0];
|
181 | events.emit('log', `No target specified for emulator. Deploying to "${target}" simulator.`);
|
182 | }
|
183 |
|
184 | return startSim(appPath, target);
|
185 | }
|
186 |
|
187 | function startSim (appPath, target) {
|
188 | const projectPath = path.join(path.dirname(appPath), '../..');
|
189 | const logPath = path.join(projectPath, 'cordova/console.log');
|
190 | const deviceTypeId = `com.apple.CoreSimulator.SimDeviceType.${target}`;
|
191 |
|
192 | const subprocess = execa(
|
193 | require.resolve('ios-sim/bin/ios-sim'),
|
194 | ['launch', appPath, '--devicetypeid', deviceTypeId, '--log', logPath, '--exit'],
|
195 | { cwd: projectPath }
|
196 | );
|
197 |
|
198 |
|
199 | subprocess.stderr.on('data', data => {
|
200 | events.emit('error', `[ios-sim] ${data}`);
|
201 | });
|
202 | subprocess.stdout.on('data', data => {
|
203 | events.emit('log', `[ios-sim] ${data}`);
|
204 | });
|
205 |
|
206 | subprocess.then(() => {
|
207 | events.emit('log', 'Simulator successfully started via `ios-sim`.');
|
208 | });
|
209 |
|
210 | return subprocess;
|
211 | }
|
212 |
|
213 | function listDevices () {
|
214 | return require('./listDevices').run()
|
215 | .then(devices => {
|
216 | events.emit('log', 'Available iOS Devices:');
|
217 | devices.forEach(device => {
|
218 | events.emit('log', `\t${device}`);
|
219 | });
|
220 | });
|
221 | }
|
222 |
|
223 | function listEmulators () {
|
224 | return require('./listEmulatorImages').run()
|
225 | .then(emulators => {
|
226 | events.emit('log', 'Available iOS Simulators:');
|
227 | emulators.forEach(emulator => {
|
228 | events.emit('log', `\t${emulator}`);
|
229 | });
|
230 | });
|
231 | }
|
232 |
|
233 | module.exports.runListDevices = async function (options = {}) {
|
234 | const { options: cliArgs = {} } = options;
|
235 |
|
236 | if (cliArgs?.device) {
|
237 | await module.exports.listDevices.call(this);
|
238 | } else if (cliArgs?.emulator) {
|
239 | await module.exports.listEmulators.call(this);
|
240 | } else {
|
241 | await module.exports.listDevices.call(this);
|
242 | await module.exports.listEmulators.call(this);
|
243 | }
|
244 |
|
245 | return true;
|
246 | };
|