Home / Alt manpages / x86_64-linux-gnu-python3.12-config(1)

  • x86_64-linux-gnu-python3.12-config(1)
  • User command
  • linux

Use python3.12-config to Build Extensions and Embedded Python

You will use x86_64-linux-gnu-python3.12-config to obtain the compiler and linker flags for the Python 3.12 development files, then use them in two small builds: a loadable C extension and a program that embeds Python. The aliases python3.12-config and x86_64-linux-gnu-python3-config select the same installed configuration on this machine.

Allow 15 to 20 minutes if the development packages are already installed. You need a shell, a C compiler and the development packages, but no elevated privileges for the checks or examples below. The commands only create temporary files under /tmp; do not use sudo unless your own source or destination directory is protected.

1. Confirm the interpreter and configuration script

The installed package set is Python 3.12.3, from python3.12-dev 3.12.3-1ubuntu0.17 and python3-dev 3.12.3-0ubuntu2.1. Check the command you intend to use instead of assuming that an unqualified python3-config belongs to the system Python:

$ command -v x86_64-linux-gnu-python3.12-config
/usr/bin/x86_64-linux-gnu-python3.12-config
$ x86_64-linux-gnu-python3.12-config --help
Usage: /usr/bin/x86_64-linux-gnu-python3.12-config --prefix|--exec-prefix|--includes|--libs|--cflags|--ldflags|--extension-suffix|--help|--abiflags|--configdir|--embed

The local manual page lists the traditional reporting options, while this installed script also accepts --configdir and --embed. That difference is version and packaging specific. Capture the path and version together when a build must be reproducible:

$ python3.12 --version
Python 3.12.3
$ x86_64-linux-gnu-python3.12-config --configdir
/usr/lib/python3.12/config-3.12-x86_64-linux-gnu

Checkpoint: if command -v finds a different prefix, stop and inspect that installation before mixing its flags with /usr/bin/python3.12.

2. Inspect the flags before putting them in a build

The script prints fragments intended for a shell command. --includes supplies header search paths, --cflags adds the installed compiler settings, --ldflags supplies library search paths and ordinary link libraries, and --libs prints the library portion without the search paths. --extension-suffix gives the filename ending expected for a binary extension.

$ x86_64-linux-gnu-python3.12-config --includes
-I/usr/include/python3.12 -I/usr/include/python3.12
$ x86_64-linux-gnu-python3.12-config --cflags
-I/usr/include/python3.12 -I/usr/include/python3.12  -fno-strict-overflow -Wsign-compare  -DNDEBUG -g -O2 -Wall
$ x86_64-linux-gnu-python3.12-config --ldflags
-L/usr/lib/python3.12/config-3.12-x86_64-linux-gnu -L/usr/lib/x86_64-linux-gnu  -ldl  -lm
$ x86_64-linux-gnu-python3.12-config --extension-suffix
.cpython-312-x86_64-linux-gnu.so

Do not treat these values as portable constants. The include directory, ABI suffix, library directory and optimisation flags can change with the Python build, architecture and distribution. Let command substitution obtain them at build time. The output from --abiflags is empty here, which is valid for this Python 3.12 build.

Do not confuse --libs with the libraries needed by an embedding application. On this installation it prints -ldl -lm, whereas --embed adds the Python library to the link flags.

3. Build and import a small extension

A CPython extension is a shared library with an initialisation function whose name matches the module. This example uses a module called check and writes the ABI-specific filename into /tmp. It uses standard input so there is no source file to clean up.

$ printf '%s\n' \
    '#include <Python.h>' \
    'static struct PyModuleDef module = { PyModuleDef_HEAD_INIT, "check", 0, -1, 0 };' \
    'PyMODINIT_FUNC PyInit_check(void) { return PyModule_Create(&module); }' |
    gcc -x c - $(x86_64-linux-gnu-python3.12-config --cflags --ldflags) \
    -shared -fPIC \
    -o /tmp/check$(x86_64-linux-gnu-python3.12-config --extension-suffix)
$ python3.12 -c 'import sys; sys.path.insert(0, "/tmp"); import check; print(check.__name__)'
check

The -shared and -fPIC options are compiler choices for a loadable Unix shared object; python3.12-config does not add them for you. The filename matters: Python searches for the module name followed by an appropriate suffix, so using the reported suffix avoids guessing the ABI tag.

Checkpoint: an import error mentioning PyInit_check usually means the module name, initialisation function or output filename disagrees. An error for Python.h means the development headers are missing or the flags came from a different Python installation.

Embedding is the reverse arrangement: your C program is the main executable and starts the Python runtime. Ask for --embed alongside the ordinary flags. The option changes the library list; it does not initialise Python and it does not grant any extra operating-system privilege.

$ printf '%s\n' \
    '#include <Python.h>' \
    'int main(void) { Py_Initialize(); Py_Finalize(); return 0; }' |
    gcc -x c - $(x86_64-linux-gnu-python3.12-config --cflags --embed --ldflags) \
    -o /tmp/python-embed-check
$ /tmp/python-embed-check
$ printf '%s\n' "$?"
0

Without --embed, the installed --ldflags output does not contain -lpython3.12, so a program that calls the Python C API can fail at link time with undefined references. That is deliberate: extension modules are loaded by an existing interpreter, while embedded applications must link the interpreter library.

These examples leave two test binaries in /tmp. Remove those exact files when you have finished testing, or keep them for inspection. Do not replace a real project output with shell redirection or a guessed filename without checking first, because redirection truncates an existing destination before compilation starts.

5. Handle common failures without changing system state

  • No command found: install or select the matching development package through your normal package-management process. This guide does not need sudo for diagnosis.
  • Headers are found but linking fails: compare command -v python3.12 with the configuration script path. Do not combine Homebrew or virtual-environment tooling with Debian or Ubuntu system flags by accident.
  • The extension imports under another Python: check python3.12 --version, python3.12 -c 'import sys; print(sys.executable)' and the reported suffix. Binary extensions are tied to the interpreter ABI and platform.
  • Only --libs was used for embedding: rebuild with --embed --ldflags. Do not paper over missing symbols by adding arbitrary system libraries.
  • The command prints usage and exits non-zero: it received no option or an unknown option. Check spelling against --help; status 1 is expected for that input.

The configuration command itself is read-only. Building can still overwrite an output path, and running an embedded program can execute arbitrary Python code, so review source and destinations before changing the example for a real project.

Done means

  • x86_64-linux-gnu-python3.12-config and python3.12 resolve to the intended installation.
  • --cflags, --ldflags and --extension-suffix were obtained from the installed tool, not copied from another machine.
  • A test extension compiled and imported as check.
  • An embedding test linked with --embed and exited with status 0.
  • Temporary binaries were reviewed and removed or deliberately retained.