Shared Libs Policy

Материал из ALT Linux Wiki
Stub.png
Черновик политики Sisyphus
Автор(ы) — PavlovKonstantin, AntonFarygin, IgorVlasenko, SergeyTurchin, DmitryLevin
Нарушения политики отслеживаются: repocop тесты library-pkgnames, lib-contains-devel-so


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:

  1. Переименовать бинарный пакет `libfoo` → `libfooM` (или `libfooN` → `libfooM`)
  2. Проверить совместимость при установке:
 hsh-install libfoo libfooM

Поддержка клиентов библиотеки

Рекомендуется иметь только один `-devel` пакет — для самой новой версии.

Если новая версия несовместима и вызывает ошибки сборки у многих пакетов — допустимо иметь 2 `-devel` пакета с `Conflicts` друг на друга. Зависимые пакеты разделяются по версии сборки.

Для `libfooM-devel` желательно прописывать:

 Provides: libfoo-devel = %EVR

Старые версии библиотек

  • Старые библиотеки помещаются в группу `System/Legacy libraries`.
  • Они удаляются, если не используются другими пакетами.
  • Такие пакеты не проходят в стабильные ветки.

Возможные проблемы

  • Если разные пакеты с ABI-версиями библиотек устанавливают файлы в один и тот же путь, они будут мешать друг другу (конфликтовать при обновлении). В такой ситуации нужно:
  1. Удалить старую версию;
  2. Исправить новую версию так, чтобы она соответствовала SharedLibsPolicy;
  3. Пересобрать все пакеты, которые зависят от этой библиотеки.
  • Проблемы apt при переименовании пакетов: пример
  • В одной программе могут оказаться разные версии одной библиотеки через цепочку зависимостей (libX → libZ1, libY → libZ2) — возможны трудные для отладки ошибки.

Ссылки