搞python环境杂七杂八那些事

多版本python环境管理

可能是本篇唯一有点用的一章

纯官方方法

无法做到python版本号第三位(micro version)的不同版本共存(例如3.10.6和3.10.11共存)。不过这似乎也没有实用价值,一般前两位符合需求就没问题。

Windows: py launcher(py.exe)

Windows下,如果同时安装多个版本的python,则添加进PATH的只能有最多一个,添加多个的话,python命令只会调用PATH中位置靠前的那个。
为了便于在命令行下管理多版本,python官方默认安装了py launcher,并且用管理员权限安装到全局目录;也可以在自定义安装选项中选择:

已经安装过py launcher之后,再安装第二版本的python选项就不可用了:

使用例:

py -X.Y等价于直接使用相应的python.exe,所以诸如py -X.Y -m pip install foo或者py -X.Y -m venv venv这样的命令都是合法的操作。

Linux: 直接共存

Linux下是可以非常方便地直接同时安装多版本的python的。

  1. ubuntu
    直接添加ppa源即可:

    1
    2
    sudo add-apt-repository ppa:deadsnakes/ppa
    sudo apt update

    需要注意的是这个ppa源按照debian系拆包的习惯,把每个版本的python都拆分成了多个小包,类似于:

    Built packages
    idle-python3.11 IDE for Python (v3.11) using Tkinter
    libpython3.11 Shared Python runtime library (version 3.11)
    libpython3.11-dbg Debug Build of the Python Interpreter (version 3.11)
    libpython3.11-dev Header files and a static library for Python (v3.11)
    libpython3.11-minimal Minimal subset of the Python language (version 3.11)
    libpython3.11-stdlib Interactive high-level object-oriented language (standard library, version 3.11)
    libpython3.11-testsuite Testsuite for the Python standard library (v3.11)
    python3.11 Interactive high-level object-oriented language (version 3.11)
    python3.11-dbg Debug Build of the Python Interpreter (version 3.11)
    python3.11-dev Header files and a static library for Python (v3.11)
    python3.11-distutils distutils package for Python (version 3.11)
    python3.11-examples Examples for the Python language (v3.11)
    python3.11-full Python Interpreter with complete class library (version 3.11)
    python3.11-gdbm GNU dbm database support for Python (version 3.11)
    python3.11-gdbm-dbg GNU dbm database support for Python (version 3.11 debug extension)
    python3.11-lib2to3 lib2to3 package for Python (version 3.11)
    python3.11-minimal Minimal subset of the Python language (version 3.11)
    python3.11-tk Tkinter - Writing Tk applications with Python (version 3.11)
    python3.11-tk-dbg Tkinter - Writing Tk applications with Python (version 3.11 debug extension)
    python3.11-venv Interactive high-level object-oriented language (pyvenv binary, version 3.11)

    按需取用,如果图省事就直接安装python3.*-full

    Debian不支持ppa,不过把ppa源的deb包倒腾过来用也不是不行…吧?

  2. Arch系
    AUR都有,用喜欢的AUR助手直接装就行;不过AUR的本质也是拉取python源码然后按照预设选项全自动编译,感觉上还没有手动编译好…

  3. Linux通用法
    根据官方github的方法直接编译就行,调整下--prefix的位置以便管理,然后最后使用make altinstall安装即可。
    后面会有大量篇幅碎碎念编译(或者说这篇就是为折腾编译cpython这盘醋包的饺子)

共存安装后,直接使用对应的可执行文件调用即可。
例如python3调用系统自带python,python3.11调用安装的3.11,pip3.12调用安装的3.12版本的pip

第三方工具uv

不喜欢conda系(anaconda/miniconda/micromamba)

  1. 虽然conda系支持安装非python软件包(比如可以通过conda install安装cmake甚至是nvcc)但是咱不需要;这些工具用官方方案感觉更稳。
  2. 虽然conda系支持细分到小版本的python版本管理(比如可以同时管理3.10.113.10.6),但是同个3.X内的包是兼容的,对于咱普通使用者来说不需要这么细。
  3. conda环境管理,如果使用conda create -n <name>就只能强制塞在c盘,如果用-p指定路径,则conda activate之后,终端开头会把完整的绝对路径前缀上去,又臭又长观感极差。
  4. conda不认自行安装的python,常用的大部分工具都来自conda-forge的重打包,心理上用起来不如第一方包舒适。
  5. conda环境只能通过conda命令激活,与很多开源项目启动脚本默认的venv方法不兼容。
  6. conda环境需要专门通过conda prompt终端进入,麻烦。

uv并不能算是conda的替代品,只是它也提供了管理多版本python环境的功能。
uv的全部功能参见uv文档,官方支持的所有安装方式参见文档
uv的文档另有中译,但任何时候不建议过多依赖非官方中译文档。
简单列举下咱常用的功能:

安装和更新

Windows推荐使用winget,在cmd和powershell下均可用:

1
winget install astral-sh.uv

更新:

1
winget upgrade astral-sh.uv

Arch Linux直接从官方软件源装还能吃到镜像加速就是爽:

1
sudo pacman -S uv

另有额外的python-uv包,安装后可以在python脚本中import uv使用,效果等同于在python环境中pip install uv

其它Linux如果软件源中没有(例如ubuntu),则建议使用官方安装脚本:

1
curl -LsSf https://astral.sh/uv/install.sh | sh

更新:

1
uv self upgrade

有时会遇到github api限制,要求提供token的问题。建议配合github-cli,安装并登录后使用gh auth token获取token。

使用

  1. 管理python环境
    1
    uv python list
    输出所有可用的python版本(本地+远程),其中本地python只在PATH中搜索,Windows下如果PATH中搜索到py launcher(py.exe),则还检测py launcher能检测到的python
    1
    uv python install X.Y
    从远程源拉取最简python环境。安装的远程源来自github
    选择的文件是cpython-*-install_only_stripped.tar.gz

    似乎不区分过于具体的指令集。在Linux上只使用x86_64版本,即使该平台支持x86_64_v2(SSE4)/v3(AVX2)/v4(AVX512)

    1
    uv venv <name> -p X.Y
    使用指定的X.Y版本python创建虚拟环境。如果省略name,则目录名为.venv

    uv创建的虚拟环境里面不包含任何初始包(即使是pip,setuptoolswheel)。
    使用UV_VENV_SEED=true环境变量可以覆盖这个行为(在3.12以上的版本中只会包含pip)。等价于使用uv venv <name> --seed <other options>

  2. 管理pip包
    1
    uv pip install foo
    uv安装pip包foo。接受-i/--index-url(uv推荐使用--default-index代替)和--extra-index-url(uv推荐使用--index代替),但是对-f/--find-links的兼容似乎不好。
    uv支持设置环境变量UV_DEFAULT_INDEX,UV_INDEXUV_FIND_LINKS

    uv默认状态下,会使用硬链接(hardlink)管理缓存,即:
    uv会把whl包的内容直接在缓存目录里面展开,然后如果目标环境和uv cache目录位于同一硬盘分区,则会直接把包内容hardlink到环境里面。如果不在同一分区则会回退到copy模式。
    这意味着在多个不同虚拟环境下安装同一个whl包的话,实际上每个环境里的包都和缓存包互为hardlink,只占一份空间。
    如果想要使用更激进的省空间方案,可以使用symlink模式,该模式下,所有包本体会被置于缓存目录,venv中的包均为指向缓存的symlink。
    uv不推荐此模式,因为uv cache clean会破坏所有安装。
    管理方式为设置UV_LINK_MODE环境变量,或者在pip install时使用--link-mode参数。
    完整的原文在下面附上。
    碎碎念:Linux上使用具有copy-on-write功能的文件系统感觉也可以试试clone模式?(btrfs,xfs等等)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    --link-mode <LINK_MODE>
    The method to use when installing packages from the global cache.

    Defaults to `clone` (also known as Copy-on-Write) on macOS, and `hardlink` on
    Linux and Windows.

    WARNING: The use of symlink link mode is discouraged, as they create tight
    coupling between the cache and the target environment. For example, clearing the
    cache (`uv cache clean`) will break all installed packages by way of removing the
    underlying source files. Use symlinks with caution.

    Possible values:
    - clone: Clone (i.e., copy-on-write) packages from the wheel into the
    `site-packages` directory
    - copy: Copy packages from the wheel into the `site-packages` directory
    - hardlink: Hard link packages from the wheel into the `site-packages` directory
    - symlink: Symbolically link packages from the wheel into the `site-packages`
    directory

    [env: UV_LINK_MODE=]

    uv默认状态下会拒绝在系统全局环境下装包。如果venv未激活,uv会试图在当前目录查找名为.venv的虚拟环境,如果未找到则会返回报错。
    使用--system参数或者UV_SYSTEM_PYTHON环境变量覆盖这一行为。

    使用uv安装pytorch的时候,可以使用--torch-backend或者UV_TORCH_BACKEND=指定安装的torch后端(例如,cpu/cu128/rocm6.3/xpu,也可以设定为auto让uv根据驱动自主选择)。
    指定后uv将完全忽略所有设定的index,而是使用torch源,因此如果想使用torch镜像(目前仍在同步的有阿里源(findlinks)/南京大学源(index))的话就不能使用这个参数。

理论而言,使用uv管理环境可以覆盖几乎全部的需求,但是还有两个小缺憾:
首先是使用uv python install安装的python不方便往全局环境塞包,有时想使用--system-site-packages节约空间的方法就不可行了;
在这个前提下,python官方又不为安全更新阶段之后的版本发安装包,想追个小版本最新都追不了。
再加上看这种怪一点的版本号咱有种奇妙的舒适感:

于是,折腾python源码编译就提上了日程…

编译cpython源码的那些事

获取源码

发布包

python官网每个版本的发布都可以在这里快捷地找到。
除了官网之外,也有镜像源可以使用:阿里源/华为源/CNPM源/中科大源
按照官方的发布规则,bugfix阶段的既提供源码包,也提供Windows/MacOS的安装包;security阶段的仅提供源码包。.tgz.tar.xz两种格式的源码内容完全相同,仅压缩格式不同。

git源码

cpython的官方仓库可以获取到任何版本以及还未加入正式发布的源码。
整个仓库很大,因此建议有选择性的clone。
比如如果想要获取3.11版本的最新代码:

1
git clone https://github.com/python/cpython.git -b 3.11 --depth 1

其它需求同样,灵活使用--single-branch --shallow-since--shallow-exclude等参数。
其中--shallow since接受格式为YYYY-MM-DD的日期,只保留该日之后(不含当日);

可以进一步指定时分秒,例如--shallow-since 2025-07-21-12-09-00,但是意义不大,github上面又不方便看具体的提交时间。

--shallow-exclude接受tag作为参数,仅保留该tag之后的提交(不包含该tag)。

官方发布的安装包,版本信息是这样的:

但是用发布的源码包编译的版本信息是这样的:

因此可以判断官方发布的包采用了从git仓库使用git checkout <tag>的源码编译。

Windows

准备编译环境

  1. 需要安装git-for-windows,用于自动拉取编译必需的一些其它依赖;有镜像源(CNPM/华为源)可用。
  2. 需要VS2022或者Visual Studio Build Tools。对于最简安装,下拉到“所有下载”,然后下载“Visual Studio 2022 生成工具”即可。
    安装的时候只需要选择这些项即可:
    首先选择使用C++的桌面开发,保留核心的生成工具和Windows SDK即可(Windows SDK测试过为必需):

    然后对于python3.11及以上,前往单个组件页面,搜索ARM64,安装ARM64生成工具(必需,按官方说法是wix3.14以上强制要求):
  3. Windows功能:对于3.10及以下,需要启用.NET Framework 3.5(新系统默认不启用);对于3.11及以上,需要启用.NET Framework 4.8(新系统默认启用无需修改):
  4. 可选:需要有一个已有的python安装;如果编译python3.12安装包,需要3.11及以上。如果系统没有安装,则后续会使用nuget拉取一个python环境;但是需要注意,如果有python但是当前激活的环境版本过低会编译失败。

编译

获取源码后,建一个虚拟环境(以3.11为例)并激活,进入Tools/msi目录(两步不分先后,虚拟环境建在哪都一样)。

1
2
3
py -3.11 venv venv
venv\Scripts\activate
cd Tools\msi

然后直接执行buildrelease.bat即可,需要使用的参数列在下面:

1
2
3
4
-D: 跳过帮助文档构建。如果没有已构建的帮助文档,会报错;
-B: 跳过已经编译的内容。如果不是第一次执行buildrelease.bat可以加上这个;
-o <dir>: 指定编译的输出路径。-o输出的目录结构与原始编译产物的目录结构不同,-o输出的更便于整理,推荐使用;
-x64: 仅编译x64;也可以指定针对x86或者ARM64编译;如果不显式指明,默认同时编译x86x64的安装包。
  1. 准备编译帮助文档的依赖:
    一部分需要从github拉取;
    另一部分为python依赖,这部分首先会检查当前是否有可用的python环境,如果没有就用nuget获取一个;如果有,就用pip获取必需工具sphinx
    pip获取的依赖按照Doc/requirements.txt指定。
  2. 编译帮助文档;
  3. 从github拉取编译安装包的依赖;
  4. 编译安装包本体;
  5. 如果指定了-o参数,编译完成后将编译产物复制到指定目录。
    其中所有从github获取的依赖均会位于externals目录内。

编译产物整理

如果未指定-o目录,前往PCBuild目录,x64的位于amd64/en-us目录内。
目录结构应该是这样:

如果指定了-o目录,前往对应目录,x64的位于amd64目录内。
目录结构应该是这样:

其中:

  1. *-amd64.exe是完整安装包,可以单独完成默认安装;
  2. *-embeded-amd64.zip等效于官网的Windows embeddable package (64-bit)
  3. 其它的所有msi安装包,可以留作备用。当需要使用到自定义安装中,描述中带有”Download”选项的,会用到这其中的一部分msi文件,但是咱没测试到底具体需要的是哪些
    需要用到这些msi的时候,放在和exe同一个目录内即可,exe自己会调用;不需要手动运行这些msi
  4. python3.10的输出还会有一个*webinstall.exe,也是调用这些msi,但是没必要留下,直接留完整安装包就行

咱编译过的安装包已经全部上传到huggingface上留档。

Linux

依赖准备

由于个人喜好,不记录rpm系(yum/dnf)的必需依赖。
信息存在部分冲突,所以分信息来源收录。

特别注意:如果需要使用tcl/tk(import tkinter),必须在编译之前准备好必需的头文件,例如apt系的tk-dev, pacman系的tk
否则,当需要tkinter功能的时候,需要重新编译,仅仅补充依赖是不够的。

apt系

  1. python官方编译文档
    首先需要启用源码仓库:Ubuntu等有软件源管理GUI的,直接开启即可;Debian没有GUI管理,需要编辑/etc/apt/source.list(旧格式)或者/etc/apt/sources.list.d/debian.sources(新DEB822格式),将deb-src源取消注释(有的版本默认已经启用)。
    然后执行:
    1
    2
    3
    apt update
    apt build-dep python3
    apt install pkg-config
    执行过apt build-dep之后源码仓库即不再需要,可以关回去而不影响使用;以上为必需依赖。
    如果需要编译完整功能,则:
    1
    2
    3
    4
    5
    apt install build-essential gdb lcov pkg-config \
    libbz2-dev libffi-dev libgdbm-dev libgdbm-compat-dev liblzma-dev \
    libncurses5-dev libreadline6-dev libsqlite3-dev libssl-dev \
    lzma lzma-dev tk-dev uuid-dev zlib1g-dev libmpdec-dev libzstd-dev \
    inetutils-inetd
    1. Debian12和Ubuntu24.04因为未知原因,仓库中不包含`libmpdec-dev`。可以不安装,自动调用python内置的版本;	或者,可以调用[Ubuntu ppa源](https://launchpad.net/~ondrej/+archive/ubuntu/php)以及[Debian DPA源](https://packages.sury.org/php/),参见https://deb.sury.org/	Ubuntu添加方式:`add-apt-repository ppa:ondrej/php`	[Debian添加方式](https://packages.sury.org/php/README.txt)	Debian13(trixie)和Debian unstable(sid)恢复了这个包,因此正常安装即可。	有关这个问题的所有讨论可以从[这里](https://github.com/python/cpython/issues/119114)开始了解,内部的其它链接也可以进一步拓展信息来源。	<div class="note note-warning">        <p>按照cpython开发计划,对内置mpdecimal的支持将在python3.16终止。</p>      </div>
    1. libzstd-devinetutils-inetd貌似是在python3.14新引入的。
  2. cpython官方github action工作流
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    apt-get update

    apt-get -yq install \
    build-essential \
    pkg-config \
    ccache \
    cmake \
    gdb \
    lcov \
    libb2-dev \
    libbz2-dev \
    libffi-dev \
    libgdbm-dev \
    libgdbm-compat-dev \
    liblzma-dev \
    libncurses5-dev \
    libreadline6-dev \
    libsqlite3-dev \
    libssl-dev \
    libzstd-dev \
    lzma \
    lzma-dev \
    strace \
    tk-dev \
    uuid-dev \
    xvfb \
    zlib1g-dev

    # Workaround missing libmpdec-dev on ubuntu 24.04:
    # https://launchpad.net/~ondrej/+archive/ubuntu/php
    # https://deb.sury.org/
    sudo add-apt-repository ppa:ondrej/php
    apt-get update
    apt-get -yq install libmpdec-dev
  3. pyenv文档的建议

    pyenv提供了一套管理多版本python共存的方案,但是是通过一键式拉取源码编译的方式完成的,所以感觉不如手动编译可控性强。

    1
    2
    3
    4
    apt update
    apt install make build-essential libssl-dev zlib1g-dev \
    libbz2-dev libreadline-dev libsqlite3-dev curl git \
    libncursesw5-dev xz-utils tk-dev libxml2-dev libxmlsec1-dev libffi-dev liblzma-dev
    对于python3.14及以上,额外安装libzstd-dev编译compress.zstd模块。但是ubuntu20.04及更旧的系统版本不具有足够版本。
    可选建议,不在推荐集合中,通常只有在有特殊需求或其中存在问题的情况下才应考虑使用:
    1
    2
    apt install libedit-dev #libreadline-dev的替代品
    apt install libncurses5-dev #libncursesw5更差的替代品

pacman系

Arch可以参照的文档就没那么多,主打一个自己找吃的:

  1. python官方编译文档的deb包转写:
    1
    2
    pacman -S base-devel #必需,不会有Arch人没装这个吧
    pacman -S gdb lcov bzip2 libffi gdbm xz ncurses readline sqlite openssl tk utils-linux-lib zlib mpdecimal zstd inetutils --needed
    其中zstdinetutils貌似是在python3.14新引入的。
  2. cpython官方github action工作流的deb包转写:
    1
    pacman -S --needed base-devel ccache cmake gdb lcov libb2 bzip2 libffi gdbm xz ncurses readline sqlite openssl zstd strace tk utils-linux-lib xorg-server-xvfb zlib mpdecimal
  3. pyenv文档的建议
    1
    pacman -S --needed base-devel openssl zlib xz tk zstd
    可选建议,不在推荐集合中,通常只有在有特殊需求或其中存在问题的情况下才应考虑使用:
    如果需要旧版本ncurses(ncurses5),需要通过AUR安装ncurses5-compat-libs。archlinuxcn源和arch4edu源都提供了该包。
  4. 原先的官方编译脚本(AUR的就不搬了):
    • python3.10.10-1
      1
      2
      pacman -S --needed bzip2 expat gdbm libffi libnsl libxcrypt openssl zlib #depends
      pacman -S --needed tk sqlite bluez-libs mpdecimal llvm gdb xorg-server-xvfb ttf-font #makedepends
    • python3.11.8-1
      1
      2
      pacman -S --needed bzip2 expat gdbm libffi libnsl libxcrypt openssl zlib tzdata #depends
      pacman -S --needed tk sqlite bluez-libs mpdecimal llvm gdb xorg-server-xvfb ttf-font #makedepends
    • python3.12.7-1
      1
      2
      pacman -S --needed bzip2 expat gdbm libffi libnsl libxcrypt openssl zlib tzdata mpdecimal #depends
      pacman -S --needed tk sqlite bluez-libs llvm gdb xorg-server-xvfb ttf-font #makedepends

土法编译

依赖和源码准备好之后,进入源码根目录:

1
./configure <编译参数>

编译参数这里,通常来说会需要这些:

1
2
3
--prefix=/path/to/dir: 安装路径,默认是/usr/local,也就是把文件分列进/usr/local/{bin,lib,include,share}等等;修改prefix为单独目录会便于卸载。如果想要使用系统包管理打包,就指定为/usr
--enable-optimizations: 启用pgo优化,并在部分平台自动开启lto优化
--with-lto: 强制开启lto优化

上面这些最好是都用上的;
对于手动管理的版本,比较好的选择是--prefix=/opt/python/python3.X或者--prefix=/usr/local/python/python3.X
Debian12和Ubuntu24.04,如果没补装libmpdec-dev,编译python3.12及以下版本时,保险起见最好显式指定使用python内置的mpdec:--with-system-mpdec=no或者--without-system-libmpdec
对于其它有系统mpdecimal头文件的,python官方推荐使用系统内置的版本:--with-system-mpdec
在python3.12及以前,--with-system-libmpdec的默认值均为no;3.13开始,默认值变为yes,并且在未检测到必需文件时自动回退到使用内置版本

1
./configure --enable-optimizations --with-lto --prefix=/opt/python/python3.X

除此之外,收录部分发行版使用的所有configure参数:

从distro里面抄参数可能会在configure阶段遇到缺依赖的报错,照着报错补就行。
例如Ubuntu24.04遇到

1
2
checking for dtrace... not found
configure: error: dtrace command not found on $PATH

直接输入dtrace检测:

1
2
找不到命令 “dtrace”,但可以通过以下软件包安装它:
sudo apt install systemtap-sdt-dev

补装即可。
或者保险起见可以把系统里面默认版本的编译集合全拉下来:

1
2
apt build-dep python3.12 #Ubuntu24.04
apt build-dep python3.13 #Debian13

至于Arch,从PKGBUILD全抄了完事。

--prefix=/usr不要抄!

参考源码-3.10.10
参考源码-3.11.8
参考源码-3.12.7

1
2
3
4
5
6
7
8
9
10
11
12
13
# Disable bundled pip & setuptools
./configure --prefix=/usr \
--enable-shared \
--with-computed-gotos \
--enable-optimizations \
--with-lto \
--enable-ipv6 \
--with-system-expat \
--with-dbmliborder=gdbm:ndbm \
--with-system-libmpdec \
--enable-loadable-sqlite-extensions \
--without-ensurepip \
--with-tzpath=/usr/share/zoneinfo

参考源码

1
2
3
4
5
6
7
8
9
10
11
12
13
./configure \
--prefix=/usr \
--libdir=/usr/lib/x86_64-linux-gnu \
--enable-ipv6 \
--enable-loadable-sqlite-extensions \
--with-dbmliborder=bdb:gdbm \
--with-computed-gotos \
--without-ensurepip \
--with-system-expat \
--with-system-ffi \
--with-dtrace \
--with-ssl-default-suites=openssl \
--with-wheel-pkg-dir=/usr/share/python-wheels/ \

参考源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
./configure \
--enable-shared \
--prefix=/usr \
--libdir=/usr/lib/x86_64-linux-gnu \
--enable-ipv6 \
--enable-loadable-sqlite-extensions \
--with-dbmliborder=bdb:gdbm \
--with-computed-gotos \
--without-ensurepip \
--with-system-expat \
--with-dtrace \
--with-ssl-default-suites=openssl \
--with-system-libmpdec=no \
--with-wheel-pkg-dir=/usr/share/python-wheels/ \

参考源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
./configure \
--prefix=/usr \
--libdir=/usr/lib/x86_64-linux-gnu \
--enable-ipv6 \
--enable-loadable-sqlite-extensions \
--with-dbmliborder=bdb:gdbm \
--with-computed-gotos \
--without-ensurepip \
--with-system-expat \
--with-dtrace \
--with-system-libmpdec \
--with-system-ffi \
--with-wheel-pkg-dir=/usr/share/python-wheels/ \
--enable-shared

参考源码

1
2
3
4
5
6
7
8
9
10
11
12
13
./configure \
--prefix=/usr \
--libdir=/usr/lib/x86_64-linux-gnu \
--enable-ipv6 \
--enable-loadable-sqlite-extensions \
--with-dbmliborder=bdb:gdbm \
--with-computed-gotos \
--without-ensurepip \
--with-system-expat \
--with-dtrace \
--with-ssl-default-suites=openssl \
--with-wheel-pkg-dir=/usr/share/python-wheels/ \
--enable-shared

configure配置完后,执行编译和安装:

1
2
3
make #使用-jx多线程,例如-j9
make test #测试,可选
make altinstall #使用altinstall安装不会创建python3和pip3软链接
但是手动执行的时候直接是no module
直接忽略即可,不影响使用;其它测试出错也可用这种方式排查。此错误在cpython v3.10.14+, commit 31302f5出现,在3.10.18和3.11上均未复现,在mint22上面未复现。

打包

PKGBUILD

直接放三份PKGBUILD在这里,都是最简单粗暴的写法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# Maintainer: Silver Tuanzi
pkgname=python310-git
pkgver=v3.10.18.r13.f610f9e
pkgrel=1
pkgdesc="The Python programming language (version 3.10)"
arch=('x86_64' 'aarch64')
license=("PSF-2.0")
url="www.python.org"
depends=('bzip2' 'expat' 'gdbm' 'libffi' 'libnsl' 'libxcrypt' 'openssl' 'zlib' 'mpdecimal')
makedepends=('git' 'ccache' 'gdb' 'tk' 'xz' 'sqlite')
provides=('python310')
conflicts=('python310')
options=(strip !debug)
source=('python310::git+https://github.com/python/cpython.git#branch=3.10')
sha256sums=('SKIP')

pkgver() {
cd "$srcdir/python310"
printf "%s" "$(git describe --long | sed 's/\([^-]*-\)g/r\1/;s/-/./g')"
}

build() {
cd "$srcdir/python310"
./configure --prefix=/usr \
--with-computed-gotos \
--enable-optimizations \
--with-lto \
--enable-ipv6 \
--with-system-expat \
--with-dbmliborder=gdbm:ndbm \
--with-system-libmpdec \
--enable-loadable-sqlite-extensions \
--enable-shared \
ax_cv_c_float_words_bigendian=no
make
}

package() {
cd "$srcdir/python310"
make DESTDIR="$pkgdir/" altinstall
rm -f "${pkgdir}/usr/lib/libpython3.so"
cd "$pkgdir"/usr/lib/python*/
rm -r test && rm -r */*test*/
sed -i '1s|^#!/usr/bin/python$|#!/usr/bin/python3.10|' "${pkgdir}/usr/bin/pip3.10"
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# Maintainer: Silver Tuanzi
pkgname=python311-git
pkgver=v3.11.13.r15.8adac49
pkgrel=1
pkgdesc="The Python programming language (version 3.11)"
arch=('x86_64' 'aarch64')
license=("PSF-2.0")
url="www.python.org"
depends=('bzip2' 'expat' 'gdbm' 'libffi' 'libnsl' 'libxcrypt' 'openssl' 'zlib' 'mpdecimal')
makedepends=('git' 'ccache' 'gdb' 'tk' 'xz' 'sqlite')
provides=('python311')
conflicts=('python311')
options=(strip !debug)
source=('python311::git+https://github.com/python/cpython.git#branch=3.11')
sha256sums=('SKIP')

pkgver() {
cd "$srcdir/python311"
printf "%s" "$(git describe --long | sed 's/\([^-]*-\)g/r\1/;s/-/./g')"
}

build() {
cd "$srcdir/python311"
./configure --prefix=/usr \
--with-computed-gotos \
--enable-optimizations \
--with-lto \
--enable-ipv6 \
--with-system-expat \
--with-dbmliborder=gdbm:ndbm \
--with-system-libmpdec \
--enable-loadable-sqlite-extensions \
--enable-shared \
ax_cv_c_float_words_bigendian=no
make
}

package() {
cd "$srcdir/python311"
make DESTDIR="$pkgdir/" altinstall
rm -f "${pkgdir}/usr/lib/libpython3.so"
cd "$pkgdir"/usr/lib/python*/
rm -r test && rm -r */*test*/
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# Maintainer: Silver Tuanzi
pkgname=python312-git
pkgver=v3.12.11.r16.ade85bc
pkgrel=1
pkgdesc="The Python programming language (version 3.12)"
arch=('x86_64' 'aarch64')
license=("PSF-2.0")
url="www.python.org"
depends=('bzip2' 'expat' 'gdbm' 'libffi' 'libnsl' 'libxcrypt' 'openssl' 'zlib' 'mpdecimal')
makedepends=('git' 'ccache' 'gdb' 'tk' 'xz' 'sqlite')
provides=('python312')
conflicts=('python312')
options=(strip !debug)
source=('python312::git+https://github.com/python/cpython.git#branch=3.12')
sha256sums=('SKIP')

pkgver() {
cd "$srcdir/python312"
printf "%s" "$(git describe --long | sed 's/\([^-]*-\)g/r\1/;s/-/./g')"
}

build() {
cd "$srcdir/python312"
./configure --prefix=/usr \
--with-computed-gotos \
--enable-optimizations \
--with-lto \
--enable-ipv6 \
--with-system-expat \
--with-dbmliborder=gdbm:ndbm \
--with-system-libmpdec \
--enable-loadable-sqlite-extensions \
--enable-shared \
ax_cv_c_float_words_bigendian=no
make
}

package() {
cd "$srcdir/python312"
make DESTDIR="$pkgdir/" altinstall
rm -f "${pkgdir}/usr/lib/libpython3.so"
cd "$pkgdir"/usr/lib/python*/
rm -r test && rm -r */*test*/
}
  1. 只按照最简单的configure make make altinstall三步走的流程。
  2. 依赖精挑细选了一些,可以调整;尤其注意,如果想使用tkinter相关功能,tk包需要保留;这种情况下可以考虑把tk直接移入depends中。直接不保留任何依赖也行,反正准备依赖的时候已经把需要的包显式安装了。
  3. makepkg自己拉代码会拉下来很多多余的内容;似乎没有包含--single-branch参数。拉下来的仓库会达到吓人的2.6GB甚至更多。因此可以在执行makepkg之前,手动把仓库浅克隆下来:
    1
    2
    3
    git clone https://github.com/python/cpython.git -b 3.10 --shallow-since 2025-06-03 --bare python310 #python3.10
    git clone https://github.com/python/cpython.git -b 3.11 --shallow-since 2025-06-03 --bare python311 #python3.11
    git clone https://github.com/python/cpython.git -b 3.12 --shallow-since 2025-06-02 --bare python312 #python3.12
    这里使用--shallow-since而非--depth 1,是因为脚本需要git describe来获取版本号,因此需要保留最近的一个git tag;使用--shallow-exclude的话也可以,但是要回溯到倒数第二个tag,因为shallow-exclude指定的提交不会被保留。例如,如果当前版本是3.12.10,就需要--shallow-exclude v3.12.9
  4. 没有使用--with-dtrace;dtrace本就不是为Linux准备的,如果强行使用的话,需要安装依赖systemtab,但是在咱这里会编译失败。
  5. 没有使用make test;如果想要使用的话,在build()函数和package()函数中间增加一个check()函数,在这里面执行;不使用是因为test失败不便排查且只能重新编译,而大部分时候测试的问题都是干扰项问题。
  6. 如果不开启--enable-shared,则编译成静态库,可以因此去掉package()中的
    1
    rm -f "${pkgdir}/usr/lib/libpython3.so"
  7. 如果想要安装man手册,在altinstall后面再添加maninstall,然后打包的时候为了避免冲突执行
    1
    rm -f "${pkgdir}/usr/share/man/man1/python3.1"
  8. 按照PEP668标准,可以准备一个文件,文件名为EXTERNALLY-MANAGED,内容为:
    1
    [externally-managed]
    然后package()阶段,把它安装进去:
    1
    install -Dm644 "$srcdir"/EXTERNALLY-MANAGED -t "${pkgdir}/usr/lib/python3.X/" # X换为对应版本
    但是不建议在手动编译安装的环境里面加这个,它会阻止你用pip往系统环境里面塞包。

.deb

.deb包dpkg-buildpackage自动化编译写起来不太方便,这里选择手动编译再打包的方法:
首先按照上一节的手动编译方法,配置好编译参数,其中给到--prefix=/usr,如果想要更贴近debian的打包规范就再给到--libdir=/usr/lib/x86_64-linux-gnu
执行完./configuremake之后不执行make altinstall,而是改为先找一个临时存放的目录,例如/tmp/py,然后执行make DESTDIR="/path/to/dir" altinstall
接着进入前面的DESTDIR目录,里面应该是只有一个/usr目录。在这个文件夹下再新建一个DEBIAN目录。
DEBIAN目录下新建一个control文件,填入以下内容:(以3.10为例,包名命名为python3.10-git)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Package: python3.10-git
Version: 3.10.18+git12.g5c19c5b #来自git describe
Section: python
Priority: optional
Architecture: amd64
Installed-Size: 247424 #自行查看大小之后转写为KiB单位
Conflicts: python3.10-minimal,python3.10 #为了避免和deadsnake ppa源的python包冲突;可以省略,反正要自己管理的
Maintainer: Silver Tuanzi
Homepage: https://www.python.org/
Description: Git build of Python 3.10
This is a custom git build of Python 3.10 compiled from source.
.
The package provides a complete Python 3.10 installation including
the standard library, development headers, and virtual environment
support.
.
Git describe: v3.10.18-9-g5c19c5b
Built from git commit: 5c19c5b (9 commits after v3.10.18)

这个control文件没有设置任何的Depends,ProvidesReplace字段,因为必需的依赖已经显式安装;自行管理即可。
python包不需要postinstpostrm脚本执行操作,因此不作设置。

如果编译的时候使用了--enable-shared,进入/usr/lib目录,删除libpython3.so文件,避免冲突。

对于python3.10版本,进入/usr/bin目录,检查pip3.10文件的第一行是否错误地标注成了#!/usr/bin/python;如果是,将其手动修复为#!/usr/bin/python3.10
当然如果编译的时候本来就带了--without-ensurepip就不用考虑这个问题了。

最后,回到/usr/DEBIAN目录所在的文件夹,执行

1
dpkg -b . python3.10-git.deb

deb文件名可以随意,稍等片刻之后打包成功,可以使用dpkg -i或者apt install安装。
建议使用apt install ./python3.10-git.deb的方式安装,如果打包不规范,可能会出现apt安装失败但是dpkg能安装成功的情况,使用apt有助于规避未来使用可能遇到的一些奇怪的依赖问题。


搞python环境杂七杂八那些事
http://silvertuanzi.github.io/2025/10/01/python-envs/
作者
silver-lasombra
发布于
2025年10月1日
许可协议