Building from source
Build targets
What scripts/build.sh builds, in which order, and where the output goes.
On this page
The loader
scripts/build.shOutput: dist/renpy-loader.nro, built with 0 warnings under -Wall -Wextra. It links LVGL as liblvgl.a, built by third_party/lvgl/Makefile from the source that third_party/lvgl/fetch.sh downloads.
The runtime
Three steps, in this order. The first build of CPython takes about 10 minutes; Ren’Py about 5.
scripts/build.sh pythonscripts/build.sh renpyscripts/build.sh runtimeOutput: dist/renpy-runtime.nro, about 55 MB.
| Target | What it does |
|---|---|
python | Static CPython 3.12.8 for the Switch: libpython3.12.a and a 511-module stdlib zip of sourceless .pyc. Six small patches, documented in runtime/python/README.md |
renpy | Runs Cython on Ren’Py’s sources, compiles its 60 native modules into librenpy.a, and stages the engine for RomFS with precompiled .pyc and the SDK’s digest-checked common scripts |
runtime | Links dist/renpy-runtime.nro with no unresolved symbols |
clean, others | Passed to make |
important
After changing the boot scripts in runtime/renpy/boot/ or the cursor, run scripts/build.sh renpy again before runtime: they are staged into RomFS in that step.
How Ren’Py is built
- Generate C. Ren’Py’s own
setup.py generateruns Cython. A two-classsetuptoolsstand-in lets it run on the bare host CPython. - Compile the native modules.
build_modules.pyreads the module list from Ren’Py’ssrc/Setup, compiles every source with devkitA64, renames each Cython init function to its dotted name, and generates an inittab that registers the 60 modules. - Stage the engine.
stage_engine.pycopiesrenpy.pyandrenpy/, then the overlays (runtime/compatibility,runtime/renpy/boot), writes.pycfiles, and adds the SDK’s precompiledcommon/*.rpycafter checking each one’s digest. Without them, the engine scripts would be recompiled on every launch, because RomFS is read-only.
None of Ren’Py’s own .py files are modified. The upstream changes:
| Change | Why |
|---|---|
| libhydrogen uses its getentropy backend | No Switch random source upstream; getentropy is backed by libnx randomGet |
renpy.gl2.assimp replaced | assimp (C++) has no Switch port; GLTFModel raises a clear error |
renpy.gl2.live2dmodel excluded | The Live2D Cubism Core is proprietary; Ren’Py treats it as optional |
posix_memalign shim | Used by Ren’Py’s ffmedia.c; newlib does not provide it |
CPython posixpath patch | sdmc:/… and romfs:/… must count as absolute paths |
Loader UI mockups
scripts/mockups.shRenders every loader screen on the Mac, at 1280×720, through the same app/ui/ code and lv_conf.h, under ASan and UBSan. Output: build/host/mockups/*.png. It also prints each screen’s render time, which is host time, not Switch time.
Pushing to the Switch
scripts/switch-ftp.sh 192.168.1.20:5000 push-loaderscripts/switch-ftp.sh 192.168.1.20:5000 push-runtimeEach uploads over sphaira’s FTP server, reads the file back and compares it.