nekizo
January 4, 2026, 10:19pm
1
I’m trying to compile c code using the python API.
main.c
#define PY_SSIZE_T_CLEAN
#include <Python.h>
int
main(int argc, char *argv[])
{
PyStatus status;
PyConfig config;
PyConfig_InitPythonConfig(&config);
status = PyConfig_SetBytesString(&config, &config.program_name, argv[0]);
if (PyStatus_Exception(status)) {
goto exception;
}
status = Py_InitializeFromConfig(&config);
if (PyStatus_Exception(status)) {
goto exception;
}
PyConfig_Clear(&config);
PyRun_SimpleString(
"import sys\n"
"from time import time,ctime\n"
"print('Today is', ctime(time()))\n"
);
if (Py_FinalizeEx() < 0) {
exit(120);
}
return 0;
exception:
PyConfig_Clear(&config);
Py_ExitStatusException(status);
}
I decided to use gcc for this along with flake
flake.nix
{
description = "A very basic python c api";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
};
outputs = { self, nixpkgs }:
let
system = "x86_64-linux";
pkgs = import nixpkgs { inherit system; };
in {
packages.${system}.default =
pkgs.stdenv.mkDerivation {
name = "hello_python";
src = self;
nativeBuildInputs= with pkgs; [
python313
gcc
];
buildPhase = ''
gcc main.c -o hello_python
'';
installPhase = ''
mkdir -p $out/bin;
install -t $out/bin hello_python
'';
NIX_CFLAGS_COMPILE = [
"-I${pkgs.python313.outPath}/include/${pkgs.python313.executable}"
];
NIX_LDFLAGS = [
"-L${pkgs.python313.outPath}/lib"
];
};
};
}
$ tree
.
├── flake.lock
├── flake.nix
└── main.c
But when I tried to run it, I got a liker error
$ nix run .
error: Cannot build '/nix/store/q0l1ad8a2r0li5w2prhz8kxxvkdhj77q-hello_python.drv'.
Reason: builder failed with exit code 1.
Output paths:
/nix/store/9vbjswb7q3n8csyf428fzdr6yzsk4vxw-hello_python
Last 20 log lines:
> Running phase: unpackPhase
> unpacking source archive /nix/store/navqf7pq5f08byh7kmbq0f84lpm7l7sd-source
> source root is source
> Running phase: patchPhase
> Running phase: updateAutotoolsGnuConfigScriptsPhase
> Running phase: configurePhase
> no configure script, doing nothing
> Running phase: buildPhase
> /nix/store/dc9vaz50jg7mibk9xvqw5dqv89cxzla3-binutils-2.44/bin/ld: /build/ccimUTjq.o: in function `main':
> main.c:(.text.startup+0x32): undefined reference to `PyConfig_InitPythonConfig'
> /nix/store/dc9vaz50jg7mibk9xvqw5dqv89cxzla3-binutils-2.44/bin/ld: main.c:(.text.startup+0x48): undefined reference to `PyConfig_SetBytesString'
> /nix/store/dc9vaz50jg7mibk9xvqw5dqv89cxzla3-binutils-2.44/bin/ld: main.c:(.text.startup+0x6a): undefined reference to `PyStatus_Exception'
> /nix/store/dc9vaz50jg7mibk9xvqw5dqv89cxzla3-binutils-2.44/bin/ld: main.c:(.text.startup+0x7d): undefined reference to `Py_InitializeFromConfig'
> /nix/store/dc9vaz50jg7mibk9xvqw5dqv89cxzla3-binutils-2.44/bin/ld: main.c:(.text.startup+0x9f): undefined reference to `PyStatus_Exception'
> /nix/store/dc9vaz50jg7mibk9xvqw5dqv89cxzla3-binutils-2.44/bin/ld: main.c:(.text.startup+0xaf): undefined reference to `PyConfig_Clear'
> /nix/store/dc9vaz50jg7mibk9xvqw5dqv89cxzla3-binutils-2.44/bin/ld: main.c:(.text.startup+0xbd): undefined reference to `PyRun_SimpleStringFlags'
> /nix/store/dc9vaz50jg7mibk9xvqw5dqv89cxzla3-binutils-2.44/bin/ld: main.c:(.text.startup+0xc2): undefined reference to `Py_FinalizeEx'
> /nix/store/dc9vaz50jg7mibk9xvqw5dqv89cxzla3-binutils-2.44/bin/ld: main.c:(.text.startup+0xea): undefined reference to `PyConfig_Clear'
> /nix/store/dc9vaz50jg7mibk9xvqw5dqv89cxzla3-binutils-2.44/bin/ld: main.c:(.text.startup+0x10c): undefined reference to `Py_ExitStatusException'
> collect2: error: ld returned 1 exit status
For full logs, run:
nix log /nix/store/q0l1ad8a2r0li5w2prhz8kxxvkdhj77q-hello_python.drv
We are looking for an answer in Python - Official NixOS Wiki , C - NixOS Wiki and Cannot link against Python but I didn’t find anything.
Can someone help?
nekizo
January 17, 2026, 12:17am
2
If anyone is curious, I managed to solve it.
{
description = "A very basic python c api";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
};
outputs = { self, nixpkgs }:
let
system = "x86_64-linux";
pkgs = import nixpkgs { inherit system; };
in {
packages.${system}.default =
pkgs.stdenv.mkDerivation {
name = "hello_python";
src = self;
nativeBuildInputs= with pkgs; [
python313
gcc
autoPatchelfHook
];
buildPhase = ''
gcc main.c -o hello_python
'';
installPhase = ''
mkdir -p $out/bin;
install -t $out/bin hello_python
'';
NIX_CFLAGS_COMPILE = [
"-I${pkgs.python313.outPath}/include/${pkgs.python313.executable}"
];
NIX_LDFLAGS = [
"-L. -l:libpython3.13.so"
];
# autoPatchelfIgnoreMissingDeps = [
# "libpython3.13.so"
# ];
};
};
}
tejing
January 17, 2026, 12:46am
3
Put python in buildInputs, not nativeBuildInputs. Should take care of the include path and linker flags automatically. nativeBuildInputs is for inputs that are run during the build process (things that need to have been compiled for the build machine’s architecture when cross-compiling), so stdenv doesn’t try to include or link against them.
Also, gcc is included automatically in stdenv, so there’s no need to explicitly list it.
And why did you include autoPatchelfHook? That’s for when you’re packaging a prebuilt binary rather than compiling…
1 Like
nekizo
January 17, 2026, 10:20am
4
Thanks, but practice has shown that there are errors in your statement.
“Put python in buildInputs, not nativeBuildInputs.” Doesn’t work. I don’t know why but it doesn’t work.
"gcc is included automatically.” You’re just right.
“why did you include autoPatchelfHook?”. After I managed to compile the file. I encountered a problem.
$ nix run
/nix/store/69222wrf2g2szxa161v53wsgin9r254y-hello_python/bin/hello_python: error while loading shared libraries: libpython3.13.so.1.0: cannot open shared object file: No such file or directory
add autoPatchelfHook help.
tejing
January 17, 2026, 10:28am
5
Can you be more specific about 1? It should work. What kind of error do you get?
As for 3, this is fundamentally because you haven’t told stdenv that python is an input in the correct way. The fact that autoPatchelfHook fixes it is essentially an accident. This isn’t what it’s for, and it shouldn’t be used in a situation like this.
Overall, you seem to have found a bunch of brute-force workarounds to the fact that you haven’t made correct use of stdenv here. This should work with just buildInputs, buildPhase, and installPhase.
1 Like
nekizo
January 17, 2026, 10:50am
6
pkgs.stdenv.mkDerivation {
name = "hello_python";
src = self;
buildInputs= with pkgs; [
python313
# autoPatchelfHook
];
buildPhase = ''
gcc main.c -o hello_python
'';
installPhase = ''
mkdir -p $out/bin;
install -t $out/bin hello_python
'';
# NIX_CFLAGS_COMPILE = [
# "-I${pkgs.python313.outPath}/include/${pkgs.python313.executable}"
# ];
# NIX_LDFLAGS = [
# "-L. -l:libpython3.13.so"
# ];
};
$ nix run
error: Cannot build '/nix/store/pnmiaz4ry46vgc4kn6rcqkl754jmvyhv-hello_python.drv'.
Reason: builder failed with exit code 1.
Output paths:
/nix/store/jl75wbl009yl0lmc5kq9navqssrky0jb-hello_python
Last 12 log lines:
> Running phase: unpackPhase
> unpacking source archive /nix/store/461gwgmc393k1rrpqa5ayck20p1igdhr-source
> source root is source
> Running phase: patchPhase
> Running phase: updateAutotoolsGnuConfigScriptsPhase
> Running phase: configurePhase
> no configure script, doing nothing
> Running phase: buildPhase
> main.c:2:10: fatal error: Python.h: No such file or directory
> 2 | #include <Python.h>
> | ^~~~~~~~~~
> compilation terminated.
For full logs, run:
nix log /nix/store/pnmiaz4ry46vgc4kn6rcqkl754jmvyhv-hello_python.drv
pkgs.stdenv.mkDerivation {
name = "hello_python";
src = self;
buildInputs= with pkgs; [
python313
# autoPatchelfHook
];
buildPhase = ''
gcc main.c -o hello_python
'';
installPhase = ''
mkdir -p $out/bin;
install -t $out/bin hello_python
'';
NIX_CFLAGS_COMPILE = [
"-I${pkgs.python313.outPath}/include/${pkgs.python313.executable}"
];
# NIX_LDFLAGS = [
# "-L. -l:libpython3.13.so"
# ];
};
$ nix run
error: Cannot build '/nix/store/3wy2ivsjis4k6j5m7pkdbhssmq58m7li-hello_python.drv'.
Reason: builder failed with exit code 1.
Output paths:
/nix/store/nalaf8jwf1fv5g9vzba6mlkp4jf76d47-hello_python
Last 20 log lines:
> Running phase: unpackPhase
> unpacking source archive /nix/store/2j4l8zj0w0yfnh8kabgqs3zlyjh957jz-source
> source root is source
> Running phase: patchPhase
> Running phase: updateAutotoolsGnuConfigScriptsPhase
> Running phase: configurePhase
> no configure script, doing nothing
> Running phase: buildPhase
> /nix/store/dc9vaz50jg7mibk9xvqw5dqv89cxzla3-binutils-2.44/bin/ld:/build/ccBYD2d3.o: i
n function `main':
> main.c:(.text.startup+0x32): undefined reference to`PyConfig_InitPythonConfig'
> /nix/store/dc9vaz50jg7mibk9xvqw5dqv89cxzla3-binutils-2.44/bin/ld:main.c:(.text.startup+0x48): undefined reference to `PyConfig_SetBytesString'
> /nix/store/dc9vaz50jg7mibk9xvqw5dqv89cxzla3-binutils-2.44/bin/ld: main.c:(.text.startup+0x6a): undefined reference to `PyStatus_Exception'
> /nix/store/dc9vaz50jg7mibk9xvqw5dqv89cxzla3-binutils-2.44/bin/ld: main.c:(.text.startup+0x7d): undefined reference to `Py_InitializeFromConfig'
> /nix/store/dc9vaz50jg7mibk9xvqw5dqv89cxzla3-binutils-2.44/bin/ld: main.c:(.text.startup+0x9f): undefined reference to `PyStatus_Exception'
> /nix/store/dc9vaz50jg7mibk9xvqw5dqv89cxzla3-binutils-2.44/bin/ld: main.c:(.text.startup+0xaf): undefined reference to `PyConfig_Clear'
> /nix/store/dc9vaz50jg7mibk9xvqw5dqv89cxzla3-binutils-2.44/bin/ld: main.c:(.text.startup+0xbd): undefined reference to `PyRun_SimpleStringFlags'
> /nix/store/dc9vaz50jg7mibk9xvqw5dqv89cxzla3-binutils-2.44/bin/ld: main.c:(.text.startup+0xc2): undefined reference to `Py_FinalizeEx'
> /nix/store/dc9vaz50jg7mibk9xvqw5dqv89cxzla3-binutils-2.44/bin/ld: main.c:(.text.startup+0xea): undefined reference to `PyConfig_Clear'
> /nix/store/dc9vaz50jg7mibk9xvqw5dqv89cxzla3-binutils-2.44/bin/ld: main.c:(.text.startup+0x10c): undefined reference to `Py_ExitStatusException'
> collect2: error: ld returned 1 exit status
For full logs, run:
nix log /nix/store/3wy2ivsjis4k6j5m7pkdbhssmq58m7li-hello_python.drv
pkgs.stdenv.mkDerivation {
name = "hello_python";
src = self;
buildInputs= with pkgs; [
python313
# autoPatchelfHook
];
buildPhase = ''
gcc main.c -o hello_python
'';
installPhase = ''
mkdir -p $out/bin;
install -t $out/bin hello_python
'';
NIX_CFLAGS_COMPILE = [
"-I${pkgs.python313.outPath}/include/${pkgs.python313.executable}"
];
NIX_LDFLAGS = [
"-L. -l:libpython3.13.so"
];
};
$ nix run
/nix/store/nyndmyvx4zyrbbns7jsnrwcz6ic3wnmm-hello_python/bin/hello_python: error while loading shared libraries: libpython3.13.so.1.0: cannot open shared object file: No such file or directory
pkgs.stdenv.mkDerivation {
name = "hello_python";
src = self;
buildInputs= with pkgs; [
python313
autoPatchelfHook
];
buildPhase = ''
gcc main.c -o hello_python
'';
installPhase = ''
mkdir -p $out/bin;
install -t $out/bin hello_python
'';
NIX_CFLAGS_COMPILE = [
"-I${pkgs.python313.outPath}/include/${pkgs.python313.executable}"
];
NIX_LDFLAGS = [
"-L. -l:libpython3.13.so"
];
};
$ nix run
Today is Sat Jan 17 11:48:44 2026
I can also list which specific flags are missing at specific stages, but I believe my brute-force methods are self-explanatory. Besides, it’s nix, if you want you can check it yourself.
NobbZ
January 17, 2026, 11:13am
7
Try passing $(pkg-config --cflags python --libs python) to your gcc invocation, or use a configuration system. Alternatively you can use pkg-config in the postConfig to export augmented but not completely overriden NIX_* envvars.
Though in my opinion CLI args should be prefered, or for projects of a certain size, proper configuration through autotools, cmake, or whatever else there is available.
2 Likes
tejing
January 17, 2026, 11:52am
8
Yeah, using pkg-config is probably the cleanest option in this case. The next-stage option would be a small makefile that uses pkg-config to set flag variables… which would just be moving the custom build and install phases into a different file, essentially. Then there’s full autotools, which is pretty clearly overkill.
1 Like
nekizo
January 17, 2026, 5:22pm
9
$(pkg-config --libs python) return -L/nix/store/3lll9y925zz9393sa59h653xik66srjb-python3-3.13.9/lib but this flag is already added twice by buildInputs. That’s why I added -l:libpython3.13.so instead and work
pkgs.stdenv.mkDerivation {
name = "hello_python";
src = self;
buildInputs= with pkgs; [
python313
autoPatchelfHook
pkg-config
];
buildPhase = ''
gcc $(pkg-config --cflags python) -l:libpython3.13.so \
main.c -o hello_python
'';
installPhase = ''
mkdir -p $out/bin;
install -t $out/bin hello_python
'';
};
NobbZ
January 17, 2026, 5:26pm
10
If -L is given, the compiler knows where to look for shared objects. You still need -l to tell which ones to link.
More idiomatic would be -lpython3 or -lpython3.13. depending on which one you actually need.
1 Like