Skip to content

Couldn't find "PLATFORM_NAME" variable in Xcodebuild output when providing binaryPath #2517

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

Open
emilundg opened this issue Oct 1, 2024 · 8 comments

Comments

@emilundg
Copy link

emilundg commented Oct 1, 2024

Environment

System:
  OS: macOS 14.6.1
  CPU: (12) arm64 Apple M2 Pro
  Memory: 435.45 MB / 32.00 GB
  Shell:
    version: 3.6.1
    path: /opt/homebrew/bin/fish
Binaries:
  Node:
    version: 22.7.0
    path: /opt/homebrew/bin/node
  Yarn:
    version: 1.22.19
    path: /opt/homebrew/bin/yarn
  npm:
    version: 10.8.2
    path: /opt/homebrew/bin/npm
  Watchman: Not Found
Managers:
  CocoaPods:
    version: 1.15.0
    path: /Users/kgwc595/.rbenv/shims/pod
SDKs:
  iOS SDK:
    Platforms:
      - DriverKit 24.0
      - iOS 18.0
      - macOS 15.0
      - tvOS 18.0
      - visionOS 2.0
      - watchOS 11.0
  Android SDK: Not Found
IDEs:
  Android Studio: 2023.3 AI-233.14808.21.2331.11709847
  Xcode:
    version: 16.0/16A242d
    path: /usr/bin/xcodebuild
Languages:
  Java:
    version: 17.0.11
    path: /opt/homebrew/opt/openjdk@17/bin/javac
  Ruby:
    version: 3.1.4
    path: /Users/kgwc595/.rbenv/shims/ruby
npmPackages:
  "@react-native-community/cli":
    installed: 13.6.1
    wanted: 13.6.1
  react:
    installed: 18.3.1
    wanted: 18.3.1
  react-native:
    installed: 0.74.1
    wanted: 0.74.1
  react-native-macos: Not Found
npmGlobalPackages:
  "*react-native*": Not Found
Android:
  hermesEnabled: Not found
  newArchEnabled: Not found
iOS:
  hermesEnabled: false
  newArchEnabled: false

Description

When providing the option --binary-path to run-ios the following error happens and fails the install:

Couldn't find "PLATFORM_NAME variable in Xcodebuild output

After doing some digging in the code for cli-platform-apple we see that in the file getBuildSettings.ts the function getPlatformName is called with buildOutput. The problem is that in the file runOnSimulator.ts (that calls getBuildSettings) the buildOutput variable is set as an empty string when providing a binaryPath.

Reproducible Demo

Haven't tried in a fresh React Native project but it should be present there as well. So create a React Native project with an iOS artifact already built and then run:

yarn react-native run-ios --binary-path= ./ios/build/Build/Products/Debug-iphonesimulator/MyApp.app

Copy link

There hasn't been any activity on this issue in the past 3 months, so it has been marked as stale and it will be closed automatically if no further activity occurs in the next 7 days.

@jenskuhrjorgensen
Copy link

@emilundg I'm running into this as well during my RN 0.79.2 upgrade. Did you find a solution?

@jenskuhrjorgensen
Copy link

Currently, I had to downgrade the CLI packages to 12.3.7 in order for it to work properly with pre-built binaries on iOS :(

@jenskuhrjorgensen
Copy link

jenskuhrjorgensen commented May 14, 2025

@cipolleschi or @mikehardy would you be able to reopen this issue as it still exists?

The short version of the issue is that when you run npx react-native run-ios with --binary-path the CLI will execute runOnSimulator but it will not assign a value to buildOutput (because binaryPath is set) which means the call to installApp will eventually fail because of a call to getBuildSettings which calls getPlatformName(buildOutput) - with an undefined buildOutput because the app was already built.

@cipolleschi
Copy link
Contributor

@szymonrybczak @thymikee can you check whether this is still valid and reopen it if needed?

@thymikee thymikee reopened this May 15, 2025
@jenskuhrjorgensen
Copy link

@thymikee thanks for reopening. I can add that this can be reproduced in a vanilla RN project with:

npx @react-native-community/cli@latest init AwesomeProject --version 0.79.2

And then running run-ios with a random file, e.g.:

npx react-native run-ios --binary-path index.js

This gives the following output:

info Found Xcode project "AwesomeProject.xcodeproj"
info Found booted iPhone 15 Pro
node:internal/process/promises:392
      new UnhandledPromiseRejection(reason);
      ^

UnhandledPromiseRejection: This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). The promise rejected with the reason "Error: Couldn't find "PLATFORM_NAME" variable in xcodebuild output. Please report this issue and run your project with Xcode instead.".
    at throwUnhandledRejectionsMode (node:internal/process/promises:392:7)
    at processPromiseRejections (node:internal/process/promises:475:17)
    at process.processTicksAndRejections (node:internal/process/task_queues:106:32) {
  code: 'ERR_UNHANDLED_REJECTION'
}

Node.js v22.9.0

@github-actions github-actions bot removed the stale label May 16, 2025
@jenskuhrjorgensen
Copy link

jenskuhrjorgensen commented May 28, 2025

@szymonrybczak or @thymikee any news on this?

@jenskuhrjorgensen
Copy link

jenskuhrjorgensen commented May 29, 2025

For now, I've created this patch to fix various issues with the CLI (primarily issues with prebuilt binaries):

Patch part 1:
Fix destination in buildProject when running with device arg but no specific UDID or args.destination.

Patch part 2:
Select the right target in getBuildSettings when having multiple targets in the Xcode build scheme (my "app target" is the third one in the build list).

Patch part 3:
Harcode PLATFORM_NAME in getPlatformName (getBuildSettings) because it cannot detect it when using prebuilt binaries. I've simply hardcoded it to 'iphonesimulator' but it appears to also work fine when installing a prebuilt binary on a physical device.

Patch part 4:
Fix plist path in installApp when running with a prebuilt binary (when appPath is set) located at a custom path.

diff --git a/node_modules/@react-native-community/cli-platform-apple/build/commands/buildCommand/buildProject.js b/node_modules/@react-native-community/cli-platform-apple/build/commands/buildCommand/buildProject.js
index e41378d..1b1fa68 100644
--- a/node_modules/@react-native-community/cli-platform-apple/build/commands/buildCommand/buildProject.js
+++ b/node_modules/@react-native-community/cli-platform-apple/build/commands/buildCommand/buildProject.js
@@ -48,12 +48,26 @@ function prettifyXcodebuildMessages(output) {
 }
 function buildProject(xcodeProject, platform, udid, mode, scheme, args) {
   return new Promise((resolve, reject) => {
-    const simulatorDest = _simulatorDestinationMap.simulatorDestinationMap === null || _simulatorDestinationMap.simulatorDestinationMap === void 0 ? void 0 : _simulatorDestinationMap.simulatorDestinationMap[platform];
-    if (!simulatorDest) {
-      reject(new (_cliTools().CLIError)(`Unknown platform: ${platform}. Please, use one of: ${Object.values(_cliConfigApple().supportedPlatforms).join(', ')}.`));
-      return;
+    const isDevice = args.device
+    let destination = ""
+    if (udid) {
+      destination = `id=${udid}`
+    } else if (isDevice) {
+      destination = "generic/platform=iOS"
+    } else if (mode === 'Debug') {
+      const simulatorDest = _simulatorDestinationMap.simulatorDestinationMap === null || _simulatorDestinationMap.simulatorDestinationMap === void 0 ? void 0 : _simulatorDestinationMap.simulatorDestinationMap[platform];
+      if (!simulatorDest) {
+        reject(new (_cliTools().CLIError)(`Unknown platform: ${platform}. Please, use one of: ${Object.values(_cliConfigApple().supportedPlatforms).join(', ')}.`));
+        return;
+      }
+      destination = `generic/platform=${simulatorDest}`
+    } else {
+      destination = `generic/platform=${platform}`
+    }
+    if (args.destination) {
+      destination += `,${args.destination}`
     }
-    const xcodebuildArgs = [xcodeProject.isWorkspace ? '-workspace' : '-project', xcodeProject.name, ...(args.xcconfig ? ['-xcconfig', args.xcconfig] : []), ...(args.buildFolder ? ['-derivedDataPath', args.buildFolder] : []), '-configuration', mode, '-scheme', scheme, '-destination', (udid ? `id=${udid}` : mode === 'Debug' ? `generic/platform=${simulatorDest}` : `generic/platform=${platform}`) + (args.destination ? ',' + args.destination : '')];
+    const xcodebuildArgs = [xcodeProject.isWorkspace ? '-workspace' : '-project', xcodeProject.name, ...(args.xcconfig ? ['-xcconfig', args.xcconfig] : []), ...(args.buildFolder ? ['-derivedDataPath', args.buildFolder] : []), '-configuration', mode, '-scheme', scheme, '-destination', destination];
     if (args.extraParams) {
       xcodebuildArgs.push(...args.extraParams);
     }
diff --git a/node_modules/@react-native-community/cli-platform-apple/build/commands/runCommand/getBuildSettings.js b/node_modules/@react-native-community/cli-platform-apple/build/commands/runCommand/getBuildSettings.js
index 8e60c59..1a86938 100644
--- a/node_modules/@react-native-community/cli-platform-apple/build/commands/runCommand/getBuildSettings.js
+++ b/node_modules/@react-native-community/cli-platform-apple/build/commands/runCommand/getBuildSettings.js
@@ -34,7 +34,7 @@ async function getBuildSettings(xcodeProject, mode, buildOutput, scheme, target)
   const targets = settings.map(({
     target: settingsTarget
   }) => settingsTarget);
-  let selectedTarget = targets[0];
+  let selectedTarget = targets[2]; // In our Xcode schemes, the actual app is the third target in the build list
   if (target) {
     if (!targets.includes(target)) {
       _cliTools().logger.info(`Target ${_chalk().default.bold(target)} not found for scheme ${_chalk().default.bold(scheme)}, automatically selected target ${_chalk().default.bold(selectedTarget)}`);
@@ -54,9 +54,11 @@ async function getBuildSettings(xcodeProject, mode, buildOutput, scheme, target)
 }
 function getPlatformName(buildOutput) {
   // Xcode can sometimes escape `=` with a backslash or put the value in quotes
-  const platformNameMatch = /export PLATFORM_NAME\\?="?(\w+)"?$/m.exec(buildOutput);
+  let platformNameMatch = /export PLATFORM_NAME\\?="?(\w+)"?$/m.exec(buildOutput);
   if (!platformNameMatch) {
-    throw new (_cliTools().CLIError)('Couldn\'t find "PLATFORM_NAME" variable in xcodebuild output. Please report this issue and run your project with Xcode instead.');
+    platformNameMatch = ["","iphonesimulator"] // Hardcode platform because it cannot be found when we are just trying to install a prebuilt binary and not build a new one. This is an overly simple fix for the bug and can potentially hide other iOS building issues. It also seems to work when installing prebuilt binaries on physical iOS devices.
+    console.warn(`Manually setting 'platform' to '${platformNameMatch[1]}' due to a bug in the CLI with prebuilt binaries: https://github.com/react-native-community/cli/issues/2517.`)
+    // throw new (_cliTools().CLIError)('Couldn\'t find "PLATFORM_NAME" variable in xcodebuild output. Please report this issue and run your project with Xcode instead.');
   }
   return platformNameMatch[1];
 }
diff --git a/node_modules/@react-native-community/cli-platform-apple/build/commands/runCommand/installApp.js b/node_modules/@react-native-community/cli-platform-apple/build/commands/runCommand/installApp.js
index f95f97a..e796d62 100644
--- a/node_modules/@react-native-community/cli-platform-apple/build/commands/runCommand/installApp.js
+++ b/node_modules/@react-native-community/cli-platform-apple/build/commands/runCommand/installApp.js
@@ -61,9 +61,11 @@ async function installApp({
   if (!appPath) {
     appPath = await (0, _getBuildPath.getBuildPath)(buildSettings, platform);
   }
+
   const targetBuildDir = buildSettings.TARGET_BUILD_DIR;
   const infoPlistPath = buildSettings.INFOPLIST_PATH;
-  if (!infoPlistPath) {
+  const plistPath = appPath ? _path().default.join(appPath, "Info.plist") : _path().default.join(targetBuildDir, infoPlistPath)
+  if (!plistPath) {
     throw new (_cliTools().CLIError)('Failed to find Info.plist');
   }
   if (!targetBuildDir) {
@@ -76,7 +78,7 @@ async function installApp({
       stdio: 'inherit'
     });
   }
-  const bundleID = _child_process().default.execFileSync('/usr/libexec/PlistBuddy', ['-c', 'Print:CFBundleIdentifier', _path().default.join(targetBuildDir, infoPlistPath)], {
+  const bundleID = _child_process().default.execFileSync('/usr/libexec/PlistBuddy', ['-c', 'Print:CFBundleIdentifier', plistPath], {
     encoding: 'utf8'
   }).trim();
   _cliTools().logger.info(`Launching "${_chalk().default.bold(bundleID)}"`);

The patch seems to work for new builds, builds with prebuilt binaries and both on simulators and physical devices.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants