A custom build of CPython 3.13.13 for embedding into a host application. Builds Windows x86, Windows x64, Linux x86_64, macOS arm64 artifacts to be used as development and link targets.
Each platform produces one zip with the same shape — a MagPython/
directory containing the main shared lib, the OpenSSL libs, the headers,
and the pure-Python stdlib. zlib and libmpdec are linked statically into
the main library on every platform; libffi is linked statically on
Windows/Linux, while macOS uses the SDK's system libffi; sqlite ships as
a sibling shared library on Linux/macOS and statically on Windows;
ncurses is linked statically into the POSIX builds only (the Windows
artifact ships no curses module — CPython upstream relies on the
windows-curses PyPI shim there).
Windows (MagPython-windows-x86.zip and MagPython-windows-x64.zip,
same shape — only the binaries' architecture and OpenSSL DLL names
differ; OpenSSL's VC-WIN64A target adds a -x64 shlib_variant
suffix to the DLLs):
MagPython/
MagPython.dll # Python core + builtin modules + zlib + sqlite + libffi + libmpdec
MagPython.exe # python3.exe equivalent — Py_Main wrapper linked against MagPython.dll
python3.exe # copy of MagPython.exe under the canonical CPython binary name
libcrypto-3.dll / libcrypto-3-x64.dll # OpenSSL (x86 / x64)
libssl-3.dll / libssl-3-x64.dll # OpenSSL (x86 / x64)
include/Python/... # Public + cpython + internal headers, plus PC/pyconfig.h
lib/... # Pure-Python stdlib (.py files copied from Python/Lib)
Linux (MagPython-linux-x86_64.zip):
MagPython/
libMagPython.so # SONAME libMagPython.so, RUNPATH $ORIGIN
MagPython # python3 equivalent — Py_BytesMain wrapper, RUNPATH $ORIGIN
python3 # copy of MagPython under the canonical CPython binary name
libcrypto.so.3
libssl.so.3
libsqlite3.so.0 # SONAME libsqlite3.so.0
libsqlite3.so # symlink -> libsqlite3.so.0
include/Python/...
lib/python3.13/ # stdlib at the path Python's Unix discovery
# looks for (lib/python<X.Y>/os.py)
lib/python3.13/lib-dynload/ # empty (modules are statically linked)
macOS arm64 (MagPython-macos-arm64.zip):
MagPython/
libMagPython.dylib # install_name @rpath/libMagPython.dylib
MagPython # python3 equivalent — Py_BytesMain wrapper, LC_RPATH @loader_path
python3 # copy of MagPython under the canonical CPython binary name
libcrypto.3.dylib # install_name @rpath/libcrypto.3.dylib
libssl.3.dylib # install_name @rpath/libssl.3.dylib
libsqlite3.0.dylib # install_name @rpath/libsqlite3.0.dylib
libsqlite3.dylib # symlink -> libsqlite3.0.dylib
include/Python/...
lib/python3.13/
lib/python3.13/lib-dynload/
The MagPython / MagPython.exe binary is upstream's Programs/python.c
compiled against the shipped headers and dynamically linked against
libMagPython.{so,dylib,dll}. The artifact is laid out so the binary's
path discovery finds the bundled stdlib without env vars:
PATH/@loader_path/$ORIGIN resolves the lib next door, and Python's
os.py lookup walks up to lib/python<X.Y>/os.py in the same artifact
directory.
Notable differences from a stock CPython Windows build:
- A single
MagPython.dllinstead ofpython313.dllplus a forest of.pydfiles. Modules that upstream ships as separate.pyds (_ssl,_socket,select,_sqlite3,unicodedata,_ctypes,_decimal, …) are linked directly into the DLL — see the<ClCompile>items inMagPython/MagPython.vcxproj. - Builds for both
x86(32-bit Win32) andx64(64-bit AMD64). ThePlatformdefaults toWin32inMagPython/common.props; the x64 build is selected by passing/p:Platform=x64to MSBuild.PlatformToolsetisv142(VS 2019) on both. Deployment target is Windows 8.1 —Py_WINVERis0x0603inPython/PC/pyconfig.h.in, soMagPython.dllstatically imports Windows 8.1 APIs (e.g. PSS) and is expected to load on Windows 8.1 and newer with the VC++ 2015-2022 redistributable installed. - Frozen modules under
Python/Python/frozen_modules/are regenerated as part of the build (see below) and are gitignored. The freezer binary itself is built but not shipped.
MagPython/MagPython.metaproj is the top-level MSBuild project. It runs the
following sub-projects in order, each BuildInParallel="True" StopOnFirstFailure="True":
LibFFI.vcxproj— buildslibffi.lib(static) from the build-time-downloaded source tree (MagPython/libffi/libffi-<v>/, populated bydownload-libffi.ps1running before the C compile). On Win32 it builds theX86_WIN32variant (ffi.c+sysv_intel.S, assembled withml.exe); on x64 it builds theX86_WIN64variant (ffiw64.c+win64_intel.S, assembled withml64.exe).openssl.vcxproj— aMakefile-type project that runsperl Configure VC-WIN32-ONECORE <no-* set>(Win32) orperl Configure VC-WIN64A-ONECORE <no-* set>(x64) and thenjom -j%NUMBER_OF_PROCESSORS% build_libsinside the build-time- downloaded source tree (MagPython/openssl/openssl-<v>/, populated bydownload-openssl.ps1and verified against the pinned SHA-256).download-nasm.ps1fetches NASM 2.16.01 from nasm.us before the build (NASM is required by OpenSSL's x86/x86_64 assembly).download-jom.ps1fetches jom — Qt's drop-innmakereplacement that supports parallel jobs (-j); Microsoft's nmake is single-threaded, so this cuts the OpenSSL build from sequentialcl.exespawns down to one process per core. Outputs:libcrypto-<N>{,-x64}.dll,libssl-<N>{,-x64}.dll(where<N>is derived fromopenssl-version's major component, and the-x64suffix is OpenSSL'sshlib_varianton x64; see theOpenSslShlibVariantproperty inMagPython/common.props), import libs,applink.c, and the headers, all copied intoMagPython/Release/. AVerifyOpenSSLpost-build target compiles + runsMagPython/openssl-verify.cagainst the staged libs to catch a misconfiguredno-*set or a missing soname here, with a clear error, instead of surfacing later as a bafflingMagPython.dlllink failure.ZLib.vcxproj— aMakefile-type project that runs upstream'swin32/Makefile.mscviajom -j%NUMBER_OF_PROCESSORS%(re-using the same jom downloaded for OpenSSL) inside the build-time- downloaded zlib tree (MagPython/zlib/zlib-<v>/, populated bydownload-zlib.ps1). Targets justzlib.lib;CopyArtifactsstages it alongsidezlib.h+zconf.hunderMagPython/Release/(andRelease/include/).SQLite.vcxproj— aMakefile-type project that drivescl.exe + lib.exedirectly on the upstream amalgamation (MagPython/sqlite/sqlite-<v>/sqlite3.c, populated bydownload-sqlite.ps1). sqlite ships no Makefile.msc — only the amalgamation — so there's nothing for jom to parallelise across.CopyArtifactsstagessqlite3.lib+sqlite3.h+sqlite3ext.hunderMagPython/Release/(andRelease/include/).LibMpdec.vcxproj— aMakefile-type project that runsnmake /f Makefile.vc MACHINE=pproon Win32 (the x87 inline-asm flavour) orMACHINE=x64on x64 (the AMD64 flavour), inside the build-time-downloaded mpdecimal source (MagPython/libmpdec/mpdecimal-<v>/, populated bydownload-libmpdec.ps1). Both match what CPython's bundled_decimal.vcxprojpicks for the same platforms.CopyArtifactsrenameslibmpdec-<version>.libtolibmpdec.liband stages alongsidempdecimal.hunderMagPython/Release/(andRelease/include/).FreezeMagPython.vcxproj— buildsFreezeMagPython.exefrom CPython'sPrograms/_freeze_module.c. After it builds, post-build targets re-freeze the Python modules listed in the project (importlib bootstrap,os,site,runpy, the__phello__modules, etc.) intoPython/Python/frozen_modules/*.hand freezegetpath.pyseparately. The generated tree is gitignored. CPython 3.13 removed deepfreeze entirely (the pre-3.13 deep-baked importlib bootstrap as a generated.cfile), so frozen modules are now the only regen step.MagPython.vcxproj— the main DLL. Compiles the Python core,Objects/,Parser/, selectedModules/,PC/glue,_sqlite/*,_decimal/_decimal.c,_ssl,_hashopenssl,_socket,select,unicodedata,_ctypes, and CPython'szlibmodule.cwrapper. Links against thelibcrypto.lib,libssl.lib,libffi.lib,zlib.lib,sqlite3.lib, andlibmpdec.libproduced by the earlier steps. TheCopyArtifactstarget then stages headers and the pure-Python stdlib intoRelease/include/Python/andRelease/lib/so the output directory is a complete SDK drop.MagPythonExe.vcxproj— compiles CPython'sPrograms/python.c(the tinywmain/Py_Mainwrapper that ships aspython3.exeupstream) and emitsMagPython.exeintoRelease/, dynamically linked againstMagPython.dllvia the#pragma comment(lib, "MagPython.lib")in the project-patchedpyconfig.h. A post-build<Exec>runsMagPython.exe --versionas a smoke test.test.vcxproj— compilesMagPython/test.c(a tiny embedding host that callsPy_Initialize, prints the compiler string, and runs animport sysline), copies the DLLs andlib/next to it, and executestest.exeas part of the build via an<Exec>task. A failed smoke test fails the build.
MagPython/common.props pins the defaults: Platform=Win32,
Configuration=Release, PlatformToolset=v142. Build the x64 artifact
by overriding the platform: msbuild /m /p:Configuration=Release /p:Platform=x64 MagPython\MagPython.metaproj.
MagPython/build-linux.sh and MagPython/build-macos.sh orchestrate the
Unix builds. Both source MagPython/build-common.sh and follow the same
shape as the Windows metaproj:
- Static deps — zlib's
libz.a(built from the build-time- downloaded source underMagPython/zlib/zlib-<v>/) is linked into the main library on both Linux and macOS. On Linux only, libffi'slibffi.a(fromMagPython/libffi/libffi-<v>/) is also linked statically; macOS instead uses the SDK's/usr/lib/libffi.dylibvia CPython's Darwin-specific autoconf block, so no MagPython-built libffi exists in the macOS artifact. SQLite is built from the build-time-downloaded amalgamation (MagPython/sqlite/sqlite-<v>/sqlite3.c) into a shared library (build-out/sqlite/libsqlite3.so.0on Linux,build-out/sqlite/libsqlite3.0.dylibon macOS) that is shipped next tolibMagPythonrather than statically linked, so CPython'sconfigurecan find it via the standard-L/-lsqlite3autoconf probe path. Windows continues to build all three as separate static libs viaZLib.vcxproj/LibFFI.vcxproj/SQLite.vcxproj. - OpenSSL —
./Configure linux-x86_64ordarwin64-arm64-cc, thenmake && make install_swintobuild-out/openssl-out. Produceslibcrypto.{so.3,3.dylib}+libssl.{so.3,3.dylib}. - libmpdec —
setup_libmpdecfetchesmpdecimal-<version>.tar.gzfrom bytereef.org (version pinned inMagPython/libmpdec-version), verifies it against the SHA-256 hash pinned inMagPython/libmpdec-sha256, and extracts underMagPython/libmpdec/(cached acrossprep_build_treewipes since it lives outsidebuild-out/).build_libmpdecthen runs upstream's./configure --disable-cxx --enable-static --disable-shared(with--with-machine=universalon macOS to match what CPython's own configure picks for arm64) and installs intobuild-out/libmpdec-out/. - ncurses —
build_ncursesfetchesncurses-<version>.tar.gzfrom ftp.gnu.org (version pinned inMagPython/ncurses-version, hash inMagPython/ncurses-sha256), then runs upstream's./configure --without-shared --with-pic --enable-widec --without-debug --without-tests --without-progs --without-cxx --without-cxx-binding --without-ada --without-manpages --enable-pc-files=no --without-termliband installs intobuild-out/ncurses-out/. Produceslibncursesw.a+libpanelw.a(terminfo functions stay insidelibncursesw.abecause we don't pass--with-termlib, so there is no separatelibtinfo.ato plumb through). POSIX-only — there is no Windows equivalent. - Configure libpython — out-of-tree configure in
build-out/mainwith--enable-shared --without-static-libpython --with-openssl=...build-out/openssl-out --with-system-libmpdec, plusZLIB_*,LIBSQLITE3_*,LIBMPDEC_*, andCURSES_*/PANEL_*env vars pointing at the libs from the earlier stages.LDFLAGS=-Lbuild-out/sqliteputs the shared sqlite directory on the linker's search path soAC_CHECK_LIB([sqlite3], …)probes resolve-lsqlite3cleanly. On Linux the call additionally passesLIBFFI_CFLAGS/LIBFFI_LIBSfor the bundled static libffi; on macOS those are omitted so CPython's Darwin block inconfigureauto-detects the SDK'sffi.hand-lffi. - Regen frozen, then make —
make regen-frozenfollowed by an awk pass that rewritesModules/Setup.stdlib: it flips*shared*to*static*, then comments out the lines for modules listed under*disabled*inMagPython/Setup.local. (The*disabled*directive on its own only affects runtime registration inModules/config.c— to keep modules out of the build entirely we need to drop their stdlib lines.) This produces a libpython that contains the same module subset asMagPython/MagPython.vcxprojdoes on Windows, plus the POSIX-only_curses/_curses_panelmodules backed by the static ncurses. - Rename, stage, smoke test, zip —
libpython3.13.{so.1.0,dylib}is copied tolibMagPython.{so,dylib}and its SONAME / install name is rewritten withpatchelf(Linux) orinstall_name_tool(macOS). Linux additionally rewrites the RUNPATH to$ORIGINso the artifact is relocatable; macOS rewrites OpenSSLLC_LOAD_DYLIBpaths and absoluteLC_RPATHentries to@rpath/.... The sharedlibsqlite3.{so.0,0.dylib}(already built withSONAME=libsqlite3.so.0/install_name=@rpath/libsqlite3.0.dylib) is copied alongside, with the unversioned symlink consumers expect from a normal sqlite install. Headers and the pure-Python stdlib are staged next to the libs (with aPython/python.h -> Python.hsymlink soMagPython/test.c's lowercase include resolves on case-sensitive filesystems),MagPython/test.cis built and run against the staged tree (failure fails the build), theMagPythoninterpreter binary (CPython'sPrograms/python.c, dynamically linked againstlibMagPython.{so,dylib}with$ORIGIN/@loader_pathrpath) is compiled into$STAGE/and smoke-tested with--version, and the final zip is produced.
The host Python required by regen-frozen is
/opt/python/cp313-cp313/bin/python3 inside the manylinux_2_28 container
on Linux, and the macos-14 runner's preinstalled python3 on macOS.
Requirements:
- Windows with Visual Studio 2019 build tools (MSVC v142, x86 and x64 cross tools, Windows 8.1 SDK baseline).
- Perl in
PATH(for OpenSSLConfigure). - A host Python in
PATHor one discoverable byPython/PCbuild/find_python.bat(used by the frozen-modules regen). - Network access on the first build (NASM is downloaded by
MagPython/download-nasm.ps1).
From a "x86 Native Tools Command Prompt for VS 2019" at the repo root:
msbuild /m /p:Configuration=Release MagPython\MagPython.metaprojFrom a "x64 Native Tools Command Prompt for VS 2019" at the repo root:
msbuild /m /p:Configuration=Release /p:Platform=x64 MagPython\MagPython.metaprojThese are exactly what the CI invokes. The shipped artifact is built by
then renaming MagPython\Release to MagPython\MagPython and zipping it.
docker run --rm -v "$PWD":/src -w /src \
quay.io/pypa/manylinux_2_28_x86_64 ./MagPython/build-linux.sh(glibc 2.28 baseline — covers RHEL 8 / Ubuntu 20.04+.) Produces
MagPython-linux-x86_64.zip at the repo root. Build artifacts live under
build-out/ (gitignored).
On an Apple Silicon Mac with Xcode Command Line Tools installed:
./MagPython/build-macos.shProduces MagPython-macos-arm64.zip at the repo root.
Every dep follows the same shape: a <dep>-version and
<dep>-sha256 pin file under MagPython/, the build downloads the
tarball at build time (Windows: MagPython/download-<dep>.ps1; Unix:
setup_<dep> in build-common.sh), verifies it against the pinned
hash, and caches under MagPython/<dep>/ (gitignored). Each dep has
an update-<dep>.sh wrapper that bumps the pins; the shared logic
lives in MagPython/update-pin-common.sh, and the wrappers refuse
cross-major bumps so the build glue can be reviewed manually when an
ABI line changes.
The in-tree pin is the sole hash check; update-<dep>.sh downloads
the tarball and computes SHA-256 locally.
| Dep | Source | Line | Bump command |
|---|---|---|---|
| OpenSSL | openssl/openssl GitHub Releases |
3.x | update-openssl.sh 3.5.7 |
| zlib | madler/zlib GitHub Releases |
1.x | update-zlib.sh 1.3.3 |
| SQLite | sqlite.org | 3.x | update-sqlite.sh 3.53.2 2025 |
| libffi | libffi/libffi GitHub Releases |
3.x | update-libffi.sh 3.5.3 |
| libmpdec | bytereef.org | 2.x | update-libmpdec.sh 2.5.2 |
| ncurses | ftp.gnu.org | 6.x | update-ncurses.sh 6.5 |
| CPython | python/cpython tag archive on GitHub |
3.x | update-python.sh 3.13.14 |
libmpdec is the C library behind the _decimal module. ncurses is
POSIX-only — the Windows artifact ships no curses module, so there is
no Windows download or build step; the canonical upstream is
invisible-island.net but the build pulls from ftp.gnu.org's mirror
because it serves the same tarball over stable HTTPS.
For a patch-level bump within the same major line nothing else needs
editing: every consumer (the .vcxproj files, build-common.sh,
common.props, the verification step) substitutes the version from
the relevant pin file, and the Windows DLL suffix / Unix soname for
OpenSSL is derived from the version's major component. A few deps
have extra moving parts noted below.
sqlite.org's download URL embeds a calendar-year segment that isn't
derivable from the version (e.g.
https://sqlite.org/2025/sqlite-amalgamation-3530100.zip), so the
year is pinned in MagPython/sqlite-year and passed as a second
positional arg to update-sqlite.sh. Look it up on
https://sqlite.org/chronology.html or in the release announcement.
The numeric 3530100 in the URL is the version encoded as
<major>*1000000 + <minor>*10000 + <patch>*100; the download scripts
compute it inline from the version pin.
The Verify python drift workflow (.github/workflows/Verify python drift.yml) downloads the new tarball on Linux and confirms every
$(PythonSourceDir)\<path>\<name>.c|h ref in MagPython.vcxproj /
FreezeMagPython.vcxproj exists in the new tree. Cross-minor bumps
(e.g. 3.13 → 3.14) routinely surface MISSING refs here, and
reconciling them is part of the upgrade work — expect:
MagPython.vcxproj— per-file<ClCompile>/<ClInclude>lists may need fixups for upstream renames or additions.MagPython/Setup.local— verify the disabled-modules list still matches the upstreamModules/Setup.stdlibshape.- The Linux/macOS build scripts derive
python3.<minor>paths fromPY_X_Y(computed from the pin), so the libpython soname and stagedlib/python<X.Y>/directory follow automatically.
update-<dep>.sh rejects anything outside the configured major line
because cross-major bumps typically need build-glue revisions —
Configure flag sets, soname conventions, OpenSSL's OpenSslDllSuffix
derivation, mpdecimal's MACHINE=ppro / --with-machine=universal
/ CONFIG_32;PPRO flavour selection, and so on.
.github/workflows/Build All.yml is a single matrix-based workflow that
fans out across four platforms:
windows-2025(x86, MSVC) — runs the existingmsbuild MagPython.metaprojflow withPlatform=Win32(the default).windows-2025(x64, MSVC) — runs the same flow withPlatform=x64. Shares the image with the x86 job; the x86 and x64 MSVC tools both come from theVC.Tools.142.x86.x64component group.ubuntu-22.04inside thequay.io/pypa/manylinux_2_28_x86_64container — runsMagPython/build-linux.sh.macos-14— runsMagPython/build-macos.sh.
fail-fast: false so a transient failure on one platform doesn't cancel
the others. timeout-minutes: 30 per matrix job. Each job uploads its
zip as a separate artifact named MagPython-<platform> with 7-day
retention; downstream consumers (the host application's CI) fetch them
by name.
| Dependency | License |
|---|---|
| CPython | PSF License |
| OpenSSL | Apache-2.0 license |
| zlib | zlib license |
| libffi | MIT |
| libmpdec | BSD-2-Clause |
| SQLite | public domain (from the comment in sqlite3.h, cf. https://www.sqlite.org/copyright.html) |
| ncurses | MIT-style "X11" license |
License files are added to the build artifacts for downstream consumers.