2025, Dec 23 18:02

Как собрать и распространять pybind11‑расширение на Linux: manylinux и auditwheel

Соберите переносимое C++‑расширение на pybind11 для Linux: manylinux_2_28, wheels и auditwheel; без статической glibc, совместимо с Debian, CentOS и RHEL.

Поставлять быстрое C++‑расширение на pybind11 на Windows просто, если уйти в полную статику. На Linux, однако, статически собрать всё в один .so и получить тот же эффект не получится. В экосистеме упаковки Python есть стандартный способ распространять бинарные расширения, которые действительно работают на разных дистрибутивах и версиях glibc, — и это не «просто всё статически залинковать».

Что на самом деле нужно решить

Цель — выпустить бинарное Python‑расширение, которое стабильно работает на Debian, CentOS и других дистрибутивах на протяжении нескольких лет. В Linux устойчивость обеспечивается таргетированием определённой базовой версии glibc и контролируемого набора разделяемых библиотек, а не полной статической линковкой glibc. По‑питоновски это означает: собрать wheel, соответствующий правилам совместимости платформ из PEP 599 и PEP 600, а затем позволить pip подобрать нужный артефакт по тегам.

Минимальный и корректный процесс сборки

Базовый подход — собирать внутри официальных образов manylinux. Они фиксируют целевую версию glibc и набор разрешённых разделяемых библиотек для широкой совместимости и уже содержат несколько версий CPython. Практичный современный таргет — manylinux_2_28 на основе AlmaLinux 8, охватывающий широкий спектр актуальных дистрибутивов.

Внутри контейнера вы собираете wheels, используя интерпретаторы под /opt/python. Затем проверяете и при необходимости исправляете бинарник с помощью auditwheel — он гарантирует соответствие PEP 599 и корректно переписывает платформенный тег колеса.

/opt/python/cp311-cp311/bin/pip wheel -w wheelhouse .
auditwheel repair --plat manylinux_2_28_x86_64 wheelhouse/*.whl

В результате получится wheel с тегом manylinux_2_28, который pip выберет по стандартным платформенным тегам. Если нужно поддержать более старые ОС и ваш C++‑код собирается старым инструментарием, используйте образы manylinux2014 — они покрывают примерно последнее десятилетие.

Почему полностью статический .so — неверная цель

Статическая линковка glibc в Linux настоятельно не рекомендуется. Она ведёт себя не так, как многие ожидают, и часто становится источником тонких, трудноуловимых сбоев. Напротив, линковка с достаточно старой разделяемой версией glibc даёт .so, который запускается на широком спектре систем на базе glibc. Остальное при желании можно линковать статически — в основном расплатой будет размер файла. Имейте в виду: сделать статическими рантаймы C и C++ возможно, но на практике это может оказаться нетривиальным.

Если вам нужен действительно статический рантайм для отдельного нативного исполняемого файла и вы можете менять технологии, можно линковаться с Musl через zig cc или собрать бинарник на Go, где по умолчанию используется статическая линковка. Это иной вариант развёртывания, чем Python‑расширение, но он жизнеспособен, когда вы контролируете язык и стек рантайма.

Совместимость, теги и как pip ими пользуется

Wheels содержат платформенные теги, которые кодируют ABI и базовую версию libc. PEP 599 и PEP 600 описывают, на какие версии glibc целиться и какие библиотеки разрешено линковать для обеспечения широкой совместимости. pip использует эти теги, чтобы выбрать артефакт, подходящий окружению, поэтому пользователи на Debian или CentOS получат правильное колесо без ручных действий.

Старые системы и практичная стратегия сборки

Простое и рабочее правило: собирать на или под самую старую ОС, которую вы хотите поддерживать. Образы manylinux как раз и существуют, чтобы стандартизовать эту базовую линию. Если ваши требования по совместимости уходят далеко назад и код компилируется старым инструментарием, используйте manylinux2014 как резервный вариант. Для более точной настройки можно посмотреть версии glibc у разных дистрибутивов на DistroWatch и согласовать целевую платформу.

Альтернатива для охвата версий Python

Если для вас важнее сократить число сборок, чем придерживаться конкретной ABI‑базы, можно перейти на nanobind и воспользоваться стабильным ABI Python, собирая лишь под одну версию интерпретатора. Это отдельный компромисс — он может подойти, а может и нет, в зависимости от ваших ограничений.

Почему это важно

Следуя пути manylinux и auditwheel, вы остаётесь в русле стандартов упаковки, на которых держится большая часть Python‑экосистемы. Ваши колёса будут устанавливаться без сюрпризов, pip будет точно знать, когда их выбирать, а пользователям на разных дистрибутивах не придётся охотиться за отсутствующими системными библиотеками. И главное — вы обходите ловушки статической glibc и связанные с ней непредсказуемые сбои.

Собираем всё воедино

Собирайте колёса в контейнере manylinux_2_28, используя интерпретаторы под /opt/python для каждого нужного ABI. Запускайте auditwheel repair, чтобы убедиться, что вы зависите только от разрешённых библиотек, и проставить корректный платформенный тег. Если нужно покрыть значительно более старые системы, повторите процесс в manylinux2014. Сторонние библиотеки можно держать статическими, если так удобнее, но glibc статически не линкуйте. Не уверены в охвате дистрибутивов — проверьте базовые версии glibc на DistroWatch и подберите подходящий образ manylinux.

Итоги

В Linux переносимый способ распространять C++‑расширение на pybind11 — это wheel, совместимый с manylinux, а не полностью статический .so. Используйте инструментальную цепочку manylinux_2_28, собирайте под версии CPython из /opt/python и завершайте процесс auditwheel repair. Нужна поддержка старых парков машин — берите manylinux2014. Избегайте статической glibc, опирайтесь на теги wheel для совместимости — и получите цепочку распространения, которая без проблем устанавливается на Debian, CentOS и дальше.