Important

This is part of a Draft of the Python Contributor’s Guide. Text in square brackets are notes about content to fill in. Currently, the devguide and this new Contributor’s Guide co-exist in the repo. We are using Sphinx include directives to demonstrate the re-organization. The final Contributor’s Guide will replace the devguide with content in only one place. We welcome help with this!

The [Plan for the Contributor’s Guide] page has more details about the current state of this draft and how you can help. See more info about the Contributor Guide in the discussion forum: Refactoring the DevGuide.

Compile and build

Note

[This is the Compile and build section from the devguide. I think this page is too long and could be split by build target, but we can leave that for a later time.]

CPython provides several compilation flags which help with debugging various things. While all of the known flags can be found in the Misc/SpecialBuilds.txt file, the most critical one is the Py_DEBUG flag which creates what is known as a “pydebug” build. This flag turns on various extra sanity checks which help catch common issues. The use of the flag is so common that turning on the flag is a basic compile option.

You should always develop under a pydebug build of CPython (the only instance of when you shouldn’t is if you are taking performance measurements). Even when working only on pure Python code the pydebug build provides several useful checks that one should not skip.

See also

The effects of various configure and build flags are documented in the Python configure docs.

Unix

The core CPython interpreter only needs a C compiler to be built, however, some of the extension modules will need development headers for additional libraries (such as the zlib library for compression). Depending on what you intend to work on, you might need to install these additional requirements so that the compiled interpreter supports the desired features.

If you want to install these optional dependencies, consult the Install dependencies section below.

If you don’t need to install them, the basic steps for building Python for development is to configure it and then compile it.

Configuration is typically:

$ ./configure --with-pydebug

More flags are available to configure, but this is the minimum you should do to get a pydebug build of CPython.

Note

You might need to run make clean before or after re-running configure in a particular build directory.

Once configure is done, you can then compile CPython with:

$ make -s -j $(nproc)

This will build CPython with only warnings and errors being printed to stderr. The -j argument means that make will concurrently run tasks, limiting the number of parallel jobs to the number of CPU cores in your computer. You can adjust the number passed to the -j flag to change the limit on parallel jobs, which can trade RAM usage versus compilation time.

At the end of the build you should see a success message, followed by a list of extension modules that haven’t been built because their dependencies were missing:

The necessary bits to build these optional modules were not found:
_gdbm
To find the necessary bits, look in configure.ac and config.log.

Checked 106 modules (31 built-in, 74 shared, 0 n/a on macosx-13.4-arm64, 0 disabled, 1 missing, 0 failed on import)

If the build failed and you are using a C89 or C99-compliant compiler, please open a bug report on the issue tracker.

If you decide to Install dependencies, you will need to re-run both configure and make.

Once CPython is done building you will then have a working build that can be run in-place; ./python on most machines (and what is used in all examples), ./python.exe wherever a case-insensitive filesystem is used (for example, on macOS by default), in order to avoid conflicts with the Python directory. There is normally no need to install your built copy of Python! The interpreter will realize where it is being run from and thus use the files found in the working copy. If you are worried you might accidentally install your working copy build, you can add --prefix=/tmp/python to the configuration step. When running from your working directory, it is best to avoid using the --enable-shared flag to configure; unless you are very careful, you may accidentally run with code from an older, installed shared Python library rather than from the interpreter you just built.

Clang

If you are using clang to build CPython, some flags you might want to set to quiet some standard warnings which are specifically superfluous to CPython are -Wno-unused-value -Wno-empty-body -Qunused-arguments. You can set your CFLAGS environment variable to these flags when running configure.

If you are using clang with ccache, turn off the noisy parentheses-equality warnings with the -Wno-parentheses-equality flag. These warnings are caused by clang not having enough information to detect that extraneous parentheses in expanded macros are valid, because the preprocessing is done separately by ccache.

If you are using LLVM 2.8, also use the -no-integrated-as flag in order to build the ctypes module (without the flag the rest of CPython will still build properly).

Optimization

If you are trying to improve CPython’s performance, you will probably want to use an optimized build of CPython. It can take a lot longer to build CPython with optimizations enabled, and it’s usually not necessary to do so. However, it’s essential if you want accurate benchmark results for a proposed performance optimization.

For an optimized build of Python, use configure --enable-optimizations --with-lto. This sets the default make targets up to enable Profile Guided Optimization (PGO) and may be used to auto-enable Link Time Optimization (LTO) on some platforms. See --enable-optimizations and --with-lto to learn more about these options.

$ ./configure --enable-optimizations --with-lto

Windows

Note

If you are using the Windows Subsystem for Linux (WSL), clone the repository from a native Windows shell program like PowerShell or the cmd.exe command prompt, and use a build of Git targeted for Windows, for example, the Git for Windows download from the official Git website. Otherwise, Visual Studio will not be able to find all the project’s files and will fail the build.

For a concise step by step summary of building Python on Windows, you can read Victor Stinner’s guide.

All supported versions of Python can be built using Microsoft Visual Studio 2017 or later. You can download and use any of the free or paid versions of Visual Studio.

When installing it, select the Python development workload and the optional Python native development tools component to obtain all of the necessary build tools. You can find Git for Windows on the Individual components tab if you don’t already have it installed.

Note

If you want to build MSI installers, be aware that the build toolchain for them has a dependency on the Microsoft .NET Framework Version 3.5 (which may not be included on recent versions of Windows, such as Windows 10). If you are building on a recent Windows version, use the Control Panel (Programs ‣ Programs and Features ‣ Turn Windows Features on or off) and ensure that the entry .NET Framework 3.5 (includes .NET 2.0 and 3.0) is enabled.

Your first build should use the command line to ensure any external dependencies are downloaded:

PCbuild\build.bat -c Debug

The above command line build uses the -c Debug argument to build in the Debug configuration, which enables checks and assertions helpful for developing Python. By default, it builds in the Release configuration and for the 64-bit x64 platform rather than 32-bit Win32; use -c and -p to control build config and platform, respectively.

After this build succeeds, you can open the PCbuild\pcbuild.sln solution in the Visual Studio IDE to continue development, if you prefer. When building in Visual Studio, make sure to select build settings that match what you used with the script (the Debug configuration and the x64 platform) from the dropdown menus in the toolbar.

Note

If you need to change the build configuration or platform, build once with the build.bat script set to those options first before building with them in VS to ensure all files are rebuilt properly, or you may encounter errors when loading modules that were not rebuilt.

Avoid selecting the PGInstrument and PGUpdate configurations, as these are intended for PGO builds and not for normal development.

You can run the build of Python you’ve compiled with:

PCbuild\amd64\python_d.exe

See the PCBuild readme for more details on what other software is necessary and how to build.

WASI

WASI is a system interface standard for WebAssembly. Through a combination of C compilers that can target WebAssembly and wasi-libc providing POSIX-compatible shims for WASI, it’s possible for CPython to run on a WASI host/runtime as a guest.

Note

The instructions below assume a Unix-based OS due to cross-compilation for CPython being designed for ./configure / make.

To build for WASI, you will need to cross-compile CPython. This requires a C compiler just like building for Unix as well as:

  1. A C compiler that can target WebAssembly (for example, WASI SDK)

  2. A WASI host/runtime (for example, Wasmtime)

All of this is provided in the devcontainer. You can also use what’s installed in the container as a reference of what versions of these tools are known to work.

Note

CPython has only been verified with the above tools for WASI. Using other compilers, hosts, or WASI versions should work, but the above tools and their versions specified in the container are tested via a buildbot.

Building for WASI requires doing a cross-build where you have a build Python to help produce a WASI build of CPython (technically it’s a “host x host” cross-build because the build Python is also the target Python while the host build is the WASI build). This means you effectively build CPython twice: once to have a version of Python for the build system to use and another that’s the build you ultimately care about (that is, the build Python is not meant for use by you directly, only the build system).

The easiest way to get a debug build of CPython for WASI is to use the Tools/wasm/wasi.py build command (which should be run w/ a recent version of Python you have installed on your machine):

$ python3 Tools/wasm/wasi.py build --quiet -- --config-cache --with-pydebug

That single command will configure and build both the build Python and the WASI build in cross-build/build and cross-build/wasm32-wasi, respectively.

You can also do each configuration and build step separately; the command above is a convenience wrapper around the following commands:

$ python Tools/wasm/wasi.py configure-build-python --quiet -- --config-cache --with-pydebug
$ python Tools/wasm/wasi.py make-build-python --quiet
$ python Tools/wasm/wasi.py configure-host --quiet -- --config-cache
$ python Tools/wasm/wasi.py make-host --quiet

Note

The configure-host command infers the use of --with-pydebug from the build Python.

Running the separate commands after wasi.py build is useful if you, for example, only want to run the make-host step after making code changes.

Once everything is complete, there will be a cross-build/wasm32-wasi/python.sh helper file which you can use to run the python.wasm file (see the output from the configure-host subcommand):

$ cross-build/wasm32-wasi/python.sh --version

You can also use Makefile targets and they will work as expected thanks to the HOSTRUNNER environment variable having been set to a similar value as used in python.sh:

$ make -C cross-build/wasm32-wasi test

Note

WASI uses a capability-based security model. This means that the WASI host does not give full access to your machine unless you tell it to. This also means things like files can end up being mapped to a different path inside the WASI host. So, if you try passing a file path to python.wasm/ python.sh, it needs to match the path inside the WASI host, not the path on your machine (much like using a container).

Emscripten

Emscripten is a complete open-source compiler toolchain. It compiles C/C++ code into WebAssembly/JavaScript executables, for use in JavaScript runtimes, including browsers and Node.js.

Note

The instructions below assume a Unix-based OS due to cross-compilation for CPython being designed for ./configure / make.

To build for Emscripten, you will need to cross-compile CPython. This requires a C compiler just like building for Unix as well as:

  • The Emscripten compiler

  • Node.js

The simplest way to install the Emscripten compiler is:

# Install Emscripten
git clone https://github.com/emscripten-core/emsdk
./emsdk/emsdk install 4.0.5
./emsdk/emsdk activate 4.0.5
source ./emsdk/emsdk_env.sh

Updating the Emscripten compiler version often causes breakages. For the best compatibility, use the Emscripten version suggested in the cpython repository in Tools/wasm/README.md.

Building for Emscripten requires doing a cross-build where you have a build Python to help produce an Emscripten build of CPython. This means you build CPython twice: once to have a version of Python for the build system to use and another that’s the build you ultimately care about (that is, the build Python is not meant for use by you directly, only the build system).

The easiest way to get a debug build of CPython for Emscripten is to use the Tools/wasm/emscripten build command (which should be run with a recent version of Python you have installed on your machine):

python3 Tools/wasm/emscripten build --quiet -- --config-cache --with-pydebug

That single command will configure and build both the build Python and the Emscripten build in cross-build/build and cross-build/wasm32-emscripten/build/python/, respectively.

You can also do each configuration and build step separately; the command above is a convenience wrapper around the following commands:

python Tools/wasm/emscripten configure-build-python --quiet -- --config-cache --with-pydebug
python Tools/wasm/emscripten make-build-python --quiet
python Tools/wasm/emscripten make-libffi --quiet
python Tools/wasm/emscripten configure-host --quiet -- --config-cache
python Tools/wasm/emscripten make-host --quiet

Note

The configure-host command infers the use of --with-pydebug from the build Python.

Running the separate commands after emscripten build is useful if you, for example, only want to run the make-host step after making code changes.

Once everything is complete, there will be a cross-build/wasm32-emscripten/build/python/python.sh helper file which you can use to run the python.mjs file:

cross-build/wasm32-emscripten/build/python/python.sh --version

You can also use Makefile targets and they will work as expected thanks to the HOSTRUNNER environment variable having been set to a similar value as used in python.sh:

make -C cross-build/wasm32-emscripten/build/python/ test

Android

Build and test instructions for Android are maintained in the CPython repository at Android/README.md.

iOS

Compiling Python for iOS requires a macOS machine, on a recent version of macOS, running a recent version of Xcode. Apple expects developers to keep their operating systems and tools up-to-date; if your macOS version is more than one major release out of date, or your Xcode version is more than a couple of minor versions out of date, you’ll likely encounter difficulties. It is not possible to compile for iOS using Windows or Linux as a build machine.

A complete build for Python on iOS requires compiling CPython four times: once for macOS; then once for each of the three underlying platforms used by iOS:

  • An ARM64 device (an iPhone or iPad);

  • An ARM64 simulator running on a recent macOS machine; and

  • An x86_64 simulator running on older macOS machine.

The macOS build is required because building Python involves running some Python code. On a normal desktop build of Python, you can compile a Python interpreter and then use that interpreter to run Python code. However, the binaries produced for iOS won’t run on macOS, so you need to provide an external Python interpreter. From the root of a CPython code checkout, run the following:

$ ./configure --prefix=$(pwd)/cross-build/macOS
$ make -j4 all
$ make install

This will build and install Python for macOS into the cross-build/macOS directory.

The CPython build system can compile a single platform at a time. It is possible to test a single platform at a time; however, for distribution purposes, you must compile all three, and merge the results. See the iOS README for details on this merging process.

The following instructions will build CPython for iOS with all extensions enabled, provided you have installed the build dependencies XZ, BZip2, OpenSSL and libFFI in subfolders of the cross-build folder. See the iOS section on installing build dependencies for details on how to obtain these dependencies. These dependencies are all strictly optional, however, including libFFI is highly recommended, as it is required by the ctypes module which is used on iOS to support accessing native system APIs.

$ export PATH="$(pwd)/iOS/Resources/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Library/Apple/usr/bin"
$ ./configure \
      LIBLZMA_CFLAGS="-I$(pwd)/cross-build/iphoneos.arm64/xz/include" \
      LIBLZMA_LIBS="-L$(pwd)/cross-build/iphoneos.arm64/xz/lib -llzma" \
      BZIP2_CFLAGS="-I$(pwd)/cross-build/iphoneos.arm64/bzip2/include" \
      BZIP2_LIBS="-L$(pwd)/cross-build/iphoneos.arm64/bzip2/lib -lbz2" \
      LIBFFI_CFLAGS="-I$(pwd)/cross-build/iphoneos.arm64/libffi/include" \
      LIBFFI_LIBS="-L$(pwd)/cross-build/iphoneos.arm64/libffi/lib -lffi" \
      --with-openssl="$(pwd)/cross-build/iphoneos.arm64/openssl" \
      --host=arm64-apple-ios12.0 \
      --build=arm64-apple-darwin \
      --with-build-python=$(pwd)/cross-build/macOS/bin/python3.13 \
      --enable-framework
$ make -j4 all
$ make install
$ export PATH="$(pwd)/iOS/Resources/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Library/Apple/usr/bin"
$ ./configure \
      LIBLZMA_CFLAGS="-I$(pwd)/cross-build/iphonesimulator.arm64/xz/include" \
      LIBLZMA_LIBS="-L$(pwd)/cross-build/iphonesimulator.arm64/xz/lib -llzma" \
      BZIP2_CFLAGS="-I$(pwd)/cross-build/iphonesimulator.arm64/bzip2/include" \
      BZIP2_LIBS="-L$(pwd)/cross-build/iphonesimulator.arm64/bzip2/lib -lbz2" \
      LIBFFI_CFLAGS="-I$(pwd)/cross-build/iphonesimulator.arm64/libffi/include" \
      LIBFFI_LIBS="-L$(pwd)/cross-build/iphonesimulator.arm64/libffi/lib -lffi" \
      --with-openssl="$(pwd)/cross-build/iphonesimulator.arm64/openssl" \
      --host=arm64-apple-ios12.0-simulator \
      --build=arm64-apple-darwin \
      --with-build-python=$(pwd)/cross-build/macOS/bin/python3.13 \
      --enable-framework
$ make -j4 all
$ make install
$ export PATH="$(pwd)/iOS/Resources/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Library/Apple/usr/bin"
$ ./configure \
      LIBLZMA_CFLAGS="-I$(pwd)/cross-build/iphonesimulator.x86_64/xz/include" \
      LIBLZMA_LIBS="-L$(pwd)/cross-build/iphonesimulator.x86_64/xz/lib -llzma" \
      BZIP2_CFLAGS="-I$(pwd)/cross-build/iphonesimulator.x86_64/bzip2/include" \
      BZIP2_LIBS="-L$(pwd)/cross-build/iphonesimulator.x86_64/bzip2/lib -lbz2" \
      LIBFFI_CFLAGS="-I$(pwd)/cross-build/iphonesimulator.x86_64/libffi/include" \
      LIBFFI_LIBS="-L$(pwd)/cross-build/iphonesimulator.x86_64/libffi/lib -lffi" \
      --with-openssl="$(pwd)/cross-build/iphonesimulator.x86_64/openssl" \
      --host=x86_64-apple-ios12.0-simulator \
      --build=arm64-apple-darwin \
      --with-build-python=$(pwd)/cross-build/macOS/bin/python3.13 \
      --enable-framework
$ make -j4 all
$ make install

These instructions modify your PATH before the build. As iOS and macOS share a hardware architecture (ARM64), it is easy for a macOS ARM64 binary to be accidentally linked into your iOS build. This is especially common when Homebrew is present on the build system. The most reliable way to avoid this problem is to remove any potential source of other libraries from your PATH.

However, the PATH is not completely bare — it includes the iOS/Resources/bin folder. This folder contains a collection of scripts that wrap the invocation of the Xcode xcrun tool, removing user- and version-specific paths from the values encoded in the sysconfig module. Copies of these scripts are included in the final build products.

Once this build completes, the iOS/Frameworks folder will contain a Python.framework that can be used for testing.

To run the test suite on iOS, complete a build for a simulator platform, ensure the path modifications from the build are still in effect, and run:

$ make testios

The full test suite takes approximately 12 minutes to run on a 2022 M1 MacBook Pro, plus a couple of extra minutes to build the testbed application and boot the simulator. There will be an initial burst of console output while the Xcode test project is compiled; however, while the test suite is running, there is no console output or progress. This is a side effect of how Xcode operates when executed at the command line. You should see an iOS simulator appear during the testing process; the simulator will booth to an iOS landing screen, the testbed app will be installed, and then started. The screen of the simulator will be black while the test suite is running. When the test suite completes, success or failure will be reported at the command line. In the case of failure, you will see the full log of CPython test suite output.

You can also run the test suite in Xcode itself. This is required if you want to run on a physical device; it is also the easiest approach if you need to run a single test, or a subset of tests. See the iOS README for details.