Concepts개념

What the plugin does when Gradle configures your project, and why it is shaped that way.

Gradle 이 프로젝트를 구성할 때 플러그인이 하는 일, 그리고 그렇게 생긴 이유.

Three owners세 주인

Layer계층Owns맡는 것Here it is여기서는
toolchainThe Gradle vocabulary: DSL, tasks, wiring into AGP and KotlinGradle 어휘: DSL, 태스크, AGP·Kotlin 연결this repository이 저장소
pypackpackThe work: uv installs, resource bundling, target triples작업: uv 설치, 리소스 번들링, 타깃 트리플BackendInterface.create(BackendType.UV), BundlerInterface.create(BundleType.RESOURCE)BackendInterface.create(BackendType.UV), BundlerInterface.create(BundleType.RESOURCE)
python-multiplatformThe runtime: the interpreter and its stdlib (libpython linked into its binaries), loading the payload, sys.path, reload런타임: 인터프리터와 그 stdlib (libpython 을 자기 바이너리에 링크), 페이로드 로드, sys.path, 리로드beyond the staged directory스테이징된 디렉터리 너머

A feature that resolves a dependency or builds a bundle belongs in pypackpack, even when a Gradle task triggers it. When pypackpack cannot do something yet, the plugin rejects the DSL value instead of approximating it.

의존성을 해석하거나 번들을 만드는 기능은 Gradle 태스크가 촉발하더라도 pypackpack 의 몫입니다. pypackpack 이 아직 못 하는 것은 플러그인이 흉내 내지 않고 DSL 값을 거부합니다.

toolchain ships only python/: your modules and the wheels compileSdk selected. It never bundles an interpreter, at any embedLevel. python-multiplatform links libpython into its binaries and ships the matching stdlib, so a second interpreter from toolchain could differ from the linked one even at the same version, and the app would carry two copies. embedLevel = 2 therefore means “python-multiplatform embeds the interpreter”, and at that level compileSdk must name the same X.Y.Z as python-multiplatform's pythonVersion: a mismatch fails that variant's buildPython… with both versions named. toolchain reads that version from -Ppython.multiplatform.pythonVersion, else from a :python-multiplatform project in the same build; where published consumers read it is not settled yet.

toolchain 은 python/ 만 싣습니다: 여러분의 모듈과 compileSdk 가 고른 wheel 입니다. embedLevel 이 무엇이든 인터프리터를 번들에 넣지 않습니다. python-multiplatform 이 libpython 을 자기 바이너리에 링크하고 맞는 stdlib 를 함께 싣기 때문에, toolchain 이 따로 넣은 인터프리터는 같은 버전이어도 링크된 것과 다를 수 있고 앱에 두 벌이 들어갑니다. 그래서 embedLevel = 2 는 “python-multiplatform 이 인터프리터를 내장한다”는 뜻이며, 이 레벨에서 compileSdk 는 python-multiplatform 의 pythonVersion 과 같은 X.Y.Z 여야 합니다. 다르면 그 변형의 buildPython… 이 두 버전을 밝히며 실패합니다. toolchain 은 그 버전을 -Ppython.multiplatform.pythonVersion 에서, 없으면 같은 빌드의 :python-multiplatform 프로젝트에서 읽습니다. 배포된 의존으로 쓰는 경우 어디서 읽을지는 아직 정해지지 않았습니다.

The task chain태스크 사슬

installPythonDependencies   uv add <implementation + integration deps>   (pypackpack)
installPythonDependencies<Set>
                            uv pip install --target build/pythonDeps/<set>
                              --python-platform <triple>               (pypackpack)
        ↓
buildPython                 BundleRequest(libDirs = pythonDeps/<set>) → ResourceBundler
        ↓
packagePython               build/distributions/<fileName>.zip

stagePythonBundle           → stagePythonBundleAndroid / Ios / Desktop
                              build/pythonStaging/<platform>/python/
hotReloadPython             adb push *.py + RELOAD_PYTHON broadcast
codePushPython              validation only; reports that upload is not implemented

All tasks live in the python group. A missing package directory makes install and bundle skip with a message rather than fail: a module that has not configured Python yet builds as before.

모든 태스크는 python 그룹에 있습니다. 패키지 디렉터리가 없으면 설치와 번들은 실패하지 않고 메시지와 함께 건너뜁니다. 아직 Python 을 설정하지 않은 모듈도 전처럼 빌드됩니다.

Variants변형

Without a declared platform variant there is one chain, built for the host. With one, every declared platform variant is crossed with every build type, and each pair gets its own task and its own output directory:

선언된 플랫폼 변형이 없으면 호스트용 사슬 하나뿐입니다. 있으면 선언된 플랫폼 변형마다 빌드 타입이 곱해지고, 각 쌍이 자기 태스크와 출력 디렉터리를 가집니다.

buildPythonAndroidArm64Debug     → build/pythonBundle/androidArm64-debug/
packagePythonAndroidArm64Debug   → build/distributions/app-androidArm64-debug.zip
buildPythonMacosArm64Release     → build/pythonBundle/macosArm64-release/

buildPython and packagePython then become lifecycle tasks, the way AGP's assemble is.

이때 buildPython 과 packagePython 은 AGP 의 assemble 처럼 생명주기 태스크가 됩니다.

How it fails실패하는 방식

Mistake실수When언제Scope범위
Malformed compileSdk형식이 틀린 compileSdkconfiguration구성 시점whole build빌드 전체
Platform with no pypackpack triple (androidArm32, androidX86)pypackpack 트리플이 없는 플랫폼 (androidArm32, androidX86)configuration구성 시점whole build: no valid task exists빌드 전체: 유효한 태스크가 없음
Kotlin target not enabled for a declared platform선언된 플랫폼의 Kotlin 타깃이 꺼져 있음configuration구성 시점warning only경고만
compileLevel = native / mixed, or bytecode without a matching .venvcompileLevel = native / mixed, 또는 맞는 .venv 가 없는 bytecodetask execution태스크 실행 시점that variant only: --continue builds the rest그 변형만: --continue 로 나머지는 빌드
compileSdk differs from python-multiplatform's pythonVersion at embedLevel = 2embedLevel = 2 에서 compileSdk 가 python-multiplatform 의 pythonVersion 과 다름task execution태스크 실행 시점that variant only: --continue builds the rest그 변형만: --continue 로 나머지는 빌드
Unknown build type in -Ppython.buildType-Ppython.buildType 의 알 수 없는 빌드 타입configuration구성 시점whole build빌드 전체