CPython Build System: How configure and Makefile Are Generated¶
Overview¶
CPython uses the GNU Autotools build system to support compilation across Linux, macOS, Windows (via WSL/Cygwin), FreeBSD, and more. The core idea is:
Write once in portable macros → generate platform-specific shell scripts → detect system capabilities → produce a tailored
Makefile.
The pipeline looks like this:
flowchart TD
A["configure.ac\naclocal.m4"] -->|autoconf / autoheader| B["configure\npyconfig.h.in"]
B -->|./configure run by developer| C["Makefile.pre\npyconfig.h"]
D["Modules/Setup"] --> E
C -->|make makesetup| E["Makefile"]
1. Source Files (What Developers Maintain)¶
configure.ac¶
The heart of the build system. Written in M4 macro language mixed with shell script. CPython's configure.ac is ~8,000 lines long and lives at the root of the repo.
Key responsibilities:
- Declares the project name and required Autoconf version
- Detects the C compiler, linker, and their flags
- Probes for system headers, libraries, and functions
- Handles cross-compilation settings
- Generates
#definevalues forpyconfig.h
Example snippet from configure.ac:
AC_INIT([python], [3.13])
AC_PREREQ([2.71])
# Check for C compiler
AC_PROG_CC
# Check if we have a working pthread
AC_CHECK_LIB([pthread], [pthread_create])
# Check for specific headers
AC_CHECK_HEADERS([fcntl.h sys/file.h sys/socket.h])
# Custom CPython macro (defined in aclocal.m4)
PY_CHECK_FUNC([clock_gettime], [time.h])
aclocal.m4¶
Contains custom M4 macros specific to CPython that extend what stock Autoconf provides. These macros are prefixed with PY_ by convention.
Examples of CPython-specific macros:
| Macro | Purpose |
|---|---|
PY_CHECK_FUNC |
Check if a C function exists and is usable |
PY_CHECK_LIB_X |
Check for optional extension module libraries |
PY_STDLIB_MOD |
Register a standard library C extension module |
PY_TEST_COMPILER_FLAGS |
Probe whether a compiler flag is accepted |
Makefile.pre.in¶
A template for the final Makefile. Autoconf substitutes @VARIABLE@ placeholders with values detected at configure time.
# In Makefile.pre.in (template)
CC= @CC@
CXX= @CXX@
OPT= @OPT@
LDFLAGS= @LDFLAGS@
prefix= @prefix@
LIBDIR= @libdir@
# Becomes in Makefile (after ./configure)
CC= gcc
CXX= g++
OPT= -O2 -g
LDFLAGS= -L/usr/local/lib
prefix= /usr/local
LIBDIR= /usr/local/lib
pyconfig.h.in¶
A template for the C header pyconfig.h. Autoconf fills in #define / #undef entries based on what was detected on the system.
/* In pyconfig.h.in */
#undef HAVE_FCNTL_H
#undef HAVE_PTHREAD_H
#undef SIZEOF_LONG
/* Becomes in pyconfig.h (after ./configure on a Linux x86-64) */
#define HAVE_FCNTL_H 1
#define HAVE_PTHREAD_H 1
#define SIZEOF_LONG 8
2. Generating configure (What Release Managers Do)¶
The configure shell script is not stored in version control in CPython's main repo — it is generated from configure.ac by running:
This invokes:
| Tool | Input | Output |
|---|---|---|
autoconf |
configure.ac + aclocal.m4 |
configure |
autoheader |
configure.ac |
pyconfig.h.in |
aclocal |
configure.ac |
updates aclocal.m4 |
Note
CPython ships a pre-generated configure in tarballs and GitHub releases so end users don't need Autoconf installed. The configure script is committed to the CPython GitHub repository at the root level.
What autoconf does internally¶
autoconf expands all AC_* and PY_* macros from configure.ac into a portable POSIX shell script (~20,000+ lines) that:
- Accepts
-prefix,-enable-*,-with-*flags from the user - Runs compiler feature tests by compiling small C snippets
- Writes detected values into
config.status
3. Running ./configure (What Developers Do)¶
When you run ./configure, the generated shell script executes on your specific machine and does the following:
Phase 1 — Argument parsing¶
./configure \
--prefix=/usr/local \
--enable-optimizations \
--with-lto \
--enable-shared \
--with-openssl=/usr/local/opt/openssl
Common flags:
| Flag | Effect |
|---|---|
--prefix=PATH |
Installation root (default /usr/local) |
--enable-optimizations |
Enable PGO (Profile-Guided Optimization) |
--with-lto |
Enable Link-Time Optimization |
--enable-shared |
Build libpython as a shared library |
--disable-ipv6 |
Disable IPv6 support |
--with-pydebug |
Build with Py_DEBUG (assertions, extra checks) |
--with-openssl=DIR |
Path to a custom OpenSSL installation |
--host=ARCH |
Cross-compilation target triple |
Phase 2 — Feature detection¶
configure compiles hundreds of small C programs to test capabilities. For example:
# configure tests if clock_gettime is available
cat > conftest.c << 'EOF'
#include <time.h>
int main() { struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); return 0; }
EOF
gcc -o conftest conftest.c
If the test compiles and links, configure sets HAVE_CLOCK_GETTIME=1 in pyconfig.h.
Phase 3 — Output generation¶
configure produces three key outputs:
Makefile.pre— intermediate Makefile (fromMakefile.pre.in)pyconfig.h— C header with all detected#definesconfig.status— a shell script that can regenerate outputs without re-running all tests
4. From Makefile.pre to Makefile¶
CPython has a second stage of Makefile generation that most projects don't need. After configure produces Makefile.pre, running make triggers:
This calls a Python script called makesetup:
Modules/Setup¶
This file lists which C extension modules should be built statically (compiled into python itself) vs dynamically (compiled as .so files):
# Modules/Setup (simplified)
# Format: module_name source_files compiler_flags libraries
# Built-in (static) modules
posix posixmodule.c
errno errnomodule.c
_sre _sre/sre.c
# Dynamic modules (compiled as .so)
*shared*
_csv _csv.c
_json _json.c # links nothing extra
_ssl _ssl.c -I$(OPENSSL_INCLUDES) -L$(OPENSSL_LIBDIR) -lssl -lcrypto
What makesetup does¶
makesetup is a Python script (Modules/makesetup) that:
- Reads
Modules/Setupline by line - Generates
makerules for each module - Appends those rules to
Makefile.preto produce the finalMakefile
This means the final Makefile has explicit targets like:
Modules/_ssl.cpython-313-x86_64-linux-gnu.so: Modules/_ssl.c
$(CC) $(CCSHARED) $(CFLAGS) -I/usr/local/opt/openssl/include \
-c Modules/_ssl.c -o Modules/_ssl.o
$(LDSHARED) Modules/_ssl.o -L/usr/local/opt/openssl/lib \
-lssl -lcrypto -o $@
5. Cross-Compilation Support¶
For building CPython on one platform to run on another (e.g., building for ARM on an x86 machine):
./configure \
--build=x86_64-linux-gnu \ # machine running the compiler
--host=aarch64-linux-gnu \ # target machine
--prefix=/opt/python-arm
Autoconf uses the --build / --host / --target triplet convention. CPython's configure.ac includes guards like:
if test "$cross_compiling" = yes; then
# Cannot run test programs, use cached or conservative values
AC_MSG_WARN([Cross compiling: assuming SIZEOF_LONG=8])
ac_cv_sizeof_long=8
fi
A config.site file can pre-seed these cached values:
# config.site for ARM cross-compilation
ac_cv_file__dev_ptmx=yes
ac_cv_file__dev_ptc=no
ac_cv_sizeof_long=8
ac_cv_sizeof_size_t=8
6. Tracing the Full Flow: End to End¶
CPython source checkout
│
├── configure.ac ← M4 macro script
├── aclocal.m4 ← Custom PY_* macros
├── Makefile.pre.in ← Makefile template
├── pyconfig.h.in ← C header template
│
│ [autoconf/autoheader — run by release manager]
│ ↓
├── configure ← generated POSIX shell script (~20k lines)
│
│ [./configure — run by developer]
│ ↓
├── Makefile.pre ← partially filled Makefile
├── pyconfig.h ← platform-specific C header
├── config.status ← regeneration script
│
│ [make -f Makefile.pre Makefile]
│ [calls Modules/makesetup]
│ ↓
├── Makefile ← final build script with module rules
│
│ [make]
│ ↓
└── python ← compiled interpreter
7. Useful Developer Commands¶
# Regenerate configure from configure.ac (needs autoconf >= 2.71)
autoreconf -ivf
# See all available ./configure options
./configure --help
# Configure with common developer flags
./configure --with-pydebug --without-pymalloc CFLAGS="-O0 -g"
# Regenerate Makefile without re-running ./configure
./config.status
# Regenerate only pyconfig.h
./config.status pyconfig.h
# See what configure detected (all substituted variables)
./config.status --version
grep "^#define" pyconfig.h | head -40
# Verbose make (see exact compiler invocations)
make V=1
# Build only a specific extension module
make Modules/_ssl.cpython-313-x86_64-linux-gnu.so
8. Key Files Reference¶
| File | Maintained by | Generated by | Purpose |
|---|---|---|---|
configure.ac |
CPython devs | — | Autoconf source |
aclocal.m4 |
CPython devs | partly aclocal |
Custom macros |
Makefile.pre.in |
CPython devs | — | Makefile template |
pyconfig.h.in |
autoheader |
autoconf |
C header template |
configure |
autoconf |
— | Platform detection script |
Makefile.pre |
./configure |
— | Intermediate Makefile |
pyconfig.h |
./configure |
— | Platform C header |
config.status |
./configure |
— | Regeneration script |
Modules/Setup |
CPython devs | — | Module build list |
Makefile |
makesetup |
— | Final build rules |