-
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
Conversation
…l if Product Name and Scheme aren't equal
|
By analyzing the blame information on this pull request, we identified @LearningDave and @grabbou to be potential reviewers. |
|
Thank you for your pull request and welcome to our community. We require contributors to sign our Contributor License Agreement, and we don't seem to have you on file. In order for us to review and merge your code, please sign up at https://code.facebook.com/cla - and if you have received this in error or have any questions, please drop us a line at [email protected]. Thanks! |
|
@StevePotter updated the pull request - view changes |
|
@StevePotter updated the pull request - view changes |
…wnSync stdio param had to change from inherit to pipe, so user was no longer seeing build output
|
Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Facebook open source project. Thanks! |
|
@StevePotter updated the pull request - view changes |
|
Not sure why the Travis CI failed. Can an expert look at this? I don't see how my fix would affect that script. |
|
@StevePotter updated the pull request - view changes |
local-cli/runIOS/runIOS.js
Outdated
| } | ||
|
|
||
| buildProject(xcodeProject, selectedSimulator.udid, scheme); | ||
| var appName = buildProject(xcodeProject, selectedSimulator.udid, scheme); |
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.
Use let for this
local-cli/runIOS/runIOS.js
Outdated
|
|
||
| function runOnDevice(selectedDevice, scheme, xcodeProject){ | ||
| buildProject(xcodeProject, selectedDevice.udid, scheme); | ||
| var appName = buildProject(xcodeProject, selectedDevice.udid, scheme); |
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.
Use let here too
local-cli/runIOS/runIOS.js
Outdated
| child_process.spawnSync('xcodebuild', xcodebuildArgs, {stdio: 'inherit'}); | ||
| var buildProcess = child_process.spawnSync('xcodebuild', xcodebuildArgs, {stdio: 'pipe'}); | ||
| var buildOutput = buildProcess.stdout.toString(); | ||
| console.log(buildOutput); |
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.
Doing console.log like this means we will buffer all output and it will seem like the process is stuck while xcode is building. Can you stream it to the console while also scanning it?
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.
I had the same concern. I tried different combinations for stdout and got nothing that would pipe output and allow me to buffer it. I totally get the problem it creates but node basically gives you the option to buffer or pipe: https://nodejs.org/api/child_process.html#child_process_child_process_spawnsync_command_args_options
Another option could be to do try using child_process.spawn, as done here.
I'll see what I can do. But IMO it's worth it now, considering it is fixing a bug that I consider to be major. Even adding a console.log("Building, please wait this can take a while." before could be good enough for now.
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.
I think the spawn approach is more correct here. We're seeing issues with console output being hidden already on travis (e.g. your build hits a timeout and gets killed, this console.log will never get executed).
Given that in the default configuration for RN, the app name is identical to the scheme, I think we should try to get it right instead of a hacking a fix in.
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.
Done via promises.
local-cli/runIOS/runIOS.js
Outdated
| var buildOutput = buildProcess.stdout.toString(); | ||
| console.log(buildOutput); | ||
| //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" | ||
| var exportFullProductLines = buildOutput.match(new RegExp("export FULL_PRODUCT_NAME=.+\.app")); |
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.
Use a regex capture group to match once here and extract the value you're looking for later: http://www.regular-expressions.info/refcapture.html
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.
Will do
…atching for the app name
|
@StevePotter updated the pull request - view changes |
local-cli/runIOS/runIOS.js
Outdated
| let productNameMatch = /(export FULL_PRODUCT_NAME=)("*)(.+)(.app)/.exec(buildOutput); | ||
| if (productNameMatch && productNameMatch.length && productNameMatch.length > 3) | ||
| { | ||
| return productNameMatch[3];//0 is the full match, 1 is 'export FULL_PRODUCT_NAME=', 2 is possible double quote, 3 is app name and 4 is '.app' |
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.
@javache, this better?
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.
You don't need to use () for everything, just put it around the part of the input string you care about.
const productNameMatch = /export FULL_PRODUCT_NAME="?(.+)\.app/.exec(buildOutput);
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.
Done
…se child_process.spawn instead of spawnsync, which fixes the problem of build output not being shown in the console.
|
@StevePotter updated the pull request - view changes |
|
@StevePotter updated the pull request - view changes |
|
@javache, I employed promises to allow for the use of child_process.spawn function, which then you can listen to events on asynchronously. This allowed me to buffer the build output to isolate the app name while still streaming build output to the console. All your other requests have been fulfilled. I also added the fix from PR 10155, which allows you just use --device without a name if you have a single device connected. This makes testing easier, as entering a full device name is tedious and typically in my experience there is just one device attached at a time. Please review. Thanks! |
…nto fixiosappname
|
@StevePotter updated the pull request - view changes |
|
|
||
|
|
||
| function matchingDevice(devices, deviceName) { | ||
| if (deviceName === true && devices.length === 1) |
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.
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.
javache
left a comment
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.
Looks good! Thanks for making these changes. Just a couple of style nits.
local-cli/runIOS/runIOS.js
Outdated
| console.log(`Installing ${appPath}`); | ||
| child_process.spawnSync('xcrun', ['simctl', 'install', 'booted', appPath], {stdio: 'inherit'}); | ||
| return new Promise((resolve) => | ||
| { |
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.
nit: have opening brace on previous line
| return new Promise((resolve) => | ||
| { | ||
| try { | ||
| var simulators = JSON.parse( |
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 simulators
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.
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 simulators outside of the try-catch block would be the preferred style.
local-cli/runIOS/runIOS.js
Outdated
| }) | ||
| .then((udid) => buildProject(xcodeProject, udid, scheme)) | ||
| .then((appName) => | ||
| { |
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.
nit: open brace on previous line
local-cli/runIOS/runIOS.js
Outdated
| .then((udid) => buildProject(xcodeProject, udid, scheme)) | ||
| .then((appName) => | ||
| { | ||
| if (!appName) |
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.
nit: Add braces after the if
| .then((appName) => | ||
| { | ||
| if (!appName) | ||
| appName = scheme; |
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.
Same nits as above
local-cli/runIOS/runIOS.js
Outdated
| '--justlaunch' | ||
| ]; | ||
| console.log(`installing and launching your app on ${selectedDevice.name}...`); | ||
| let iosDeployOutput = child_process.spawnSync('ios-deploy', iosDeployInstallArgs, {encoding: 'utf8'}); |
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.
This should be const
local-cli/runIOS/runIOS.js
Outdated
| buildOutput += data.toString(); | ||
| }); | ||
| buildProcess.stderr.on('data', function(data) { | ||
| console.log(data.toString()); |
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.
This should be console.error? Also, indent with 2 spaces.
local-cli/runIOS/runIOS.js
Outdated
| }); | ||
| 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); |
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.
Since there's just going to be 1 or 0 " in your regex, use "? instead of "*"
local-cli/runIOS/runIOS.js
Outdated
| //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) | ||
| { |
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.
nit: on previous line
|
Could you also check out the approach taken in https://github.com/facebook/react-native/pull/10156/files. It seems to be a lot simpler. |
|
@StevePotter updated the pull request - view changes |
|
@facebook-github-bot shipit |
|
Thanks for importing. If you are a Facebook employee, you can view this diff on Phabricator. |
…equal Summary: In xcode, you can modify the Product Name per build configuration. During the build, the app file name is made using that value. For example, I name my app "MyApp Dev", it will build into "MyApp Dev.app". react-native run-ios doesn't extract the proper app name. Instead it uses the scheme name + ".app". So if in the example above I use the default Scheme "MyApp", which references the build configuration whose name is "MyApp Dev". The build will succeed, but when runIOS.js goes to run the app, it fails because the file name doesn't exist. My fix parses the build output and extracts the app name from the line "export FULL_PRODUCT_NAME=$(appfilename)" and uses that instead of the scheme. If there is any issue parsing, the scheme name is used like it currently is. **Test plan (required)** 1) Change the Product Name in xcode project manager to be something different than the scheme name. 2) Run react-native run-ios and ensure it works. Closes facebook/react-native#10178 Differential Revision: D4022116 Pulled By: javache fbshipit-source-id: c1bd8e7a1f6364d681c505557a96955a293bc05c
In xcode, you can modify the Product Name per build configuration. During the build, the app file name is made using that value. For example, I name my app "MyApp Dev", it will build into "MyApp Dev.app".
react-native run-ios doesn't extract the proper app name. Instead it uses the scheme name + ".app". So if in the example above I use the default Scheme "MyApp", which references the build configuration whose name is "MyApp Dev". The build will succeed, but when runIOS.js goes to run the app, it fails because the file name doesn't exist.
My fix parses the build output and extracts the app name from the line "export FULL_PRODUCT_NAME=$(appfilename)" and uses that instead of the scheme. If there is any issue parsing, the scheme name is used like it currently is.
Test plan (required)