Into the app앱 안으로

A bundle on disk is not a bundle in your APK. Staging is the step between.

디스크의 번들은 APK 안의 번들이 아닙니다. 스테이징이 그 사이의 단계입니다.

What staging does스테이징이 하는 일

stagePythonBundleAndroid, …Ios and …Desktop each copy a bundle's python/ directory into build/pythonStaging/<platform>/python/. stagePythonBundle runs all three.

stagePythonBundleAndroid, …Ios, …Desktop 은 각각 번들의 python/ 디렉터리를 build/pythonStaging/<platform>/python/ 로 복사합니다. stagePythonBundle 은 셋을 모두 실행합니다.

  • Only python/ travels: the manifest is build metadata.python/ 만 옮깁니다: 매니페스트는 빌드 메타데이터입니다.
  • The previous payload is deleted first, so a module you removed stops shipping.이전 페이로드를 먼저 지우므로, 지운 모듈은 더 이상 실리지 않습니다.
  • The payload is a declared input: change your Python and the next build re-stages.페이로드가 선언된 입력이라, Python 을 바꾸면 다음 빌드가 다시 스테이징합니다.
  • Everything stays under build/; nothing is generated into src/.모든 것이 build/ 아래에 있고, src/ 에는 아무것도 생성하지 않습니다.

Which bundle goes where어느 번들이 어디로

A packaging step takes one directory, so one variant is chosen per destination: only from variants of the active build type:

패키징 단계는 디렉터리 하나를 받으므로 목적지마다 변형 하나를 고릅니다; 활성 빌드 타입의 변형 중에서만.

Destination목적지Chosen variant고르는 변형Handed to넘겨받는 곳
Desktop데스크톱the one whose triple is the host's, else the first declared호스트와 트리플이 같은 것, 없으면 처음 선언된 것the JVM target's processResources → jarJVM 타깃의 processResources → jarpartial부분
AndroidAndroidthe first declared처음 선언된 것android.sourceSets.main.assets → APKandroid.sourceSets.main.assets → APKpartial부분
iOSiOSthe first declared처음 선언된 것stagePythonBundleIosForXcode prints the path; an Xcode build phase copies itstagePythonBundleIosForXcode 가 경로를 출력하고 Xcode 빌드 단계가 복사합니다partial부분

Without a declared platform variant, every destination gets the single host bundle and the build logs that it did so. A destination with no matching variant stages nothing rather than a bundle built for another platform.

선언된 플랫폼 변형이 없으면 모든 목적지가 호스트 번들 하나를 받고, 빌드가 그 사실을 기록합니다. 맞는 변형이 없는 목적지는 다른 플랫폼용 번들 대신 아무것도 스테이징하지 않습니다.

iOS: the Xcode build phaseiOS: Xcode 빌드 단계

stagePythonBundleIosForXcode runs stagePythonBundleIos and prints exactly one line, PYTHON_PAYLOAD_DIR=<absolute path>, even under --quiet. It fails, naming why, when no iOS variant of the active build type exists or the staged directory is empty (no package configured). It never copies into the .app. The phase that does is owned by python-multiplatform (its #59); this plugin only stages and exposes the path. Wiring the phase by hand, without python-multiplatform, it is:

stagePythonBundleIosForXcode 는 stagePythonBundleIos 를 실행한 뒤 --quiet 에서도 정확히 한 줄, PYTHON_PAYLOAD_DIR=<절대 경로> 를 출력합니다. 활성 빌드 타입의 iOS 변형이 없거나 스테이징 디렉터리가 비어 있으면(패키지 미설정) 이유를 밝히며 실패합니다. .app 안으로는 절대 복사하지 않습니다. 복사하는 단계는 python-multiplatform(#59)이 소유하며, 이 플러그인은 스테이징과 경로 노출만 합니다. 사용하는 python-multiplatform 없이 손으로 배선한다면 단계는 다음과 같습니다:

set -euo pipefail
out=$(./gradlew -q :app:stagePythonBundleIosForXcode)
dir=$(printf '%s\n' "$out" | sed -n 's/^PYTHON_PAYLOAD_DIR=//p')
[ -n "$dir" ] && [ -d "$dir" ] || { echo "error: no Python payload dir from Gradle" >&2; exit 1; }
rsync -a --delete "$dir/" "$TARGET_BUILD_DIR/$UNLOCALIZED_RESOURCES_FOLDER_PATH/python/"

With python-multiplatform, its tools/xcode/install-python.sh is the one phase that copies python/ (and the stdlib): give it the directory this task prints as PYTHON_PAYLOAD_DIR, as the sample's sample/src/iosMain/install-python-phase.sh does, and do not add the snippet above as well. Only one Xcode phase may copy python/, because each rsync --deletes the same directory. The snippet above is for wiring by hand without python-multiplatform.

python-multiplatform 을 쓰면 그쪽 tools/xcode/install-python.sh 가 python/(와 stdlib)을 복사하는 유일한 단계입니다. 이 태스크가 출력한 디렉터리를 PYTHON_PAYLOAD_DIR 로 넘기세요(샘플의 sample/src/iosMain/install-python-phase.sh 처럼). 위 스니펫을 함께 넣지 마세요. 둘 다 같은 디렉터리를 rsync --delete 하므로 python/ 을 복사하는 Xcode 단계는 하나여야 합니다. 위 스니펫은 python-multiplatform 없이 손으로 배선할 때를 위한 것입니다.

Do not shorten it to dir=$(./gradlew … | sed …) && rsync …: the pipeline's status is sed's, so a failed Gradle leaves dir empty and rsync --delete "/" runs.

dir=$(./gradlew … | sed …) && rsync … 처럼 줄이면 안 됩니다. 파이프라인의 종료 상태는 sed 의 것이라, Gradle 이 실패하면 dir 이 비어 rsync --delete "/" 가 실행됩니다.

After staging스테이징 이후

Putting the staged python/ on sys.path when the app starts is the runtime's job: python-multiplatform's side of the boundary, not this plugin's.

앱이 시작할 때 스테이징된 python/ 을 sys.path 에 올리는 것은 런타임의 일입니다: 이 플러그인이 아니라 python-multiplatform 쪽 경계입니다.