1{
2 lib,
3 stdenv,
4 fetchurl,
5 fetchpatch,
6 fetchgit,
7
8 # build dependencies
9 autoconf-archive,
10 autoreconfHook,
11 nukeReferences,
12 pkg-config,
13 python-setup-hook,
14
15 # runtime dependencies
16 bzip2,
17 expat,
18 libffi,
19 libuuid,
20 libxcrypt,
21 mpdecimal,
22 ncurses,
23 openssl,
24 sqlite,
25 xz,
26 zlib,
27
28 # platform-specific dependencies
29 bashNonInteractive,
30 windows,
31
32 # optional dependencies
33 bluezSupport ? false,
34 bluez,
35 mimetypesSupport ? true,
36 mailcap,
37 tzdata,
38 withGdbm ? !stdenv.hostPlatform.isWindows,
39 gdbm,
40 withReadline ? !stdenv.hostPlatform.isWindows,
41 readline,
42 x11Support ? false,
43 tcl,
44 tk,
45 tclPackages,
46 libX11,
47 xorgproto,
48
49 # splicing/cross
50 pythonAttr ? "python${sourceVersion.major}${sourceVersion.minor}",
51 self,
52 pkgsBuildBuild,
53 pkgsBuildHost,
54 pkgsBuildTarget,
55 pkgsHostHost,
56 pkgsTargetTarget,
57 __splices ? { },
58
59 # build customization
60 sourceVersion,
61 hash,
62 passthruFun,
63 stripConfig ? false,
64 stripIdlelib ? false,
65 stripTests ? false,
66 stripTkinter ? false,
67 rebuildBytecode ? true,
68 stripBytecode ? true,
69 includeSiteCustomize ? true,
70 static ? stdenv.hostPlatform.isStatic,
71 enableFramework ? false,
72 noldconfigPatch ? ./. + "/${sourceVersion.major}.${sourceVersion.minor}/no-ldconfig.patch",
73 enableGIL ? true,
74
75 # pgo (not reproducible) + -fno-semantic-interposition
76 # https://docs.python.org/3/using/configure.html#cmdoption-enable-optimizations
77 enableOptimizations ? false,
78
79 # improves performance, but remains reproducible
80 enableNoSemanticInterposition ? true,
81
82 # enabling LTO on 32bit arch causes downstream packages to fail when linking
83 enableLTO ?
84 stdenv.hostPlatform.isDarwin || (stdenv.hostPlatform.is64bit && stdenv.hostPlatform.isLinux),
85
86 # enable asserts to ensure the build remains reproducible
87 reproducibleBuild ? false,
88
89 # for the Python package set
90 packageOverrides ? (self: super: { }),
91
92 # tests
93 testers,
94
95}@inputs:
96
97# Note: this package is used for bootstrapping fetchurl, and thus
98# cannot use fetchpatch! All mutable patches (generated by GitHub or
99# cgit) that are needed here should be included directly in Nixpkgs as
100# files.
101
102assert x11Support -> tcl != null && tk != null && xorgproto != null && libX11 != null;
103
104assert bluezSupport -> bluez != null;
105
106assert lib.assertMsg (
107 enableFramework -> stdenv.hostPlatform.isDarwin
108) "Framework builds are only supported on Darwin.";
109
110assert lib.assertMsg (
111 reproducibleBuild -> stripBytecode
112) "Deterministic builds require stripping bytecode.";
113
114assert lib.assertMsg (
115 reproducibleBuild -> (!enableOptimizations)
116) "Deterministic builds are not achieved when optimizations are enabled.";
117
118assert lib.assertMsg (
119 reproducibleBuild -> (!rebuildBytecode)
120) "Deterministic builds are not achieved when (default unoptimized) bytecode is created.";
121
122let
123 inherit (lib)
124 concatMapStringsSep
125 concatStringsSep
126 enableFeature
127 getDev
128 getLib
129 optionals
130 optionalString
131 replaceStrings
132 versionOlder
133 ;
134
135 # mixes libc and libxcrypt headers and libs and causes segfaults on importing crypt
136 libxcrypt = if stdenv.hostPlatform.isFreeBSD then null else inputs.libxcrypt;
137
138 buildPackages = pkgsBuildHost;
139 inherit (passthru) pythonOnBuildForHost;
140
141 tzdataSupport = tzdata != null && passthru.pythonAtLeast "3.9";
142
143 passthru =
144 let
145 # When we override the interpreter we also need to override the spliced versions of the interpreter
146 # bluez is excluded manually to break an infinite recursion.
147 inputs' = lib.filterAttrs (n: v: n != "bluez" && n != "passthruFun" && !lib.isDerivation v) inputs;
148 # Memoization of the splices to avoid re-evaluating this function for all combinations of splices e.g.
149 # python3.pythonOnBuildForHost.pythonOnBuildForTarget == python3.pythonOnBuildForTarget by consuming
150 # __splices as an arg and using the cache if populated.
151 splices = {
152 pythonOnBuildForBuild = override pkgsBuildBuild.${pythonAttr};
153 pythonOnBuildForHost = override pkgsBuildHost.${pythonAttr};
154 pythonOnBuildForTarget = override pkgsBuildTarget.${pythonAttr};
155 pythonOnHostForHost = override pkgsHostHost.${pythonAttr};
156 pythonOnTargetForTarget = lib.optionalAttrs (lib.hasAttr pythonAttr pkgsTargetTarget) (
157 override pkgsTargetTarget.${pythonAttr}
158 );
159 } // __splices;
160 override =
161 attr:
162 let
163 python = attr.override (
164 inputs'
165 // {
166 self = python;
167 __splices = splices;
168 }
169 );
170 in
171 python;
172 in
173 passthruFun rec {
174 inherit self sourceVersion packageOverrides;
175 implementation = "cpython";
176 libPrefix = "python${pythonVersion}${lib.optionalString (!enableGIL) "t"}";
177 executable = libPrefix;
178 pythonVersion = with sourceVersion; "${major}.${minor}";
179 sitePackages = "lib/${libPrefix}/site-packages";
180 inherit hasDistutilsCxxPatch pythonAttr;
181 inherit (splices)
182 pythonOnBuildForBuild
183 pythonOnBuildForHost
184 pythonOnBuildForTarget
185 pythonOnHostForHost
186 pythonOnTargetForTarget
187 ;
188 };
189
190 version = with sourceVersion; "${major}.${minor}.${patch}${suffix}";
191
192 nativeBuildInputs =
193 [
194 nukeReferences
195 ]
196 ++ optionals (!stdenv.hostPlatform.isDarwin) [
197 autoconf-archive # needed for AX_CHECK_COMPILE_FLAG
198 autoreconfHook
199 pkg-config
200 ]
201 ++ optionals (stdenv.hostPlatform != stdenv.buildPlatform) [
202 buildPackages.stdenv.cc
203 pythonOnBuildForHost
204 ]
205 ++
206 optionals
207 (
208 stdenv.cc.isClang
209 && (!stdenv.hostPlatform.useAndroidPrebuilt or false)
210 && (enableLTO || enableOptimizations)
211 )
212 [
213 stdenv.cc.cc.libllvm.out
214 ];
215
216 buildInputs = lib.filter (p: p != null) (
217 [
218 bzip2
219 expat
220 libffi
221 libuuid
222 libxcrypt
223 mpdecimal
224 ncurses
225 openssl
226 sqlite
227 xz
228 zlib
229 ]
230 ++ optionals bluezSupport [
231 bluez
232 ]
233 ++ optionals stdenv.hostPlatform.isMinGW [
234 windows.dlfcn
235 windows.mingw_w64_pthreads
236 ]
237 ++ optionals tzdataSupport [
238 tzdata
239 ]
240 ++ optionals withGdbm [
241 gdbm
242 ]
243 ++ optionals withReadline [
244 readline
245 ]
246 ++ optionals x11Support [
247 libX11
248 tcl
249 tk
250 xorgproto
251 ]
252 );
253
254 hasDistutilsCxxPatch = !(stdenv.cc.isGNU or false);
255
256 pythonOnBuildForHostInterpreter =
257 if stdenv.hostPlatform == stdenv.buildPlatform then
258 "$out/bin/python"
259 else
260 pythonOnBuildForHost.interpreter;
261
262 src = fetchurl {
263 url =
264 with sourceVersion;
265 "https://www.python.org/ftp/python/${major}.${minor}.${patch}/Python-${version}.tar.xz";
266 inherit hash;
267 };
268
269 # win32 is added by Fedora’s patch
270 machdep = if stdenv.hostPlatform.isWindows then "win32" else stdenv.hostPlatform.parsed.kernel.name;
271
272 # https://github.com/python/cpython/blob/e488e300f5c01289c10906c2e53a8e43d6de32d8/configure.ac#L428
273 # The configure script uses "arm" as the CPU name for all 32-bit ARM
274 # variants when cross-compiling, but native builds include the version
275 # suffix, so we do the same.
276 pythonHostPlatform =
277 let
278 cpu =
279 {
280 # According to PEP600, Python's name for the Power PC
281 # architecture is "ppc", not "powerpc". Without the Rosetta
282 # Stone below, the PEP600 requirement that "${ARCH} matches
283 # the return value from distutils.util.get_platform()" fails.
284 # https://peps.python.org/pep-0600/
285 powerpc = "ppc";
286 powerpcle = "ppcle";
287 powerpc64 = "ppc64";
288 powerpc64le = "ppc64le";
289 }
290 .${stdenv.hostPlatform.parsed.cpu.name} or stdenv.hostPlatform.parsed.cpu.name;
291 in
292 "${machdep}-${cpu}";
293
294 execSuffix = stdenv.hostPlatform.extensions.executable;
295in
296with passthru;
297stdenv.mkDerivation (finalAttrs: {
298 pname = "python3";
299 inherit src version;
300
301 inherit nativeBuildInputs;
302 buildInputs =
303 lib.optionals (!stdenv.hostPlatform.isWindows) [
304 bashNonInteractive # only required for patchShebangs
305 ]
306 ++ buildInputs;
307
308 prePatch = optionalString stdenv.hostPlatform.isDarwin ''
309 substituteInPlace configure --replace-fail '`/usr/bin/arch`' '"i386"'
310 '';
311
312 patches =
313 [
314 # Disable the use of ldconfig in ctypes.util.find_library (since
315 # ldconfig doesn't work on NixOS), and don't use
316 # ctypes.util.find_library during the loading of the uuid module
317 # (since it will do a futile invocation of gcc (!) to find
318 # libuuid, slowing down program startup a lot).
319 noldconfigPatch
320 ]
321 ++ optionals (stdenv.hostPlatform != stdenv.buildPlatform && stdenv.hostPlatform.isFreeBSD) [
322 # Cross compilation only supports a limited number of "known good"
323 # configurations. If you're reading this and it's been a long time
324 # since this diff, consider submitting this patch upstream!
325 ./freebsd-cross.patch
326 ]
327 ++ optionals (pythonOlder "3.13") [
328 # Make sure that the virtualenv activation scripts are
329 # owner-writable, so venvs can be recreated without permission
330 # errors.
331 ./virtualenv-permissions.patch
332 ]
333 ++ optionals (pythonAtLeast "3.13") [
334 ./3.13/virtualenv-permissions.patch
335 ]
336 ++ optionals mimetypesSupport [
337 # Make the mimetypes module refer to the right file
338 ./mimetypes.patch
339 ]
340 ++ optionals (pythonAtLeast "3.9" && pythonOlder "3.11" && stdenv.hostPlatform.isDarwin) [
341 # Stop checking for TCL/TK in global macOS locations
342 ./3.9/darwin-tcl-tk.patch
343 ]
344 ++ optionals (hasDistutilsCxxPatch && pythonOlder "3.12") [
345 # Fix for http://bugs.python.org/issue1222585
346 # Upstream distutils is calling C compiler to compile C++ code, which
347 # only works for GCC and Apple Clang. This makes distutils to call C++
348 # compiler when needed.
349 (
350 if pythonAtLeast "3.7" && pythonOlder "3.11" then
351 ./3.7/python-3.x-distutils-C++.patch
352 else if pythonAtLeast "3.11" then
353 ./3.11/python-3.x-distutils-C++.patch
354 else
355 fetchpatch {
356 url = "https://bugs.python.org/file48016/python-3.x-distutils-C++.patch";
357 sha256 = "1h18lnpx539h5lfxyk379dxwr8m2raigcjixkf133l4xy3f4bzi2";
358 }
359 )
360 ]
361 ++ optionals (pythonAtLeast "3.7" && pythonOlder "3.12") [
362 # LDSHARED now uses $CC instead of gcc. Fixes cross-compilation of extension modules.
363 ./3.8/0001-On-all-posix-systems-not-just-Darwin-set-LDSHARED-if.patch
364 # Use sysconfigdata to find headers. Fixes cross-compilation of extension modules.
365 ./3.7/fix-finding-headers-when-cross-compiling.patch
366 ]
367 ++ optionals (pythonOlder "3.12") [
368 # https://github.com/python/cpython/issues/90656
369 ./loongarch-support.patch
370 # fix failing tests with openssl >= 3.4
371 # https://github.com/python/cpython/pull/127361
372 ]
373 ++ optionals (pythonAtLeast "3.11" && pythonOlder "3.13") [
374 # backport fix for https://github.com/python/cpython/issues/95855
375 ./platform-triplet-detection.patch
376 ]
377 ++ optionals (stdenv.hostPlatform.isMinGW) (
378 let
379 # https://src.fedoraproject.org/rpms/mingw-python3
380 mingw-patch = fetchgit {
381 name = "mingw-python-patches";
382 url = "https://src.fedoraproject.org/rpms/mingw-python3.git";
383 rev = "3edecdbfb4bbf1276d09cd5e80e9fb3dd88c9511"; # for python 3.11.9 at the time of writing.
384 hash = "sha256-kpXoIHlz53+0FAm/fK99ZBdNUg0u13erOr1XP2FSkQY=";
385 };
386 in
387 (builtins.map (f: "${mingw-patch}/${f}") [
388 # The other patches in that repo are already applied to 3.11.10
389 "mingw-python3_distutils.patch"
390 "mingw-python3_frozenmain.patch"
391 "mingw-python3_make-sysconfigdata.py-relocatable.patch"
392 "mingw-python3_mods-failed.patch"
393 "mingw-python3_module-select.patch"
394 "mingw-python3_module-socket.patch"
395 "mingw-python3_modules.patch"
396 "mingw-python3_platform-mingw.patch"
397 "mingw-python3_posix-layout.patch"
398 "mingw-python3_pthread_threadid.patch"
399 "mingw-python3_pythonw.patch"
400 "mingw-python3_setenv.patch"
401 "mingw-python3_win-modules.patch"
402 ])
403 );
404
405 postPatch =
406 optionalString (!stdenv.hostPlatform.isWindows) ''
407 substituteInPlace Lib/subprocess.py \
408 --replace-fail "'/bin/sh'" "'${bashNonInteractive}/bin/sh'"
409 ''
410 + optionalString mimetypesSupport ''
411 substituteInPlace Lib/mimetypes.py \
412 --replace-fail "@mime-types@" "${mailcap}"
413 ''
414 + optionalString (pythonOlder "3.13" && x11Support && ((tclPackages.tix or null) != null)) ''
415 substituteInPlace "Lib/tkinter/tix.py" --replace-fail \
416 "os.environ.get('TIX_LIBRARY')" \
417 "os.environ.get('TIX_LIBRARY') or '${tclPackages.tix}/lib'"
418 '';
419
420 env = {
421 CPPFLAGS = concatStringsSep " " (map (p: "-I${getDev p}/include") buildInputs);
422 LDFLAGS = concatStringsSep " " (map (p: "-L${getLib p}/lib") buildInputs);
423 LIBS = "${optionalString (!stdenv.hostPlatform.isDarwin) "-lcrypt"}";
424 NIX_LDFLAGS = lib.optionalString (stdenv.cc.isGNU && !stdenv.hostPlatform.isStatic) (
425 {
426 "glibc" = "-lgcc_s";
427 "musl" = "-lgcc_eh";
428 }
429 ."${stdenv.hostPlatform.libc}" or ""
430 );
431 # Determinism: We fix the hashes of str, bytes and datetime objects.
432 PYTHONHASHSEED = 0;
433 };
434
435 # https://docs.python.org/3/using/configure.html
436 configureFlags =
437 [
438 "--without-ensurepip"
439 "--with-system-expat"
440 "--with-system-libmpdec"
441 ]
442 ++ optionals (openssl != null) [
443 "--with-openssl=${openssl.dev}"
444 ]
445 ++ optionals tzdataSupport [
446 "--with-tzpath=${tzdata}/share/zoneinfo"
447 ]
448 ++ optionals (execSuffix != "") [
449 "--with-suffix=${execSuffix}"
450 ]
451 ++ optionals enableLTO [
452 "--with-lto"
453 ]
454 ++ optionals (!static && !enableFramework) [
455 "--enable-shared"
456 ]
457 ++ optionals enableFramework [
458 "--enable-framework=${placeholder "out"}/Library/Frameworks"
459 ]
460 ++ optionals (pythonAtLeast "3.13") [
461 (enableFeature enableGIL "gil")
462 ]
463 ++ optionals enableOptimizations [
464 "--enable-optimizations"
465 ]
466 ++ optionals (sqlite != null) [
467 "--enable-loadable-sqlite-extensions"
468 ]
469 ++ optionals (libxcrypt != null) [
470 "CFLAGS=-I${libxcrypt}/include"
471 "LIBS=-L${libxcrypt}/lib"
472 ]
473 ++ optionals (stdenv.hostPlatform != stdenv.buildPlatform) [
474 "ac_cv_buggy_getaddrinfo=no"
475 # Assume little-endian IEEE 754 floating point when cross compiling
476 "ac_cv_little_endian_double=yes"
477 "ac_cv_big_endian_double=no"
478 "ac_cv_mixed_endian_double=no"
479 "ac_cv_x87_double_rounding=yes"
480 "ac_cv_tanh_preserves_zero_sign=yes"
481 # Generally assume that things are present and work
482 "ac_cv_posix_semaphores_enabled=yes"
483 "ac_cv_broken_sem_getvalue=no"
484 "ac_cv_wchar_t_signed=yes"
485 "ac_cv_rshift_extends_sign=yes"
486 "ac_cv_broken_nice=no"
487 "ac_cv_broken_poll=no"
488 "ac_cv_working_tzset=yes"
489 "ac_cv_have_long_long_format=yes"
490 "ac_cv_have_size_t_format=yes"
491 "ac_cv_computed_gotos=yes"
492 # Both fail when building for windows, normally configure checks this by itself but on other platforms this is set to yes always.
493 "ac_cv_file__dev_ptmx=${if stdenv.hostPlatform.isWindows then "no" else "yes"}"
494 "ac_cv_file__dev_ptc=${if stdenv.hostPlatform.isWindows then "no" else "yes"}"
495 ]
496 ++ optionals (stdenv.hostPlatform != stdenv.buildPlatform && pythonAtLeast "3.11") [
497 "--with-build-python=${pythonOnBuildForHostInterpreter}"
498 ]
499 ++ optionals stdenv.hostPlatform.isLinux [
500 # Never even try to use lchmod on linux,
501 # don't rely on detecting glibc-isms.
502 "ac_cv_func_lchmod=no"
503 ]
504 ++ optionals static [
505 "LDFLAGS=-static"
506 "MODULE_BUILDTYPE=static"
507 ]
508 ++ optionals (stdenv.hostPlatform.isStatic && stdenv.hostPlatform.isMusl) [
509 # dlopen is a no-op in static musl builds, and since we build everything without -fPIC it's better not to pretend.
510 "ac_cv_func_dlopen=no"
511 ];
512
513 preConfigure =
514 ''
515 # Attempt to purify some of the host info collection
516 sed -E -i -e 's/uname -r/echo/g' -e 's/uname -n/echo nixpkgs/g' config.guess
517 sed -E -i -e 's/uname -r/echo/g' -e 's/uname -n/echo nixpkgs/g' configure
518 ''
519 + optionalString (pythonOlder "3.12") ''
520 # Improve purity
521 for path in /usr /sw /opt /pkg; do
522 substituteInPlace ./setup.py --replace-warn $path /no-such-path
523 done
524 ''
525 + optionalString (stdenv.hostPlatform.isDarwin && pythonOlder "3.12") ''
526 # Fix _ctypes module compilation
527 export NIX_CFLAGS_COMPILE+=" -DUSING_APPLE_OS_LIBFFI=1"
528 ''
529 + optionalString stdenv.hostPlatform.isDarwin ''
530 # Override the auto-detection in setup.py, which assumes a universal build
531 export PYTHON_DECIMAL_WITH_MACHINE=${if stdenv.hostPlatform.isAarch64 then "uint128" else "x64"}
532 # Ensure that modern platform features are enabled on Darwin in spite of having no version suffix.
533 sed -E -i -e 's|Darwin/\[12\]\[0-9\]\.\*|Darwin/*|' configure
534 ''
535 + optionalString (pythonAtLeast "3.11") ''
536 # Also override the auto-detection in `configure`.
537 substituteInPlace configure \
538 --replace-fail 'libmpdec_machine=universal' 'libmpdec_machine=${
539 if stdenv.hostPlatform.isAarch64 then "uint128" else "x64"
540 }'
541 ''
542 + optionalString (stdenv.hostPlatform.isDarwin && x11Support && pythonAtLeast "3.11") ''
543 export TCLTK_LIBS="-L${tcl}/lib -L${tk}/lib -l${tcl.libPrefix} -l${tk.libPrefix}"
544 export TCLTK_CFLAGS="-I${tcl}/include -I${tk}/include"
545 ''
546 + optionalString stdenv.hostPlatform.isWindows ''
547 export NIX_CFLAGS_COMPILE+=" -Wno-error=incompatible-pointer-types"
548 ''
549 + optionalString stdenv.hostPlatform.isMusl ''
550 export NIX_CFLAGS_COMPILE+=" -DTHREAD_STACK_SIZE=0x100000"
551 ''
552 +
553
554 # enableNoSemanticInterposition essentially sets that CFLAG -fno-semantic-interposition
555 # which changes how symbols are looked up. This essentially means we can't override
556 # libpython symbols via LD_PRELOAD anymore. This is common enough as every build
557 # that uses --enable-optimizations has the same "issue".
558 #
559 # The Fedora wiki has a good article about their journey towards enabling this flag:
560 # https://fedoraproject.org/wiki/Changes/PythonNoSemanticInterpositionSpeedup
561 optionalString enableNoSemanticInterposition ''
562 export CFLAGS_NODIST="-fno-semantic-interposition"
563 '';
564
565 setupHook = python-setup-hook sitePackages;
566
567 postInstall =
568 let
569 # References *not* to nuke from (sys)config files
570 keep-references = concatMapStringsSep " " (val: "-e ${val}") (
571 [
572 (placeholder "out")
573 ]
574 ++ lib.optional (libxcrypt != null) libxcrypt
575 ++ lib.optional tzdataSupport tzdata
576 );
577 in
578 lib.optionalString enableFramework ''
579 for dir in include lib share; do
580 ln -s $out/Library/Frameworks/Python.framework/Versions/Current/$dir $out/$dir
581 done
582 ''
583 + ''
584 # needed for some packages, especially packages that backport functionality
585 # to 2.x from 3.x
586 for item in $out/lib/${libPrefix}/test/*; do
587 if [[ "$item" != */test_support.py*
588 && "$item" != */test/support
589 && "$item" != */test/libregrtest
590 && "$item" != */test/regrtest.py* ]]; then
591 rm -rf "$item"
592 else
593 echo $item
594 fi
595 done
596 touch $out/lib/${libPrefix}/test/__init__.py
597
598 # Determinism: Windows installers were not deterministic.
599 # We're also not interested in building Windows installers.
600 find "$out" -name 'wininst*.exe' | xargs -r rm -f
601
602 # Use Python3 as default python
603 ln -s "$out/bin/idle3" "$out/bin/idle"
604 ln -s "$out/bin/pydoc3" "$out/bin/pydoc"
605 ln -s "$out/bin/python3${execSuffix}" "$out/bin/python${execSuffix}"
606 ln -s "$out/bin/python3-config" "$out/bin/python-config"
607 ln -s "$out/lib/pkgconfig/python3.pc" "$out/lib/pkgconfig/python.pc"
608 ln -sL "$out/share/man/man1/python3.1.gz" "$out/share/man/man1/python.1.gz"
609
610 # Get rid of retained dependencies on -dev packages, and remove
611 # some $TMPDIR references to improve binary reproducibility.
612 # Note that the .pyc file of _sysconfigdata.py should be regenerated!
613 for i in $out/lib/${libPrefix}/_sysconfigdata*.py $out/lib/${libPrefix}/config-${sourceVersion.major}${sourceVersion.minor}*/Makefile; do
614 sed -i $i -e "s|$TMPDIR|/no-such-path|g"
615 done
616
617 # Further get rid of references. https://github.com/NixOS/nixpkgs/issues/51668
618 find $out/lib/python*/config-* -type f -print -exec nuke-refs ${keep-references} '{}' +
619 find $out/lib -name '_sysconfigdata*.py*' -print -exec nuke-refs ${keep-references} '{}' +
620
621 # Make the sysconfigdata module accessible on PYTHONPATH
622 # This allows build Python to import host Python's sysconfigdata
623 mkdir -p "$out/${sitePackages}"
624 ln -s "$out/lib/${libPrefix}/"_sysconfigdata*.py "$out/${sitePackages}/"
625 ''
626 + optionalString (pythonAtLeast "3.14") ''
627 # Get rid of retained dependencies on -dev packages, and remove from _sysconfig_vars*.json introduced with Python3.14
628 for i in $out/lib/${libPrefix}/_sysconfig_vars*.json; do
629 sed -i $i -e "s|$TMPDIR|/no-such-path|g"
630 done
631 find $out/lib -name '_sysconfig_vars*.json*' -print -exec nuke-refs ${keep-references} '{}' +
632 ln -s "$out/lib/${libPrefix}/"_sysconfig_vars*.json "$out/${sitePackages}/"
633 ''
634 + optionalString stripConfig ''
635 rm -R $out/bin/python*-config $out/lib/python*/config-*
636 ''
637 + optionalString stripIdlelib ''
638 # Strip IDLE (and turtledemo, which uses it)
639 rm -R $out/bin/idle* $out/lib/python*/{idlelib,turtledemo}
640 ''
641 + optionalString stripTkinter ''
642 rm -R $out/lib/python*/tkinter
643 ''
644 + optionalString stripTests ''
645 # Strip tests
646 rm -R $out/lib/python*/test $out/lib/python*/**/test{,s}
647 ''
648 + optionalString includeSiteCustomize ''
649 # Include a sitecustomize.py file
650 cp ${../sitecustomize.py} $out/${sitePackages}/sitecustomize.py
651 ''
652 + optionalString stripBytecode ''
653 # Determinism: deterministic bytecode
654 # First we delete all old bytecode.
655 find $out -type d -name __pycache__ -print0 | xargs -0 -I {} rm -rf "{}"
656 ''
657 + optionalString rebuildBytecode ''
658 # Python 3.7 implements PEP 552, introducing support for deterministic bytecode.
659 # compileall uses the therein introduced checked-hash method by default when
660 # `SOURCE_DATE_EPOCH` is set.
661 # We exclude lib2to3 because that's Python 2 code which fails
662 # We build 3 levels of optimized bytecode. Note the default level, without optimizations,
663 # is not reproducible yet. https://bugs.python.org/issue29708
664 # Not creating bytecode will result in a large performance loss however, so we do build it.
665 find $out -name "*.py" | ${pythonOnBuildForHostInterpreter} -m compileall -q -f -x "lib2to3" -i -
666 find $out -name "*.py" | ${pythonOnBuildForHostInterpreter} -O -m compileall -q -f -x "lib2to3" -i -
667 find $out -name "*.py" | ${pythonOnBuildForHostInterpreter} -OO -m compileall -q -f -x "lib2to3" -i -
668 ''
669 + ''
670 # *strip* shebang from libpython gdb script - it should be dual-syntax and
671 # interpretable by whatever python the gdb in question is using, which may
672 # not even match the major version of this python. doing this after the
673 # bytecode compilations for the same reason - we don't want bytecode generated.
674 mkdir -p $out/share/gdb
675 sed '/^#!/d' Tools/gdb/libpython.py > $out/share/gdb/libpython.py
676
677 # Disable system-wide pip installation. See https://peps.python.org/pep-0668/.
678 cat <<'EXTERNALLY_MANAGED' > $out/lib/${libPrefix}/EXTERNALLY-MANAGED
679 [externally-managed]
680 Error=This command has been disabled as it tries to modify the immutable
681 `/nix/store` filesystem.
682
683 To use Python with Nix and nixpkgs, have a look at the online documentation:
684 <https://nixos.org/manual/nixpkgs/stable/#python>.
685 EXTERNALLY_MANAGED
686 ''
687 + optionalString stdenv.hostPlatform.isWindows ''
688 # Shebang files that link against the build python. Shebang don’t work on windows
689 rm $out/bin/2to3*
690 rm $out/bin/idle*
691 rm $out/bin/pydoc*
692
693 echo linking DLLs for python’s compiled librairies
694 linkDLLsInfolder $out/lib/python*/lib-dynload/
695 '';
696
697 preFixup = lib.optionalString (stdenv.hostPlatform != stdenv.buildPlatform) ''
698 # Ensure patch-shebangs uses shebangs of host interpreter.
699 export PATH=${lib.makeBinPath [ "$out" ]}:$PATH
700 '';
701
702 # Add CPython specific setup-hook that configures distutils.sysconfig to
703 # always load sysconfigdata from host Python.
704 postFixup = lib.optionalString (!stdenv.hostPlatform.isDarwin) ''
705 # https://github.com/python/cpython/blob/e488e300f5c01289c10906c2e53a8e43d6de32d8/configure.ac#L78
706 sysconfigdataName="$(make --eval $'print-sysconfigdata-name:
707 \t@echo _sysconfigdata_$(ABIFLAGS)_$(MACHDEP)_$(MULTIARCH) ' print-sysconfigdata-name)"
708
709 # The CPython interpreter contains a _sysconfigdata_<platform specific suffix>
710 # module that is imported by the sysconfig and distutils.sysconfig modules.
711 # The sysconfigdata module is generated at build time and contains settings
712 # required for building Python extension modules, such as include paths and
713 # other compiler flags. By default, the sysconfigdata module is loaded from
714 # the currently running interpreter (ie. the build platform interpreter), but
715 # when cross-compiling we want to load it from the host platform interpreter.
716 # This can be done using the _PYTHON_SYSCONFIGDATA_NAME environment variable.
717 # The _PYTHON_HOST_PLATFORM variable also needs to be set to get the correct
718 # platform suffix on extension modules. The correct values for these variables
719 # are not documented, and must be derived from the configure script (see links
720 # below).
721 cat <<EOF >> "$out/nix-support/setup-hook"
722 sysconfigdataHook() {
723 if [ "\$1" = '$out' ]; then
724 export _PYTHON_HOST_PLATFORM='${pythonHostPlatform}'
725 export _PYTHON_SYSCONFIGDATA_NAME='$sysconfigdataName'
726 fi
727 }
728
729 addEnvHooks "\$hostOffset" sysconfigdataHook
730 EOF
731 '';
732
733 # Enforce that we don't have references to the OpenSSL -dev package, which we
734 # explicitly specify in our configure flags above.
735 disallowedReferences =
736 lib.optionals (openssl != null && !static && !enableFramework) [
737 openssl.dev
738 ]
739 ++ lib.optionals (stdenv.hostPlatform != stdenv.buildPlatform) [
740 # Ensure we don't have references to build-time packages.
741 # These typically end up in shebangs.
742 pythonOnBuildForHost
743 buildPackages.bashNonInteractive
744 ];
745
746 separateDebugInfo = true;
747
748 passthru = passthru // {
749 doc = stdenv.mkDerivation {
750 inherit src;
751 name = "python${pythonVersion}-${version}-doc";
752
753 postPatch = lib.optionalString (pythonAtLeast "3.9" && pythonOlder "3.11") ''
754 substituteInPlace Doc/tools/extensions/pyspecific.py \
755 --replace-fail "from sphinx.util import status_iterator" "from sphinx.util.display import status_iterator"
756 '';
757
758 dontConfigure = true;
759
760 dontBuild = true;
761
762 sphinxRoot = "Doc";
763
764 postInstallSphinx = ''
765 mv $out/share/doc/* $out/share/doc/python${pythonVersion}-${version}
766 '';
767
768 nativeBuildInputs = with pkgsBuildBuild.python3.pkgs; [
769 sphinxHook
770 python-docs-theme
771 ];
772 };
773
774 tests = passthru.tests // {
775 pkg-config = testers.testMetaPkgConfig finalAttrs.finalPackage;
776 };
777 };
778
779 enableParallelBuilding = true;
780
781 meta = with lib; {
782 homepage = "https://www.python.org";
783 changelog =
784 let
785 majorMinor = versions.majorMinor version;
786 dashedVersion = replaceStrings [ "." "a" "b" ] [ "-" "-alpha-" "-beta-" ] version;
787 in
788 if sourceVersion.suffix == "" then
789 "https://docs.python.org/release/${version}/whatsnew/changelog.html"
790 else
791 "https://docs.python.org/${majorMinor}/whatsnew/changelog.html#python-${dashedVersion}";
792 description = "High-level dynamically-typed programming language";
793 longDescription = ''
794 Python is a remarkably powerful dynamic programming language that
795 is used in a wide variety of application domains. Some of its key
796 distinguishing features include: clear, readable syntax; strong
797 introspection capabilities; intuitive object orientation; natural
798 expression of procedural code; full modularity, supporting
799 hierarchical packages; exception-based error handling; and very
800 high level dynamic data types.
801 '';
802 license = licenses.psfl;
803 pkgConfigModules = [ "python3" ];
804 platforms = platforms.linux ++ platforms.darwin ++ platforms.windows ++ platforms.freebsd;
805 mainProgram = executable;
806 teams = [ lib.teams.python ];
807 # static build on x86_64-darwin/aarch64-darwin breaks with:
808 # configure: error: C compiler cannot create executables
809
810 # mingw patches only apply to Python 3.11 currently
811 broken =
812 (lib.versions.minor version != "11" && stdenv.hostPlatform.isWindows)
813 || (stdenv.hostPlatform.isStatic && stdenv.hostPlatform.isDarwin);
814 };
815})