Shared Libs Policy
Цель
Позволить пакетам с библиотеками с разными несовместимыми ABI-версиями сосуществовать в системе. Это снижает риски поломок при обновлении, упрощает сопровождение и позволяет проводить обновления более гибко.
Кратко: Нельзя класть новую библиотеку с новым soname в тот же бинарный пакет, где была старая.
Область действия
Данная политика распространяется на:
- Публичные разделяемые библиотеки — `.so.*`-файлы (например, `libfoo.so.1`, `libbar.so.2.3`, а также `libbaz-3.1.so`), устанавливаемые в стандартные пути для динамического линковщика и предназначенные для использования несколькими независимыми программами. Это определение включает в себя библиотеки с альтернативными схемами именования, в которых ABI-версия зашита непосредственно в имя файла (например, `libname-X.Y.so`);
- Пакеты разработки (`-devel`), содержащие файлы для компоновки (`libfoo.so`), заголовочные файлы (`.h`) и другие материалы, необходимые для сборки программ, использующих библиотеку;
- Общие (`-common`) пакеты, включающие файлы, не зависящие от ABI, — такие как конфигурационные файлы, ресурсы (иконки, XML, PNG, словари и т.п.), базы данных, скрипты, справочные материалы.
Политика не распространяется на:
- `.so`-файлы, загружаемые конкретным приложением как плагины (например, через `dlopen`);
- статические библиотеки (`.a`), если они используются отдельно и не влияют на ABI-совместимость;
- библиотеки, являющиеся частью одного монолитного пакета и не предназначенные для переиспользования другими программами (например, `libinternal.so`, установленная в нестандартный путь и используемая только одним приложением);
- библиотеки, собранные как часть одного приложения, устанавливаемые в стандартный путь (например, `/usr/lib64`), но не предназначенные для использования другими программами и не имеющие `-devel` пакета (например, `libmyappsupport.so.0`, используемая исключительно `myapp`).
Примеры:
- Под действие политики попадает `libfoo.so.1`, установленный в `/usr/lib64/`, используемый несколькими приложениями.
- Под действие политики также попадает `libfoo-devel`, содержащий `libfoo.so` и заголовки.
- Под действие политики также попадает `libfoo-common`, если в нём находятся ресурсы, применимые ко всем версиям ABI.
- Под действие политики также попадает `libbaz-3.1.so`, если она используется как разделяемая библиотека.
- Не попадает `libplugin_mytask.so`, загружаемый только одним приложением как модуль (plugin).
- Не попадает `libinternal.so`, устанавливаемый в `/opt/myapp/lib/` и используемый исключительно программой `myapp`.
- Не попадает `libmyappsupport.so.0`, установленный в `/usr/lib64`, но используемый только приложением `myapp`, если отсутствует `libmyappsupport-devel`.
Дополнительную информацию о принципах работы разделяемых библиотек в GNU/Linux-среде можно найти в [Program Library HOWTO](https://tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html).
Упаковка библиотек (runtime часть)
- Пакеты библиотек должны размещать файлы так (через уникальные имена и отдельные каталоги), чтобы разные ABI-версии можно было установить в систему одновременно
- Каждая несовместимая версия библиотеки должна быть в отдельном бинарном пакете.
- Название пакета: `libfoo%abiversion`
- Если имя библиотеки заканчивается на цифру, то используется подчёркивание: `libfoo_%abiversion`
- Библиотека с одним и тем же ABI не должна переходить из одного пакета в другой. Исключение допускается только при приведении пакета к требованиям SharedLibsPolicy для новой версии библиотеки (старая должна при этом оставаться в старом пакете со старым именем). Это очень сложный процесс и идеально делать такое изменение при смене soname бибиотеки.
Если есть общие файлы (напр., man-страницы, примеры), их нужно вынести в `libfoo-common`.
- Общий (-common) пакет должен быть только один — он используется всеми ABI-версиями библиотеки.
- -common пакет не должен включать файлы, которые зависят от ABI.
- Старый (Legacy) ABI-специфичный пакет не должен иметь жёсткой версионированной зависимости (Requires: с конкретной версией) от libfoo-common.
Пример: libxmlb
Имя исходного пакета
- Имя **исходного пакета (SRPM)** желательно сохранять таким же, как у апстрима — это упрощает отслеживание обновлений, использование автоматических инструментов (`repology.or`, `repocop`) и повышает предсказуемость.
- Однако, при обновлении библиотеки с ломкой ABI, когда требуется сохранить в репозитории старую версию библиотеки:
* исходный пакет со старой версией нужно **сохранять под отдельным именем** (например, `libfoo1`, `libfoo0`, `libfoo3.2`); * новый ABI собирается уже из SRPM с апстримным именем (`libfoo`), а старый SRPM остаётся в репозитории под переименованным именем.
- Таким образом, в репозитории могут одновременно присутствовать несколько SRPM, каждый из которых собирает свою версию ABI:
- `libfoo` → собирает `libfoo2`, `libfoo-devel`, ... - `libfoo1` → собирает `libfoo1`
- Для бэкпортов:
* имя SRPM должно соответствовать новому ABI; * старые SRPM не модифицируются; * оба SRPM должны собирать бинарные пакеты, которые могут быть установлены параллельно (если это возможно).
- Как только в репозитории **не останется ни одного пакета**, использующего старую версию ABI:
* соответствующий SRPM (и его бинарные пакеты) должен быть **удалён**; * это уменьшает нагрузку на инфраструктуру и снижает риски путаницы; * наличие библиотеки в `System/Legacy libraries` не освобождает от этого требования — она должна быть удалена, если не используется ни одним другим пакетом.
- Для автоматизации отслеживания можно использовать:
* `apt-cache rdepends libfoo1` * скрипты из `repocop` и `srpm-uploader` * веб-интерфейса packages.altlinux.org → вкладка пакета Аналитика → Зависящие пакеты
development-файлы
- Заголовочные файлы и `.so`-файл без версии (используемый для линковки) должны быть вынесены в отдельный `-devel` пакет.
- По умолчанию `-devel` пакет должен быть только **один** — и он должен относиться к самой новой версии библиотеки:
- `libfoo-devel` - или, если есть несколько версий ABI в системе: `libfoo%abiversion-devel`
- Поддержка нескольких `-devel` пакетов (например, `libfoo1-devel` и `libfoo2-devel`) допускается **только в исключительных случаях**, когда:
- большое количество клиентов ещё не может быть пересобрано под новую библиотеку; - либо API существенно отличается, и одновременная поддержка действительно необходима.
- В случае одновременного существования нескольких `-devel` пакетов, они должны иметь явные `Conflicts:` друг с другом, чтобы исключить их совместную установку.
Для `.a` файлов:
- Если вместе с `.so`: `libfoo-devel-static` или `libfoo%abiversion-devel-static`
- Если только `.a`: просто `libfoo-devel`
Файлы, не предназначенные для линковки, а работающие как плагины (`dlopen`), не попадают под полиси. Пример: `libtcnative-1.so` из `tomcat-native`.
Об исключениях стоит сообщать в багзиллу: `altlinux-policy-shared-lib-contains-devel-so`
Пример упаковки разных ABI
Пакет с разными версиями ABI должен содержать только одну из них. Если библиотека имеет несколько .so с разными ABI — каждую версию упаковывают в отдельный пакет.
Пример: ffmpeg
Как выбирать %abiversion
Если библиотека использует `soversion` — используем его как %abiversion.
Если `soversion` отсутствует или нестабилен — используем:
- возрастающее число: `libfoo0`, `libfoo1`
- major-версию: `libqt3`, `libqt4`
- major.minor: `libdb4.0`, `libdb4.1`
- часть soname: `libcurl4`, `libflac8`
Что делать при ломке ABI
Допустим, soname изменился с N на M:
- Переименовать бинарный пакет `libfoo` → `libfooM` (или `libfooN` → `libfooM`)
- Проверить совместимость при установке:
hsh-install libfoo libfooM
Поддержка клиентов библиотеки
Рекомендуется иметь только один `-devel` пакет — для самой новой версии.
Если новая версия несовместима и вызывает ошибки сборки у многих пакетов — допустимо иметь 2 `-devel` пакета с `Conflicts` друг на друга. Зависимые пакеты разделяются по версии сборки.
Для `libfooM-devel` желательно прописывать:
Provides: libfoo-devel = %EVR
Старые версии библиотек
- Старые библиотеки помещаются в группу `System/Legacy libraries`.
- Они удаляются, если не используются другими пакетами.
- Такие пакеты не проходят в стабильные ветки.
Возможные проблемы
- Если разные пакеты с ABI-версиями библиотек устанавливают файлы в один и тот же путь, они будут мешать друг другу (конфликтовать при обновлении). В такой ситуации нужно:
- Удалить старую версию;
- Исправить новую версию так, чтобы она соответствовала SharedLibsPolicy;
- Пересобрать все пакеты, которые зависят от этой библиотеки.
- Проблемы apt при переименовании пакетов: пример
- В одной программе могут оказаться разные версии одной библиотеки через цепочку зависимостей (libX → libZ1, libY → libZ2) — возможны трудные для отладки ошибки.
Ссылки
- Комментарий к Shared Libs Policy с пошаговой инструкцией
- https://www.debian.org/doc/debian-policy/ch-sharedlibs.html
- Смена API/ABI
- Данное полиси и обновления
- Shared Library Symbol Versioning HOWTO
- Пример проблемы, возникающей при некорректной упаковке разделяемой библиотеки
- Список зафиксированных ошибок, приводящих к проблема с обновлениями вследствии нарушения SharedLibsPolicy