Package: src:python3.15
Version: 3.15.0~rc2-2
Tags: patch, forky, sid
Severity: important

Hi!

Python 3.15 brings the stabilization of abi3t, the free threaded (no
gil) abi. Python extensions that use the Limited API can then be built
to provide a free threaded extension.

I've worked on a proposal that creates the -nogil packages from the
python3.15 package [1]. This would add the python3.15t interpreter and
its corresponding standard library.

In the MR I've taken some decisions, like the names of the packages and
the shipping of the standard library. I've also added the triggers so
that if the -nogil package is included, the pyc files are generated.

For convenience, I'm attaching the diff here as well, but if you have
comments, it's probably better to discuss in Salsa.

Happy hacking,

[1]: https://salsa.debian.org/cpython-team/python3/-/merge_requests/46

--
"Can you imagine what I would do if I could do all I can?" -- Sun Tzu
Saludos /\/\ /\ >< `/
diff --git a/.gitignore b/.gitignore
index 53fe32b..dcca30c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -67,9 +67,12 @@ debian/idle-python3.15
 debian/libpython3.15-dbg
 debian/libpython3.15-dev
 debian/libpython3.15-minimal
+debian/libpython3.15-nogil
+debian/libpython3.15-nogil-dev
 debian/libpython3.15-stdlib
 debian/libpython3.15-testsuite
 debian/libpython3.15
+debian/python3.15-nogil
 debian/python3.15-dbg
 debian/python3.15-dev
 debian/python3.15-doc
@@ -82,6 +85,7 @@ debian/python3.15-tk
 debian/python3.15
 debian/test_results
 debian/tmp-dbg
+debian/tmp-nogil
 debian/tmp-shdbg
 debian/tmp
 
@@ -105,6 +109,9 @@ debian/libpython3.15-minimal.overrides
 debian/libpython3.15-minimal.postinst
 debian/libpython3.15-minimal.postrm
 debian/libpython3.15-minimal.prerm
+debian/libpython3.15-nogil-dev.overrides
+debian/libpython3.15-nogil.overrides
+debian/libpython3.15-nogil.prerm
 debian/libpython3.15-stdlib.overrides
 debian/libpython3.15-stdlib.prerm
 debian/libpython3.15-testsuite.overrides
@@ -141,6 +148,10 @@ debian/python3.15-nopie.preinst
 debian/python3.15-nopie.postrm
 debian/python3.15-tk.postinst
 debian/python3.15-tk.prerm
+debian/python3.15-nogil.overrides
+debian/python3.15-nogil.postinst
+debian/python3.15-nogil.prerm
+debian/python3.15-nogil.triggers
 debian/python3.15.desktop
 debian/python3.15.overrides
 debian/python3.15.postinst
diff --git a/debian/PVER-nogil.overrides.in b/debian/PVER-nogil.overrides.in
new file mode 100644
index 0000000..fd8fd92
--- /dev/null
+++ b/debian/PVER-nogil.overrides.in
@@ -0,0 +1,8 @@
+# no, it's not unusual
+@PVER@-nogil binary: unusual-interpreter
+
+# pointless lintian ...
+@PVER@-nogil binary: hardening-no-fortify-functions
+
+# pyexpat.c contains expat error messages that lintian mis-attributes to embedding
+@PVER@-nogil binary: embedded-library
diff --git a/debian/PVER-nogil.postinst.in b/debian/PVER-nogil.postinst.in
new file mode 100644
index 0000000..1519d9b
--- /dev/null
+++ b/debian/PVER-nogil.postinst.in
@@ -0,0 +1,41 @@
+#! /bin/sh
+
+set -e
+
+compile_distpackages()
+{
+    if [ -x /usr/bin/@PVER@t ] && [ -d /usr/lib/python3/dist-packages ]; then
+	/usr/bin/@PVER@t -E -S -m compileall -q /usr/lib/python3/dist-packages
+	if grep -sq '^byte-compile[^#]*optimize' /etc/python/debian_config; then
+	    /usr/bin/@PVER@t -E -S -O -m compileall -q /usr/lib/python3/dist-packages
+	fi
+    fi
+}
+
+case "$1" in
+    configure)
+    files=$(LC_ALL=C.UTF8 dpkg -L lib@PVER@-nogil@HOST_QUAL@ 2>/dev/null | sed -n '/^\/usr\/lib\/@PVER@t\/.*\.py$/p')
+    if [ -n "$files" ]; then
+	/usr/bin/@PVER@t -E -S /usr/lib/@PVER@t/py_compile.py $files
+	if grep -sq '^byte-compile[^#]*optimize' /etc/python/debian_config; then
+	    /usr/bin/@PVER@t -E -S -O /usr/lib/@PVER@t/py_compile.py $files
+	fi
+    else
+	echo >&2 "@PVER@-nogil: can't get files for byte-compilation"
+    fi
+    compile_distpackages
+    ;;
+    triggered)
+    for trigger in $2; do
+	case "$trigger" in
+	    /usr/lib/python3/dist-packages)
+		compile_distpackages
+		;;
+	esac
+    done
+    ;;
+esac
+
+#DEBHELPER#
+
+exit 0
diff --git a/debian/PVER-nogil.prerm.in b/debian/PVER-nogil.prerm.in
new file mode 100644
index 0000000..54526d6
--- /dev/null
+++ b/debian/PVER-nogil.prerm.in
@@ -0,0 +1,40 @@
+#! /bin/sh
+
+set -e
+
+remove_bytecode()
+{
+    pkg=$1
+    max=$(LANG=C LC_ALL=C xargs --show-limits < /dev/null 2>&1 | awk '/Maximum length/ {print int($NF / 4)}')
+    LC_ALL=C.UTF8 dpkg -L $pkg 2>/dev/null \
+	| awk -F/ 'BEGIN {OFS="/"} /\.py$/ {$NF=sprintf("__pycache__/%s.*.py[co]", substr($NF,1,length($NF)-3)); print}' \
+	| xargs --max-chars="$max" echo \
+	| while read files; do rm -f $files; done
+    find /usr/lib/@PVER@t -name __pycache__ -type d -empty -print 2>/dev/null \
+	| xargs -r rm -rf
+}
+
+clean_distpackages_bytecode()
+{
+    if [ -d /usr/lib/python3/dist-packages ]; then
+	find /usr/lib/python3/dist-packages -name "*cpython-@EXT_VER@t*.py[co]" -delete 2>/dev/null || true
+	find /usr/lib/python3/dist-packages -name __pycache__ -type d -empty -delete 2>/dev/null || true
+    fi
+}
+
+case "$1" in
+    remove|upgrade)
+	remove_bytecode lib@PVER@-nogil@HOST_QUAL@
+	clean_distpackages_bytecode
+        ;;
+    deconfigure)
+        ;;
+    failed-upgrade)
+        ;;
+    *)
+        echo "prerm called with unknown argument \`$1'" >&2
+        exit 1
+    ;;
+esac
+
+#DEBHELPER#
diff --git a/debian/PVER-nogil.triggers.in b/debian/PVER-nogil.triggers.in
new file mode 100644
index 0000000..72a007d
--- /dev/null
+++ b/debian/PVER-nogil.triggers.in
@@ -0,0 +1 @@
+interest-noawait /usr/lib/python3/dist-packages
diff --git a/debian/control b/debian/control
index 1d34aad..5abf73b 100644
--- a/debian/control
+++ b/debian/control
@@ -262,6 +262,41 @@ Description: Debug Build of the Python Interpreter (version 3.15)
  .
  See the README.debug for more information.
 
+Package: python3.15-nogil
+Architecture: linux-any
+Multi-Arch: allowed
+Depends: python3.15-minimal (= ${binary:Version}), libpython3.15-nogil (= ${binary:Version}), ${shlibs:Depends}, ${misc:Depends}
+Suggests: libpython3.15-nogil-dev
+Description: Free-threaded build of the Python Interpreter (version 3.15)
+ A build of the Python interpreter configured with --disable-gil, which runs
+ Python code without the global interpreter lock (PEP 703). The interpreter is
+ installed as python3.15t.
+XB-Cnf-Visible-Pkgname: python3.15t
+
+Package: libpython3.15-nogil
+Architecture: linux-any
+Multi-Arch: same
+Section: libs
+Pre-Depends: ${misc:Pre-Depends}
+Depends: ${shlibs:Depends}, ${misc:Depends}
+Description: Free-threaded Python runtime library (version 3.15)
+ A build of the Python runtime configured with --disable-gil, which runs Python
+ code without the global interpreter lock (PEP 703).
+ .
+ This package contains the standard library and the shared runtime library for
+ the free-threaded interpreter.
+
+Package: libpython3.15-nogil-dev
+Architecture: linux-any
+Multi-Arch: same
+Section: libdevel
+Depends: libpython3.15-nogil (= ${binary:Version}), libpython3.15-dev (= ${binary:Version}),
+  ${shlibs:Depends}, ${misc:Depends}
+Description: Header files and a static library for free-threaded Python (v3.15)
+ Header files, a static library and development tools for building Python
+ extension modules against the free-threaded (--disable-gil, PEP 703) build of
+ Python 3.15.
+
 Package: python3.15-full
 Architecture: any
 Multi-Arch: allowed
diff --git a/debian/control.in b/debian/control.in
index 3477d96..f4caec8 100644
--- a/debian/control.in
+++ b/debian/control.in
@@ -262,6 +262,41 @@ Description: Debug Build of the Python Interpreter (version @VER@)
  .
  See the README.debug for more information.
 
+Package: @PVER@-nogil
+Architecture: linux-any
+Multi-Arch: allowed
+Depends: @PVER@-minimal (= ${binary:Version}), lib@PVER@-nogil (= ${binary:Version}), ${shlibs:Depends}, ${misc:Depends}
+Suggests: lib@PVER@-nogil-dev
+Description: Free-threaded build of the Python Interpreter (version @VER@)
+ A build of the Python interpreter configured with --disable-gil, which runs
+ Python code without the global interpreter lock (PEP 703). The interpreter is
+ installed as @PVER@t.
+XB-Cnf-Visible-Pkgname: @PVER@t
+
+Package: lib@PVER@-nogil
+Architecture: linux-any
+Multi-Arch: same
+Section: libs
+Pre-Depends: ${misc:Pre-Depends}
+Depends: ${shlibs:Depends}, ${misc:Depends}
+Description: Free-threaded Python runtime library (version @VER@)
+ A build of the Python runtime configured with --disable-gil, which runs Python
+ code without the global interpreter lock (PEP 703).
+ .
+ This package contains the standard library and the shared runtime library for
+ the free-threaded interpreter.
+
+Package: lib@PVER@-nogil-dev
+Architecture: linux-any
+Multi-Arch: same
+Section: libdevel
+Depends: lib@PVER@-nogil (= ${binary:Version}), lib@PVER@-dev (= ${binary:Version}),
+  ${shlibs:Depends}, ${misc:Depends}
+Description: Header files and a static library for free-threaded Python (v@VER@)
+ Header files, a static library and development tools for building Python
+ extension modules against the free-threaded (--disable-gil, PEP 703) build of
+ Python @VER@.
+
 Package: @PVER@-full
 Architecture: any
 Multi-Arch: allowed
diff --git a/debian/libPVER-nogil-dev.overrides.in b/debian/libPVER-nogil-dev.overrides.in
new file mode 100644
index 0000000..9e29825
--- /dev/null
+++ b/debian/libPVER-nogil-dev.overrides.in
@@ -0,0 +1,8 @@
+lib@PVER@-nogil-dev binary: python3-script-but-no-python3-dep
+
+lib@PVER@-nogil-dev binary: arch-dependent-file-not-in-arch-specific-directory
+
+lib@PVER@-nogil-dev binary: package-contains-python-header-in-incorrect-directory
+
+# no, it's not unusual
+lib@PVER@-nogil-dev binary: unusual-interpreter
diff --git a/debian/libPVER-nogil.overrides.in b/debian/libPVER-nogil.overrides.in
new file mode 100644
index 0000000..ce846b6
--- /dev/null
+++ b/debian/libPVER-nogil.overrides.in
@@ -0,0 +1,22 @@
+# the package is lib@PVER@-nogil, the soname is libpython@VER@t
+lib@PVER@-nogil binary: package-name-doesnt-match-sonames
+
+# no, it's not unusual
+lib@PVER@-nogil binary: unusual-interpreter
+
+# the standard library ships plenty of scripts, but this package is the
+# interpreter's own runtime and must not depend on the default python3
+lib@PVER@-nogil binary: python3-script-but-no-python3-dep
+
+# pointless lintian ...
+lib@PVER@-nogil binary: hardening-no-fortify-functions
+
+# the extensions in /usr/lib/python@VER@t/lib-dynload carry the multiarch
+# triplet in their file names instead, exactly as for the default flavour
+lib@PVER@-nogil binary: arch-dependent-file-not-in-arch-specific-directory
+
+# yes, some extensions don't have references to any external lib
+lib@PVER@-nogil binary: shared-lib-without-dependency-information
+
+# pyexpat.c contains expat error messages that lintian mis-attributes to embedding
+lib@PVER@-nogil binary: embedded-library
diff --git a/debian/libPVER-nogil.prerm.in b/debian/libPVER-nogil.prerm.in
new file mode 100644
index 0000000..36a617e
--- /dev/null
+++ b/debian/libPVER-nogil.prerm.in
@@ -0,0 +1,42 @@
+#! /bin/sh
+
+set -e
+
+remove_bytecode()
+{
+    pkg=$1
+    max=$(LANG=C LC_ALL=C xargs --show-limits < /dev/null 2>&1 | awk '/Maximum length/ {print int($NF / 4)}')
+    LC_ALL=C.UTF8 dpkg -L $pkg 2>/dev/null \
+	| awk -F/ 'BEGIN {OFS="/"} /\.py$/ {$NF=sprintf("__pycache__/%s.*.py[co]", substr($NF,1,length($NF)-3)); print}' \
+	| xargs --max-chars="$max" echo \
+	| while read files; do rm -f $files; done
+    # source and bytecode filename mismatch
+    rm -f /usr/lib/@PVER@t/__pycache__/__phello__.cpython-*.py*
+    find /usr/lib/@PVER@t \
+	\( -name __pycache__ -type d -empty -print \) 2>/dev/null \
+	| xargs -r rm -rf
+}
+
+case "$1" in
+    remove)
+	pc=$(LC_ALL=C.UTF8 dpkg-query -f '${db:Status-Abbrev} ${binary:Package}\n' -W lib@PVER@-nogil \
+	     | grep -v '^.n' | wc -l)
+	if [ "$pc" -le 1 ]; then
+	    remove_bytecode lib@PVER@-nogil@HOST_QUAL@
+	fi
+        ;;
+    upgrade)
+	remove_bytecode lib@PVER@-nogil@HOST_QUAL@
+	# byte compilation in @PVER@-nogil postinst, strict dependency
+        ;;
+    deconfigure)
+        ;;
+    failed-upgrade)
+        ;;
+    *)
+        echo "prerm called with unknown argument \`$1'" >&2
+        exit 1
+    ;;
+esac
+
+#DEBHELPER#
diff --git a/debian/patches/distutils-install-layout.diff b/debian/patches/distutils-install-layout.diff
index 4ca5d36..8adbcf0 100644
--- a/debian/patches/distutils-install-layout.diff
+++ b/debian/patches/distutils-install-layout.diff
@@ -43,29 +43,29 @@ Forwarded: not-needed
      for prefix in prefixes:
          if not prefix or prefix in seen:
              continue
-@@ -764,10 +772,21 @@ def getsitepackages(prefixes=None):
+@@ -764,11 +772,21 @@ def getsitepackages(prefixes=None):
              if sys.platlibdir != "lib":
                  libdirs.append("lib")
  
 +            if is_virtual_environment:
 +                sitepackages.append(os.path.join(prefix, "lib",
-+                                                 "python%d.%d" % sys.version_info[:2],
++                                                 f"python{ver[0]}.{ver[1]}{abi_thread}",
 +                                                 "site-packages"))
 +            sitepackages.append(os.path.join(prefix, "local/lib",
-+                                             "python%d.%d" % sys.version_info[:2],
++                                             f"python{ver[0]}.{ver[1]}{abi_thread}",
 +                                             "dist-packages"))
-+            sitepackages.append(os.path.join(prefix, "lib",
-+                                             "python3",
-+                                             "dist-packages"))
-+            # this one is deprecated for Debian
              for libdir in libdirs:
                  path = os.path.join(prefix, libdir,
                                      f"{implementation}{ver[0]}.{ver[1]}{abi_thread}",
 -                                    "site-packages")
 +                                    "dist-packages")
                  sitepackages.append(path)
++            sitepackages.append(os.path.join(prefix, "lib",
++                                             "python3",
++                                             "dist-packages"))
          else:
              sitepackages.append(prefix)
+             sitepackages.append(os.path.join(prefix, "Lib", "site-packages"))
 --- a/Lib/test/test_site.py
 +++ b/Lib/test/test_site.py
 @@ -354,16 +354,16 @@ class HelperFunctionsTests(unittest.Test
@@ -74,7 +74,7 @@ Forwarded: not-needed
              if sys.platlibdir != "lib":
 -                self.assertEqual(len(dirs), 2)
 -                wanted = os.path.join('xoxo', sys.platlibdir,
-+                self.assertEqual(len(dirs), 3)
++                self.assertEqual(len(dirs), 4)
 +                wanted = os.path.join('xoxo', 'local', 'lib',
                                        f'python{sysconfig._get_python_version_abi()}',
 -                                      'site-packages')
@@ -84,8 +84,9 @@ Forwarded: not-needed
 -                self.assertEqual(len(dirs), 1)
 +                self.assertEqual(len(dirs), 3)
              wanted = os.path.join('xoxo', 'lib',
-                                   f'python{sysconfig._get_python_version_abi()}',
+-                                  f'python{sysconfig._get_python_version_abi()}',
 -                                  'site-packages')
++                                  'python3',
 +                                  'dist-packages')
              self.assertEqual(dirs[-1], wanted)
          else:
diff --git a/debian/patches/sysconfig-debian-schemes.diff b/debian/patches/sysconfig-debian-schemes.diff
index 699c161..7500de9 100644
--- a/debian/patches/sysconfig-debian-schemes.diff
+++ b/debian/patches/sysconfig-debian-schemes.diff
@@ -14,8 +14,8 @@ This adds two schemes:
          'data': '{base}',
          },
 +    'deb_system': {
-+        'stdlib': '{installed_base}/lib/python{py_version_short}',
-+        'platstdlib': '{platbase}/lib/python{py_version_short}',
++        'stdlib': '{installed_base}/lib/python{py_version_short}{abi_thread}',
++        'platstdlib': '{platbase}/lib/python{py_version_short}{abi_thread}',
 +        'purelib': '{base}/lib/python3/dist-packages',
 +        'platlib': '{platbase}/lib/python3/dist-packages',
 +        'include':
@@ -26,10 +26,10 @@ This adds two schemes:
 +        'data': '{base}',
 +        },
 +    'posix_local': {
-+        'stdlib': '{installed_base}/lib/python{py_version_short}',
-+        'platstdlib': '{platbase}/lib/python{py_version_short}',
-+        'purelib': '{base}/local/lib/python{py_version_short}/dist-packages',
-+        'platlib': '{platbase}/local/lib/python{py_version_short}/dist-packages',
++        'stdlib': '{installed_base}/lib/python{py_version_short}{abi_thread}',
++        'platstdlib': '{platbase}/lib/python{py_version_short}{abi_thread}',
++        'purelib': '{base}/local/lib/python{py_version_short}{abi_thread}/dist-packages',
++        'platlib': '{platbase}/local/lib/python{py_version_short}{abi_thread}/dist-packages',
 +        'include':
 +            '{installed_base}/include/python{py_version_short}{abiflags}',
 +        'platinclude':
diff --git a/debian/rules b/debian/rules
index 0841e5f..a7cb2a2 100755
--- a/debian/rules
+++ b/debian/rules
@@ -92,6 +92,10 @@ SVER=3.15.0~rc1
 NVER=3.16
 PVER=python$(VER)
 EXT_VER=$(subst .,,$(VER))
+# LDVERSION and interpreter name of the free-threaded flavour: 3.15t / python3.15t.
+# Upstream derives these from ABIFLAGS/ABI_THREAD (configure.ac:1768, 6656).
+TVER=$(VER)t
+PTVER=python$(TVER)
 PRIORITY=$(shell echo $(VER) | tr -d '.')0
 
 PREVVER	:= $(shell awk '/^python/ && NR > 1 {print substr($$2,2,length($$2)-2); exit}' debian/changelog)
@@ -122,6 +126,10 @@ with_gdbm := yes
 with_interp := static
 #with_interp := shared
 
+ifeq ($(DEB_HOST_ARCH_OS),linux)
+  with_nogil := yes
+endif
+
 PY_INTERPRETER = /usr/bin/python$(VER)
 
 ifeq ($(DEFAULT_VERSION),yes)
@@ -269,11 +277,17 @@ buildd_nopie	:= $(CURDIR)/build-nopie
 buildd_shared	:= $(CURDIR)/build-shared
 buildd_debug	:= $(CURDIR)/build-debug
 buildd_shdebug	:= $(CURDIR)/build-shdebug
+buildd_nogil	:= $(CURDIR)/build-nogil
+buildd_shnogil	:= $(CURDIR)/build-shnogil
 
 d		:= debian/tmp
 scriptdir	=  usr/share/lib/python$(VER)
 scriptdir	=  usr/share/python$(VER)
 scriptdir	=  usr/lib/python$(VER)
+# The free-threaded flavour gets its own stdlib directory: upstream derives
+# LIBDEST from ABI_THREAD, not ABIFLAGS, so unlike the -dbg flavour it does
+# not share $(scriptdir). See configure.ac:6709.
+ftscriptdir	=  usr/lib/python$(TVER)
 
 # package names and directories
 p_base	:= $(PVER)
@@ -290,12 +304,15 @@ p_dbg	:= $(PVER)-dbg
 p_udeb	:= $(PVER)-udeb
 p_venv	:= $(PVER)-venv
 p_full	:= $(PVER)-full
+p_nogil	:= $(PVER)-nogil
 
 p_lbase	:= lib$(PVER)-stdlib
 p_lmin	:= lib$(PVER)-minimal
 p_ldev	:= lib$(PVER)-dev
 p_ldbg	:= lib$(PVER)-dbg
 p_ltst	:= lib$(PVER)-testsuite
+p_lnogil	:= lib$(PVER)-nogil
+p_lnogildev := lib$(PVER)-nogil-dev
 
 d_base	:= debian/$(p_base)
 d_min	:= debian/$(p_min)
@@ -311,12 +328,15 @@ d_dbg	:= debian/$(p_dbg)
 d_udeb	:= debian/$(p_udeb)
 d_venv	:= debian/$(p_venv)
 d_full	:= debian/$(p_full)
+d_nogil	:= debian/$(p_nogil)
 
 d_lbase	:= debian/$(p_lbase)
 d_lmin	:= debian/$(p_lmin)
 d_ldev	:= debian/$(p_ldev)
 d_ldbg	:= debian/$(p_ldbg)
 d_ltst	:= debian/$(p_ltst)
+d_lnogil	:= debian/$(p_lnogil)
+d_lnogildev := debian/$(p_lnogildev)
 
 build-arch: stamps/stamp-build
 build-indep: stamps/stamp-build-doc
@@ -326,6 +346,8 @@ stamps/stamp-build: stamps/stamp-build-static \
     stamps/stamp-mincheck \
     stamps/stamp-build-shared stamps/stamp-build-debug \
     stamps/stamp-build-shared-debug \
+    $(if $(filter yes, $(with_nogil)),stamps/stamp-build-nogil) \
+    $(if $(filter yes, $(with_nogil)),stamps/stamp-build-shared-nogil) \
     stamps/stamp-check stamps/stamp-pybench
 	touch $@
 
@@ -402,6 +424,11 @@ ifneq ($(DEB_HOST_GNU_TYPE),$(DEB_BUILD_GNU_TYPE))
   endif
 endif
 
+nogil_configure_args = \
+		$(filter-out --enable-experimental-jit=%,$(common_configure_args)) \
+		--disable-gil \
+		--with-mimalloc
+
 stamps/stamp-configure-shared: stamps/stamp-patch
 	rm -rf $(buildd_shared)
 	mkdir -p $(buildd_shared)
@@ -537,6 +564,61 @@ stamps/stamp-build-shared-debug: stamps/stamp-configure-shared-debug
 	$(MAKE) $(NJOBS) -C $(buildd_shdebug)
 	touch $@
 
+stamps/stamp-configure-nogil: stamps/stamp-patch
+	rm -rf $(buildd_nogil)
+	mkdir -p $(buildd_nogil)
+	cd $(buildd_nogil) && \
+	  AR="$(AR)" RANLIB="$(RANLIB)" LLVM_VERSION="$(LLVM_VERSION)" \
+	  CFLAGS_NODIST="$(DPKG_PIE_CPPFLAGS) $(OPT_PIE_CFLAGS)" \
+	  LDFLAGS_NODIST="$(DPKG_PIE_LDFLAGS) $(LTO_CFLAGS)" \
+	    $(config_site) \
+	    ../configure \
+		$(nogil_configure_args)
+
+	$(call __post_configure,$(buildd_nogil))
+	touch $@
+
+stamps/stamp-build-nogil: stamps/stamp-configure-nogil
+	dh_testdir
+	$(MAKE) $(NJOBS) -C $(buildd_nogil)
+
+ifeq (,$(filter nocheck,$(DEB_BUILD_OPTIONS)))
+	: # check that the GIL really is disabled, and that the flavour works
+	cd $(buildd_nogil) && ./python -c 'import sys; assert sys.abiflags == "t", sys.abiflags'
+	cd $(buildd_nogil) && ./python -c 'import sys; assert not sys._is_gil_enabled()'
+	cd $(buildd_nogil) && ./python -c 'import math, cmath'
+	cd $(buildd_nogil) && ./python -c 'import _decimal'
+  ifneq (,$(filter $(DEB_HOST_ARCH_OS), linux))
+	cd $(buildd_nogil) && ./python -c 'from _multiprocessing import SemLock'
+  endif
+	: # the SOABI must carry the t abiflag, or extensions would collide
+	cd $(buildd_nogil) && ./python -c 'import sysconfig; \
+	  soabi = sysconfig.get_config_var("SOABI"); \
+	  assert soabi.startswith("cpython-$(EXT_VER)t-"), soabi'
+endif
+	touch $@
+
+stamps/stamp-configure-shared-nogil: stamps/stamp-patch
+	rm -rf $(buildd_shnogil)
+	mkdir -p $(buildd_shnogil)
+	cd $(buildd_shnogil) && \
+	  AR="$(AR)" RANLIB="$(RANLIB)" LLVM_VERSION="$(LLVM_VERSION)" \
+	  CFLAGS_NODIST="$(DPKG_CPPFLAGS) $(OPT_CFLAGS)" \
+	  LDFLAGS_NODIST="$(DPKG_LDFLAGS) $(LTO_CFLAGS)" \
+	    $(config_site) \
+	    ../configure \
+		$(nogil_configure_args) \
+		--enable-shared
+
+	$(call __post_configure,$(buildd_shnogil))
+	touch $@
+
+stamps/stamp-build-shared-nogil: stamps/stamp-configure-shared-nogil
+	dh_testdir
+	: # build the shared nogil library
+	$(MAKE) $(NJOBS) -C $(buildd_shnogil)
+	touch $@
+
 
 define __post_configure
 	egrep \
@@ -805,6 +887,7 @@ stamps/stamp-build-doc: stamps/stamp-doc-html stamps/stamp-doc-info
 control-file:
 	sed -e "s/@PVER@/$(PVER)/g" \
 	    -e "s/@VER@/$(VER)/g" \
+	    -e "s/@EXT_VER@/$(EXT_VER)/g" \
 	    -e "s/@PYSTDDEP@/$(PYSTDDEP)/g" \
 	    -e "s/@PRIO@/$(PY_PRIO)/g" \
 	    -e "s/@MINPRIO@/$(PY_MINPRIO)/g" \
@@ -842,10 +925,12 @@ clean: control-file
 	$(MAKE) -C Doc clean
 	sed 's/^@/#/' Makefile.pre.in | $(MAKE) -f - srcdir=. distclean
 	rm -rf $(buildd_static) $(buildd_shared) $(buildd_debug) $(buildd_shdebug) $(buildd_nopie)
+	rm -rf $(buildd_nogil) $(buildd_shnogil)
 	find -name '*.py[co]' | xargs -r rm -f
 	rm -f Lib/dist-packages
 	rm -rf locales
 	rm -rf $(d)-dbg
+	rm -rf $(d)-nogil
 	rm -f configure
 	rm -f netlify.toml
 	rm -rf $(TEST_DIR) debian/testdir
@@ -867,6 +952,7 @@ stamps/stamp-control:
 	  f2=`echo $$f | sed "s,PVER,$(PVER),g;s/@VER@/$(VER)/g;s,\.in$$,,"`; \
 	  if [ $$f2 != debian/control ]; then \
 	    sed -e "s/@PVER@/$(PVER)/g;s/@VER@/$(VER)/g;s/@SVER@/$(SVER)/g" \
+		    -e "s/@EXT_VER@/$(EXT_VER)/g" \
 		    -e "s/@PRIORITY@/$(PRIORITY)/g" \
 		    -e "s,@SCRIPTDIR@,/$(scriptdir),g" \
 		    -e "s,@INFO@,$(info_docs),g" \
@@ -1247,13 +1333,6 @@ endif
 	cp -p debian/$(PVER).desktop \
 		$(d_base)/usr/share/applications/$(PVER).desktop
 
-	: # remove some things
-	-find debian -name .cvsignore | xargs rm -f
-	-find debian -name '*.py[co]' | xargs rm -f
-
-	: # remove empty directories, when all components are in place
-	-find debian ! -name lib-dynload ! -name dist-packages -type d -empty -delete
-
 	: # install debug package
 	rm -rf $(d)-dbg
 	install -d $(d)-dbg/$(scriptdir)/lib-dynload
@@ -1382,6 +1461,146 @@ endif
 	ln -sf $(PVER).1.gz $(d_dbg)/usr/share/man/man1/$(PVER)d.1.gz
 	ln -sf $(PVER)-config.1.gz $(d_dbg)/usr/share/man/man1/$(PVER)d-config.1.gz
 
+ifeq ($(with_nogil),yes)
+	rm -rf $(d)-nogil
+	install -d $(d)-nogil
+	$(MAKE) -C $(buildd_nogil) install DESTDIR=$(CURDIR)/$(d)-nogil
+
+	rm -f $(d)-nogil/usr/bin/python3 $(d)-nogil/usr/bin/python3-config
+	rm -f $(d)-nogil/usr/bin/$(PVER) $(d)-nogil/usr/bin/$(PVER)-config
+	rm -f $(d)-nogil/usr/bin/idle3 $(d)-nogil/usr/bin/idle$(VER)
+	rm -f $(d)-nogil/usr/bin/pydoc3 $(d)-nogil/usr/bin/pydoc$(VER)
+	cd $(d)-nogil/usr/lib/$(DEB_HOST_MULTIARCH)/pkgconfig && \
+	  rm -f python3.pc python3-embed.pc \
+		python-$(VER).pc python-$(VER)-embed.pc
+	: # nothing named $(VER) may be left behind; fail the build if it is
+	test -z "$$(find $(d)-nogil/usr/bin $(d)-nogil/usr/lib/$(DEB_HOST_MULTIARCH)/pkgconfig \
+	  -maxdepth 1 \( -name 'python3' -o -name 'python3-*' \
+	  -o -name 'python$(VER)' -o -name 'python$(VER)-*' \
+	  -o -name 'python-$(VER).pc' -o -name 'python-$(VER)-embed.pc' \) -print)"
+
+	: # the config Makefile and sysconfig data come from the shared build,
+	: # so that extensions are linked the way the shared library was
+	sed $(sysconfig_substflags) \
+		$(buildd_shnogil)/Makefile \
+		> $(d)-nogil/$(ftscriptdir)/config-$(TVER)-$(DEB_HOST_MULTIARCH)/Makefile
+	sed $(sysconfig_substflags) \
+		$(buildd_shnogil)/$(shell cat $(buildd_shnogil)/pybuilddir.txt)/_sysconfigdata_t_$(PLAT)_$(DEB_HOST_MULTIARCH).py \
+		> $(d)-nogil/$(ftscriptdir)/_sysconfigdata_t_$(PLAT)_$(DEB_HOST_MULTIARCH).py
+	sed -i $(sysconfig_substflags) \
+		$(d)-nogil/$(ftscriptdir)/_sysconfigdata_t_$(PLAT)_$(DEB_HOST_MULTIARCH).py
+	sed $(sysconfig_substflags) \
+		$(buildd_shnogil)/$(shell cat $(buildd_shnogil)/pybuilddir.txt)/_sysconfig_vars_t_$(PLAT)_$(DEB_HOST_MULTIARCH).json \
+		> $(d)-nogil/$(ftscriptdir)/_sysconfig_vars_t_$(PLAT)_$(DEB_HOST_MULTIARCH).json
+	sed -i $(sysconfig_substflags) \
+		$(d)-nogil/$(ftscriptdir)/_sysconfig_vars_t_$(PLAT)_$(DEB_HOST_MULTIARCH).json
+
+	: # fix /usr/local path in build-details.*.json
+	sed -i -e '/base_interpreter/s,/usr/local/,/usr/,' \
+		$(d)-nogil/$(ftscriptdir)/build-details.t.$(DEB_HOST_MULTIARCH).json
+
+	: # ---- $(p_lnogil): stdlib, extensions and the shared library ----
+	dh_installdirs -p$(p_lnogil) \
+		$(ftscriptdir) \
+		usr/lib/$(DEB_HOST_MULTIARCH)
+	cp -a $(d)-nogil/$(ftscriptdir)/. $(d_lnogil)/$(ftscriptdir)/
+	cp -p $(buildd_shnogil)/libpython$(TVER).so.1.0 \
+		$(d_lnogil)/usr/lib/$(DEB_HOST_MULTIARCH)/
+	dh_link -p$(p_lnogil) \
+	    /usr/lib/$(DEB_HOST_MULTIARCH)/libpython$(TVER).so.1.0 \
+		/usr/lib/$(DEB_HOST_MULTIARCH)/libpython$(TVER).so.1
+
+	: # Move the test framework into $(p_lnogil) and remove the full testsuite
+	mkdir -p $(d_lnogil)/$(ftscriptdir)/test-keep
+	mv $(d_lnogil)/$(ftscriptdir)/test/{libregrtest,support} \
+		$(d_lnogil)/$(ftscriptdir)/test/typinganndata \
+		$(d_lnogil)/$(ftscriptdir)/test/{regrtest,test_support,__init__,__main__}.py \
+		$(d_lnogil)/$(ftscriptdir)/test-keep/
+	rm -rf $(d_lnogil)/$(ftscriptdir)/test
+	mv $(d_lnogil)/$(ftscriptdir)/test-keep $(d_lnogil)/$(ftscriptdir)/test
+
+	# Mark the interpreter as being PEP668 EXTERNALLY-MANAGED
+	install -m 644 debian/EXTERNALLY-MANAGED \
+		$(d_lnogil)/$(ftscriptdir)
+
+	dh_link -p$(p_lnogil) \
+		/etc/$(PVER)/sitecustomize.py /$(ftscriptdir)/sitecustomize.py
+
+	rm -f $(d_lnogil)/$(ftscriptdir)/idlelib/idle.bat
+	rm -rf $(d_lnogil)/$(ftscriptdir)/ctypes/macholib
+	rm -rf $(d_lnogil)/$(ftscriptdir)/ensurepip/_bundled
+	rm -rf $(d_lnogil)/$(ftscriptdir)/site-packages
+	find $(d_lnogil) -name 'wininst*.exe' | xargs -r rm -f
+
+ifneq ($(with_dbmmodule),yes)
+	rm -f $(d_lnogil)/$(ftscriptdir)/lib-dynload/_dbm*.so
+endif
+ifneq ($(with_tk),yes)
+	rm -f $(d_lnogil)/$(ftscriptdir)/lib-dynload/_tkinter*.so
+	rm -rf $(d_lnogil)/$(ftscriptdir)/tkinter
+endif
+ifneq ($(with_gdbm),yes)
+	rm -f $(d_lnogil)/$(ftscriptdir)/lib-dynload/_gdbm*.so
+endif
+
+	: # ---- $(p_lnogildev): headers, static library, pkgconfig, config dir ----
+	dh_installdirs -p$(p_lnogildev) \
+		usr/bin \
+		usr/share/man/man1 \
+		usr/include/$(PTVER) \
+		usr/include/$(DEB_HOST_MULTIARCH)/$(PTVER) \
+		usr/lib/$(DEB_HOST_MULTIARCH)/pkgconfig \
+		$(ftscriptdir)
+
+	: # the build machinery belongs in -dev, not in the runtime package
+	mv $(d_lnogil)/$(ftscriptdir)/config-$(TVER)-$(DEB_HOST_MULTIARCH) \
+		$(d_lnogildev)/$(ftscriptdir)/
+	dh_link -p$(p_lnogildev) \
+	    /usr/lib/$(DEB_HOST_MULTIARCH)/libpython$(TVER).so.1.0 \
+		/usr/lib/$(DEB_HOST_MULTIARCH)/libpython$(TVER).so \
+	    /$(ftscriptdir)/config-$(TVER)-$(DEB_HOST_MULTIARCH)/libpython$(TVER).a \
+		/usr/lib/$(DEB_HOST_MULTIARCH)/libpython$(TVER).a
+
+	: # Every header except pyconfig.h is ABI-independent - only pyconfig.h
+	: # defines Py_GIL_DISABLED, the rest merely test it - so symlink them
+	: # into $(PVER) and keep this package Multi-Arch: same.
+	for i in $(d_ldev)/usr/include/$(PVER)/*; do \
+	  i=$$(basename $$i); \
+	  case $$i in pyconfig.h) continue; esac; \
+	  ln -sf ../$(PVER)/$$i $(d_lnogildev)/usr/include/$(PTVER)/$$i; \
+	done
+	cp -p $(buildd_nogil)/pyconfig.h $(d_lnogildev)/usr/include/$(DEB_HOST_MULTIARCH)/$(PTVER)/
+	sed 's/@subdir@/$(PTVER)/;s/@header@/pyconfig.h/' \
+		debian/multiarch.h.in > $(d_lnogildev)/usr/include/$(PTVER)/pyconfig.h
+
+	cp $(d)-nogil/usr/lib/$(DEB_HOST_MULTIARCH)/pkgconfig/python-$(TVER).pc \
+	  $(d_lnogildev)/usr/lib/$(DEB_HOST_MULTIARCH)/pkgconfig/.
+	cp $(d)-nogil/usr/lib/$(DEB_HOST_MULTIARCH)/pkgconfig/python-$(TVER)-embed.pc \
+	  $(d_lnogildev)/usr/lib/$(DEB_HOST_MULTIARCH)/pkgconfig/.
+
+	: # in $(p_lnogildev), prefix python-config with triplets
+	sed -i $(sysconfig_substflags) $(d)-nogil/usr/bin/$(PTVER)-config
+	cp $(d)-nogil/usr/bin/$(PTVER)-config \
+		$(d_lnogildev)/usr/bin/$(DEB_HOST_MULTIARCH)-$(PTVER)-config
+	ln -sf $(DEB_HOST_MULTIARCH)-$(PVER)-config.1.gz \
+		$(d_lnogildev)/usr/share/man/man1/$(DEB_HOST_MULTIARCH)-$(PTVER)-config.1.gz
+ifneq ($(DEB_HOST_MULTIARCH),$(DEB_HOST_GNU_TYPE))
+	ln -sf $(DEB_HOST_MULTIARCH)-$(PTVER)-config \
+		$(d_lnogildev)/usr/bin/$(DEB_HOST_GNU_TYPE)-$(PTVER)-config
+	ln -sf $(DEB_HOST_MULTIARCH)-$(PVER)-config.1.gz \
+		$(d_lnogildev)/usr/share/man/man1/$(DEB_HOST_GNU_TYPE)-$(PTVER)-config.1.gz
+endif
+
+	: # ---- $(p_nogil): the interpreter itself ----
+	dh_installdirs -p$(p_nogil) \
+		usr/bin \
+		usr/share/man/man1
+	cp -p $(buildd_nogil)/python $(d_nogil)/usr/bin/$(PTVER)
+	ln -sf $(DEB_HOST_MULTIARCH)-$(PTVER)-config $(d_nogil)/usr/bin/$(PTVER)-config
+	ln -sf $(PVER).1.gz $(d_nogil)/usr/share/man/man1/$(PTVER).1.gz
+	ln -sf $(PVER)-config.1.gz $(d_nogil)/usr/share/man/man1/$(PTVER)-config.1.gz
+endif
+
 ifeq ($(with_udeb),yes)
 	: # Copy the most important files from $(p_min) into $(p_udeb).
 	dh_installdirs -p$(p_udeb) \
@@ -1428,6 +1647,13 @@ endif
 	  install -D -m 644 $$i debian/$$b/usr/share/lintian/overrides/$$b; \
 	done
 
+	: # remove some things
+	-find debian -name .cvsignore | xargs rm -f
+	-find debian -name '*.py[co]' | xargs rm -f
+
+	: # remove empty directories, when all components are in place
+	-find debian ! -name lib-dynload ! -name dist-packages -type d -empty -delete
+
 	touch stamps/stamp-install
 
 # Build architecture-independent files here.
@@ -1512,6 +1738,16 @@ binary-arch: build-arch install
 	  rm -rf debian/$$i/usr/share/doc/$$i; \
 	  ln -s $(p_lbase) debian/$$i/usr/share/doc/$$i; \
 	done
+ifeq ($(with_nogil),yes)
+	for i in $(p_nogil); do \
+	  rm -rf debian/$$i/usr/share/doc/$$i; \
+	  ln -s $(p_min) debian/$$i/usr/share/doc/$$i; \
+	done
+	for i in $(p_lnogildev); do \
+	  rm -rf debian/$$i/usr/share/doc/$$i; \
+	  ln -s $(p_lnogil) debian/$$i/usr/share/doc/$$i; \
+	done
+endif
 	-find debian ! -perm -200 -print -exec chmod +w {} \;
 ifneq ($(with_tk),yes)
 	rm -f $(d_lbase)/$(scriptdir)/tkinter
@@ -1550,6 +1786,9 @@ endif
 
 	dh_makeshlibs -p$(p_lib) -V '$(p_lib)'
 	dh_makeshlibs -p$(p_ldbg) -V '$(p_ldbg)'
+ifeq ($(with_nogil),yes)
+	dh_makeshlibs -p$(p_lnogil) -V '$(p_lnogil)'
+endif
 # don't include the following symbols, found in extensions
 # which either can be built as builtin or extension.
 	sed -ri \

Reply via email to