Skip to content

Commit 750df58

Browse files
committed
fix(build): Target a generic iOS simulator for building
1 parent 49a97b3 commit 750df58

File tree

2 files changed

+32
-118
lines changed

2 files changed

+32
-118
lines changed

lib/build.js

Lines changed: 6 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -70,33 +70,6 @@ function createProjectObject (projectPath) {
7070
return projectFile.parse(locations);
7171
}
7272

73-
/**
74-
* Returns a promise that resolves to the default simulator target; the logic here
75-
* matches what `cordova emulate ios` does.
76-
*
77-
* The return object has two properties: `name` (the Xcode destination name),
78-
* `identifier` (the simctl identifier), and `simIdentifier` (essentially the cordova emulate target)
79-
*
80-
* @return {Promise}
81-
*/
82-
function getDefaultSimulatorTarget () {
83-
events.emit('log', 'Select last emulator from list as default.');
84-
return require('./listEmulatorBuildTargets').run()
85-
.then(emulators => {
86-
if (emulators.length === 0) {
87-
return Promise.reject(new CordovaError('Could not find any iOS simulators. Use Xcode to install simulator devices for testing.'));
88-
}
89-
90-
let targetEmulator = emulators[0];
91-
emulators.forEach(emulator => {
92-
if (emulator.name.indexOf('iPhone') === 0) {
93-
targetEmulator = emulator;
94-
}
95-
});
96-
return targetEmulator;
97-
});
98-
}
99-
10073
function parseOptions (options) {
10174
options = options || {};
10275
options.argv = nopt({
@@ -154,14 +127,11 @@ module.exports.run = function (buildOpts) {
154127
}
155128

156129
const projectPath = this.root;
157-
let emulatorTarget = 'iOS Device';
158130

159131
if (buildOpts.target && buildOpts.target.match(/mac/i)) {
160132
buildOpts.catalyst = true;
161133
buildOpts.device = true;
162134
buildOpts.emulator = false;
163-
164-
emulatorTarget = 'macOS Catalyst';
165135
}
166136

167137
return Promise.resolve()
@@ -175,33 +145,6 @@ module.exports.run = function (buildOpts) {
175145
});
176146
}
177147
})
178-
.then(() => {
179-
// CB-12287: Determine the device we should target when building for a simulator
180-
if (!buildOpts.device) {
181-
let newTarget = buildOpts.target || '';
182-
183-
if (newTarget) {
184-
// only grab the device name, not the runtime specifier
185-
newTarget = newTarget.split(',')[0];
186-
}
187-
// a target was given to us, find the matching Xcode destination name
188-
const promise = require('./listEmulatorBuildTargets').targetForSimIdentifier(newTarget);
189-
return promise.then(theTarget => {
190-
if (!theTarget) {
191-
return getDefaultSimulatorTarget().then(defaultTarget => {
192-
emulatorTarget = defaultTarget.name;
193-
events.emit('warn', `No simulator found for "${newTarget}". Falling back to the default target.`);
194-
events.emit('log', `Building for "${emulatorTarget}" Simulator (${defaultTarget.identifier}, ${defaultTarget.simIdentifier}).`);
195-
return emulatorTarget;
196-
});
197-
} else {
198-
emulatorTarget = theTarget.name;
199-
events.emit('log', `Building for "${emulatorTarget}" Simulator (${theTarget.identifier}, ${theTarget.simIdentifier}).`);
200-
return emulatorTarget;
201-
}
202-
});
203-
}
204-
})
205148
.then(() => check_reqs.run())
206149
.then(() => {
207150
let extraConfig = '';
@@ -243,18 +186,18 @@ module.exports.run = function (buildOpts) {
243186
return fs.promises.writeFile(path.join(projectPath, 'cordova', 'build-extras.xcconfig'), extraConfig, 'utf-8');
244187
}).then(() => {
245188
const configuration = buildOpts.release ? 'Release' : 'Debug';
189+
const platform = buildOpts.catalyst ? 'maccatalyst' : (buildOpts.device ? 'iphoneos' : 'iphonesimulator');
246190

247191
events.emit('log', `Building project: ${path.join(projectPath, 'App.xcworkspace')}`);
248192
events.emit('log', `\tConfiguration: ${configuration}`);
249-
events.emit('log', `\tPlatform: ${buildOpts.device ? 'device' : 'emulator'}`);
250-
events.emit('log', `\tTarget: ${emulatorTarget}`);
193+
events.emit('log', `\tPlatform: ${platform}`);
251194

252-
const buildOutputDir = path.join(projectPath, 'build', `${configuration}-${(buildOpts.device ? 'iphoneos' : 'iphonesimulator')}`);
195+
const buildOutputDir = path.join(projectPath, 'build', `${configuration}-${platform}`);
253196

254197
// remove the build output folder before building
255198
fs.rmSync(buildOutputDir, { recursive: true, force: true });
256199

257-
const xcodebuildArgs = getXcodeBuildArgs(projectPath, configuration, emulatorTarget, buildOpts);
200+
const xcodebuildArgs = getXcodeBuildArgs(projectPath, configuration, buildOpts);
258201
return execa('xcodebuild', xcodebuildArgs, { cwd: projectPath, stdio: 'inherit' });
259202
}).then(() => {
260203
if (!buildOpts.device || buildOpts.catalyst || buildOpts.noSign) {
@@ -323,11 +266,10 @@ module.exports.run = function (buildOpts) {
323266
* Returns array of arguments for xcodebuild
324267
* @param {String} projectPath Path to project file. Will be used to set CWD for xcodebuild
325268
* @param {String} configuration Configuration name: debug|release
326-
* @param {String} emulatorTarget Target for emulator (rather than default)
327269
* @param {Object} buildConfig The build configuration options
328270
* @return {Array} Array of arguments that could be passed directly to spawn method
329271
*/
330-
function getXcodeBuildArgs (projectPath, configuration, emulatorTarget, buildConfig = {}) {
272+
function getXcodeBuildArgs (projectPath, configuration, buildConfig = {}) {
331273
let options;
332274
let buildActions;
333275
let settings;
@@ -396,7 +338,7 @@ function getXcodeBuildArgs (projectPath, configuration, emulatorTarget, buildCon
396338
} else {
397339
options = options.concat([
398340
'-sdk', customArgs.sdk || 'iphonesimulator',
399-
'-destination', customArgs.destination || `platform=iOS Simulator,name=${emulatorTarget}`
341+
'-destination', customArgs.destination || 'generic/platform=iOS Simulator'
400342
]);
401343
}
402344

tests/spec/build.spec.js

Lines changed: 26 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ const rewire = require('rewire');
2323
const { CordovaError } = require('cordova-common');
2424
const build = rewire('../../lib/build');
2525

26+
const realBuild = require('../../lib/build');
27+
2628
describe('build', () => {
2729
const testProjectPath = path.join('/test', 'project', 'path');
2830

@@ -31,7 +33,7 @@ describe('build', () => {
3133
build.__set__('__dirname', path.join('/test', 'dir'));
3234

3335
it('should generate appropriate args if a single buildFlag is passed in', () => {
34-
const args = getXcodeBuildArgs(testProjectPath, 'TestConfiguration', '', { device: true, buildFlag: '' });
36+
const args = getXcodeBuildArgs(testProjectPath, 'TestConfiguration', { device: true, buildFlag: '' });
3537
expect(args).toEqual([
3638
'-workspace',
3739
'App.xcworkspace',
@@ -61,7 +63,7 @@ describe('build', () => {
6163
'-resultBundlePath="/tmp/result bundle/file"'
6264
];
6365

64-
const args = getXcodeBuildArgs(testProjectPath, 'TestConfiguration', '', { device: true, buildFlag: buildFlags });
66+
const args = getXcodeBuildArgs(testProjectPath, 'TestConfiguration', { device: true, buildFlag: buildFlags });
6567
expect(args).toEqual([
6668
'-workspace',
6769
'TestWorkspaceFlag',
@@ -83,7 +85,7 @@ describe('build', () => {
8385
});
8486

8587
it('should generate appropriate args for device', () => {
86-
const args = getXcodeBuildArgs(testProjectPath, 'TestConfiguration', '', { device: true });
88+
const args = getXcodeBuildArgs(testProjectPath, 'TestConfiguration', { device: true });
8789
expect(args).toEqual([
8890
'-workspace',
8991
'App.xcworkspace',
@@ -101,7 +103,7 @@ describe('build', () => {
101103
});
102104

103105
it('should generate appropriate args for simulator', () => {
104-
const args = getXcodeBuildArgs(testProjectPath, 'TestConfiguration', 'iPhone 5s', { device: false });
106+
const args = getXcodeBuildArgs(testProjectPath, 'TestConfiguration', { device: false });
105107
expect(args).toEqual([
106108
'-workspace',
107109
'App.xcworkspace',
@@ -112,7 +114,7 @@ describe('build', () => {
112114
'-sdk',
113115
'iphonesimulator',
114116
'-destination',
115-
'platform=iOS Simulator,name=iPhone 5s',
117+
'generic/platform=iOS Simulator',
116118
'build'
117119
]);
118120
expect(args.length).toEqual(11);
@@ -129,7 +131,7 @@ describe('build', () => {
129131
'SHARED_PRECOMPS_DIR=TestSharedPrecompsDirFlag'
130132
];
131133

132-
const args = getXcodeBuildArgs(testProjectPath, 'TestConfiguration', 'iPhone 5s', { device: false, buildFlag: buildFlags });
134+
const args = getXcodeBuildArgs(testProjectPath, 'TestConfiguration', { device: false, buildFlag: buildFlags });
133135
expect(args).toEqual([
134136
'-workspace',
135137
'TestWorkspaceFlag',
@@ -153,7 +155,7 @@ describe('build', () => {
153155
it('should add matched flags that are not overriding for device', () => {
154156
const buildFlags = '-sdk TestSdkFlag';
155157

156-
const args = getXcodeBuildArgs(testProjectPath, 'TestConfiguration', '', { device: true, buildFlag: buildFlags });
158+
const args = getXcodeBuildArgs(testProjectPath, 'TestConfiguration', { device: true, buildFlag: buildFlags });
157159
expect(args).toEqual([
158160
'-workspace',
159161
'App.xcworkspace',
@@ -175,7 +177,7 @@ describe('build', () => {
175177
it('should add matched flags that are not overriding for simulator', () => {
176178
const buildFlags = '-archivePath TestArchivePathFlag';
177179

178-
const args = getXcodeBuildArgs(testProjectPath, 'TestConfiguration', 'iPhone 5s', { device: false, buildFlag: buildFlags });
180+
const args = getXcodeBuildArgs(testProjectPath, 'TestConfiguration', { device: false, buildFlag: buildFlags });
179181
expect(args).toEqual([
180182
'-workspace',
181183
'App.xcworkspace',
@@ -186,7 +188,7 @@ describe('build', () => {
186188
'-sdk',
187189
'iphonesimulator',
188190
'-destination',
189-
'platform=iOS Simulator,name=iPhone 5s',
191+
'generic/platform=iOS Simulator',
190192
'build',
191193
'-archivePath',
192194
'TestArchivePathFlag'
@@ -203,7 +205,7 @@ describe('build', () => {
203205
authenticationKeyIssuerID: '00000000-0000-0000-0000-000000000000'
204206
};
205207

206-
const args = getXcodeBuildArgs(testProjectPath, 'TestConfiguration', '', buildOpts);
208+
const args = getXcodeBuildArgs(testProjectPath, 'TestConfiguration', buildOpts);
207209
expect(args).toEqual([
208210
'-workspace',
209211
'App.xcworkspace',
@@ -232,7 +234,7 @@ describe('build', () => {
232234
catalyst: true
233235
};
234236

235-
const args = getXcodeBuildArgs(testProjectPath, 'TestConfiguration', '', buildOpts);
237+
const args = getXcodeBuildArgs(testProjectPath, 'TestConfiguration', buildOpts);
236238
expect(args).toEqual([
237239
'-workspace',
238240
'App.xcworkspace',
@@ -479,53 +481,23 @@ describe('build', () => {
479481
});
480482
});
481483

482-
describe('getDefaultSimulatorTarget method', () => {
483-
it('should find iPhone X as the default simulator target.', () => {
484-
const mockedEmulators = [{
485-
name: 'iPhone 7',
486-
identifier: 'com.apple.CoreSimulator.SimDeviceType.iPhone-7',
487-
simIdentifier: 'iPhone-7'
488-
},
489-
{
490-
name: 'iPhone 8',
491-
identifier: 'com.apple.CoreSimulator.SimDeviceType.iPhone-8',
492-
simIdentifier: 'iPhone-8'
493-
},
494-
{
495-
name: 'iPhone X',
496-
identifier: 'com.apple.CoreSimulator.SimDeviceType.iPhone-X',
497-
simIdentifier: 'iPhone-X'
498-
}];
499-
500-
// This method will require a module that supports the run method.
501-
build.__set__('require', () => ({
502-
run: () => Promise.resolve(mockedEmulators)
503-
}));
504-
505-
const getDefaultSimulatorTarget = build.__get__('getDefaultSimulatorTarget');
506-
507-
return getDefaultSimulatorTarget().then(actual => {
508-
expect(actual).toEqual({
509-
name: 'iPhone X',
510-
identifier: 'com.apple.CoreSimulator.SimDeviceType.iPhone-X',
511-
simIdentifier: 'iPhone-X'
512-
});
513-
});
484+
describe('run method', () => {
485+
it('should not accept debug and release options together', () => {
486+
return expectAsync(realBuild.run({ debug: true, release: true }))
487+
.toBeRejectedWithError(CordovaError, 'Cannot specify "debug" and "release" options together.');
514488
});
515489

516-
it('should handle the case of no simulators being available', () => {
517-
// This method will require a module that supports the run method.
518-
build.__set__('require', () => ({
519-
run: () => Promise.resolve([])
520-
}));
490+
it('should not accept device and emulator options together', () => {
491+
return expectAsync(realBuild.run({ device: true, emulator: true }))
492+
.toBeRejectedWithError(CordovaError, 'Cannot specify "device" and "emulator" options together.');
493+
});
521494

522-
const getDefaultSimulatorTarget = build.__get__('getDefaultSimulatorTarget');
495+
it('should not accept a build config file that does not exist', () => {
496+
spyOn(fs, 'existsSync').and.returnValue(false);
497+
const buildConfig = './some/config/path';
523498

524-
return getDefaultSimulatorTarget().then(sim => {
525-
return Promise.reject(new Error('Should not resolve if no simulators are present'));
526-
}, (err) => {
527-
expect(err).toBeInstanceOf(CordovaError);
528-
});
499+
return expectAsync(realBuild.run({ buildConfig: './some/config/path' }))
500+
.toBeRejectedWithError(CordovaError, `Build config file does not exist: ${buildConfig}`);
529501
});
530502
});
531503
});

0 commit comments

Comments
 (0)