Detach Crashlytics upload into its own process group (setsid) so iOS archive stops hanging

Co-authored-by: premnirmal <1255689+premnirmal@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-08-01 09:57:58 +00:00
committed by GitHub
co-authored by premnirmal
parent 4421901a4c
commit a2c513ab9b
2 changed files with 36 additions and 23 deletions
+9 -6
View File
@@ -203,12 +203,15 @@ information has to be available to Crashlytics:
and `upload-symbols` are synchronous, so running them on Debug simulator builds only slows them down
(and appears to hang the build over a slow/blocked network) for symbols you never need locally. Even
for `Release`, the entire Crashlytics sequence (the `run` helper **and** the `upload-symbols`
invocations) is **detached to the background** so the build/archive finishes immediately instead of
hanging at "Run custom shell script 'Firebase Crashlytics'" while the network transfer completes.
`nohup … &` alone is **not** sufficient — Xcode's build system waits for the run-script phase's whole
process group, so the job is launched inside a `( … & )` subshell (with stdio redirected to
`crashlytics-upload-symbols.log` next to `DerivedData`) so it is reparented away from the shell Xcode
monitors and the phase returns right away.
invocations) is **detached into its own session/process group** so the build/archive finishes
immediately instead of hanging at "Run custom shell script 'Firebase Crashlytics'" while the network
transfer completes. Plain backgrounding (`nohup … &`, even inside a `( … & )` subshell) is **not**
sufficient: it reparents the process but leaves it in the build phase's **process group**, and
`xcodebuild` (the `archive` driver) blocks until that whole process group drains — so the archive
keeps waiting on the upload. The job is therefore `fork`+`setsid`-ed (via macOS's system Perl, since
`setsid` is not a macOS command) into a fresh process group, with stdio redirected to
`crashlytics-upload-symbols.log` next to `DerivedData`, so it is no longer part of the phase's group
and the phase returns right away.
No extra setup is required beyond dropping in the `GoogleService-Info.plist` and regenerating the
project; release/archive builds upload the symbols automatically.
+27 -17
View File
@@ -147,25 +147,35 @@ targets:
# the network SYNCHRONOUSLY, so running either in the foreground stalls the
# build phase — the archive appears to hang at "Run custom shell script
# 'Firebase Crashlytics'" until every dSYM has been uploaded (and can hang
# indefinitely on a slow/blocked network). Detaching with only `nohup … &`
# is NOT enough: Xcode's build system waits for the run-script phase's whole
# process group, so a backgrounded child that is still a direct child of the
# phase shell keeps the archive blocked. Wrap the job in a `( … & )` subshell
# so the intermediate subshell exits immediately and the upload is reparented
# away from the shell Xcode monitors; combined with `nohup` and fully
# redirected stdio (so no build pipe is held open), the build/archive finishes
# right away while the uploads — `run` then the supplementary `upload-symbols`
# invocations — complete in the background. A standalone Shared.framework.dSYM
# indefinitely on a slow/blocked network).
#
# Plain backgrounding (`nohup … &`, even wrapped in a `( … & )` subshell) is
# NOT enough here: those reparent the process to launchd but leave it in the
# build phase's PROCESS GROUP, and `xcodebuild` (the `archive` driver) blocks
# until that whole process group drains — so the archive keeps waiting on the
# still-running upload. The only reliable fix is to move the upload into its
# OWN session/process group so it is no longer part of the phase's group.
# macOS does not ship `setsid`, but its system Perl provides POSIX::setsid,
# so fork+setsid the upload via Perl: the phase returns immediately while the
# uploads — `run` then the supplementary `upload-symbols` invocations — run to
# completion detached in the background. A standalone Shared.framework.dSYM
# (e.g. a future dynamic-framework build) is uploaded too when present.
SHARED_DSYM="${SRCROOT}/../shared/build/xcode-frameworks/${CONFIGURATION}/${SDK_NAME}/Shared.framework.dSYM"
UPLOAD_LOG="${BUILD_DIR%/Build/*}/crashlytics-upload-symbols.log"
( nohup sh -c '
"$0"
"$1" -gsp "$2" -p ios "$3"
if [ -d "$4" ]; then "$1" -gsp "$2" -p ios "$4"; fi
' "$RUN" "$UPLOAD_SYMBOLS" "$GSP" "${DWARF_DSYM_FOLDER_PATH}" "$SHARED_DSYM" \
>"$UPLOAD_LOG" 2>&1 </dev/null & )
echo "Crashlytics dSYM upload started in the background (log: $UPLOAD_LOG)."
export UPLOAD_LOG="${BUILD_DIR%/Build/*}/crashlytics-upload-symbols.log"
/usr/bin/env perl -e '
use POSIX qw(setsid);
exit 0 if fork; # phase shell returns immediately
setsid(); # escape xcodebuild archive process group
open(STDIN, "<", "/dev/null");
open(STDOUT, ">", $ENV{UPLOAD_LOG});
open(STDERR, ">&", STDOUT);
exec @ARGV;
' /bin/sh -c '
"$0"
"$1" -gsp "$2" -p ios "$3"
if [ -d "$4" ]; then "$1" -gsp "$2" -p ios "$4"; fi
' "$RUN" "$UPLOAD_SYMBOLS" "$GSP" "${DWARF_DSYM_FOLDER_PATH}" "$SHARED_DSYM"
echo "Crashlytics dSYM upload started detached (log: $UPLOAD_LOG)."
preBuildScripts:
- name: Compile Kotlin/Native framework
# Always run so the framework is rebuilt when the shared code changes.