대규모 iOS 저장소를 클라우드 Mac에서 몇 주 동안 운영하다 보면 특정 브랜치에서 갑자기 스크립트가 argument list too long 오류를 내는 반면, 인접한 브랜치는 여전히 정상적으로 아카이브될 수 있습니다. 이때 DerivedData부터 지우거나 Xcode의 일시적인 문제로 단정하지 마세요. 이 오류는 대개 execve가 반환하는 E2BIG에서 비롯됩니다. 명령 인자, 각 인자의 종료 문자, 상속된 환경이 프로세스에서 사용할 수 있는 공간을 모두 차지한 것입니다.
먼저 어느 계층에서 실패했는지 확인하기
첫 단계는 전체 명령과 종료 상태를 보존하는 것입니다. 로그에 ‘스크립트 단계 실패’만 표시된다면 해당 Run Script에서 일시적으로 set -x를 활성화해 실패한 대상이 find, rm, 아카이브 도구, 코드 생성기 또는 컴파일러 드라이버 중 무엇인지 확인하세요. 전체 환경을 공개 로그에 출력해서는 안 됩니다. 토큰이 포함될 수 있으므로 변수 이름과 바이트 수, 민감 정보를 제거한 명령 구조만 기록해야 합니다.
흔한 원인은 세 가지입니다. 와일드카드가 한 번에 수만 개의 경로로 확장되는 경우, 스크립트가 모든 소스 파일을 하나의 변수에 이어 붙이는 경우, CI가 대용량 JSON이나 인증서 내용 또는 여러 줄로 된 설정을 환경 변수로 주입하는 경우입니다. 긴 작업 공간 경로와 반복되는 -I, -F, -D 인자도 남은 공간을 계속 소모합니다.
작은 브랜치에서 실행된다고 해서 명령 구조가 올바른 것은 아닙니다. 파일 수가 늘어난 뒤에야 실패한다면 대개 입력 전달 방식에 상한이 설계되어 있지 않다는 뜻입니다.
인자와 환경의 여유 공간 측정하기
macOS에서 사용할 수 있는 한도는 명령 텍스트 길이만으로 판단할 수 없습니다. 먼저 빌드 작업과 동일한 사용자 및 실행 방식으로 값을 측정하세요.
getconf ARG_MAX
python3 - <<'PY'
import os
limit = os.sysconf("SC_ARG_MAX")
env_bytes = sum(len(k) + len(v) + 2 for k, v in os.environ.items())
largest = sorted(
((len(k) + len(v) + 2, k) for k, v in os.environ.items()),
reverse=True
)[:10]
print("arg_max", limit)
print("environment_bytes", env_bytes)
for size, key in largest:
print(size, key)
PY
이 결과는 비교용이며, 전부 사용할 수 있는 예산으로 간주해서는 안 됩니다. 시스템에는 포인터, 종료 문자, 프로세스 시작 오버헤드를 저장할 공간도 필요합니다. 실무에서는 충분한 여유를 확보하고, 환경 크기가 과거 기준선보다 갑자기 증가했는지 확인해야 합니다.
비대해진 변수 찾기
PATH, 검색 경로, 임시 디렉터리, 의존성 관리자 인자와 CI 주입 변수를 우선 확인하세요. JSON 설정이 수십 KB에 이른다면 접근 권한이 제한된 임시 파일에 저장하고, 하위 프로세스에는 파일 경로만 전달해야 합니다. PATH를 반복해서 추가해야 한다면 각 단계에서 다시 이어 붙이지 말고 먼저 중복을 제거하세요.
도구 체인을 손상시키지 않고 상속 환경 줄이기
빈 환경에서 전체 Xcode 빌드를 바로 시작해서는 안 됩니다. HOME, PATH, 임시 디렉터리, 개발자 디렉터리가 누락되면 새로운 오류가 발생합니다. 더 안정적인 방법은 개별 도구별로 허용 목록을 구성하는 것입니다.
env -i \
HOME="$HOME" \
PATH="/usr/bin:/bin:/usr/sbin:/sbin" \
TMPDIR="$TMPDIR" \
DEVELOPER_DIR="$DEVELOPER_DIR" \
/bin/zsh -lc 'xcrun --find xcodebuild'
실제 작업에서는 사용하는 의존성에 맞춰 로케일, 캐시 디렉터리, 프록시 설정도 추가해야 합니다. 민감한 내용은 수명이 짧은 파일로 전달하고 사용 후 삭제하세요. 일반적인 불리언 플래그와 짧은 식별자는 환경 변수로 유지해도 됩니다. SetMini의 자동화 작업에서도 환경 초기화를 하나의 진입점 스크립트에 집중시켜 대화형 shell, CI runner, Xcode 스크립트가 각각 설정을 반복해서 추가하지 않도록 해야 합니다.
긴 파일 목록을 명령줄 밖으로 옮기기
범용 스크립트에서 널 문자로 구분된 배치 사용하기
파일 이름에는 공백이나 줄바꿈이 포함될 수 있으므로 for f in $(find ...)를 사용하면 안 됩니다. 배치 처리를 지원하는 도구는 find -print0 및 xargs -0와 함께 사용할 수 있습니다.
find "$PWD/Artifacts" -type f -name '*.dSYM' -print0 |
xargs -0 -n 50 /usr/bin/file
-n 50은 각 배치에 명확한 상한을 설정합니다. 도구가 표준 입력에서 목록을 읽을 수 있다면 표준 입력을 우선 사용해 프로세스가 반복해서 시작되는 횟수를 줄이세요.
Xcode 단계에서 파일 목록 사용하기
Run Script의 입력과 출력은 .xcfilelist에 작성한 뒤 Input File Lists와 Output File Lists에 설정해야 합니다. 이렇게 하면 Xcode가 의존성을 추적할 수 있고, 스크립트도 수천 개의 경로를 하나의 명령으로 확장할 필요가 없습니다. 컴파일러나 링커가 응답 파일을 명시적으로 지원한다면 변경이 적은 인자를 응답 파일에 작성할 수 있습니다. 모든 서드 파티 도구가 @file 구문을 이해한다고 가정해서는 안 됩니다.
빌드 설정에서 전달되는 옵션이 많다면 공통 값은 .xcconfig에 넣어야 합니다. 스크립트에는 꼭 필요한 소수의 인자만 전달하고, 경로 집합은 파일 목록으로, 구조화된 설정은 임시 파일로 전달하세요. 세 가지 입력을 분리하면 로그도 더 쉽게 감사할 수 있습니다.
증가 방지 기준과 회귀 검사 마련하기
수정 후에는 파일 수가 가장 많고 경로가 가장 긴 브랜치에서 클린 빌드를 한 번 이상 실행한 다음 증분 빌드도 한 번 실행하세요. 파일 이름에 공백이 포함되어도 스크립트가 올바르게 작동하는지, 배치 처리 실패 시 0이 아닌 상태를 반환하는지, 종료 경로에서 임시 파일이 정리되는지 확인해야 합니다.
CI 시작 단계에서 ARG_MAX, 전체 환경 바이트 수, 가장 큰 변수 이름을 기록할 수 있지만 변수 값은 기록하지 마세요. 환경 크기에 대한 팀 기준선을 설정하고, 임계값을 초과하면 시스템이 프로세스 생성을 거부할 때까지 기다리지 말고 경고를 표시해야 합니다. 계속 증가하는 파일 집합에 대해서는 파일 수와 가장 긴 경로의 길이도 함께 기록하세요.
최종 목표는 현재 명령을 간신히 한도 아래로 맞추는 것이 아니라, 저장소 규모가 커져도 명령 길이가 선형으로 증가하지 않게 만드는 것입니다. 환경을 작게 유지하고, 파일 집합을 전용 경로로 전달하며, 배치 처리에 고정된 상한을 두어야 E2BIG를 간헐적인 장애가 아닌 사전에 발견할 수 있는 설정 문제로 바꿀 수 있습니다.
자주 묻는 질문
같은 빌드 명령이 일부 브랜치에서만 실패하는 이유는 무엇인가요?
인자와 상속 환경이 하나의 프로세스 한도를 공유하기 때문입니다. 파일 수, 경로 길이 또는 주입 변수가 늘어난 브랜치만 남은 여유를 소진할 수 있습니다.
xargs와 응답 파일은 어떻게 선택해야 하나요?
일반 배치 작업에는 NUL 구분 xargs를 사용합니다. 컴파일러나 링커가 공식 지원한다면 응답 파일을 우선하고 Xcode 스크립트 입력에는 xcfilelist를 사용합니다.
macOS의 ARG_MAX를 높이면 해결되나요?
시스템 한도 변경에 의존하지 않는 편이 안전합니다. 환경과 중복 옵션을 줄이고 긴 파일 집합을 파일, 표준 입력 또는 크기가 제한된 배치로 전달하세요.
언제든지 접속할 수 있는 클라우드 Mac에 엔지니어링 작업을 담아보세요
두 가지 Apple Silicon 구성과 네 곳의 데이터센터 중에서 선택하세요. 리소스는 다른 테넌트와 공유되지 않으며, 실제 사용 가능 여부는 콘솔에서 실시간으로 확인할 수 있습니다.