На просторах интернета уже имеются всевозможные туториалы, описывающие пошагово процесс создания deb-пакета. Однако у меня возникла потребность собрать пакет из проекта с разделяемыми библиотеками, и я решил поделиться тут своим опытом

В качестве тренировки возьмем простенький проект на Си, в котором производится сборка приложения и двух разделяемых библиотек

Описание проекта, для которого будет создаваться deb-пакет

Назовем проект poop. Он будет имитировать авторизацию в каком-нибудь сервисе, а именно проверять наличие паролей для двух пользователей и не совпадают ли они.

Начальная структура проекта

-> /poop
	|-> Makefile
	|-> /src
		|-> input_check.h
		|-> input_check.c
		|-> login.c
		|-> login.h
		|-> input_check.c

Исходники

input_check.h

#ifndef INPUT_CHECK_H
#define INPUT_CHECK_H  

int check_input(int argc);

#endif

input_check.c

#include <stdio.h>  

int check_input(int argc) {
	int ret = argc;
	switch (ret) {
		case 1:
		printf("[%s] The poop app. "
			   "Tap TAB to make app dumb\npoop [random string]\n",
			   __FUNCTION__);
			ret = 0;
			break;
		case 2:
			break;
		default:
			printf("[%s] WTF?!\npoop [random string]\n",
					__FUNCTION__);
			ret = -1;
			break;
	}
	return ret;
}

login.h

#ifndef LOGIN_H
#define LOGIN_H  

int check_login(const char* login_to_check);  

#endif

login.c

#include <stdio.h>
#include <string.h>  

#define TYPE_1 "user_1"
#define TYPE_2 "user_2"

int check_login(const char* login_to_check) {
	int ret = 0;
	if(login_to_check != NULL) {
		if ((strcmp(login_to_check, TYPE_1) == 0) || 
			(strcmp(login_to_check, TYPE_2) == 0)) {
			printf("%s\n", login_to_check);
		} else {
			printf("[%s]: Логин не определен\n", 
					__FUNCTION__);
			ret = -2;
		}
	} else {
		printf("[%s] Пусто!\n", 
				__FUNCTION__);
		ret = -3;
	}
	return ret;
}

main.c

#include <stdio.h>
#include "input_check.h"
#include "login.h"

int main(int argc, char **argv) {
	int ret = check_input(argc);
	if (0 < ret)
		ret = check_login(argv[1]);
	return ret;
}

Makefile

# target to create first shared library
libpoopl0gin.so: src/login.c
	gcc src/login.c -o libpoopl0gin.so -shared -fPIC  

# target to create second shared library
libpoopinputch3ck.so: src/input_check.c
	gcc src/input_check.c -o libpoopinputch3ck.so -shared -fPIC  

main.o: src/main.c
	gcc -c src/main.c  

all: libpoopl0gin.so libpoopinputch3ck.so main.o
	gcc main.o -I/src libpoopl0gin.so libpoopinputch3ck.so -o poop.out  

clean:
	rm -rf *.so
	rm -rf *.o*

Сборка производится командой make all

Подготовка проекта и сборка пакета

Разделим всю работу по созданию пакета на несколько этапов:

0. Обзор зависимостей;
1. Создание манифеста;
2. Перемещение файлов;
3. Создание скрипта установки;
4. Сборка и проверка пакета.

Прежде чем приступить установим некоторые необходимые пакеты:

sudo apt install dpkg-dev devscripts equivs

А вот теперь уже приступим

Когда понял, что предстоит читать этот лонгрид
Когда понял, что предстоит читать этот лонгрид

0. Обзор зависимостей

Наши собранные библиотеки имеют собственные зависимости, поэтому их нужно посмотреть. Делается это командой objdump:

~/poop$ objdump -p ./libpoopl0gin.so | grep NEEDED
  NEEDED               libc.so.6
~/poop$ objdump -p ./libpoopinputch3ck.so | grep NEEDED
  NEEDED               libc.so.6

# В нашем случае программе необходима только libc. 
# Смотрим, в каком пакете она находится:
~/poop$ dpkg -S libc.so.6
libc6:amd64: /lib/x86_64-linux-gnu/libc.so.6

1. Создание манифеста

Создаем папку:

~/poop$ mkdir -p package/DEBIAN

Затем файл манифеста

~/poop$ vi package/DEBIAN/control

И наполняем следующим содержимым:

Package: poop
Version: 1.0
Section: uncrown
Priority: optional
Depends: libc6
Architecture: amd64
Essential: no
Installed-Size: 16 # значение из результата команды du -k ./poop.out
Maintainer: doob.poop <peep@doob.poop>
Description: Check random login from random user

Это минимальный набор параметров в файле манифеста. Вот их значение:

  • Package - имя пакета;

  • Version - версия программы в пакете, будет использована при обновлении пакета;

  • Section - категория пакета, позволяет определить зачем он нужен;

  • Priority - важность пакета, для новых пакетов, которые ни с чем не конфликтуют обычно прописывают optional, кроме того доступны значения required, important или standard;

  • Depends - от каких пакетов зависит ваш пакет, он не может быть установлен, пока не установлены эти пакеты;

  • Recommends - необязательные пакеты, но тем не менее они обычно устанавливаются по умолчанию в apt;

  • Conflicts - пакет не будет установлен, пока в системе присутствуют перечисленные здесь пакеты;

  • Architecture - архитектура системы, в которой можно установить этот пакет, доступные значения: i386, amd64, all, последнее означает, что архитектура не имеет значения;

  • Installed-Size - общий размер программы после установки;

  • Maintainer - указывает кто собрал этот пакет и кто отвечает за его поддержку;

  • Description - краткое описание пакета.

2. Перемещение файлов

Создаем папку usr/bin и поместить туда исполняемый файл программы:

~/poop$ mkdir -p package/usr/bin
~/poop$ cp ./poop.out package/usr/bin

Создаем папку usr/lib для наших разделяемых библиотек

~/poop$ mkdir -p package/usr/lib
~/poop$ cp ./libpoop* package/usr/lib

3. Создание скрипта установки

Несмотря на то, что система установки пакетов очень мощная и позволяет делать многое, некоторые вещи всё же сделать нельзя. Для решения этой проблемы была предусмотрена возможность выполнять скрипты перед установкой пакета и после. Аналогично это работает для удаления пакета - перед и после. Эти скрипты называются preinst, postinst, prerm и postrm. Каждый файл просто содержит набор скриптов, которые надо выполнить. Например:

~/poop$ vi package/DEBIAN/postinst

#!/bin/bash
echo "Hello from poop installed"

Разработчики Debian не рекомендуют использовать эти скрипты без крайней надобности, поскольку они дают вам полный контроль над системой пользователя и вы можете случайно что-то повредить. Обычно эти скрипты используются для того чтобы задавать пользователям вопросы и на основе этого генерировать конфигурационные файлы.

4. Сборка и проверка пакета

Осталось собрать настроенный пакет:

~/poop$ dpkg-deb --build ./package

После завершения сборки устанавливаем его с помощью apt:

~/poop$ sudo apt install ~/package.deb
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
Note, selecting 'poop' instead of './package.deb'
The following NEW packages will be installed:
  poop
0 upgraded, 1 newly installed, 0 to remove and 8 not upgraded.
Need to get 0 B/4 454 B of archives.
After this operation, 16,4 kB of additional disk space will be used.
Get:1 /home/user/Projects/poop/package.deb poop amd64 1.0 [4 454 B]
Selecting previously unselected package poop.
(Reading database ... 281406 files and directories currently installed.)
Preparing to unpack .../user/Projects/poop/package.deb ...
Unpacking poop (1.0) ...
Setting up poop (1.0) ...
Hello from poop installed

Теперь проект можно запустить:

~/poop$ poop.out user
[check_login]: Логин не определен

Конечная структура проекта

-> /poop
	|-> Makefile
	|-> /src
		|-> input_check.h
		|-> input_check.c
		|-> login.c
		|-> login.h
		|-> input_check.c
	|-> /package
		|-> /DEBIAN
		|-> /usr
			|-> /bin
				|-> poop.out
			|-> /lib
				|-> libpoopinputch3ck.so
				|-> libpoopl0gin.so

Как видно из структуры выше, добавилась папка package, в которой находятся все поддиректории и файлы, необходимые для успешной сборки deb-пакета простенького проекта

Для простоты проверки всего вышего сказанного, можно клонировать с Github уже готовый настроенный проект.

Так выглядит deb-пакет по мнению Kandinsky
Так выглядит deb-пакет по мнению Kandinsky

Буду рад ответить на вопросы и предложения.

Полезные ссылки и материалы по теме:

Создание deb пакетов

Описание процесса сборки пакета deb

Комментарии (15)


  1. classx
    17.01.2024 08:26
    +1

    я понимаю что это простой пакет, однако и тут есть 2 замечания

    1. обычно после установки so файлов необходимо изменить LD_LIBRARY_PATH если только установка не производится в стандартные пути

    2. как происходит upgrade/downgrade пакета?


    1. Vanovsky714 Автор
      17.01.2024 08:26

      1. Установка происходит в стандартные пути

      2. В этом случае никак, потому что нет версионности


  1. Abobcum
    17.01.2024 08:26
    -1

    Как добавить и удалить автозагрузку (unit сервис) для бинарников из пакета?


    1. buldo
      17.01.2024 08:26
      +1

      Ни кто не запрещает добавить `systemctl enable` в postinst


      1. andreymal
        17.01.2024 08:26
        +1

        А можно я запрещу, пожалуйста? Меня жутко бесит, что службы в дебианах и убунтах запускаются сразу после установки, не давая мне шанса на перенастройку под мои нужды перед запуском


        1. buldo
          17.01.2024 08:26
          +2

          Все вопросы к создателям пакетов, которые вас бесят


    1. classx
      17.01.2024 08:26

      нужно положить файлик с описанием сервиса в нужное место и добавить триггер в пакет


    1. Vanovsky714 Автор
      17.01.2024 08:26

      В этой статье показан самый минимальный набор действий, которые нужно сделать для сборки пакетов. Ваш вопрос скорее относится к деталям, заостряться на которых не было изначальной целью публикации


  1. LeshiyUrban
    17.01.2024 08:26
    +4

    Хорошая статья для обучения.
    Еще есть http://nfpm.goreleaser.com/ где можно чуть упростить процесс


    1. me21
      17.01.2024 08:26
      +3

      Спасибо, как раз хотел fpm упомянуть, а тут такое)

      Ещё добавлю, что если проект собирается с помощью CMake, то оно само умеет собирать установочный пакет при условии правильного CMakeLists.txt.


  1. NickyX3
    17.01.2024 08:26
    +1

    Для таких простых пакетов с make install можно и checkinstall юзать, который все сделает сам


    1. DungeonLords
      17.01.2024 08:26

      checkinstall разве не устарел морально? Он обновлялся последний раз лет 8 назад.


      1. NickyX3
        17.01.2024 08:26
        +1

        Ну а че ему будет? С – Стабильность! На самом деле для простых вещей вполне себе работает. Для сложных пакетов конечно лучше дебианизаию сделать и собирать нормально, для десятка файлов это лютый оверхед


  1. DungeonLords
    17.01.2024 08:26

    Я пытаюсь сделать пакет с помощью CPack Объясните люди добрые, как не переходить при этом в подкаталог build/?

    Мой base_folder/ содержит src/ и build/ для временных build files and build artifacts.
    Далее я вызываю
    cpack -B build/
    И получаю "CPack Error: CPack generator not specified"
    Но вот если таки перейти в каталог, то есть сделать
    cd build/
    Тогда cpack сработает. Как не переходить в подкаталог, оставаться в base_folder/? Неужели никто не знает


  1. big-mdm
    17.01.2024 08:26
    -2

    И ни слова про файл debian/rules. Ни слова про проверку на валидность пакета с помощью lintian...