Skip to content

Conversation

@StevePotter
Copy link
Contributor

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.

@facebook-github-bot
Copy link
Contributor

By analyzing the blame information on this pull request, we identified @LearningDave and @grabbou to be potential reviewers.

@facebook-github-bot
Copy link
Contributor

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!

@facebook-github-bot facebook-github-bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Sep 29, 2016
@facebook-github-bot
Copy link
Contributor

@StevePotter updated the pull request - view changes

@facebook-github-bot facebook-github-bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Sep 29, 2016
@facebook-github-bot
Copy link
Contributor

@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
@facebook-github-bot
Copy link
Contributor

Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Facebook open source project. Thanks!

@facebook-github-bot facebook-github-bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Sep 29, 2016
@facebook-github-bot
Copy link
Contributor

@StevePotter updated the pull request - view changes

@facebook-github-bot facebook-github-bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Sep 29, 2016
@StevePotter
Copy link
Contributor Author

Not sure why the Travis CI failed. Can an expert look at this? I don't see how my fix would affect that script.

@facebook-github-bot
Copy link
Contributor

@StevePotter updated the pull request - view changes

@facebook-github-bot facebook-github-bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Oct 3, 2016
}

buildProject(xcodeProject, selectedSimulator.udid, scheme);
var appName = buildProject(xcodeProject, selectedSimulator.udid, scheme);
Copy link
Member

Choose a reason for hiding this comment

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

Use let for this


function runOnDevice(selectedDevice, scheme, xcodeProject){
buildProject(xcodeProject, selectedDevice.udid, scheme);
var appName = buildProject(xcodeProject, selectedDevice.udid, scheme);
Copy link
Member

Choose a reason for hiding this comment

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

Use let here too

child_process.spawnSync('xcodebuild', xcodebuildArgs, {stdio: 'inherit'});
var buildProcess = child_process.spawnSync('xcodebuild', xcodebuildArgs, {stdio: 'pipe'});
var buildOutput = buildProcess.stdout.toString();
console.log(buildOutput);
Copy link
Member

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?

Copy link
Contributor Author

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.

Copy link
Member

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done via promises.

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"));
Copy link
Member

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Will do

@facebook-github-bot
Copy link
Contributor

@StevePotter updated the pull request - view changes

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'
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@javache, this better?

Copy link
Member

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);

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

@facebook-github-bot facebook-github-bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Oct 10, 2016
…se child_process.spawn instead of spawnsync, which fixes the problem of build output not being shown in the console.
@facebook-github-bot
Copy link
Contributor

@StevePotter updated the pull request - view changes

@facebook-github-bot facebook-github-bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Oct 11, 2016
@facebook-github-bot
Copy link
Contributor

@StevePotter updated the pull request - view changes

@StevePotter
Copy link
Contributor Author

@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!

@facebook-github-bot facebook-github-bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Oct 11, 2016
@facebook-github-bot
Copy link
Contributor

@StevePotter updated the pull request - view changes



function matchingDevice(devices, deviceName) {
if (deviceName === true && devices.length === 1)
Copy link
Contributor Author

@StevePotter StevePotter Oct 11, 2016

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.

@facebook-github-bot facebook-github-bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Oct 11, 2016
Copy link
Member

@javache javache left a 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.

console.log(`Installing ${appPath}`);
child_process.spawnSync('xcrun', ['simctl', 'install', 'booted', appPath], {stdio: 'inherit'});
return new Promise((resolve) =>
{
Copy link
Member

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(
Copy link
Member

Choose a reason for hiding this comment

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

const simulators

Copy link
Contributor Author

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?

Copy link
Member

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.

})
.then((udid) => buildProject(xcodeProject, udid, scheme))
.then((appName) =>
{
Copy link
Member

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

.then((udid) => buildProject(xcodeProject, udid, scheme))
.then((appName) =>
{
if (!appName)
Copy link
Member

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;
Copy link
Member

Choose a reason for hiding this comment

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

Same nits as above

'--justlaunch'
];
console.log(`installing and launching your app on ${selectedDevice.name}...`);
let iosDeployOutput = child_process.spawnSync('ios-deploy', iosDeployInstallArgs, {encoding: 'utf8'});
Copy link
Member

Choose a reason for hiding this comment

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

This should be const

buildOutput += data.toString();
});
buildProcess.stderr.on('data', function(data) {
console.log(data.toString());
Copy link
Member

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.

});
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);
Copy link
Member

@javache javache Oct 11, 2016

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 "*"

//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)
{
Copy link
Member

Choose a reason for hiding this comment

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

nit: on previous line

@javache
Copy link
Member

javache commented Oct 11, 2016

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.

@facebook-github-bot facebook-github-bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Oct 11, 2016
@StevePotter
Copy link
Contributor Author

StevePotter commented Oct 12, 2016

@javache I fixed all issues you brought up, except one I commented on. I also commented on #10156, which is a great approach but unfortunately doesn't work yet.

@facebook-github-bot
Copy link
Contributor

@StevePotter updated the pull request - view changes

@facebook-github-bot facebook-github-bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Oct 12, 2016
@javache
Copy link
Member

javache commented Oct 14, 2016

@facebook-github-bot shipit

@facebook-github-bot facebook-github-bot added GH Review: accepted Import Started This pull request has been imported. This does not imply the PR has been approved. and removed GH Review: review-needed labels Oct 14, 2016
@facebook-github-bot
Copy link
Contributor

Thanks for importing. If you are a Facebook employee, you can view this diff on Phabricator.

@facebook-github-bot facebook-github-bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Oct 14, 2016
grabbou pushed a commit to react-native-community/cli that referenced this pull request Sep 26, 2018
…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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Import Started This pull request has been imported. This does not imply the PR has been approved.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants