Skip to content

Commit b1d0b79

Browse files
committed
feat(ios): share DerivedData with Xcode and speed up incremental builds
DerivedData moves into platforms/ios/build through the per-user WorkspaceSettings, so CLI and Xcode builds reuse each other's output. Settings that made the two builds differ come off the xcodebuild command line: -sdk and forced explicit modules are gone, and the deployment target moves into the Podfile post_install. Simulator builds target the specific destination, debug device builds use a plain build instead of an archive, and the compilation cache lives in platforms/.cache/ios. Device discovery stops waiting as soon as the requested device is found.
1 parent 259682e commit b1d0b79

15 files changed

Lines changed: 972 additions & 271 deletions

‎lib/common/definitions/mobile.d.ts‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1174,6 +1174,11 @@ declare global {
11741174
shouldReturnImmediateResult: boolean;
11751175
platform: string;
11761176
fullDiscovery?: boolean;
1177+
/**
1178+
* Ends the wait for physical devices once the device requested on the
1179+
* command line has been found by any discovery.
1180+
*/
1181+
isRequestedDeviceFound?: () => boolean;
11771182
}
11781183

11791184
interface IAndroidDeviceHashService {

‎lib/common/mobile/ios/device/ios-device-operations.ts‎

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -86,21 +86,25 @@ export class IOSDeviceOperations
8686

8787
// We need this because we need to make sure that we have devices.
8888
await new Promise<void>((resolve, reject) => {
89-
let iterationsCount = 0;
90-
const maxIterationsCount = 3;
89+
const pollInterval = 100;
90+
const maxWait = 6000;
91+
let waited = 0;
9192

9293
const intervalHandle: NodeJS.Timer = setInterval(() => {
93-
if (foundDevice && !options.fullDiscovery) {
94+
if (
95+
(foundDevice && !options.fullDiscovery) ||
96+
options.isRequestedDeviceFound?.()
97+
) {
9498
resolve();
9599
return clearInterval(intervalHandle as unknown as number);
96100
}
97101

98-
iterationsCount++;
99-
if (iterationsCount >= maxIterationsCount) {
102+
waited += pollInterval;
103+
if (waited >= maxWait) {
100104
clearInterval(intervalHandle as unknown as number);
101105
return resolve();
102106
}
103-
}, 2000);
107+
}, pollInterval);
104108
});
105109
}
106110
}

‎lib/common/mobile/mobile-core/devices-service.ts‎

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -415,13 +415,23 @@ export class DevicesService
415415
deviceInitOpts?: Mobile.IDeviceLookingOptions,
416416
): Promise<void> {
417417
const options = this.getDeviceLookingOptions(deviceInitOpts);
418-
419-
for (const deviceDiscovery of this._allDeviceDiscoveries) {
418+
const startLooking = async (deviceDiscovery: Mobile.IDeviceDiscovery) => {
420419
try {
421420
await deviceDiscovery.startLookingForDevices(options);
422421
} catch (err) {
423422
this.$logger.trace("Error while checking for devices.", err);
424423
}
424+
};
425+
426+
// Concurrently, so a requested simulator ends the wait for physical
427+
// devices. Otherwise in order, as device indexes follow discovery order.
428+
if (options.isRequestedDeviceFound) {
429+
await Promise.all(this._allDeviceDiscoveries.map(startLooking));
430+
return;
431+
}
432+
433+
for (const deviceDiscovery of this._allDeviceDiscoveries) {
434+
await startLooking(deviceDiscovery);
425435
}
426436
}
427437

@@ -516,6 +526,18 @@ export class DevicesService
516526
* The identifier is expected to be the same as the running device declares it (emulator-5554 for android or GUID for ios).
517527
* @param identifier running emulator or device identifier
518528
*/
529+
private findDevice(deviceOption: string): Mobile.IDevice {
530+
return _.find(
531+
this.getDeviceInstances(),
532+
(d) =>
533+
(d.deviceInfo.identifier && d.deviceInfo.identifier === deviceOption) ||
534+
(d.deviceInfo.displayName &&
535+
d.deviceInfo.displayName === deviceOption) ||
536+
(d.deviceInfo.imageIdentifier &&
537+
d.deviceInfo.imageIdentifier === deviceOption),
538+
);
539+
}
540+
519541
public getDeviceByIdentifier(identifier: string): Mobile.IDevice {
520542
const searchedDevice = _.find(
521543
this.getDeviceInstances(),
@@ -581,20 +603,7 @@ export class DevicesService
581603
* @param deviceOption parameter passed by the user to --device flag. Can be name, identifier or imageIdentifier.
582604
*/
583605
public async getDevice(deviceOption: string): Promise<Mobile.IDevice> {
584-
let device: Mobile.IDevice = null;
585-
586-
if (!device) {
587-
device = _.find(
588-
this.getDeviceInstances(),
589-
(d) =>
590-
(d.deviceInfo.identifier &&
591-
d.deviceInfo.identifier === deviceOption) ||
592-
(d.deviceInfo.displayName &&
593-
d.deviceInfo.displayName === deviceOption) ||
594-
(d.deviceInfo.imageIdentifier &&
595-
d.deviceInfo.imageIdentifier === deviceOption),
596-
);
597-
}
606+
let device = this.findDevice(deviceOption);
598607

599608
if (!device && helpers.isNumberWithoutExponent(deviceOption)) {
600609
device = this.getDeviceByIndex(parseInt(deviceOption, 10));
@@ -907,6 +916,13 @@ export class DevicesService
907916
}
908917

909918
deviceInitOpts = deviceInitOpts || {};
919+
const deviceOption = deviceInitOpts.deviceId;
920+
if (deviceOption && !helpers.isNumberWithoutExponent(deviceOption)) {
921+
deviceInitOpts = {
922+
...deviceInitOpts,
923+
isRequestedDeviceFound: () => !!this.findDevice(deviceOption),
924+
};
925+
}
910926
this._data = deviceInitOpts;
911927

912928
if (!deviceInitOpts.skipEmulatorStart) {
@@ -915,13 +931,13 @@ export class DevicesService
915931
}
916932

917933
const platform = deviceInitOpts.platform;
918-
const deviceOption = deviceInitOpts.deviceId;
919934
const deviceLookingOptions: Mobile.IDeviceLookingOptions = {
920935
emulator: deviceInitOpts.emulator,
921936
platform: deviceInitOpts.platform,
922937
shouldReturnImmediateResult: deviceInitOpts.shouldReturnImmediateResult,
923938
detectionInterval: deviceInitOpts.detectionInterval,
924939
fullDiscovery: deviceInitOpts.fullDiscovery,
940+
isRequestedDeviceFound: deviceInitOpts.isRequestedDeviceFound,
925941
};
926942

927943
if (platform && deviceOption) {
@@ -1198,6 +1214,7 @@ export class DevicesService
11981214
shouldReturnImmediateResult: !!shouldReturnImmediateResult,
11991215
emulator: !!emulator,
12001216
fullDiscovery: deviceInitOpts.fullDiscovery,
1217+
isRequestedDeviceFound: deviceInitOpts.isRequestedDeviceFound,
12011218
};
12021219
}
12031220

‎lib/common/test/unit-tests/mobile/devices-service.ts‎

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1237,6 +1237,39 @@ describe("devicesService", () => {
12371237
);
12381238
});
12391239

1240+
it("stops waiting for physical devices once the requested simulator is found", async () => {
1241+
let waitedForRequestedDevice = false;
1242+
(<any>iOSDeviceDiscovery).startLookingForDevices = (
1243+
options: Mobile.IDeviceLookingOptions,
1244+
) =>
1245+
new Promise<void>((resolve) => {
1246+
const poll = setInterval(() => {
1247+
if (options.isRequestedDeviceFound?.()) {
1248+
waitedForRequestedDevice = true;
1249+
clearInterval(poll);
1250+
resolve();
1251+
}
1252+
}, 5);
1253+
});
1254+
(<any>iOSSimulatorDiscovery).startLookingForDevices = async () => {
1255+
iOSSimulatorDiscovery.emit(
1256+
DeviceDiscoveryEventNames.DEVICE_FOUND,
1257+
iOSSimulator,
1258+
);
1259+
};
1260+
1261+
await devicesService.initialize({
1262+
platform: "ios",
1263+
deviceId: iOSSimulator.deviceInfo.identifier,
1264+
skipEmulatorStart: true,
1265+
});
1266+
1267+
assert.isTrue(waitedForRequestedDevice);
1268+
assert.deepStrictEqual(devicesService.getDeviceInstances(), <any>[
1269+
iOSSimulator,
1270+
]);
1271+
});
1272+
12401273
describe("when only deviceIdentifier is passed", () => {
12411274
const assertAllMethodsResults = async (deviceId: string) => {
12421275
assert.isFalse(

‎lib/definitions/ios.d.ts‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,9 @@ declare global {
114114
platformData: IPlatformData,
115115
projectData: IProjectData,
116116
): string[];
117+
shouldArchiveDeviceBuild(
118+
buildConfig: IBuildConfig & { buildForAppStore?: boolean },
119+
): boolean;
117120
}
118121

119122
interface IXcodebuildCommandService {

‎lib/definitions/project.d.ts‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -563,6 +563,12 @@ interface ICocoaPodsService {
563563
platformData: IPlatformData,
564564
): Promise<void>;
565565

566+
applyPodfileBuildSettings(
567+
projectData: IProjectData,
568+
platformData: IPlatformData,
569+
buildSettings: Record<string, string> | null,
570+
): Promise<void>;
571+
566572
applyPodfileFromExtensions(
567573
projectData: IProjectData,
568574
platformData: IPlatformData,

0 commit comments

Comments
 (0)