-
Notifications
You must be signed in to change notification settings - Fork 25k
Fix error in react-native run-ios when Product Name and Scheme are inequal #10178
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
d4e9495
028535a
d9e2d2d
93d3aab
4db0df2
c48a6df
ee2b353
e37439d
9501c1d
e357742
c01f837
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -31,7 +31,7 @@ function runIOS(argv, config, args) { | |
| if (args.device) { | ||
| const selectedDevice = matchingDevice(devices, args.device); | ||
| if (selectedDevice){ | ||
| runOnDevice(selectedDevice, scheme, xcodeProject); | ||
| return runOnDevice(selectedDevice, scheme, xcodeProject); | ||
| } else { | ||
| if (devices){ | ||
| console.log('Could not find device with the name: "' + args.device + '".'); | ||
|
|
@@ -42,16 +42,16 @@ function runIOS(argv, config, args) { | |
| } | ||
| } | ||
| } else if (args.udid) { | ||
| runOnDeviceByUdid(args.udid, scheme, xcodeProject, devices); | ||
| return runOnDeviceByUdid(args.udid, scheme, xcodeProject, devices); | ||
| } else { | ||
| runOnSimulator(xcodeProject, args, inferredSchemeName, scheme); | ||
| return runOnSimulator(xcodeProject, args, inferredSchemeName, scheme); | ||
| } | ||
| } | ||
|
|
||
| function runOnDeviceByUdid(udid, scheme, xcodeProject, devices) { | ||
| const selectedDevice = matchingDeviceByUdid(devices, udid); | ||
| if (selectedDevice){ | ||
| runOnDevice(selectedDevice, scheme, xcodeProject); | ||
| return runOnDevice(selectedDevice, scheme, xcodeProject); | ||
| } else { | ||
| if (devices){ | ||
| console.log('Could not find device with the udid: "' + udid + '".'); | ||
|
|
@@ -64,82 +64,119 @@ function runOnDeviceByUdid(udid, scheme, xcodeProject, devices) { | |
| } | ||
|
|
||
| function runOnSimulator(xcodeProject, args, inferredSchemeName, scheme){ | ||
| try { | ||
| var simulators = JSON.parse( | ||
| child_process.execFileSync('xcrun', ['simctl', 'list', '--json', 'devices'], {encoding: 'utf8'}) | ||
| ); | ||
| } catch (e) { | ||
| throw new Error('Could not parse the simulator list output'); | ||
| } | ||
|
|
||
| const selectedSimulator = findMatchingSimulator(simulators, args.simulator); | ||
| if (!selectedSimulator) { | ||
| throw new Error(`Cound't find ${args.simulator} simulator`); | ||
| } | ||
|
|
||
| const simulatorFullName = formattedDeviceName(selectedSimulator); | ||
| console.log(`Launching ${simulatorFullName}...`); | ||
| try { | ||
| child_process.spawnSync('xcrun', ['instruments', '-w', selectedSimulator.udid]); | ||
| } catch (e) { | ||
| // instruments always fail with 255 because it expects more arguments, | ||
| // but we want it to only launch the simulator | ||
| } | ||
|
|
||
| buildProject(xcodeProject, selectedSimulator.udid, scheme); | ||
|
|
||
| const appPath = `build/Build/Products/Debug-iphonesimulator/${inferredSchemeName}.app`; | ||
| console.log(`Installing ${appPath}`); | ||
| child_process.spawnSync('xcrun', ['simctl', 'install', 'booted', appPath], {stdio: 'inherit'}); | ||
| return new Promise((resolve) => { | ||
| try { | ||
| var simulators = JSON.parse( | ||
| child_process.execFileSync('xcrun', ['simctl', 'list', '--json', 'devices'], {encoding: 'utf8'}) | ||
| ); | ||
| } catch (e) { | ||
| throw new Error('Could not parse the simulator list output'); | ||
| } | ||
|
|
||
| const bundleID = child_process.execFileSync( | ||
| '/usr/libexec/PlistBuddy', | ||
| ['-c', 'Print:CFBundleIdentifier', path.join(appPath, 'Info.plist')], | ||
| {encoding: 'utf8'} | ||
| ).trim(); | ||
| const selectedSimulator = findMatchingSimulator(simulators, args.simulator); | ||
| if (!selectedSimulator) { | ||
| throw new Error(`Cound't find ${args.simulator} simulator`); | ||
| } | ||
|
|
||
| console.log(`Launching ${bundleID}`); | ||
| child_process.spawnSync('xcrun', ['simctl', 'launch', 'booted', bundleID], {stdio: 'inherit'}); | ||
| const simulatorFullName = formattedDeviceName(selectedSimulator); | ||
| console.log(`Launching ${simulatorFullName}...`); | ||
| try { | ||
| child_process.spawnSync('xcrun', ['instruments', '-w', selectedSimulator.udid]); | ||
| } catch (e) { | ||
| // instruments always fail with 255 because it expects more arguments, | ||
| // but we want it to only launch the simulator | ||
| } | ||
| resolve(selectedSimulator.udid) | ||
| }) | ||
| .then((udid) => buildProject(xcodeProject, udid, scheme)) | ||
| .then((appName) => { | ||
| if (!appName) { | ||
| appName = inferredSchemeName; | ||
| } | ||
| const appPath = `build/Build/Products/Debug-iphonesimulator/${appName}.app`; | ||
| console.log(`Installing ${appPath}`); | ||
| child_process.spawnSync('xcrun', ['simctl', 'install', 'booted', appPath], {stdio: 'inherit'}); | ||
|
|
||
| const bundleID = child_process.execFileSync( | ||
| '/usr/libexec/PlistBuddy', | ||
| ['-c', 'Print:CFBundleIdentifier', path.join(appPath, 'Info.plist')], | ||
| {encoding: 'utf8'} | ||
| ).trim(); | ||
|
|
||
| console.log(`Launching ${bundleID}`); | ||
| child_process.spawnSync('xcrun', ['simctl', 'launch', 'booted', bundleID], {stdio: 'inherit'}); | ||
| }) | ||
| } | ||
|
|
||
| function runOnDevice(selectedDevice, scheme, xcodeProject){ | ||
| buildProject(xcodeProject, selectedDevice.udid, scheme); | ||
| const iosDeployInstallArgs = [ | ||
| '--bundle', 'build/Build/Products/Debug-iphoneos/' + scheme + '.app', | ||
| '--id' , selectedDevice.udid, | ||
| '--justlaunch' | ||
| ]; | ||
| console.log(`installing and launching your app on ${selectedDevice.name}...`); | ||
| var iosDeployOutput = child_process.spawnSync('ios-deploy', iosDeployInstallArgs, {encoding: 'utf8'}); | ||
| if (iosDeployOutput.error) { | ||
| console.log(''); | ||
| console.log('** INSTALLATION FAILED **'); | ||
| console.log('Make sure you have ios-deploy installed globally.'); | ||
| console.log('(e.g "npm install -g ios-deploy")'); | ||
| } else { | ||
| console.log('** INSTALLATION SUCCEEDED **'); | ||
| } | ||
| return buildProject(xcodeProject, selectedDevice.udid, scheme) | ||
| .then((appName) => { | ||
| if (!appName) { | ||
| appName = scheme; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same nits as above |
||
| } | ||
| const iosDeployInstallArgs = [ | ||
| '--bundle', 'build/Build/Products/Debug-iphoneos/' + appName + '.app', | ||
| '--id' , selectedDevice.udid, | ||
| '--justlaunch' | ||
| ]; | ||
| console.log(`installing and launching your app on ${selectedDevice.name}...`); | ||
| const iosDeployOutput = child_process.spawnSync('ios-deploy', iosDeployInstallArgs, {encoding: 'utf8'}); | ||
| if (iosDeployOutput.error) { | ||
| console.log(''); | ||
| console.log('** INSTALLATION FAILED **'); | ||
| console.log('Make sure you have ios-deploy installed globally.'); | ||
| console.log('(e.g "npm install -g ios-deploy")'); | ||
| } else { | ||
| console.log('** INSTALLATION SUCCEEDED **'); | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| function buildProject(xcodeProject, udid, scheme) { | ||
| const xcodebuildArgs = [ | ||
| xcodeProject.isWorkspace ? '-workspace' : '-project', xcodeProject.name, | ||
| '-scheme', scheme, | ||
| '-destination', `id=${udid}`, | ||
| '-derivedDataPath', 'build', | ||
| ]; | ||
| console.log(`Building using "xcodebuild ${xcodebuildArgs.join(' ')}"`); | ||
| child_process.spawnSync('xcodebuild', xcodebuildArgs, {stdio: 'inherit'}); | ||
| return new Promise((resolve,reject) => | ||
| { | ||
| const xcodebuildArgs = [ | ||
| xcodeProject.isWorkspace ? '-workspace' : '-project', xcodeProject.name, | ||
| '-scheme', scheme, | ||
| '-destination', `id=${udid}`, | ||
| '-derivedDataPath', 'build', | ||
| ]; | ||
| console.log(`Building using "xcodebuild ${xcodebuildArgs.join(' ')}"`); | ||
| const buildProcess = child_process.spawn('xcodebuild', xcodebuildArgs); | ||
| let buildOutput = ""; | ||
| buildProcess.stdout.on('data', function(data) { | ||
| console.log(data.toString()); | ||
| buildOutput += data.toString(); | ||
| }); | ||
| buildProcess.stderr.on('data', function(data) { | ||
| console.error(data.toString()); | ||
| }); | ||
| buildProcess.on('close', function(code) { | ||
| //FULL_PRODUCT_NAME is the actual file name of the app, which actually comes from the Product Name in the build config, which does not necessary match a scheme name, example output line: export FULL_PRODUCT_NAME="Super App Dev.app" | ||
| let productNameMatch = /export FULL_PRODUCT_NAME="?(.+).app/.exec(buildOutput); | ||
| if (productNameMatch && productNameMatch.length && productNameMatch.length > 1) { | ||
| return resolve(productNameMatch[1]);//0 is the full match, 1 is the app name | ||
| } | ||
| return buildProcess.error? reject(error) : resolve(); | ||
| }); | ||
| }); | ||
| } | ||
|
|
||
|
|
||
| function matchingDevice(devices, deviceName) { | ||
| if (deviceName === true && devices.length === 1) | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the part that will honor an empty device name ("deviceName === true") with only one device connected, and use that name. Much easier for testing on a single device with zero side effects. |
||
| { | ||
| console.log(`Using first available device ${devices[0].name} due to lack of name supplied.`) | ||
| return devices[0]; | ||
| } | ||
| for (let i = devices.length - 1; i >= 0; i--) { | ||
| if (devices[i].name === deviceName || formattedDeviceName(devices[i]) === deviceName) { | ||
| return devices[i]; | ||
| } | ||
| } | ||
| } | ||
|
|
||
|
|
||
| function matchingDeviceByUdid(devices, udid) { | ||
| for (let i = devices.length - 1; i >= 0; i--) { | ||
| if (devices[i].udid === udid) { | ||
|
|
@@ -190,7 +227,7 @@ module.exports = { | |
| default: 'ios', | ||
| }, { | ||
| command: '--device [string]', | ||
| description: 'Explicitly set device to use by name', | ||
| description: 'Explicitly set device to use by name. The value is not required if you have a single device connected.', | ||
| },{ | ||
| command: '--udid [string]', | ||
| description: 'Explicitly set device to use by udid', | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const simulatorsThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
left as var because simulators is used outside the context of the try/catch it's in. Turn it into a const and you get "simulators is not defined" error. You could pull it out of the try/catch, but then you have to use let. So I thought var was actually the best choice here. If you want, I'll add "let simulators;" above the try. What do you prefer?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah yes,
let simulatorsoutside of the try-catch block would be the preferred style.