Подробнее рассмотрим получившуюся библиотеку для обсуждения версионирования:
@user
[user@VM inc-pkg]$ ls -la .libs/*.so*
lrwxrwxrwx 1 user user 15 авг 4 16:08 .libs/libinc.so -> libinc.so.0.0.0
lrwxrwxrwx 1 user user 15 авг 4 16:08 .libs/libinc.so.0 -> libinc.so.0.0.0
-rwxr-xr-x 1 user user 15416 авг 4 16:08 .libs/libinc.so.0.0.0
[user@VM inc-pkg]$
При работе с библиотеками (и, в целом, любыми объектами, требующими явный параметр для отслеживания изменений и совместимости) принято использовать специальные правила
версионирования.
Во-первых, это формат хранения библиотек в виде одного файла с сопутствующими символьными ссылками. Бесчисловая версия используется для создания зависимостей на библиотеку при сборке (если не требуется строгая зависимость на конкретную версию), мажорная («основная», с одним числом) используется для указания на основную поддерживаемую версию, а последняя (единственная реальная библиотека) является самим рабочим объектом.
Во-вторых, это параметры, по которым ведётся отслеживание изменений, и правила версионирования. В случае с библиотеками версионирование описывается через изменение ABI —
Application Binary Interface. Если
API описывает, с какими параметрами следует вызывать функции библиотеки в
исходных текстах программ, и какие значения они возвращают, то ABI отражает, во что именно эта договорённость превращается
после компиляции. Таким образом, очень важно, чтобы исполняемый файл и разделяемая библиотека были
совместимы по ABI: если такая совместимость есть, пускай даже при сборке программы использовалась библиотека слегка другой версии, программа будет работать штатно. Если совместимости нет, то
лучшее, что может сделать библиотека — это немедленно завершить программу с ошибкой.
Повсеместно используемые большие библиотеки, например,
GLibc, могут быть совместимы сразу с несколькими ABI, для более простых случаев
libtool предлагает схему из трёх счётчиков —
current:revision:age:
current увеличивается при каждом изменении ABI — это т. н. «номер интерфейса»;
revision увеличивается при каждом изменении в библиотеке, которое не отразилось на ABI (исправление ошибок, изменение функциональности и т. п.); если current увеличилось, revision обнуляется;
age изменяется вместе с current; он увеличивается при каждом обратно совместимом изменении (например, при добавлении новой функции или при изменении, не затрагивающем ABI), а в противном случае — обнуляется.
Таким образом, libtool versioning решает сразу три задачи:
При любом изменении версия увеличивается;
При изменении ABI версия увеличивается настолько, что становится больше любых версий, в которых ABI не поменялось;
По версии можно узнать диапазон предоставляемых «номеров интерфейса», с которыми гарантирована обратная совместимость — это диапазон от current до current — age.
При более простом
семантическом версионировании описание ведётся значениями
MAJOR.MINOR.PATCH и меняется по следующим правилам:
обратно несовместимые изменения ABI (удаление / изменение) — MAJOR++.MINOR=0.PATCH=0;
обратно совместимые изменения ABI (добавление) — MAJOR.MINOR++.PATCH=0;
обратно совместимые исправления ABI и изменения, не затрагивающие ABI — MAJOR.MINOR.PATCH++.