Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/many-seas-love.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@rnef/platform-apple-helpers': patch
---

fix: missing -sdk for getting build settings; running incompatible destinations
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,19 @@ export const createBuild = async ({

let xcodeProject: XcodeProjectInfo;
let sourceDir: string;
const deviceOrSimulator = args.destination
? // there can be multiple destinations, so we'll pick the first one
args.destination[0].match(/simulator/i)
? 'simulator'
: 'device'
: 'simulator';
const artifactName = await formatArtifactName({
platform: 'ios',
traits: [deviceOrSimulator, args.configuration ?? 'Debug'],
root: projectRoot,
fingerprintOptions,
});
try {
const artifactName = await formatArtifactName({
platform: 'ios',
traits: [
args.destination?.[0] ?? 'simulator',
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this was bug producing e.g. ios-generic/platform=iOS-Debug-{hash}, which would never resolve correctly for a remote artifact. cc @mdjastrzebski

args.configuration ?? 'Debug',
],
root: projectRoot,
fingerprintOptions,
});
const { appPath, ...buildAppResult } = await buildApp({
projectRoot,
projectConfig,
Expand Down
75 changes: 40 additions & 35 deletions packages/platform-apple-helpers/src/lib/commands/run/createRun.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import type {
ProjectConfig,
} from '../../types/index.js';
import { buildApp } from '../../utils/buildApp.js';
import { getGenericDestination } from '../../utils/destionation.js';
import { getPlatformInfo } from '../../utils/getPlatformInfo.js';
import { listDevicesAndSimulators } from '../../utils/listDevices.js';
import { matchingDevice } from './matchingDevice.js';
Expand Down Expand Up @@ -48,12 +47,16 @@ export const createRun = async ({
reactNativePath: string;
}) => {
validateArgs(args, projectRoot);

const deviceOrSimulator = args.destination
? // there can be multiple destinations, so we'll pick the first one
args.destination[0].match(/simulator/i)
? 'simulator'
: 'device'
: 'simulator';
const artifactName = await formatArtifactName({
platform: 'ios',
traits: [
args.destination?.[0] ?? 'simulator',
args.configuration ?? 'Debug',
],
traits: [deviceOrSimulator, args.configuration ?? 'Debug'],
root: projectRoot,
fingerprintOptions,
});
Expand Down Expand Up @@ -99,10 +102,7 @@ export const createRun = async ({

if (platformName === 'macos') {
const { appPath } = await buildApp({
args: {
destination: [getGenericDestination(platformName, 'simulator')],
...args,
},
args,
projectConfig,
platformName,
projectRoot,
Expand All @@ -116,10 +116,7 @@ export const createRun = async ({
return;
} else if (args.catalyst) {
const { appPath, scheme } = await buildApp({
args: {
destination: [getGenericDestination(platformName, 'simulator')],
...args,
},
args,
projectConfig,
platformName,
projectRoot,
Expand Down Expand Up @@ -150,15 +147,22 @@ export const createRun = async ({
const device = await selectDevice(devices, args);

if (device) {
if (device.type !== deviceOrSimulator) {
throw new RnefError(
`Selected device "${device.name}" is not a ${deviceOrSimulator}.
Please select available ${deviceOrSimulator} device:
${devices
.filter(({ type }) => type === deviceOrSimulator)
.map(({ name }) => `• ${name}`)
.join('\n')}`
);
}
Comment on lines +150 to +159
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

cacheRecentDevice(device, platformName);
if (device.type === 'simulator') {
const [, { appPath, infoPlistPath }] = await Promise.all([
launchSimulator(device),
buildApp({
args: {
destination: [getGenericDestination(platformName, 'simulator')],
...args,
},
args,
projectConfig,
platformName,
udid: device.udid,
Expand All @@ -172,10 +176,7 @@ export const createRun = async ({
await runOnSimulator(device, appPath, infoPlistPath);
} else if (device.type === 'device') {
const { appPath } = await buildApp({
args: {
destination: [getGenericDestination(platformName, 'device')],
...args,
},
args,
projectConfig,
platformName,
udid: device.udid,
Expand All @@ -188,14 +189,17 @@ export const createRun = async ({
}
return;
} else {
const bootedSimulators = devices.filter(
({ state, type }) => state === 'Booted' && type === 'simulator'
const bootedDevices = devices.filter(
({ state, type }) => state === 'Booted' && type === deviceOrSimulator
);
if (bootedSimulators.length === 0) {
if (bootedDevices.length === 0) {
// fallback to present all devices when no device is selected
if (isInteractive()) {
const simulator = await promptForDeviceSelection(devices, platformName);
bootedSimulators.push(simulator);
const simulator = await promptForDeviceSelection(
devices.filter(({ type }) => type === deviceOrSimulator),
platformName
);
bootedDevices.push(simulator);
cacheRecentDevice(simulator, platformName);
} else {
logger.debug(
Expand All @@ -205,32 +209,33 @@ export const createRun = async ({
(device) => device.type === 'simulator'
)[0];
if (simulator) {
bootedSimulators.push(simulator);
bootedDevices.push(simulator);
} else {
throw new RnefError(
'No Apple simulators found. Install simulators via Xcode.'
);
}
}
}
for (const simulator of bootedSimulators) {
for (const bootedDevice of bootedDevices) {
const [, { appPath, infoPlistPath }] = await Promise.all([
launchSimulator(simulator),
launchSimulator(bootedDevice),
buildApp({
args: {
destination: [getGenericDestination(platformName, 'simulator')],
...args,
},
args,
projectConfig,
platformName,
udid: simulator.udid,
udid: bootedDevice.udid,
projectRoot,
reactNativePath,
artifactName,
binaryPath,
}),
]);
await runOnSimulator(simulator, appPath, infoPlistPath);
if (bootedDevice.type === 'simulator') {
await runOnSimulator(bootedDevice, appPath, infoPlistPath);
} else {
await runOnDevice(bootedDevice, appPath, projectConfig.sourceDir);
}
}
}
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,28 @@ type BuildSettings = {
FULL_PRODUCT_NAME: string;
};

export async function getBuildSettings(
xcodeProject: XcodeProjectInfo,
sourceDir: string,
platformName: ApplePlatform,
configuration: string,
destinations: string[],
scheme: string,
target?: string
): Promise<{ appPath: string; infoPlistPath: string }> {
export async function getBuildSettings({
xcodeProject,
sourceDir,
platformName,
configuration,
destinations,
scheme,
target,
}: {
xcodeProject: XcodeProjectInfo;
sourceDir: string;
platformName: ApplePlatform;
configuration: string;
destinations: string[];
scheme: string;
target?: string;
}): Promise<{ appPath: string; infoPlistPath: string }> {
const destination = destinations[0];
const sdk = destination.match(/simulator/i)
? getSimulatorPlatformSDK(platformName)
: getDevicePlatformSDK(platformName);

const { stdout: buildSettings } = await spawn(
'xcodebuild',
[
Expand All @@ -27,7 +40,11 @@ export async function getBuildSettings(
scheme,
'-configuration',
configuration,
...destinations.flatMap((destination) => ['-destination', destination]),
'-sdk',
sdk,
// -showBuildSettings supports exactly one -destination argument
'-destination',
destination,
'-showBuildSettings',
'-json',
],
Expand Down Expand Up @@ -111,3 +128,38 @@ function getBuildPath(
return path.join(targetBuildDir, executableFolderPath);
}
}

type PlatformSDK =
| 'iphonesimulator'
| 'macosx'
| 'appletvsimulator'
| 'xrsimulator'
| 'iphoneos'
| 'appletvos'
| 'xr';

function getSimulatorPlatformSDK(platform: ApplePlatform): PlatformSDK {
switch (platform) {
case 'ios':
return 'iphonesimulator';
case 'macos':
return 'macosx';
case 'tvos':
return 'appletvsimulator';
case 'visionos':
return 'xrsimulator';
}
}

function getDevicePlatformSDK(platform: ApplePlatform): PlatformSDK {
switch (platform) {
case 'ios':
return 'iphoneos';
case 'macos':
return 'macosx';
case 'tvos':
return 'appletvos';
case 'visionos':
return 'xr';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ import type { Device } from '../../types/index.js';
import { readKeyFromPlist } from '../../utils/plist.js';

export async function launchSimulator(device: Device) {
if (device.type !== 'simulator') {
// bail if device is not a simulator
return undefined;
}
/**
* Booting simulator through `xcrun simctl boot` will boot it in the `headless` mode
* (running in the background).
Expand Down
37 changes: 17 additions & 20 deletions packages/platform-apple-helpers/src/lib/utils/buildApp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ export async function buildApp({
args.configuration
);
const destinations = determineDestinations({
args,
destination: args.destination,
isCatalyst: 'catalyst' in args && args.catalyst,
platformName,
udid,
deviceName,
Expand All @@ -98,15 +99,15 @@ export async function buildApp({
args,
});

const buildSettings = await getBuildSettings(
const buildSettings = await getBuildSettings({
xcodeProject,
sourceDir,
platformName,
configuration,
destinations,
scheme,
args.target
);
target: args.target,
});
const appPath = buildSettings.appPath;

saveLocalBuildCache(artifactName, appPath);
Expand All @@ -120,37 +121,33 @@ export async function buildApp({
};
}

type DetermineDestinationsArgs = {
args: RunFlags | BuildFlags;
platformName: ApplePlatform;
udid?: string;
deviceName?: string;
};

function determineDestinations({
args,
destination,
isCatalyst,
platformName,
udid,
deviceName,
}: DetermineDestinationsArgs): string[] {
if ('catalyst' in args && args.catalyst) {
}: {
destination?: string[];
isCatalyst?: boolean;
platformName: ApplePlatform;
udid?: string;
deviceName?: string;
}): string[] {
if (isCatalyst) {
return ['platform=macOS,variant=Mac Catalyst'];
}

if (udid) {
return [`id=${udid}`];
}

if (deviceName) {
return [`name=${deviceName}`];
}

if (args.destination && args.destination.length > 0) {
return args.destination.map((destination) =>
if (destination && destination.length > 0) {
return destination.map((destination) =>
resolveDestination(destination, platformName)
);
}

return [getGenericDestination(platformName, 'device')];
}

Expand Down
2 changes: 0 additions & 2 deletions packages/tools/src/lib/build-cache/fetchCachedBuild.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ import {
} from './common.js';
import type { LocalBuild } from './localBuildCache.js';

export type Distribution = 'simulator' | 'device';

type FetchCachedBuildOptions = {
artifactName: string;
remoteCacheProvider: undefined | null | { (): RemoteBuildCache };
Expand Down