Linux 源码编译安装指定版本 Python

Linux 源码编译安装指定版本 Python
乔阳系统自带的 Python 是给 apt、yum 这类工具用的,版本由发行版钉死,想升级或装一个仓库里没有的版本,基本只能源码编译。内网隔离环境下更是连 apt install 都连不上仓库,把源码包带进去编译几乎是唯一可靠的路子。
下面是我常用的流程,重点说清楚几个关键点:为什么不用包管理器、--prefix 和 make altinstall 各起什么作用、离线环境怎么处理依赖。
为什么不用包管理器
Debian/Ubuntu 的 python3 跟发行版绑定,Ubuntu 22.04 默认是 3.10,想跑 3.13,apt 仓库里多半没有。即便有 deadsnakes 这类 PPA,内网也访问不了。
更关键的是系统 Python 不能动。apt、yum 和一堆系统脚本都依赖 /usr/bin/python3,一旦覆盖或改了它的软链接,包管理器先坏,连锁反应能把系统搞瘫。新版本必须装到独立目录,跟系统的彻底隔开。
源码编译能同时解决这两件事:精确指定版本,安装到任意路径,跟系统 Python 互不干扰。
准备工作
- root 或 sudo 权限
- 至少 500MB 磁盘空间,编译过程和安装目录都要占地方
- 确认目标版本号,本文用 Python 3.13.9 演示
- Debian/Ubuntu 或其他 Linux 发行版均可,依赖包名按发行版调整
下载源码
Python 官方 FTP 按版本归档了所有源码包。
1 | cd /usr/src/ |
离线场景下,在有网的机器上下好 .tar.xz,用 scp 或 U 盘带进内网即可。源码包本身不依赖外部库,搬运很干净。
安装编译依赖
依赖才是离线编译真正的麻烦点。
Debian/Ubuntu
1 | sudo apt update |
这些库各自干什么
Python 编译时对可选模块的态度是”有头文件就编进去,没有就跳过”,不报错。等你某天 import ssl 失败才发现当初漏装了 libssl-dev,这时候得回头重新编译。所以依赖一次装齐比较省事。
build-essential:gcc、make、libc 头文件,编译的基础工具libssl-dev:OpenSSL 头文件,编译出ssl模块,pip 走 HTTPS 全靠它zlib1g-dev:zlib 压缩库,zipimport和 pip 解包要用libffi-dev:libffi,ctypes模块依赖它,很多 C 绑定库间接需要libbz2-dev:bzip2,bz2模块libreadline-dev:readline,REPL 的行编辑和历史记录libsqlite3-dev:sqlite3 模块,不少 ORM 和测试框架要用liblzma-dev:lzma 压缩模块uuid-dev:uuid模块tk-dev:tkinterlibncurses5-dev/libncursesw5-dev:curses 模块libgdbm-dev:dbm/gdbm模块libnss3-dev:NSS,部分 SSL 场景用到
漏掉某个 -dev 包,Python 照样编得出来,只是对应的 stdlib 模块会缺失。libssl-dev 尤其不能漏,否则 pip 装包时报 SSL 错误,只能重编。
离线环境怎么搞依赖
真正 air-gapped 的环境,apt install 也连不上仓库。两种做法。
第一种,在一台相同发行版、相同架构、有网的机器上把 .deb 下全再搬进去。用 apt-get install --download-only 或 apt download 把上面所有包及其依赖抓下来,传进内网后 sudo dpkg -i *.deb 安装。注意要把依赖的依赖也一起抓全,dpkg 不会自动解决依赖链。
第二种,直接在另一台配置相同的联网机器上按本文流程编译完,然后把整个 /usr/local/python313 目录打包拷到内网机器解压。前提是两边 glibc 和运行库兼容,架构相同、发行版大版本一致。Python 动态链接了 libssl、libffi 等运行库,目标机器上得有对应的 .so,所以这种搬运要求两台机器环境基本一致,不是完全无脑可用。
配置编译选项
1 | cd /usr/src/Python-3.13.9 |
两个参数是重点。
--prefix=/usr/local/python313 决定安装根目录。所有产物(bin/、lib/、include/、share/)都落在这个路径下。换个 prefix 就能装另一个版本,互不干涉。这是多版本共存的机制基础:不同 prefix,不同目录,各走各的 PATH。
--enable-optimizations 启用 PGO(profile-guided optimization)和 LTO(link-time optimization)。它会先跑一遍采集运行 profile,再据此重新编译,运行时性能有实打实的提升,但编译时间会明显变长,因为要跑两遍。生产服务器值得开,快速验证或 CI 里想省时间可以去掉。
千万不要把 --prefix 指向 /usr 或 /usr/bin,也别省略 prefix 用默认的 /usr/local 再跑 make install,那会往系统目录写 python3 软链接。装到独立的 /usr/local/python3XX 是最安全的做法。
编译和安装
1 | sudo make -j$(nproc) |
make -j$(nproc) 用满所有 CPU 核心加速编译。开了 --enable-optimizations 的话,整个编译加安装大概 5 到 15 分钟,看机器。
make altinstall 和 make install 的区别是这篇文章最该记住的一点:
make install会创建python3、pip3这类不带版本号的软链接,直接指向新装版本,可能盖掉系统的python3,危险。make altinstall只装带版本号的二进制(python3.13、pip3.13),不碰python3软链接。
记住一条:永远用 altinstall,让系统 Python 安安静静待在原地。
验证安装
1 | cd /usr/local/python313/bin |
预期输出类似:
1 | Python 3.13.9 |
再配一下 PATH,让任意目录都能调用。编辑对应的 shell 配置文件,zsh 改 ~/.zshrc,bash 改 ~/.bashrc:
1 | export PATH="/usr/local/python313/bin:$PATH" |
重载配置后验证:
1 | source ~/.zshrc |
which 应该输出 /usr/local/python313/bin/python3.13。PATH 里把新版本的 bin 放前面,调用 python3.13 走新装的,但 python3 仍然指系统版本,互不影响。
多版本共存
给每个版本一个独立 prefix 就行,互不干扰:
1 | # Python 3.11 |
三个版本分别落在三个目录,各自的 python3.11、python3.12、python3.13 共存。把三个 bin 目录都加进 PATH,就能随时切换。比 pyenv 轻量,代价是要自己管理 prefix 和 PATH。
项目级隔离交给 venv:
1 | python3.13 -m venv myproject_env |
卸载
编译安装没有卸载脚本,但因为是独立 prefix,直接删目录就干净了:
1 | sudo rm -rf /usr/local/python313 |
再把 shell 配置里对应的 PATH 行删掉。venv 目录是独立的,跟 Python 安装本身无关,删不删看你自己。
几个容易踩的坑
编译过了但 import 模块失败。 编译时漏装了对应的 -dev 包,模块被静默跳过。import ssl、import ctypes、import sqlite3 报 ModuleNotFoundError 基本都是这个原因。补装依赖后重新 configure && make && make altinstall。
pip 报 SSL 错误。 libssl-dev 没装就编译,ssl 模块缺失。装上 libssl-dev 重编即可。
系统工具突然异常。 大概率是用了 make install,或者把 prefix 指到了 /usr 下,python3 软链接被覆盖。用 altinstall 加独立 prefix 能完全避免。
编译太慢。 --enable-optimizations 要跑两遍,CI 或快速验证时去掉它,能省一大半时间。







