Product SiteDocumentation Site

Глава 16. Установка проекта

16.1. Варианты установки
16.2. Управление установкой с помощью Autotools
16.3. Собственный репозиторий
16.4. Репозиторий и индексами
Итоговый продукт любой разработки почти всегда представляет из себя целую систему связанных элементов, и не всегда удобно или возможно создавать на его основе пакет. Для распространения и использования итогового продукта необходимо предусмотреть возможность «работы из коробки» исполняемых файлов и сопутствующих данных (в частности, доступ к документации). Несомненно, существует вариант распространения «чистых» исходников (через публичные репозитории, например), однако такой вариант требует от пользователя знаний и умений для донастройки (если не перенастройки) всех связей при ручной сборке и установке. Кроме этого могут возникнуть проблемы, связанные с недостатком библиотек или необходимых для сборки утилит.

16.1. Варианты установки

Прежде всего заметим, что наиболее надёжный вариант установки ПО в систему —  это пакет, собранный в эту систему сообразно дисциплине сообщества. Использование Hasher и Gear делает результат RPM-пакета ещё более совместимым. Главный недостаток пакета, созданного по всем правилам — его необходимо сопровождать, то есть пересматривать работоспособность с каждым обновлением, затрагивающим его зависимости, пересобирать и исправлять в случае чего и т. д.
Какими неприятностями грозят «классические» варианты установки свежесобранного ПО по алгоритму «крибле! крабле! бумс!», то есть ./configure, make, make install?
Установка в соответствии со стандартом иерархии файлов в файловой системе (Filesystem_Hierarchy_Standard), которую мы использовали для сборки пакета, предполагает, что исполняемые файлы устанавливаются в системный каталог /usr/bin, библиотеки — в /usr/lib64, документация — в /usr/share/doc и т. д. Установка в системные директории согласно FHS гарантирует работу устанавливаемого ПО, поскольку работа со всеми системными утилитами настроена под такое распределение файлов. С точки зрения разработчика у такого способа есть важный недостаток: размещение файлов в системных каталогах — это вмешательство в архитектуру ОС. Оно требует прав суперпользователя (даже если задача — просто потестировать очередной вариант сборки) и возлагает на программиста несвойственную ему ответственность за возможные файловые конфликты. Например, назвал программист свою программу «kitten» (котёнок) — что может пойти не так? А нет, в системе есть пакет с файлом /usr/bin/kitten, который заменился при установке.
Для избежания конфликтов с системой появился метод установки в директории /usr/local/. В традиционных Linux-дистрибутивах дерево каталогов в /usr/local не используется, но при этом каталоги /usr/local/bin, /usr/local/lib64, /usr/local/share/man и т. п. считаются стандартными местами для размещения файлов соответствующего типа. Устанавливаемое туда ПО не конфликтует с базовой системой, и именно этот способ включится по умолчанию в GNU Autotools, если отдельно не задать префикс установки. В FHS каталог /usr/local предназначен для т. н. «локальной установки» ПО, что частично совпадает с нашей целью, а частично — нет. Для установки в /usr/local нужно иметь права суперпользователя, и продолжать следить за файловыми конфликтами устанавливаемых проектов между собой.
Гарантированно не вызывающий конфликтов способ установки — использование для каждого приложения уникального расположения в файловой системе. Такой способ возможен при установке в уникальную директорию /opt/<AppName>/. Для установки всё ещё требуются права суперпользователя, хотя это уже не обязательно, достаточно программисту выдать отдельный доступный подкаталог. Данный способ позволяет избежать файловых конфликтов. Основной недостаток такого способа — необходимость явного указания в $PATH /bin-директории с устанавливаемым приложением для его запуска. Что ещё важнее — подгружать динамические библиотеки из /opt/<AppName>/lib необходимо вручную — как минимум, дополнять путь загрузки с помощью LD_LIBRARY_PATH. Следовательно, исполняемый файл в такой установке должен сопровождаться shell-сценарием с соответствующей настойкой. Кроме того, согласно дисциплине ALT, динамическое изменение параметров запуска приложений, включая LD_LIBRARY_PATH, D_PRELOAD, не заработает для SUID/SGID программ из соображений безопасности. Такая схема установки часто используется в сложных проектах, в состав которых входит большое количество собственных библиотек (в том числе совпадающих по именам с системными, но модифицированных).
Время от времени разработчикам таких проектов приходит в голову, например, добавить /opt/<AppName>/lib в настройки динамического компоновщика  — практика показывает, что это опасная идея: не ровен час, сторонняя библиотека заместит системную…
Для установки без прав суперпользователя располагать файлы можно в подкаталогах $HOME/.local/. Такие установки являются локальными для конкретного пользователя, что может приводить к установке в одну систему нескольких копий одних и тех же приложений. Также установка в локальное пространство пользователя требует полной настройки зависимостей на другие локальные установки. Такой способ полезен для систем, ориентированных на разработчика, поскольку при разработке часто требуется отдельное (а иногда и не одно) независимое пространство установки, и для него требуется ещё более замысловатая настройка путей для загрузки динамических библиотек (в которых появляется непостоянный компонент $HOME), наподобие той, что реализована в GNU Libtool.
Все схемы установки в специализированные директории файловой системы связаны с необходимостью дополнительно настраивать динамическую компоновку с библиотеками, входящими в состав проекта. Проблему можно решить полностью статической сборкой и компоновкой. В результате получается статический же исполняемый файл. Статические прогарммы не требуют для запуска никаких библиотек, даже GLibC — только поддержку ядром загружаемого формата ELF. Такие программы можно просто скопировать в произвольную Linux-систему, и они там заработают. Например, тремя статически собранными пакетами — ash-static, cpio-static и find-static — пользуется hasher для того, чтобы развернуть изолированное сборочное окружение в chroot-каталоге, в котором изначально нет вообще ничего.
@user
[user@VM ~] rpmquery -f /usr/bin/cpio /usr/bin/cpio.static
cpio-2.15-alt1.x86_64
cpio-static-2.15-alt1.x86_64
[user@VM ~] file /usr/bin/cpio /usr/bin/cpio.static
/usr/bin/cpio:        ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=d6dff4c6e0de24cd3c1334b152efcc5ebf89694a, for GNU/Linux 3.2.0, stripped
/usr/bin/cpio.static: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, BuildID[sha1]=e047490a73892cb9e762b0e409c73ea774c51ce9, for GNU/Linux 3.2.0, stripped
[user@VM ~] ldd /usr/bin/cpio /usr/bin/cpio.static
/usr/bin/cpio:
        linux-vdso.so.1 (0x00007f8786626000)
        libc.so.6 => /lib64/libc.so.6 (0x00007f87863de000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f8786628000)
/usr/bin/cpio.static:
        not a dynamic executable

Если программа разрабатывается для запуска в каком-то предсказуемом окружении, можно понадеяться на то, что в системе будут доступны стандартные библиотеки с определённым диапазоном версий. Это позволит не «таскать с собой» как минимум тот же libc, а при удачном стечении обстоятельств —  отказаться и от базовых прикладных инструментариев, таких как Qt или GTK. При этом часть библиотек всё-таки собирается, для удобства — статически. Как правило это библиотеки собственной разработки и сторонние библиотеки, требующие модификации в составе проекта, и какая-нибудь сугубая редкость, надеяться на наличие которой в системе не имеет смысла. Напомним, что разговор идёт возможности установки «по месту», а не о пакете, в котором такая «редкость» просто ставится в зависимость. В последнее время стало популярно скачивать собирать из исходных текстов в составе проекта практически все сторонние компоненты. Такой подход называется «вендоринг», и он принят как основной в экосистемах языков Go и Rust.
Программа с завендоренными сторонними компонентами получается довольно большой, версии этих компонент отличаются от сборки к сборке, а получившийся исполняемый файл компонуется динамически, но зависит от минимума системных библиотек (часто —  от одной только libc).