Poradnik maintainer'a, czyli jak zrobić pakiet deb

Spis treści

Debian posiada bardzo rozbudowany system robienia pakietów. Generalnie rzecz biorąc, to wszystkie z nich musiały przejść przez ten proces zanim trafiły do głównego repozytorium dystrybucji. Dzięki takiemu stanu rzeczy, nie musimy ręcznie powielać pracy szeregu innych osób i odpada nam własnoręczna kompilacja pakietów, a wszyscy wiemy, że zajmuje ona cenny czas i zasoby. Paczki .deb są tworzone ze źródeł i instalowane przy pomocy menadżera pakietów aptitude/apt/dpkg . Nic jednak nie stoi na przeszkodzie by daną aplikację skompilować sobie ręcznie i zainstalować ją przy pomocy make install . Problem w tym, że w taki sposób robi się śmietnik w naszym systemie i śledzenie wszystkich zainstalowanych w ten sposób pakietów w pewnym momencie stanie się wręcz niemożliwe. Dlatego też przydałby nam się mechanizm, który ułatwiłby nam nieco to zadanie. Debian udostępnia szereg narzędzi, które są w stanie w pełni zautomatyzować cały ten proces budowy pakietów. Ten poradnik zaś ma na celu zebranie wszystkich istotniejszych informacji związanych z obsługą narzędzi takich jak dh_make , dpkg-buildpackage , pbuilder , quilt czy lintian , tak by tworzyć pakiety w prosty sposób i przy tym równając do najwyższych standardów debiana.

Potrzebne pakiety

Przeciętny użytkownik debiana raczej nie będzie w stanie zbudować własnej paczki .deb , przynajmniej nie od razu. Nie musimy też rzucać się na głęboką wodę i możemy zwyczajnie zacząć od prostszej czynności jaką jest aktualizacja konkretnego pakietu, który już jest dostępny w repozytorium debiana (zobacz rozdział 9). Wszystkie paczki .deb wymagają tego by nimi się opiekować, np. dostosowywać zależności, zgłaszać błędy upstream'owi i tego typu rzeczy. Nie jest to specjalnie trudne ale największym wyzwaniem jest "debianizacja źródeł". Ten proces polega na odpowiednim dostosowaniu katalogu debian/ , który zostanie utworzony w głównym folderze ze źródłami aplikacji i nasze zadanie sprowadzać się będzie do odpowiedniego uzupełniania określonych plików w tym katalogu. W przypadku pakietów już dostępnych w dystrybucji, debianizacja źródeł już się odbywała i przeprowadził ją za nas ktoś inny. Dlatego aktualizacja tych pakietów zwykle nie powinna stwarzać problemów, a nawet jeśli już, to jest ich sporo mniej niż w przypadku, gdy byśmy budowali tę paczkę od podstaw.

Poniżej znajduje się lista narzędzi wraz z ich krótkim opisem. To przy ich pomocy będziemy odpowiednio dostosowywać katalog debian/. Spora część z nich jest w pełni zautomatyzowana i od nas wymagać będzie się jedynie ustawienia określonych przełączników przy wydawaniu konkretnych poleceń.

  • packaging-dev -- meta pakiet, który zainstaluje min:
    • build-essential -- bez tego nie ma nawet co podchodzić do budowania pakietów.
    • debhelper -- szereg programów zajmujących się obrabianiem pliku debian/rules .
    • dh-make -- debianizuje źródła (tworzy katalog debian/).
    • devscripts -- ten pakiet zawiera szereg skryptów ułatwiających zarządzanie plikami w katalogu debian/ , więcej info o tym jakie skrypty ten pakiet zawiera (oraz ich zależności) można odczytać z opisu samego pakietu, np. w aptitude .
    • lintian -- weryfikuje poprawność zbudowanych pakietów.
    • pbuilder -- buduje paczki .deb .
    • quilt -- tworzy łaty (i zarządza nimi) w oparciu o zmiany dokonywane na źródłach.
  • gnupg -- obsługa kluczy GPG, wymagane do podpisywania plików .dsc i .changes .
  • desktop-file-utils -- weryfikuje składnię plików .desktop .
  • ccache -- przyśpiesza znacznie rekompilację pakietów.
  • dh-* -- różne moduły dla debhelper'a, potrzebne podczas budowania pewnych pakietów, np. te posiadające pliki .service dla systemd wymagać będą dh-systemd , etc.
  • gmanedit -- ułatwia tworzenie manuali.
  • help2man -- generuje manualne dla plików wykonywalnych w oparciu o parametr --help .
  • piuparts -- testuje instalację, usuwanie i aktualizację zbudowanych pakietów.
  • apt-file -- przeszukuje pakiety w oparciu o wzorzec nazwy pliku.
  • patchutils, wdiff -- dodatkowe narzędzia do pracy z łatami.
  • blhc -- testuje log z budowania pakietu pod kątem braku określonych flag.
  • hardening-includes -- zawiera narzędzie hardening-check pomagające ustalić poziom bezpieczeństwa plików wykonywalnych.
  • python-minimal -- zawiera narzędzie pyversions pomagające ustalić wersję python'a.
  • binutils, strace -- binutils zawiera narzędzie dpkg-depcheck , które przy pomocy strace może pomóc w ustalaniu zależności.
  • fakeroot -- symuluje uprawnienia super użytkownika.
  • sudo -- przekazuje część uprawnień administratora root zwykłemu użytkownikowi.

Wato też nadmienić, że skoro mamy do czynienia z budową pakietów, to przydałby się gdzieś te pakiety umieszczać. Jest raczej mało prawdopodobne, że dostaną się do głównego repozytorium debiana, dlatego też dobrze jest się pierw pokusić o stworzenie własnego repozytorium przy pomocy reprepro i to w nim umieszczać pakiety, które będziemy instalować przy pomocy aptitude czy też apt .

Jak widać jest tego dość sporo, no i oczywiście będzie tego więcej, gdy każdy z powyższych pakietów dociągnie swoje zależności. Poza tymi powyższymi, dojdą nam jeszcze pewnie inne pakiety, w zależności od tego co tak naprawdę będziemy budować.

Dla ułatwienia, poniżej jest linijka instalująca wszystkie powyższe narzędzia:

# aptitude install packaging-dev gnupg desktop-file-utils ccache \
gmanedit help2man piuparts apt-file patchutils wdiff blhc hardening-includes \
python-minimal binutils strace fakeroot

Powyższe pakiety (za wyjątkiem pewnych dodatków) są jedynymi pakietami, które będziemy instalować w swoim głównym systemie. Wszelkie zależności budowanych pakietów będą już pakowane do środowiska chroot, które zostanie utworzone automatycznie przez pbuilder . Takie rozwiązanie pomoże nam utrzymać porządek w systemie.

Upychanie plików w paczkach ma na celu jedynie ich organizację i niczym zbytnio nie różni się od zwykłego przekopiowania ich do odpowiednich katalogów (czy zainstalowania via make install ), no może poza faktem, że w przypadku paczki, wszystkie pliki są śledzone przez menadżer pakietów. Chodzi generalnie o to, że w przypadku, gdyby się pojawiła nowsza wersja jakiegoś programu, lub zwyczajnie chcielibyśmy się pozbyć go z systemu, to może być to dość problematyczne. W przypadku jednego czy dwóch programów, które nie mają za wiele plików, usunięcie ich raczej nie powinno sprawić problemu, a co w przypadku bardziej rozbudowanych projektów? Część z nich potrafi się pomyślnie odinstalować sama jeśli skrypt makefile obsługuje taki ficzer ale nawet w takim przypadku trzeba, po pierwsze, trzymać źródła, a po drugie, musimy zainstalować wszystkie zależności potrzebne do zbudowania takiego pakietu w swoim systemie. Natomiast w przypadku budowania paczek, te problemy nas nie dotyczą.

Konfiguracja narzędzi

Zanim jednak przejdziemy do omawiania poszczególnych plików, które mogą (ale nie muszą) znajdować się w katalogu debian/ , musimy poświęcić chwilę czasu na dostosowanie konfiguracji tych wszystkich powyższych narzędzi.

dh-make

Standardowo przy wywoływaniu polecenia dh_make podczas debianizowania źródeł, musimy podawać kilka dodatkowych parametrów, tak by określić kto buduje paczkę. Zamiast tego, możemy wyeksportować odpowiednie zmienne i uprościć nieco cały ten proces. W tym celu dodajemy poniższe linijki do pliku konfiguracyjnego shella, .bashrc:

DEBEMAIL="morfik@nsa.com"
DEBFULLNAME="Mikhail Morfikov"
export DEBEMAIL DEBFULLNAME

W oparciu o te zmienne, szereg narzędzi będzie uzupełniać odpowiednie pola, przykładowo plik changelog .

gnupg

Jeśli planujemy robić z paczkami coś większego, np. przesyłać je do repozytorium debiana, to będziemy potrzebować kluczy GPG, by taką paczkę podpisać. Nie będę tutaj opisywał całego procesu generowania i konfigurowania kluczy GPG, bo zostało to już zrobione w odpowiednich wpisach na tym blogu. Zachęcam zatem do zapoznania się z tymi artykułami: Bezpieczny klucz GPG, Konfiguracja GPG w pliku gpg.conf oraz Serwer kluczy GPG i kwestia prywatności.

devscripts

Jak wspomniałem na początku, w tym pakiecie znajduje się szereg użytecznych skryptów, które będziemy wykorzystywać w swojej pracy. Jednym z częściej używanych narzędzi będzie debsign , które to będzie nam podpisywało pliki .changes i .dsc zawierające między innymi sumy kontrolne. I tu, podobnie jak w przypadku narzędzia dh-make , możemy określić kila parametrów, tak by nie musieć ich wpisywać za każdym razem, gdy chcemy podpisać jakąś paczkę.

Konfiguracja pakietu devscripts jest trzymana w pliku /etc/devscripts.conf . Dobrze jest przejrzeć ten plik, choć objętościowo może nieco przytłoczyć. W każdym razie interesujące nas opcje to:

..
DEBSIGN_PROGRAM=gpg
...
DEBSIGN_SIGNLIKE=gpg
...
DEBSIGN_KEYID=B820057A
...

Możemy także zweryfikować wprowadzone dane wydając poniższe polecenie:

$ debsign --help
...
Default settings modified by devscripts configuration files:
  DEBSIGN_PROGRAM=gpg
  DEBSIGN_KEYID=B820057A

lintian

Konfiguracja lintian'a trzymana jest w pliku /etc/lintianrc i w dużej mierze odpowiada za to jaki rodzaj błędów będzie nam pokazywany. Poniżej mój plik:

# /etc/lintianrc -- Lintian configuration file
#
# Note, that Lintian has reasonable default values for all variables
# specified below. Thus, you don't have to change this file unless you
# want something special.
#
# Also note, that this file uses a special syntax:
# Empty lines are allowed, comments are introduced by a hash sign (#).
# All other lines must have the format
#    VAR=text
# or
#    VAR="text"
# It is allowed to use '~' and '$HOME' in the variables, but not other
# shell/environment variables.

# Enable info tags by default (--display info)
display-info = yes

# Enable pedantic tags by default (--pedantic)
pedantic = yes

# Enable experimental tags by default (--display-experimental)
display-experimental = no

# Enable colored output for terminal output (--color)
color = always

# Show overridden tags (--show-overrides)
#show-overrides = yes

# Ignore all overrides (--no-override)
#override = no

# Verbose output by default (--verbose)
verbose = no

info = no

# Quiet by default (--quiet)
#quiet = yes

# Specify a laboratory--a directory where Lintian should store some info
# about packages being checked.
LINTIAN_LAB="/media/Kabi/pbuilder/lintian"

# Use a different directory for temporary files - useful if /tmp is a
# tmpfs with "limited" capacity.
#TMPDIR="/var/tmp"

Jeśli jesteśmy początkującymi amatorami, możemy się nieco pogubić w tym co będzie próbował nam powiedzieć lintian . Dobrze jest przestawić z początku opcję verbose oraz info na yes . Spowoduje to wyświetlenie dość obszernej informacji na temat każdego z błędu. Zawsze można wpisać kod błędu w google i pierwszy wynik odeśle nas na stronę lintian'a i tam na pewno znajdziemy wyjaśnienie.

apt-file

To narzędzie nie jest bezpośrednio powiązane z budowaniem paczek, niemniej jednak bardzo się przydaje, a to z tego względu, że często będziemy postawieni w sytuacji, gdzie pakiet nie będzie mógł się poprawnie zbudować i to przez brak jakiegoś pliku. Przy pomocy apt-file będziemy w stanie ten plik namierzyć. Nie będę tutaj opisywał tego jak korzystać z tego narzędzia, bo to zostało dokładnie wyjaśnione we wpisie dotyczącym apt-file. Zachęcam zatem do zapoznania się również i z tym tekstem.

pbuilder

Pbuilder to narzędzie, które zrobi praktycznie cała robotę za nas, przynajmniej jeśli chodzi o zbudowanie pakietu .deb . Trzeba mu tylko skonfigurować szereg parametrów. Przykładowy plik konfiguracyjny dla pbuilder'a znajduje się w /usr/share/doc/pbuilder/examples/pbuilderrc . Trzeba go skopiować do katalogu /etc/ i odpowiednio przerobić. Można także zrobić sobie lokalną wersję tego pliku i umieścić go w katalogu domowym pod nazwą .pbuilderrc . Poniżej zaś znajduje się mój aktualny plik konfiguracyjny:

# pbuilder defaults; edit /etc/pbuilderrc to override these and see
# pbuilderrc.5 for documentation

BASETGZ=/media/Kabi/pbuilder/base.tgz
EXTRAPACKAGES="apt-utils debconf-utils ccache eatmydata libfile-fcntllock-perl"
#export DEBIAN_BUILDARCH=athlon
BUILDPLACE=/media/Kabi/pbuilder/build/
MIRRORSITE=http://ftp.de.debian.org/debian/
#OTHERMIRROR="deb http://www.home.com/updates/ ./"
#export http_proxy=http://your-proxy:8080/
USEPROC=yes
USEDEVPTS=yes
USENETWORK=no
USERUNSHM=yes
USEDEVFS=no
BUILDRESULT=/media/Kabi/pbuilder/result/

# specifying the distribution forces the distribution on "pbuilder update"
DISTRIBUTION=sid
# specifying the architecture passes --arch= to debootstrap; the default is
# to use the architecture of the host
ARCHITECTURE='dpkg --print-architecture'
# specifying the components of the distribution, for instance to enable all
# components on Debian use "main contrib non-free" and on Ubuntu "main
# restricted universe multiverse"
COMPONENTS="main"
#specify the cache for APT
APTCACHE="/media/Kabi/pbuilder/pbuilder_apt_cache/"
APTCACHEHARDLINK="no"
REMOVEPACKAGES="no"
#HOOKDIR="/usr/lib/pbuilder/hooks"
HOOKDIR="/media/Kabi/pbuilder/hooks"
# NB: this var is private to pbuilder; ccache uses "CCACHE_DIR" instead
# CCACHEDIR="/var/cache/pbuilder/ccache" ccache -M 20G
CCACHEDIR="/media/Kabi/pbuilder/ccache"
export CCACHE_DIR="/media/Kabi/pbuilder/ccache"

# make debconf not interact with user
export DEBIAN_FRONTEND="noninteractive"

#for pbuilder debuild
BUILDSOURCEROOTCMD="fakeroot"
PBUILDERROOTCMD="sudo -E"
# use cowbuilder for pdebuild
#PDEBUILD_PBUILDER="cowbuilder"

# additional build results to copy out of the package build area
#ADDITIONAL_BUILDRESULTS=(xunit.xml .coverage)

# command to satisfy build-dependencies; the default is an internal shell
# implementation which is relatively slow; there are two alternate
# implementations, the "experimental" implementation,
# "pbuilder-satisfydepends-experimental", which might be useful to pull
# packages from experimental or from repositories with a low APT Pin Priority,
# and the "aptitude" implementation, which will resolve build-dependencies and
# build-conflicts with aptitude which helps dealing with complex cases but does
# not support unsigned APT repositories
#PBUILDERSATISFYDEPENDSCMD="/usr/lib/pbuilder/pbuilder-satisfydepends"
PBUILDERSATISFYDEPENDSCMD="/usr/lib/pbuilder/pbuilder-satisfydepends-experimental"

# Arguments for $PBUILDERSATISFYDEPENDSCMD.
# PBUILDERSATISFYDEPENDSOPT=()

# You can optionally make pbuilder accept untrusted repositories by setting
# this option to yes, but this may allow remote attackers to compromise the
# system. Better set a valid key for the signed (local) repository with
# $APTKEYRINGS (see below).
ALLOWUNTRUSTED=no

# Option to pass to apt-get always.
export APTGETOPT=()
# Option to pass to aptitude always.
export APTITUDEOPT=()

#Command-line option passed on to dpkg-buildpackage.
#DEBBUILDOPTS="-IXXX -iXXX"
DEBBUILDOPTS="-j1 -pgpg -kB820057A -sa"

#APT configuration files directory
APTCONFDIR="/media/Kabi/pbuilder/pbuilder_apt_conf/"

# the username and ID used by pbuilder, inside chroot. Needs fakeroot, really
BUILDUSERID=1234
BUILDUSERNAME=morfik

# BINDMOUNTS is a space separated list of things to mount
# inside the chroot.
BINDMOUNTS="${CCACHE_DIR}"

# Set the debootstrap variant to 'buildd' type.
DEBOOTSTRAPOPTS=(
    '--variant=buildd'
    '--keyring' '/usr/share/keyrings/debian-archive-keyring.gpg'
    )
# or unset it to make it not a buildd type.
# unset DEBOOTSTRAPOPTS

# Keyrings to use for package verification with apt, not used for debootstrap
# (use DEBOOTSTRAPOPTS). By default the debian-archive-keyring package inside
# the chroot is used.
APTKEYRINGS=()

# Set the PATH I am going to use inside pbuilder: default is "/usr/sbin:/usr/bin:/sbin:/bin"
#export PATH="/usr/sbin:/usr/bin:/sbin:/bin"
export PATH="/usr/lib/ccache:${PATH}"

# SHELL variable is used inside pbuilder by commands like 'su'; and they need sane values
export SHELL=/bin/bash

# The name of debootstrap command, you might want "cdebootstrap".
DEBOOTSTRAP="debootstrap"

# default file extension for pkgname-logfile
PKGNAME_LOGFILE_EXTENSION="_$(dpkg --print-architecture).build"

# default PKGNAME_LOGFILE
PKGNAME_LOGFILE=""

# default AUTOCLEANAPTCACHE
AUTOCLEANAPTCACHE="no"

#default COMPRESSPROG
COMPRESSPROG="gzip"

W EXTRAPACKAGES umieściłem kilka dodatkowych pakietów, które będą instalowane po wypakowaniu środowiska chroot, a to ze względu na szereg ostrzeżeń jakie wyrzucał mi pbuilder przy budowaniu pakietów. Te pozycje tutaj nie są obowiązkowe i raczej nic się paczkom nie stanie z powodu ich braku. Jeśli ktoś nie rozumie konkretnych opcji, to odsyłam do manuala, gdzie wszystkie z nich są przystępnie opisane.

ccache

W przypadku ccache nie musimy zbytnio nic robić, bo wszystko zostało już określone w pliku konfiguracyjnym pbuilder'a. Konkretnie chodzi o te wpisy:

...
EXTRAPACKAGES="... ccache ..."
...
CCACHEDIR="/media/Kabi/pbuilder/ccache"
export CCACHE_DIR="/media/Kabi/pbuilder/ccache"
...

Tylko jest jeden problem. Ten ccache zadziała jedynie przy wywoływaniu polecenia pbuilder --build . W przypadku, gdy napotkamy błąd podczas budowy pakietu, to mamy opcję by sprawdzić co się stało. Wtedy zostaniemy zalogowani wewnątrz środowiska chroot. Jeśli zmienimy pliki konfiguracyjne, to wewnątrz tego chroot'a będziemy w stanie zbudować pakiet via dpkg-buildpackage . Niemniej jednak, nie będziemy mieli dostępu do cache ccache i pakiet się będzie budował tak jakbyśmy z niego w ogóle nie korzystali. Nie mam pojęcia czemu się tak dzieje, być może to bug albo coś nie tak z powyższą konfiguracją, ewentualnie pbuilder tak po prostu już ma.

sudo

Dostęp do narzędzia pbuilder jest standardowo zarezerwowany jedynie dla użytkownika root. Nie jest to zbytnio wygodne i przydałoby się umożliwić korzystanie z niego zwykłemu użytkownikowi. W tym celu edytujemy konfigurację sudo wpisując w terminalu visudo . Dopisujemy tam te poniższe linijki:

Host_Alias HOSTY = localhost,morfikownia

morfik     HOSTY = (root) NOPASSWD: /usr/sbin/pbuilder

Więcej informacji na temat samego sudo jak i pliku konfiguracyjnego można znaleźć tutaj.

Przygotowanie środowiska chroot

Narzędzie puilder musi na czymś operować, inaczej odmówi współpracy. Musimy mu stworzyć podstawowe środowisko pracy. Chodzi o zrobienia minimalnego i do tego spakowanego chroot'a, który będzie używany w każdym procesie budowania pakietów. Takie minimalistyczne środowisko ma na celu zapewnienie, że pakiet będzie się budował poprawnie (chodzi o zależności) na 99% maszyn, pod warunkiem, że się zbuduje bez problemu w tym środowisku chroot. Musimy stworzyć także szereg katalogów, do których ścieżki podaliśmy w pliku konfiguracyjnym pbuilder'a. Dodatkowo musimy także dostosować konfigurację dla apt . Jeśli chodzi zaś o same repozytoria, to raczej poniżej sida nie ma co schodzić. Jeśli jakichś pakietów nie ma w repo testowym czy stabilnym, to albo przez licencję, albo przez niespełnione zależności, które zwykle muszą być w najnowszych wersjach, a te są z reguły w sid i/lub experimental, bo powodują problemy. Tworzymy zatem potrzebne nam katalogi:

$ mkdir /media/Kabi/pbuilder/

$ mkdir /media/Kabi/pbuilder/{hooks,build,result,pbuilder_apt_cache,ccache,pbuilder_apt_conf,lintian}

$ cp /etc/apt/sources.list /media/Kabi/pbuilder/pbuilder_apt_conf/
$ cp /etc/apt/trusted.gpg /media/Kabi/pbuilder/pbuilder_apt_conf/

W pliku sources.list zostawiamy jedynie wpisy od sida:

deb     http://ftp.de.debian.org/debian/ sid main non-free contrib
deb-src http://ftp.de.debian.org/debian/ sid main non-free contrib

Pewnie zdarzy się nam taki przypadek, że pakiet, który chcemy zbudować, ma w zależnościach inny pakiet, który dopiero co zbudowaliśmy i jak z takiej sytuacji wybrnąć? Potrzebne nam będzie własne repozytorium, to, o którym była mowa we wstępie. Wpisy do takiego repozytorium również dodajemy do konfiguracji apt dla pbuilder'a:

deb http://deb.morfikownia.lh/debian/ sid main contrib non-free
deb-src http://deb.morfikownia.lh/debian/ sid main contrib non-free

Wszystkie zewnętrzne repozytoria będą wymagać publicznych kluczy GPG. Trzeba je również dostarczyć pbuilder'owi. Kopiujemy zatem systemowy keyring:

$ cp /etc/apt/trusted.gpg /media/Kabi/pbuilder/pbuilder_apt_conf/

Tworzymy teraz spakowane środowisko chroot. Po wydaniu poniższego polecenia, zostanie zainicjowany debootstrap , który pobierze minimalnego debiana (sid), który to zostanie wstępnie skonfigurowany i upchnięty w paczce .tgz :

morfik:~$ sudo pbuilder create
W: /root/.pbuilderrc does not exist
I: Distribution is sid.
I: Current time: Thu Feb 19 12:36:23 CET 2015
I: pbuilder-time-stamp: 1424345783
I: Building the build environment
I: running debootstrap
/usr/sbin/debootstrap
I: Retrieving Release
I: Retrieving Release.gpg
I: Checking Release signature
I: Valid Release signature (key id A1BD8E9D78F7FE5C3E65D8AF8B48AD6246925553)
I: Retrieving Packages
I: Validating Packages
...
I: creating base tarball [/media/Kabi/pbuilder/base.tgz]
I: cleaning the build env
I: removing directory /media/Kabi/pbuilder/build//46518 and its subdirectories

W ten sposób utworzona paczka będzie wypakowywana za każdym razem, gdy będziemy budować jakiś pakiet przy pomocy sudo pbuilder --build . Niemniej jednak, całe wypakowane środowisko, niezależnie od powodzenia akcji budowania, zostanie na zakończenie usunięte. Jeśli będziemy budować wiele razy i to tylko jeden pakiet, można edytować tego spakowanego chroot'a i doinstalować tam szereg zależności, tak by podczas budowania nie były w kółko pobierane i instalowane. By edytować środowisko, wpisujemy poniższe polecenie:

$ sudo pbuilder --login --save-after-login

Każdy system, nawet ten minimalny chroot zrobiony przy pomocy debootstrap, wymaga by go aktualizować w miarę regularnie, a konkretnie, przed budowaniem pakietu. By zaktualizować takie środowisko, wpisujemy w terminalu to poniższe polecenie:

$ sudo pbuilder --update

Źródła i informacje o nich

W internecie jest wiele rozmaitych miejsc, gdzie można znaleźć źródła pakietu, który chcemy zbudować. Zwykle będzie to strona projektu lub też i jakiś git, np. GitHub. Niemniej jednak, w tych lokalizacjach bardzo rzadko spotkamy się ze źródłami, które pozwolą nam na zbudowanie pakietu tuż po ściągnięciu ich na dysk.

W sporej części przypadków, większość roboty jaką będziemy musieli odwalić, to zbieranie informacji na temat samego projektu. Chodzi generalnie o takie informacje jak licencja, zależności czy autorzy. Czasem możemy pójść nieco na skróty i skorzystać z czyjejś pracy, tak by nieco przyśpieszyć zbieranie tych danych.

Jeśli strona projektu nie jest w najlepszym stanie i brakuje kluczowych dla nas informacji, możemy oczywiście wysłać maila do twórcy i poprosić go o stosowne info ale to zajmuje czas. Możemy także zajrzeć na git debiana i tam spróbować odnaleźć interesujący nas projekt. Jeśli doszukamy się go, możemy być pewni, że część pracy przeprowadził już ktoś za nas.

Innym miejscem jest repozytorium AUR dystrybucji Archlinux. Tam zawsze idzie coś znaleźć, tylko paczki nie są zbytnio kompatybilne z debianem i trzeba wiedzieć gdzie i czego szukać. Przykładowo, załóżmy, że interesuje nas pakiet monitorix :

pakiet-aur-deb

Jak widzimy pakiet został odnaleziony i mamy tam info o licencji i o zależnościach. Niemniej jednak, to nie wszystko co możemy wyciągnąć z AUR. Tam w prawym górnym rogu jest PKGBUILD . Jest to plik, na podstawie którego buduje się pakiet dla Archlinux'a. Jak on nam może pomóc? Wystarczy do niego zajrzeć. Są tam zależności (choć nazwy pakietów mogą być inne) , patch'e (do wydobycia przez link Download tarball ), a nawet wywoływane konkretne polecenia (trzeba uważać na ścieżki). Wszystkie te informacje możemy wykorzystać przy budowaniu paczki .deb . W tym przypadku strona projektu jest w miarę rozsądna i można się doszukać na niej wszystkich informacji.

Katalog debian/

Katalog debian/ zawsze będziemy tworzyć po wypakowaniu źródeł i to w nim będziemy dokonywać większości zmian, dlatego też musimy poznać nieco jego strukturę. Poniżej postaram się opisać wszystkie pliki z tego katalogu, z którymi się spotkałem budując pakiety.

Generalnie rzecz biorąc, w oparciu o dane określone w tych plikach, narzędzie install będzie kopiować odpowiednie pliki/foldery i budować z tego drzewo katalogów, które następnie zostanie przeniesione do pakietu wynikowego. Dlatego też dobrze jest sobie przyswoić poszczególne parametry tego polecenia, bo to ułatwi ewentualne szukanie przyczyn problemów, np z błędnymi uprawnieniami plików.

Tworzenie szkieletu

Zakładając, że nie udało nam się znaleźć zdebianizowanych źródeł, musimy przejść przez proces ich zmiany. Na sam początek pobieramy źródła takie jak widzimy na stronie projektu i wypakowujemy je:

$ mkdir debian_build

$ cd debian_build/

$ wget http://www.monitorix.org/monitorix-3.6.0.tar.gz

$ tar xpf monitorix-3.6.0.tar.gz

$ cd monitorix-3.6.0/

Tworzymy teraz katalog debian/ przy pomocy dh_make :

$ dh_make -s -c gpl2 -f ../monitorix-3.6.0.tar.gz
Maintainer name  : Mikhail Morfikov
Email-Address    : morfik@nsa.com
Date             : Thu, 07 Dec 2015 14:56:01 +0100
Package Name     : monitorix
Version          : 3.6.0
License          : gpl2
Type of Package  : Single
Hit <enter> to confirm:
Done. Please edit the files in the debian/ subdirectory now. You should also
check that the monitorix Makefiles install into $DESTDIR and not in / .

Nazwa maintainera oraz adres email został automatycznie uzupełnione w oparciu o zmienne DEBEMAIL oraz DEBFULLNAME , które podaliśmy wcześniej w pliku ~/.bashrc . W zależności od tego jaki pakiet będziemy budować, możemy skorzystać ze wzorców plików. Inaczej wyglądają one w przypadku bibliotek, a inaczej w przypadku modułów kernela, etc. W tym przypadku budujemy pojedynczą binarkę i dlatego skorzystaliśmy z opcji -s. Opcja -c gpl2 odpowiada za uzupełnienie pliku copyright (o nim później) o odpowiedni tekst licencji. Ostatnia opcja -f odpowiada za podanie pliku z upstream'owymi źródłami. To w oparciu o ten plik będą robione ewentualne patche (o tym też później), bo źródeł upstream'owych jako takich nie wolno nam zmieniać i nie należy mylić ich z plikami, które mamy w wypakowanym katalogu.

Powinniśmy mieć już dostępny katalog debian/ . Z kolei w katalogu nadrzędnym powinna pojawić się dodatkowa paczka: monitorix_3.6.0.orig.tar.gz . Nazwa nowego pliku ze źródłami ma swój zdefiniowany format, który wygląda tak: pakiet_wersja.orig.tar.gz .

Przechodzimy do katalogu debian/ :

$ cd debian/

Mamy w nim szereg plików, które zostały automatycznie wygenerowane ale nie wszystkie z nich zawsze będą nam potrzebne. Dobrze jest też wiedzieć, że w przypadku, gdy skasujemy (naszym zdaniem) niepotrzebne pliki, to możemy nakazać dh_make aby przywrócił ich szablony. By odzyskać skasowane pliki, będziemy musieli wydać to poniższe polecenie:

$ dh_make --addmissing
...
File source/format exists, skipping.
File changelog exists, skipping.
File compat exists, skipping.
...

Nowe pliki powinny być już dostępne w katalogu debian/ i jak widać wyżej, system nie ruszył tych, które już istniały, a jedynie dodał te brakujące.

debian/control

Jednym z ważniejszych plików jest debian/control . To w nim są definiowane min. zależności potrzebne do zbudowania/instalacji pakietu i to w oparciu o te zależności właśnie menadżery pakietów, takie jak apt czy aptitude , będą wiedzieć jak zainstalować konkretny pakiet. W tym przypadku, ten plik wygląda następująco:

Source: monitorix
Section: net
Priority: optional
Maintainer: Mikhail Morfikov <morfik@nsa.com>
Build-Depends: debhelper (>= 9), dh-systemd (>= 1.5)
Standards-Version: 3.9.6
Homepage: http://www.monitorix.org/
Vcs-Git: git://github.com/mikaku/Monitorix.git
Vcs-Browser: https://github.com/mikaku/Monitorix

Package: monitorix
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends},
 rrdtool,
 ...
Suggests: hddtemp,
 ...
Description: Web system monitoring tool
 ...

Trochę tego jest. Jedziemy zatem od góry. Linijka Source określa nazwę źródła, czyli to co zostanie pobrane po wydaniu polecenia apt-get source , z tym, że bez wersji i sufiksu .tar.gz .

Dalej mamy Section i odpowiada to za przypisanie pakietu do konkretnej sekcji, a tych jest dość sporo. Wszystkie można znaleźć tutaj. Tylko taka mała uwaga, zwykle tam w linku spotkamy się z nazwami typu "Administration Utilities" lub "Network" i to nie są te nazwy, które musimy wpisać do pliku debian/control . Jeśli klikniemy w daną sekcję, na samej górze dopiero dostaniemy prawidłową nazwę, przykładowo: Software Packages in "sid", Subsection net i to właśnie to musimy wpisać.

Kolejna linijka to Priority i określa ona jak ważna jest paczka z punktu widzenia prawidłowego działania systemu i z reguły tutaj będziemy wpisywać optional albo extra . Różnica między tymi dwoma polega na tym, że ten pierwszy nie koliduje z żadnymi pakietami, które mają ustawiony priorytet required , important lub standard , czyli pakiety wchodzące w skład bardzo podstawowej instalacji systemu.

Następnie mamy Maintainer , czyli osobę, która budowała i będzie się opiekować danym pakietem.

Kolejna pozycja to Build-Depends i jest to nic innego jak zależności, które muszą zostać spełnione, aby podjąć próbę budowania pakietu. Jeśli jakaś zależność nie zostanie spełniona, pbuilder nawet nie podejmie się próby stworzenia takiego pakietu. Nie zawsze też pakiet zbuduje się nam poprawnie w przypadku, gdy wszystkie zależności zostaną zaspokojone. Wtedy będzie trzeba poszukać tych brakujących pakietów, których zapomnieliśmy dodać lub zwyczajnie je przeoczyliśmy i dlatego wymagane jest budowanie paczek w minimalnym środowisku chroot. Taka mała uwaga odnośnie "bardzo podstawowych" zależności, np. gcc . Jeśli zdefiniujemy gcc w tutaj zależnościach, system upomni się o sprecyzowanie konkretnej wersji takiego pakietu. Jeśli korzystamy z dodatkowych modułów dla debhelper'a, tak jak w tym przypadku wykorzystywany jest dh-systemd , trzeba ten pakiet również uwzględnić w zależnościach.

Zależności będą podane na stronie projektu ale czasem (albo i często) nazwy będą bardzo nieprecyzyjne, np. qt5 i ciężko będzie nam ustalić, o który pakiet tak naprawdę może chodzić. Zwykle też do źródeł będzie dołączony plik README lub INSTALL z instrukcjami na temat budowy programu. Przykład:

REQUIREMENTS
===============================================================================
Monitorix requires some others packages to be installed that your GNU/Linux
distribution may or may not have:

- Perl
- Perl-CGI
- Perl-libwww
- Perl-MailTools
- Perl-MIME-Lite
- Perl-DBI
- Perl-XML-Simple
- Perl-Config-General
- Perl-HTTP-Server-Simple
- perl-IO-Socket-SSL
- RRDtool and its Perl bindings (perl-rrdtool or rrdtool-perl)
- (Optional) a CGI capable Web server (Apache, Nginx, lighttpd, etc.)

Z tym, że trzeba rozróżnić zależności potrzebne do budowania pakietu i zależności potrzebne do poprawnego działania programu.

Jeśli dany pakiet posiada plik configure , możemy poszukać na podstawie tego pliku potrzebnych zależności przy pomocy dpkg-depcheck . Z tym, że trzeba mieć na uwadze, że ten dpkg-depcheck wyrzuca zależności potrzebne do wykonania się polecenia i niekoniecznie wszystkie pakiety, które nam zwróci musimy wpisać jako zależności w budowanej paczce. Poza tym informacje o zależnościach są czasem niezbyt precyzyjne, np. nie wiemy nic wersji danej zależności. Poniżej przykład:

$ dpkg-depcheck -d ./configure
checking build system type... x86_64-unknown-linux-gnu
checking host system type... x86_64-unknown-linux-gnu
...
----------------------------------------------------------------------
Packages needed:
  libc6-i386
  cpio
  perl
  ...

Zwykle będą nas interesować pakiety zaczynające się od lib , do których to będzie trzeba dodać końcówkę -dev i taki pakiet dać w zależnościach. Trzeba także pamiętać, że część z powyższych zależności może być pociągnięta przez jakiś metapakiet np. build-essential i możemy sobie darować ich wpisywanie do pliku debian/control . Na dobrą sprawę, powyższe wyjście dobrze jest traktować orientacyjne, a nie dosłownie.

Innym sposobem na ustalenie zależności może być posłużenie się objdump , z tym, że ten skanuje pliki wykonywalne i by móc z niego skorzystać, musimy mieć uprzednio skompilowany program. W każdym razie, jeśli już mamy w systemie jakiś pakiet, to zależności ustalamy tak:

$ objdump -p /usr/sbin/dnscrypt-proxy | grep NEEDED
  NEEDED               libsodium.so.13
  NEEDED               libdl.so.2
  NEEDED               libm.so.6
  NEEDED               libc.so.6

Podobnie jak w przypadku dpkg-depcheck , obcinamy .so.* i dołączamy sufiks -dev .

Następnie mamy Standards-Version i jest to wersja standardu polityki debiana jaka ma być spełniona, aby zbudować tę paczkę i zawsze trzeba ustawiać tutaj najnowszą wersję, obecnie jest to 3.9.6 . Jeśli w późniejszym czasie wersja ulegnie zmianie, pakiet trzeba będzie przebudować i prawdopodobnie będą potrzebne też jakieś mniejsze zmiany, by spełnić wymogi nowszej wersji standardu.

Opcje Homepage , Vcs-Git oraz Vcs-Browser określają położenie projektu w sieci i czy korzysta z jakiegoś systemu kontroli wersji (VCS). Jeśli projekt nie korzysta z VCS, możemy usunąć te linijki z pliku.

Część opisową źródeł mamy z głowy. Druga część pliku dotyczy tego co zostanie zbudowane, czyli jaki pakiet opuści plac budowy.

Linijka z Package określa nazwę pakietu, domyślnie taka sama jak nazwa źródła.

Kolejna linijka to Architecture i tu możemy wpisać all albo any . Różnica między tymi dwoma tkwi w rodzaju budowanych plików. Jeśli budujemy skrypty, wtedy tak naprawdę nie budujemy ich tylko kopiujemy pliki do paczki, a taki skrypt może być odpalony na każdej architekturze i w takim przypadku wybieramy all . Z kolei pliki wymagające kompilacji trzeba budować dla każdej architektury osobno i tutaj dajemy zwykle any , chyba, że pakiet ma wyraźnie zaznaczone na stronie projektu, że przeznaczony jest tylko dla np. amd64 czy i386 . Wtedy podajemy wedle opisu. Taka mała uwaga jeszcze. Przyjdzie nam budować pakiety, które będą się składać ze skryptów, obrazków, mp3 i całego mnóstwa innych rzeczy, którym normalnie byśmy nadali arch all ale dodatkowo będą zawierać jeden lub kilka plików binarnych i z tego powodu arch all odpada ale też nie możemy zbudować takiego pakietu z arch any , bo lintian będzie się rzucał, że marnujemy miejsce na serwerze, bo przecie z jego perspektywy, jeśli jakiś pakiet zawiera znaczne ilości plików niezależnych od architektury, to nie możemy dawać arch any i musimy dać all i jak z takiej sytuacji wybrnąć? Trzeba pakiet podzielić na dwie części. W jednej z nich dać pliki wymagające kompilacji, a w drugiej (zwykle paczka-common) dać wszystkie pliki niezależne od architektury i dodać odpowiednie zależności do paczki trzymającej plik binarny -- o tym będzie więcej przy okazji omawiania pliku debian/rules .

Następne linijki to Depends , Suggests , Recommends , Conflicts , Breaks , Provides i Replaces i są to zależności, które muszą być spełnione przy instalacji pakietu. Nie wszystkie z wyżej wymienionych opcji będziemy zawsze używać. Zwykle ograniczymy się do pierwszych trzech. Różnice między nimi są następujące. Jeśli pakiet wymaga do działania innego pakietu, dajemy go w Depends . Jeśli nasz pakiet może się obyć bez innego pakietu ale traci na tym trochę ze swojej funkcjonalności, to dajemy tamten pakiet w Recommends . Jeśli jakiś pakiet przydaje się ale można bez niego żyć, to dopisujemy go do Suggests . Jeśli nasz pakiet dostarcza pliki dostępne w innych pakietach, dajemy ten drugi pakiet w Conflics. Z kolei Breaks będziemy używać przy przenoszeniu plików z jednego pakietu do innego, np. przy podziale pakietu na kilka mniejszych. Jeśli nasz pakiet jest alternatywą dla innego pakietu, np. firefox jest alternatywą dla chrome (i vice versa), możemy wpisać w polu Provides www-browser i wtedy każdy inny pakiet, który ma w swoich zależnościach www-browser , przy instalacji będzie prosił o zainstalowanie firefoxa albo chrome (ew. obu). Jeśli chodzi o Replaces , to zwykle jest używany on (w połączeniu z Conflicts) przy zastępowaniu starych i nieaktualnych już pakietów, np. jeśli mamy do czynienia z forkami jakichś projektów i projekt główny nie jest już rozwijany ale w repozytorium wciąż jest dostępna paczka z nim. Generalnie nie możemy mieć zainstalowanych obu tych pakietów jednocześnie.

Każde z tych pól może zawierać określoną wersję danego pakietu. Wykorzystujemy do tego następujące operatory: << , <= , = , >= oraz >> . Jeśli pakiet ma być w określonej wersji, to dajemy = . Jeśli wersja ma być mniejsza lub równa, to wtedy używamy <= . Z kolei jeśli większa lub równa to >= . Jeśli zaś wersja ma być mniejsza, to korzystamy z << , a jeśli większa, to >> .

Jeśli chodzi zaś o same zależności wymagane do prawidłowego działania pakietu, to ${shlibs:Depends} i ${misc:Depends} automatycznie ustalą czego paczka potrzebuje w oparciu o zależności potrzebne do zbudowania tego pakietu. Niemniej jednak, czasem (jak w tym przypadku) trzeba sprecyzować kilka dodatkowych pakietów. Ponadto, jeśli budujemy paczkę ze skryptami perl'a, trzeba dopisać ${perl:Depends} , jeśli jest to python'owy skrypt, to dajemy ${python:Depends} lub ${python3:Depends} w zależności od wersji python'a. Wszystkie te dodatkowe listy z generowanymi pakietami są wpisywane do pliku debian/control w paczce wynikowej i możemy te zależności dokładnie obejrzeć i ewentualnie oszacować czy czegoś brakuje. Więcej informacji na temat określania zależności między pakietami, można znaleźć tutaj. Wspomnieć też należy, że to nie są jedyne opcje, które możemy wykorzystać przy podstawianiu zmiennych, więcej o pozostałych można przeczytać w manie deb-substvars.

I ostatnia pozycja w tym pliku to Description , czyli opis pakietu. Ten po dwukropku zwykle jest krótki i nie powinien przekraczać 65 znaków -- więcej zostanie obciętych. Jeśli zaś chodzi o dłuższy opis, to wpisujemy go bezpośrednio pod tą linijką, z tym, że wcinamy tekst o jedną spację i długość linijki nie może przekraczać 80 znaków. Każda nowa linia musi być wcięta. Jeśli chcemy zrobić odstęp między paragrafami (pusta linia), to dajemy kropkę (również wciętą) i dalej nowy paragraf. Jeśli chcemy skorzystać z list, czyli wypunktować różne rzeczy, wcinamy linijkę przy pomocy dwóch spacji i dajemy myślnik albo gwiazdkę. Ja zwykle daję gwiazdki.

debian/rules

Jak sama nazwa wskazuje, ten plik będzie zawierał szereg reguł, w oparciu o które pakiet zostanie zbudowany. Generalnie rzecz biorąc jest to plik makefile tylko nieco inny.

Budowanie pakietu odbywa się w etapach. Wywoływany jest pierwszy etap i dokonywane są zdefiniowane w nim reguły, po czym następuje przejście do kolejnego etapu i aplikowane są reguły określone tutaj i tak dalej, aż do samego końca.

Poniżej znajduje się minimalny plik debian/rules :

#!/usr/bin/make -f

export DH_VERBOSE = 1
export DH_OPTIONS = -v

%:
   dh $@

Dobrze jest wyeksportować te dwie dodatkowe zmienne DH_VERBOSE oraz DH_OPTIONS . Uwidocznią one poszczególne etapy budowania pakietu i będziemy widzieć każde polecenie jakie będzie wykonywane, dzięki czemu znacznie przyśpieszymy proces przyswajania sobie całej tej potrzebnej nam wiedzy z zakresu budowania pakietów.

Dalej w pliku mamy %: i jest to wildcard oznaczający każdy target (etap) przy budowaniu pakietu, a tych jest sporo. Poniżej jest zobrazowany proces budowania paczki dnscrypt-proxy :

 fakeroot debian/rules clean
dh clean --with systemd,autotools-dev
   dh_testdir
   dh_auto_clean
   dh_clean

 debian/rules build
dh build --with systemd,autotools-dev
   dh_testdir
   dh_auto_configure
   dh_auto_build
   dh_auto_test

 fakeroot debian/rules binary
dh binary --with systemd,autotools-dev
   dh_testroot
   dh_prep
   dh_installdirs
   dh_auto_install
   dh_install
   dh_installdocs
   dh_installchangelogs
   dh_installexamples
   dh_installman
   dh_installcatalogs
   dh_installcron
   dh_installdebconf
   dh_installemacsen
   dh_installifupdown
   dh_installinfo
   dh_systemd_enable
   dh_installinit
   dh_systemd_start
   dh_installmenu
   dh_installmime
   dh_installmodules
   dh_installlogcheck
   dh_installlogrotate
   dh_installpam
   dh_installppp
   dh_installudev
   dh_installwm
   dh_installgsettings
   dh_bugfiles
   dh_ucf
   dh_lintian
   dh_gconf
   dh_icons
   dh_perl
   dh_usrlocal
   dh_link
   dh_installxfonts
   dh_compress
   dh_fixperms
   dh_strip
   dh_makeshlibs
   dh_shlibdeps
   dh_installdeb
   dh_gencontrol
   dh_md5sums
   dh_builddeb

W każdym z powyższych targetów może znajdować się szereg akcji, np:

dh_installman
   man --recode UTF-8 ./dnscrypt\-proxy\.8 > dnscrypt\-proxy\.8\.new
   chmod 644 dnscrypt-proxy.8.new
   mv -f dnscrypt-proxy.8.new dnscrypt-proxy.8
   man --recode UTF-8 ./hostip\.8 > hostip\.8\.new
   chmod 644 hostip.8.new
   mv -f hostip.8.new hostip.8

Każdy z etapów wywołuje określone narzędzie, które ma na celu coś zrobić. Z reguły są to skrypty zaczynające się od dh_ i każdy z nich ma swój własny manual. Warto poczytać jeśli potrzebujemy informacji na temat pewnych opcji pojawiających się w logu podczas budowania. Nie zawsze będziemy korzystać ze wszystkich wyżej wylistowanych targetów. To zależeć będzie od tego co tak naprawdę będziemy budować. Jeśli budujemy pakiet mający pliki unitów dla systemd, to logiczne jest, że będziemy przechodzić przez etap dh_systemd_enable i dh_systemd_start . Warto też wiedzieć, że szereg z powyższych targetów ma swoje pliki konfiguracyjne w katalogu debian/ , np. targetowi dh_install przypisany jest plik *.install .

Wszelkie moduły, z których chcemy skorzystać przy budowie paczki, precyzujemy po dh $@ . Poniżej przykład skorzystania z modułu dh_systemd :

...
%:
   dh $@ --with dh_systemd
...

Jeśli modułów było by więcej, dodajemy je kolejno oddzielając je od siebie za pomocą przecinka. Musimy także pamiętać o dodaniu odpowiednich zależności w pliku debian/control .

Jeśli chodzi o dalszą część pliku debian/rules, to nasuwa się pytanie -- skąd ja mam wiedzieć co tutaj wpisać i jakie reguły zaaplikować? Zwykle debhelper wykryje upstream'owy plik makefile i powinien sobie z procesem budowania poradzić bez naszej ingerencji ale czasem szereg plików trzeba będzie usunąć, a niektóre stworzyć lub przenieść w inne miejsce, tak by paczka miała ręce i nogi i nie zawierała przy tym zbędnych śmieci.

Jeśli kiedyś zdarzyło nam się instalować pakiet ręcznie, tj. via ./configure (dh_auto_configure), make (dh_auto_build) i make install (dh_auto_install), to może nam to podsunąć kilka pomysłów, bo tak na dobrą sprawę, to jak sama nazwa wskazuje makefile tworzy pliki. Zatem jest to prosta instrukcja gdzie wgrać jakie pliki, by program działał jak trza. Zatem możemy podejrzeć plik makefile dołączony do źródeł i prześledzić co tak naprawdę w nim się odbywa. Z reguły pliki makefile są długie i nie będę tutaj przytaczał całości. Rzucimy jedynie okiem na najważniejsze jego fragmenty:

PN = monitorix

PREFIX ?= /usr
CONFDIR = /etc
BASEDIR = /var/lib/monitorix/www
LIBDIR = /var/lib/monitorix
INITDIR_SYSTEMD = $(PREFIX)/lib/systemd/system
INITDIR_OTHER = $(CONFDIR)/init.d
BINDIR = $(PREFIX)/bin
DOCDIR = $(PREFIX)/share/doc/$(PN)
MAN5DIR = $(PREFIX)/share/man/man5
MAN8DIR = $(PREFIX)/share/man/man8

RM = rm -f
RMD = rmdir
SED = sed
INSTALL = install -p
INSTALL_PROGRAM = $(INSTALL) -m755
INSTALL_SCRIPT = $(INSTALL) -m755
INSTALL_DATA = $(INSTALL) -m644
INSTALL_DIR = $(INSTALL) -d

Powyżej mamy nagłówek pliku makefile monitorix'a. Jak widać jest ustawianych szereg zmiennych. Głównie precyzowane są katalogi, do których powędrują pliki, jak i same polecenia instalujące pliki w tych katalogach.

Dalej mamy już zwykle operacje na plikach, np:

install-bin:
    $(Q)echo -e '\033[1;32mInstalling script and modules...\033[0m'
    $(INSTALL_DIR) "$(DESTDIR)$(BINDIR)"
    $(INSTALL_PROGRAM) $(PN) "$(DESTDIR)$(BINDIR)/$(PN)"

    $(INSTALL_DIR) "$(DESTDIR)$(BASEDIR)/cgi"
    $(INSTALL_DIR) "$(DESTDIR)$(BASEDIR)/imgs"
    $(INSTALL_PROGRAM) $(PN).cgi "$(DESTDIR)$(BASEDIR)/cgi/$(PN).cgi"
    $(INSTALL_DATA) logo_bot.png "$(DESTDIR)$(BASEDIR)/logo_bot.png"
    $(INSTALL_DATA) logo_top.png "$(DESTDIR)$(BASEDIR)/logo_top.png"
    $(INSTALL_DATA) monitorixico.png "$(DESTDIR)$(BASEDIR)/monitorixico.png"

    $(INSTALL_DIR) "$(DESTDIR)$(CONFDIR)/$(PN)"
    $(INSTALL_DATA) $(PN).conf "$(DESTDIR)$(CONFDIR)/$(PN)/$(PN).conf"
...

I jak widać niczym się to zbytnio nie różni o zwykłego Ctrl-C z jednego miejsca i Ctrl-V w inne miejsce. Zatem mamy już mniej więcej obraz tego co tak naprawdę się będzie działo podczas budowania pakietu. debhelper przetworzy ten plik makefile i zainstaluje poszczególne pliki w zdefiniowanych lokalizacjach, a potem z tego zrobi paczkę. Z tym, że trzeba wziąć pod uwagę jedną rzecz. W tym przypadku mamy do czynienia ze skryptem perl'a i tutaj nie odbywa się żadna kompilacja. Niemniej jednak, po kompilacji są tworzone pliki wynikowe i to w dużej mierze właśnie te pliki będą upychane w odpowiednich katalogach.

Jeśli nie chce nam się zbytnio brodzić w pliku makefile , to z reguły informacje na temat budowania pakietu będą podane w pliku README albo INSTALL , które są dołączone do paczki ze źródłami. Przykładowo:

INSTALLATION
===============================================================================
Running a 'make install-xxx' as root will distribute the files to the file
system. Most users will want to select from three options depending on target
init system (do not run all four)!

   # make install-systemd-all
   # make install-upstart-all
   # make install-debian-all
   # make install-redhat-all

Alternatively, users may override any of the in make targets.  For example:

   $ make DESTDIR=~/pub BASEDIR=/srv/http/monitorix install-systemd-all
...

I tu już wiemy dokładnie czego szukać w pliku makefile i jak zainstalować ten programik. Nam potrzebne są 2 (max 3) z tych 4 wyżej wymienionych rzeczy. W końcu budujemy paczkę dla debiana, zatem plik debian/rules powinien przybrać poniższą postać:

#!/usr/bin/make -f

export DH_VERBOSE = 1

%:
   dh $@

override_dh_auto_install:
   dh_auto_install -- \
      install-systemd-all \
      install-upstart-all \
      install-debian-all

Normalnie proces budowy przechodząc przez poszczególne etapy dotrze do targetu dh_auto_install , który wykona instrukcje zawarte w pliku makefile , czyli zainstaluje wszystko co tam jest podane, a tego nie zawsze chcemy. Dlatego też, mamy możliwość nadpisywania szeregu etapów budowania przez dopisanie override_* . Powyżej nadpisaliśmy etap automatycznej instalacji przekazując do polecenia make trzy parametry, uzyskując w ten sposób dokładnie taką samą sekwencję poleceń (za wyjątkiem jednego), która była określona w pliku README .

Kluczowe w tej zwrotce z dh_auto_install jest zrozumienie po co na końcu tego polecenia dodawane są -- , bo można także podać parametry bez tych dwóch myślników. Jaka jest zatem różnica? Jeśli wywołujemy jakiś target z -- , to wszystkie parametry znajdujące się za tymi myślnikami są dołączane do wynikowego polecenia. W przypadku, gdy nie podamy tych myślników, system zresetuje wszystkie poprzednie opcje i zamiast nich użyje te, które zostaną podane bezpośrednio za wywoływanym targetem.

Weźmy sobie przykład skryptu configure . Domyślnie ma on określone, dajmy na to, takie parametry --prefix=/usr --localstatedir=/var . Jeśli chcielibyśmy dodać kolejny parametr, korzystamy z myślników:

dh_auto_configure -- --sysconfdir=/etc

Jeśli chcielibyśmy użyć kompletnie innych wartości dla skryptu configure, pomijamy -- , przykładowo:

dh_auto_configure --prefix=/usr/local --localstatedir=/usr/local/var --sysconfdir=/usr/local/etc

Mamy jeszcze kilka innych opcji tyczących się nadpisywania targetów. Jeśli, przykładowo, odpowiadałby nam etap instalacji ale chcielibyśmy coś dodać do niego (przed albo po), wtedy musimy wywołać jego target i dodać swoje polecenia przed lub po nim, przykładowo:

...
override_dh_auto_install:
   dh_auto_install -- install install-gui
   rm debian/tmp/usr/share/doc/ansifilter/ChangeLog
   rm debian/tmp/usr/share/doc/ansifilter/COPYING
   rm debian/tmp/usr/share/doc/ansifilter/INSTALL
   rm debian/tmp/usr/share/man/man1/ansifilter.1.gz
...

Powyżej został wywołany make z dwoma parametrami install oraz install-gui , poza tym, część plików automatycznie zainstalowanych, zostanie usunięta, tak by nie trafiła do wynikowego pakietu.

Jeśli natomiast chcielibyśmy nadpisać jakiś etap całkowicie, tak by nie wykonała się w nim żadna akcja, to możemy to zrobić precyzując jedynie sam target + override, przykładowo:

override_dh_auto_install:

A jeśli pasowałoby nam jedynie nadpisać standardowe akcje, wtedy nie wywołujemy dh_auto_install , tylko od razu precyzujemy własne linijki, przykładowo:

...
override_dh_auto_install:
   install -d -m 755 debian/napi/usr/share/napi/
   install -d -m 755 debian/napi/usr/bin/
   install -m755 ./napi.sh debian/napi/usr/bin/
   install -m755 ./subotage.sh debian/napi/usr/bin/
   install -m755 ./napi_common.sh debian/napi/usr/share/napi/
...

Jeśli instalujemy jakieś pliki, ścieżki do nich podajemy względem głównego katalogu ze źródłami. Z kolei jeśli chcemy jakiś plik usunąć, to zwykle podajemy ścieżki debian/tmp/ lub debian/nazwa_pakietu/ + odpowiednia ścieżka w drzewie katalogu, np. debian/tmp/usr/share/doc/ansifilter/INSTALL . Jeśli nie usunęlibyśmy tego pliku, w paczce znalazłby się on pod /usr/share/doc/ansifilter/INSTALL .

debian/changelog i debian/upstream.changelog

Każdy projekt musi mieć listę wprowadzanych zmian, a te z kolei dzielimy na takie, które sami wprowadzamy lub też zostały uwzględnione w upstream'ie. Generalnie rzecz biorąc, większość projektów utrzymuje swój plik changelog (z reguły zlokalizowany w głównym katalogu źródeł) i nie musimy się o niego martwić. Niemniej jednak, spotkamy się z takimi projektami, które nie mają pliku changelog'a, lub w ogóle nie posiadają żadnej historii zmian za wyjątkiem kolejnego numerku wersji.

Jeśli jest to przypadek pierwszy, czyli projekt ma changelog, np. na swojej stronie, ale nie dostarcza pliku, w którym zmiany by były uwzględnione, możemy taki plik zrobić i do niego skopiować zawartość strony www. Plik taki nazywamy upstream.changelog . Format tego pliku jest chyba dowolny, np. może wyglądać tak:

Version 2.2
-------------
 * Fix problems with small displays like 80×25
 * Fix ATA compliance recognition (see bug #57)
 * Replace “Press any key” with “Press ‘m’ for menu”
 * Rework signal handling during procedure for stability
 * Improve build system

Version 2.1
-------------
 * Enhanced smart, smart_noreverse copying strategies;
 * Introduced new copying strategies skipfail, skipfail_noreverse;
 * Introduce journaling of copying and automatic resuming of interrupted copying;
 * Support procedure parameters overriding by ~/.whddrc config file;
 * Several bug fixes.
...

Trzeba tylko poinformować debhelper'a by przepisał nazwę pliku, pod którą ten changelog będzie widoczny w wynikowej paczce, przykładowo:

override_dh_installchangelogs:
   dh_installchangelogs -k debian/upstream.changelog

Powyższy kod wpisujemy oczywiście do pliku debian/rules i stworzy on linka changelog do pliku upstream.changelog . Więcej informacji na temat dh_installchangelogs można znaleźć w manie.

Jeśli chodzi zaś o te zmiany, które sami wprowadzamy, to dopisujemy je do pliku debian/changelog , który na początku ma poniższą postać:

monitorix (3.6.0-1) unstable; urgency=low

  * Initial release (Closes: #nnnn)

 -- Mikhail Morfikov <morfik@nsa.com>  Thu, 19 Feb 2015 14:00:01 +0100

Oczywiście nie edytujemy tego pliku ręcznie (choć można). Do jego obsługi mamy narzędzie dch , do którego doklejamy tylko odpowiedni parametr i zwykle to będzie -a (dodanie nowej pozycji w istniejącym już wpisie), -i (utworzenie nowego wpisu), -r (zaktualizowanie sygnatury czasowej), oraz -v (zmiana wersji).

Przy zmianie wersji trzeba jednak uważać, bo zostanie zmieniona nazwa katalogu roboczego, przykładowo:

morfik:~/debian_build/monitorix-3.5.0$ dch -v 3.6.0
dch warning: New package version is Debian native whilst previous version was not
dch warning: your current directory has been renamed to:
../monitorix-3.6.0

morfik:~/debian_build/monitorix-3.5.0$ cd ..

morfik:~/debian_build$ cd monitorix-3.6.0/

morfik:~/debian_build/monitorix-3.6.0$

W przypadku, gdy będziemy chcieli włączyć pakiet do repozytorium debiana, trzeba będzie założyć stosowny wątek na bugtracker, któremu zostanie przypisany odpowiedni numer identyfikacyjny i to ten numer będziemy musieli podać w pliku debian/changelog , np. (Closes: #123456 . Ma to na celu zautomatyzowanie procesu śledzenia i zamykania błędów, bo plik debian/changelog jest skanowany w poszukiwaniu pewnych wyrażeń i np. BTS po dodaniu powyższego pakietu automatycznie zamknie zawarte w nim kody błędów, oczywiście o ile wpiszemy poprawne numerki. To samo tyczy się zgłaszania kolejnych błędów. Jeśli uda nam się je poprawić albo zostaną poprawione w upstream'ie, to wystarczy dopisać w changelog'u numerki zgłoszonych przez kogoś błędów i po przesłaniu paczki, system zamknie je automatycznie. Bez tego automatu, sami musielibyśmy to robić.

Jeśli chcielibyśmy się bawić w maintainera z prawdziwego zdarzenia, trzeba będzie nam przyswoić umiejętność operowania na listach mailingowych debiana. To tam będziemy mieć dostęp do bugtracker'a, gdzie będą zgłaszane i omawiane błędy w pakietach, którymi będziemy musieli się zająć. Debian ma także kilka użytecznych narzędzi, które pomogą nam w tym zadaniu i jednym z nich jest reportbug. Przydaje się ono nie tylko do zgłaszania błędów. Zbiór list mailingowych debiana można znaleźć tutaj, z kolei pod tym linkiem można znaleźć garść użytecznych informacji na temat tego jak używać samych list.

Skrypt dch jest dość rozbudowany i ma sporo opcji. Jeśli ktoś chciałby w pełni zgłębić możliwości tego narzędzia, to informacje znajdzie w manie. Więcej informacji na temat formatu samego pliku debian/changelog można znaleźć tutaj.

debian/NEWS

Wszystkie ważne informacje na temat zmian w pakiecie możemy wrzucić również do tego pliku. Podczas instalacji nowszej wersji takiego pakietu, narzędzie takie jak apt-listchanges wyrzuci monit z tekstem, który tam umieścimy. Więcej informacji na temat dokładnej zasady działania tego mechanizmu można znaleźć w manualu.

Format tego pliku jest podobny do formatu pliku debian/changelog , z tym, że nie zawiera gwiazdek. Przy pomocy narzędzia dpkg-parsechangelog możemy sprawdzić poprawność tego pliku:

$ dpkg-parsechangelog -ldebian/NEWS
Source: ansifilter
Version: 1.11-1
Distribution: unstable
Urgency: low
Maintainer: Mikhail Morfikov <morfik@nsa.com>
Date: Tue, 03 Feb 2015 09:19:10 +0100
Changes:
 ansifilter (1.11-1) unstable; urgency=low
 .
   Some important news
   Some important news
   Some important news
   Some important news

debian/copyright

Licencja jest chyba jedną z tych rzeczy na jaką debian kładzie największy nacisk. Samych licencji mamy całe mnóstwo ale cześć z nich jest odrzucana przez politykę debiana i jeśli będziemy pakować jakąś aplikację na jednej z takich licencji, ten pakiet nigdy nie trafi do głównego repozytorium debiana.

Licencje akceptowalne przed debiana można odczytać z dh_make przy tworzeniu pakietu:

$ dh_make --help
  -c, --copyright <type>    use <type> of license in copyright file
                            (apache|artistic|bsd|gpl|gpl2|gpl3|lgpl|lgpl2|
                             lgpl3|mit)

Pliki licencji są zlokalizowane w katalogu /usr/share/common-licenses/ i mamy tam do dyspozycji licencje takie jak Apache-2.0, GPL, LGPL, czy BSD.

Nie musimy całej treści tych plików kopiować do debian/copyright . Możemy jedynie dać nagłówek i zawrzeć odnośnik do jednego z powyższych plików. Tak jak to robi standardowo dh_make . Są też licencje, które nie widnieją w powyższym katalogu i równocześnie są akceptowane przez debiana, np. MIT, i w takim przypadku trzeba podać treść całej licencji w pliku debian/copyright .

Zwykle każdy plik ma w nagłówku u siebie licencje (przynajmniej powinien mieć) ale przeglądanie dziesiątek czy setek plików w celu ustalenia, który jest na jakiej licencji może być lekko męczące. Na szczęście mamy do dyspozycji narzędzie licensecheck , które wywołane z opcją -r w głównym katalogu, zwróci nam listę plików wraz z ich licencją, przykładowo:

$ licensecheck -r *
docs/monitorix-alert.sh: *No copyright* UNKNOWN
docs/htpasswd.pl: *No copyright* UNKNOWN
lib/fail2ban.pm: GPL (v2 or later)
lib/HTTPServer.pm: GPL (v2 or later)
...

Czasem nie wszystkie pliki zostaną uwzględnione w powyższym listingu, domyślnie będą to te pasujące do następującego wzorca:

'\.(c(c|pp|xx)?|h(h|pp|xx)?|f(77|90)?|go|p(l|m)|xs|sh|php|py(|x)|rb|java|js|vala|el|sc(i|e)|cs|pas|inc|dtd|xsl|mod|m|tex|mli?|(c|l)?hs)$'

Jeśli któryś z plików w naszym pakiecie się nie łapie pod ten wzorzec, możemy ręcznie określić własny przy pomocy opcji -c .

O te pliki, które nie mają licencji, trzeba będzie się dopytać, chyba, że na stronie projektu jest informacja, że "pliki są na GPL-2", wtedy wszystko jest już jasne.

Poniżej przykład pliku debian/copyright wygenerowanego przez dh_make :

Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: monitorix
Source: http://www.monitorix.org

Files: *
Copyright: 2005-2014 Jordi Sanfeliu <jordi@fibranet.cat.>
License: GPL-2.0+

Files: debian/*
Copyright: 2015 Mikhail Morfikov <morfik@nsa.com>
License: GPL-2.0+

License: GPL-2.0+
 This package is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or
 (at your option) any later version.
 .
 This package is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 .
 You should have received a copy of the GNU General Public License
 along with this program. If not, see <http://www.gnu.org/licenses/>
 .
 On Debian systems, the complete text of the GNU General
 Public License version 2 can be found in "/usr/share/common-licenses/GPL-2".

Jeśli chodzi o samą strukturę tego pliku, to w pierwszej linijce mamy Format i zwykle jest taki jak wyżej. Uzupełniamy Upstream-Name i Source , odpowiednio, nazwę pakietu oraz skąd zostały pobrane źródła.

Dalej mamy określanie praw do plików. Jeśli wszystkie pliki w źródłach pochodzą od jednej osoby, wtedy w linijce z Files dajemy * . Jeśli autorów jest więcej i każdy z nich rości sobie prawa do innego pliku lub plików w danym katalogu, musimy te pliki wypunktować oddzielając je spacją, przykład:

Files: src/libsodium/crypto_auth/hmacsha256/cp/hmac_hmacsha256.c
 src/libsodium/crypto_auth/hmacsha512256/cp/hmac_hmacsha512256.c
 src/libsodium/crypto_hash/sha256/cp/hash_sha256.c
 src/libsodium/crypto_hash/sha512/cp/hash_sha512.c
 src/libsodium/crypto_pwhash/scryptsalsa208sha256/pbkdf2-sha256.h
 src/libsodium/crypto_pwhash/scryptsalsa208sha256/pbkdf2-sha256.c
Copyright: 2005,2007,2009 Colin Percival
License: BSD-2-clause

Sekcji z Files może być dowolna ilość, tak by każdy plik miał określoną licencję. Jeśli któryś z nich zostanie pominięty, lintian będzie miał z tym problem i nam to oświadczy. Z reguły to my będziemy mieć prawa do plików w katalogu debian/ , bo je tworzymy.

W linijce z Copyright podajemy informacje na temat tego kto jest właścicielem praw autorskich do plików w formie "data nazwisko email" (email nie jest obowiązkowy). Jeśli autorów by było więcej, wtedy definiujemy ich jeden pod drugim np:

Files: src/libsodium/crypto_pwhash/scryptsalsa208sha256/crypto_scrypt.h
 src/libsodium/crypto_pwhash/scryptsalsa208sha256/nosse/pwhash_scryptsalsa208sha256_nosse.c
 src/libsodium/crypto_pwhash/scryptsalsa208sha256/sse/pwhash_scryptsalsa208sha256_sse.c
Copyright: 2009 Colin Percival
 2012, 2013 Alexander Peslyak
License: BSD-2-clause

Ostatnia rzecz, o której trzeba wspomnieć to License i bynajmniej nie chodzi tutaj o rodzaj licencji tylko o sposób jej definiowania. Generalnie można to robić na dwa sposoby. Jeden z nich zakłada dołączania pod linijką z License treści licencji ale to troszeczkę psuje całą estetykę i lepiej jest tego nie robić. Alternatywne rozwiązanie zakłada zdefiniowanie poszczególnych bloków plików jeden pod drugim i dopiero na końcu podanie treść licencji, przykładowo:

Files: *
Copyright: © 2015 William Jon McCann
           © 2015 Peter de Ridder
           © 2015 Simon Steinbeiß
License: GPL-2

Files: debian/*
Copyright: © 2013 Yves-Alexis Perez
License: GPL-2
...
Files: data/*.1
Copyright: 2007 Sven Arvidsson
  2014 Peter de Ridder
License: GPL-2+

License: GPL-2
 On Debian systems, the complete text of the GNU General Public License version
 2 can be found in '/usr/share/common-licenses/GPL-2'.

License: GPL-2+
 On Debian systems, the complete text of the GNU General Public License can be
 found in '/usr/share/common-licenses/GPL-2'.

Formatowanie pliku odbywa się dokładnie na takich samych zasadach co w przypadku pliku debian/control , czyli paragrafy (np. w licencjach) są oddzielone kropkami, a nowe linie są wcięte minimum o jedną spację.

debian/watch i debian/upstream/signing-key.asc

W oparciu o te pliki, narzędzie uscan potrafi zweryfikować czy pojawiła się nowsza wersja jakiejś aplikacji. Plik debian/watch będzie wyglądał mniej więcej tak:

version=3
opts=filenamemangle=s/.+\/v?(\d\S*)\.tar\.gz/Monitorix-$1\.tar\.gz/ \
  https://github.com/mikaku/Monitorix/tags .*/v?(\d\S*)\.tar\.gz

Linijka z version obecnie przyjmuje wartość 3 . Natomiast kolejna linijka jest nieco skomplikowana. Mamy tam wyrażenia regularne perl'a. Nie będziemy zajmować się tutaj samymi wyrażeniami i skupimy się na wzorcach, które jedynie będziemy dostosowywać. Więcej informacji o samych wyrażeniach regularnych można znaleźć tutaj.

Większość projektów jest hostowana na GitHub czy sourceforge i w ich przypadku możemy skorzystać z czyjejś pracy, czyli skopiować sobie tę powyższą linijkę i odpowiednio dostosować. Powyżej mamy przykład projektu hostowanego na GitHub i raczej przerobienie linku nie powinno przysporzyć problemów.

Wszystkie inne większe strony z projektami opensource mają swoje regułki zebrane pod tym linkiem. Jeśli tam nie znajdziemy strony, z której pobraliśmy źródła, będziemy musieli sami naskrobać odpowiednią regułkę. Wprawdzie nie jest to obowiązkowe ale znacząco ułatwia późniejsze aktualizowanie źródeł, bo wystarczy, że w katalogu ze źródłami wydamy polecenie uscan i ten już sprawdzi czy pojawiła się nowsza wersja w upstream'ie. Jeśli tak, pobierze ją i automatycznie dostosuje nazwy plików, co przyśpieszy migrację pakietu do nowszej wersji.

Po uzupełnieniu pliku, aktualizację przeprowadzamy w poniższy sposób:

$ uscan --verbose
-- Scanning for watchfiles in .
-- Found watchfile in ./debian
-- In debian/watch, processing watchfile line:
   opts=filenamemangle=s/.+\/v?(\d\S*)\.tar\.gz/Monitorix-$1\.tar\.gz/   https://github.com/mikaku/Monitorix/tags .*/v?(\d\S*)\.tar\.gz
-- Found the following matching hrefs:
     /mikaku/Monitorix/archive/v3.6.0.tar.gz (3.6.0)
     /mikaku/Monitorix/archive/v3.5.1.tar.gz (3.5.1)
     /mikaku/Monitorix/archive/v3.5.0.tar.gz (3.5.0)
Newest version on remote site is 3.6.0, local version is 3.6.0
 => Package is up to date
-- Scan finished

Jak widzimy wyżej, plik debian/watch został odnaleziony i przetworzony, zwracając 3 pliki o różnych wersjach. Jako, że posiadam najnowszą wersję źródeł, żadna akcja mająca na celu dokonanie aktualizacji nie została podjęta.

Podpisy cyfrowe źródeł

Trzeba jeszcze wspomnieć o jeden ważnej rzeczy, mianowicie o podpisach cyfrowych. Jeśli budujemy projekt, który ma podpisane źródła, koniecznie musimy weryfikować ich podpis, z tym, że będziemy potrzebować klucza publicznego osoby składającej sygnaturę. Poniżej jest przedstawiony proces pozyskiwania klucza publicznego.

Na sam początek odwiedzamy stronę projektu i patrzymy co za pliki są do pobrania. W tym przypadku mamy min. dnscrypt-proxy-1.4.3.tar.gz oraz dnscrypt-proxy-1.4.3.tar.gz.sig . Pobieramy oba pliki i przechodzimy do katalogu gdzie zostały pobrane. Próbujemy póki co ręcznie zweryfikować podpis:

$ gpg --verify dnscrypt-proxy-1.4.3.tar.gz.sig
gpg: assuming signed data in 'dnscrypt-proxy-1.4.3.tar.gz'
gpg: Signature made Tue 10 Feb 2015 12:00:24 PM CET
gpg:                using RSA key 0x62F25B592B6F76DA
gpg: Can't check signature: public key not found

Jak widzimy, nie można zweryfikować sygnatury, bo nie posiadamy klucza publicznego. Szukamy go zatem i importujemy:

$ gpg --search-keys 0x62F25B592B6F76DA

Klucz został zaimportowany do naszego lokalnego keyring'a. Jeśli teraz byśmy spróbowali sprawdzić sygnaturę, podpis powinien zostać zweryfikowany. Musimy teraz wydobyć ten klucz z keyring'a i zapisać go do pliku w formacie ascii:

$ mkdir debian/upstream/

$ gpg --armor --export 0x62F25B592B6F76DA > debian/upstream/signing-key.asc

Zatem klucz publiczny jest już na swoim miejscu. Musimy jeszcze nieco dostosować linijkę w pliku debian/watch , tak by uscan automatycznie weryfikował podpis bez naszej ingerencji:

version=3
opts=pgpsigurlmangle=s/$/.sig/ \
  http://download.dnscrypt.org/dnscrypt-proxy/dnscrypt-proxy-([0-9].+).tar.gz

Od tej pory, gdy się pojawi nowsza wersja pakietu, zostanie ona pobrana po wywołaniu polecenia uscan , natomiast sygnatura zostanie zweryfikowana automatycznie.

debian/manpage.* i debian/*.manpages (dh_installman)

Dokumentacja projektu to bardzo ważna rzecz i bez niej ani rusz. To właśnie na jej podstawie wiemy jakie zadania ma realizować dany program. Zwykle jest nieco bardziej obszerna niż parametr --help doczepiony do wywołanego programu. Jeśli dany projekt się szanuje, powinien wypuścić przyzwoitą dokumentację i dołączyć ją paczki ze źródłami. Nie zawsze jednak taki manual ma odpowiednią formę. Pod tym linkiem znajduje się krótki tutorial na temat składni stosowanej w plikach manuala wykorzystywanych w debianie. Będziemy musieli się tego nauczyć, w przeciwnym wypadku, może się zdarzyć tak, że nasze paczki zostaną bez dokumentacji, a to niedobrze.

Z grubsza będziemy mieli styczność z trzema rodzajami przypadków. W pierwszym z nich nie będzie żadnej dokumentacji. W drugim będzie dokumentacja ale trzeba będzie ją przeformatować. Zaś w trzecim będziemy musieli poprawić błędy w formatowaniu, które wypunktuje nam lintian . Pomijam oczywiście ten rzadki przypadek, w którym wszystko będzie jak trza i nie będziemy musieli nic robić.

Manuale tyczą się głównie plików wykonywalnych i według polityki debiana, każdy taki plik musi mieć manual, nawet jeśli jest to jakaś aplikacja GUI z dwoma przyciskami na krzyż, z których jeden to OK, a drugi to ANULUJ. Nazwy plików manuali mają również swój format np. napi.1 . Numerki są od 1-9 , w zależności od tego o czym jest konkretna strona manuala. Najczęściej jednak będziemy się spotykać z numerkami 1 (info o opcjach, które program może przyjąć) i 5 (pliki konfiguracyjne). Jeśli kogoś interesują pozostałe numerki, może o nich poczytać w man man .

Bardzo rzadko będziemy tworzyć plik manuala od podstaw. Jeśli się jednak na to zdecydujemy, to istnieją graficzne narzędzia, które mogą nam ułatwić to zadanie. Jednym z nich jest gmanedit . W pozostałych przypadkach będziemy szli na skróty. Samo poprawianie poszczególnych linijek w pliku nie powinno sprawić problemów, zwłaszcza jeśli się opanuje składnię pliku manuala. Możemy także korzystać z polecenia man -l plik-mana.1 aby podejrzeć jak wyglądać będzie ten manual w systemie.

Automatyczne generowanie manuali

Watro też wspomnieć iż istnieje możliwość wygenerowania pliku manuala w oparciu o polecenie --help , z tym, że nie zawsze wszystkie aplikacje posiadają w swoim wyposażeniu ten parametr. Poza tym, jeśli chodzi o aplikacje GUI, muszą one mieć dostęp do sesji graficznej, by taki manual wygenerować. I takim automatycznym generowaniem plików manuali zajmuje się help2man . Można go wywołać bezpośrednio przy budowaniu paczki, z tym, że to pociąga za sobą dodatkowe zależności oraz kilka linijek w pliku debian/rules , a konkretnie trzeba będzie nadpisać target dh_installman oraz uwzględnić ten nowy plik przy czyszczeniu w targecie dh_clean. Poniżej przykład:

override_dh_installman:
    help2man -N --no-discard-stderr --version-string='0.3.3' -n 'feature-rich screen recorder that supports X11 and OpenGL' debian/simplescreenrecorder/usr/bin/simplescreenrecorder > simplescreenrecorder.1
    help2man -N --no-discard-stderr --version-string='0.3.3' -n 'inject the GLInject library into a given command' debian/simplescreenrecorder/usr/bin/ssr-glinject > ssr-glinject.1
    dh_installman simplescreenrecorder.1 ssr-glinject.1

override_dh_clean:
    dh_clean -- simplescreenrecorder.1 ssr-glinject.1

Możemy także wygenerować plik manuala po zbudowaniu paczki i wrzucić go później do katalogu debian/ .

Jeśli zdecydujemy się na to drugie wyjście, to manual generujemy w poniższy sposób:

$ which napi
/usr/bin/napi

$ help2man /usr/sbin/napi > napi.1

Tak wygenerowany plik dobrze jest przejrzeć i poprawić ewentualne błędy.

Mając już pliki manuali, musimy uwzględnić je w debian/*.manpages , gdzie gwiazdka zwykle odpowiada nazwie pakietu, do którego ten manual ma trafić. Generalnie chodzi o to, że w przypadku, gdy budujemy większy projekt, który ma kilka paczek, to raczej chcielibyśmy aby manual od jednego pliku trafił do konkretnej paczki, podobnie z pozostałymi. Poniżej przykład pliku napi.manpages :

debian/napi.1
debian/subotage.1

Format tego pliku jest prosty. Każda linijka to jeden plik manuala i określamy w niej położenie samego pliku w katalogu ze źródłami. Jako, że stworzyliśmy dopiero co nowe pliki manuala, to umieściliśmy je w podkatalogu debian/ . Nie używamy tutaj początkowego / , bo ścieżki są względne.

Istnieje także możliwość zmiany położenia i wtedy po spacji dodajemy kolejną ścieżkę, która określa lokalizację pliku w wynikowej paczce i też nie używamy początkowego / , przykładowo:

debian/napi.1 usr/share/man/man1/

Z reguły nie ma takiej potrzeby i wystarczy określić tylko pierwszą ścieżkę, druga zostanie dobrana automatycznie na podstawie numerka.

debian/*.docs (dh_installdocs)

Ten plik będzie zawierał ścieżki do plików, które niosą ze sobą jakąś przydatną z punktu widzenia projektu informację ale nie są to manuale. Zwykle ich nazwy będą pisane dużymi lietrami, np. README . Ze wszystkich takich plików robimy listę. Trzeba tylko się pilnować, by nie umieszczać śmieci, np. nie potrzebujemy pliku INSTALL, czy plików związanych z innymi dystrybucjami linux'a. Pakujemy tu tylko co się tyczy debiana. Poniżej przykład pliku:

AUTHORS
BUGS
COLABORATION
README.md

Z reguły system powinien wykryć cześć plików ale dobrze jest przejrzeć główny katalog źródeł i posprawdzać czy coś nie zostało pominięte.

Pliki demonów (dh_installinit)

Nie będę tutaj przytaczał żadnych skryptów -- chodzi o skrypty sysvinit, unity systemd i podobne pliki związane z odpalaniem usług systemowych, bo to jest poza zakresem tego poradnika. Niemniej jednak, zostanie załączony link do dokumentacji poszczególnych plików, tak by mieć jakiś punkt zaczepienia.

debian/*.init

Niby debhelper tworzy plik *.init.d ale wszędzie w paczkach spotkałem się tylko z samym *.init . W każdym razie, nie ma to większego znaczenia, z której nazwy skorzystamy. Oba pliki odpowiadają za instalowanie skryptów startowych sysvinit, tych w katalogu /etc/init.d/ . By sprawnie tworzyć takie skrypty, trzeba zrozumieć sam nagłówek LSB oraz ogarnąć narzędzie start-stop-daemon. Dodatkowo trzeba również opanować obsługę sh ( dash ), tak by uniknąć bashismów.

Skrypty init dobrze jest także potraktować poleceniem sh -n , które zwróci ewentualne problemy ze składnią, przykładowo:

$ sh -n dnscrypt-proxy

Jeśli jakieś błędy się pojawią, trzeba będzie je poprawić.

debian/*default

Plik *.default odpowiada za konfigurację skryptu init trzymaną w katalogu /etc/default/. Generalnie chodzi o możliwość zachowania części z ustawionych opcji, które zwykle powinny być stałe i nie ulegać resetowaniu podczas aktualizacji pakietu. Sam skrypt init może ulec zmianie ale w stopniu, w którym nie powinien ingerować w opcje określone tutaj w tym pliku, przynajmniej taka jest teoria.

Ten plik zawiera jedynie szereg zmiennych i trochę komentarzy, a do tego raczej nie potrzebujemy manuala.

debian/*service (dh_systemd_enable, dh_systemd_start)

W tym pliku jest trzymana konfiguracja unitów dla systemd. By z nich skorzystać musimy w pliku debian/rules uwzględnić moduł dh_systemd . Dokładne informacje na temat opcji, które możemy użyć w plikach unitów, możemy znaleźć w dokumentacji systemd dostępnej tutaj. Jest tego dość sporo i dla ułatwienia podpowiem tylko, że interesować nas będą głównie strony od systemd.service oraz systemd.unit.

debian/*tmpfile

W tym pliku jest trzymana konfiguracja plików tymczasowych, które są niezbędne do działania danego programu. Wszystkie pliki i katalogi określone tutaj będą tworzone i konfigurowane automatycznie na starcie systemu. Dokładne informacje na temat formatu samego pliku można znaleźć tutaj.

debian/*.symbols

Z tym plikiem spotkamy się jedynie przy budowaniu bibliotek, co nie jest prostym zadaniem. Zakładając, że udało nam się zbudować bibliotekę, będziemy musieli wygenerować dla niej plik *.symbols , a tego możemy dokonać jedynie mając do dyspozycji paczkę wynikową. Także zbudowanie biblioteki wymaga przepakowania jej co najmniej jeden dodatkowy raz.

Do generowania symboli posłuży nam narzędzie dpkg-gensymbols , zaś do wypakowania paczki dpkg-deb . Sam proces przebiega mniej więcej w poniższy sposób:

$ dpkg-deb -x /media/Kabi/pbuilder/result/libssr-glinject_0.3.3-1_amd64.deb /tmp/libssr-glinject
$ dpkg-gensymbols -v0.3.3 -plibssr-glinject -P/tmp/libssr-glinject -Olibssr-glinject.symbols
$ sed -i s/0\.3\.3$/0\.3\.3\~/g libssr-glinject.symbols

Pierwsza linijka wypakowuje paczkę z biblioteką, druga generuje symbole, a trzecia dopisuje do wersji znaczek ~ , o który domagał się lintian . Tak stworzony plik przenosimy do katalogu debian/ i budujemy paczkę jeszcze raz.

Czasem też się zdarzy tak, że nazwa, którą nadaliśmy paczce z biblioteką, jest niewłaściwa. Jak zatem ustalić odpowiednią nazwę? Do tego celu posłuży nam narzędzie readelf , poniżej przykład:

$ readelf -d  /usr/lib/x86_64-linux-gnu/libssr-glinject.so | grep SONAME
 0x000000000000000e (SONAME)             Library soname: [libssr-glinject.so]

I już wiemy, że paczka ma się nazywać libssr-glinject . Więcej informacji na temat symboli można znaleźć tutaj i tutaj.

debian/*.dirs (dh_installdirs)

Jeśli do zbudowania pakietu potrzebne nam są dodatkowe katalogi, które standardowo nie są tworzone, oznaczać to może najprawdopodobniej błąd w pliku makefile . Możemy te katalogi wpisać do tego pliku i poprawić tym samym ten błąd. Jeśli dodajemy pliki do debian/*.install , to nie musimy tutaj uwzględniać tych katalogów.

debian/*.install (dh_install)

Jeśli z jakiegoś powodu pewne pliki nie są brane pod uwagę przez makefile i zwyczajnie nie zostaną utworzone w paczce wynikowej, musimy je tutaj określić.

Ten plik będziemy także wykorzystywać do rozdzielania plików na mniejsze paczki ale tutaj taka mała uwaga. Pierw musimy zbudować pakiet bez jakiegokolwiek rozdzielania na mniejsze i zobaczyć jakie pliki uzyskamy w paczce wynikowej. Jeśli na samym początku zaczniemy bawić wpisami w *.install , jest wysokie prawdopodobieństwo, że pogubimy część potrzebnych plików, bo nie zostaną one w tych plikach *.install uwzględnione.

Jeśli z jednego źródła chcemy zbudować dwie paczki, potrzebne będą nam dwa pliki .install . Każdy z nich będzie miał nazwę wynikowego pakietu. Poniżej jest przykład budowania pakietu simplescreenrecorder i biblioteki libssr-glinject . Oba są budowane z tych samych źródeł. By odseparować pewne pliki od siebie i upchnąć je w osobnych paczkach, tworzymy poniższe pliki:

Plik debian/simplescreenrecorder.install :

usr/share/
usr/bin/

Plik debian/libssr-glinject.install :

usr/lib/*/libssr-glinject.so

W powyższym przykładzie jest podana tylko jedna ścieżka. Druga może być podana po spacji. Pierwsza z nich określa skąd kopiować plik, druga gdzie go wrzucić (w drzewie katalogu pakietu). Jeśli nie podamy tej drugiej ścieżki, zostanie przyjęta domyślnie i będzie taka sama jak pierwsza. Co do samych plików, to możemy precyzować każdy z osobna uwzględniając przy tym nazwę pliku w pierwszej ścieżce albo też możemy podawać ścieżki do całych katalogów, tak jak to widać wyżej. Trzeba pamiętać o tym, że przy pomocy *.install nie damy rady przepisać nazwy pliku, a druga ścieżka zawsze ma postać katalogu.

Jeśli chodzi o same biblioteki, to przy instalowaniu plików trzeba uważać, bo w systemie są różne ścieżki w zależności od jego architektury. Zamiast podawać dokładną ścieżkę, można użyć wildcard'a, tak by dopasować pewną jej części i sprawić tym samym, że pakiet zbuduje się na innej architekturze niż był pierwotnie budowany. Zwykle będzie to wyglądać tak: usr/lib/*/ , kluczowa jest tutaj ta gwiazdka w trzecim członie i to ona upchnie plik biblioteki w odpowiednim folderze (normalnie byśmy tam podali np. x86_64-linux-gnu).

Po rozdzieleniu paczek, trzeba będzie także odpowiednio uzupełnić plik debian/control tak by dodać opisy i zależności, przykładowo:

...
Package: simplescreenrecorder
Architecture: i386 amd64
Pre-Depends: ${misc:Pre-Depends}
Depends: ${shlibs:Depends}, ${misc:Depends}
Recommends: libssr-glinject
Description: Feature-rich screen recorder (main program)
 SimpleScreenRecorder is a feature-rich screen recorder that supports X11 and
 OpenGL. It has a Qt-based graphical user interface. It can record the entire
 creen or part of it, or record OpenGL applications directly. The recording
 can be paused and resumed at any time. Many different file formats and
 codecs are supported
 .
 This package contains the main program.

Package: libssr-glinject
Architecture: i386 amd64
Multi-Arch: same
Pre-Depends: ${misc:Pre-Depends}
Depends: ${shlibs:Depends}, ${misc:Depends}, simplescreenrecorder
Description: Feature-rich screen recorder (GLInject library)
 SimpleScreenRecorder is a feature-rich screen recorder that supports X11 and
 OpenGL. It has a Qt-based graphical user interface. It can record the entire
 screen or part of it, or record OpenGL applications directly. The recording
 can be paused and resumed at any time. Many different file formats and
 codecs are supported
 .
 This package contains the GLInject library.

debian/compat

Ten plik określa poziom kompatybilności debhelper'a i obecnie ma zawierać cyfrę 9 . Za co odpowiada ten poziom kompatybilności, to chyba nikt nie wie

Ten plik ma na celu tworzenie linków do plików, których nie chcemy bezpośrednio kopiować, bo, jakby nie patrzeć, to zajmuje cenne zasoby dyskowe, poza tym mniej danych trzeba przesłać przez sieć. Poniżej jest przykład takiego linku:

usr/share/man/man1/ansifilter.1.gz usr/share/man/man1/ansifilter-gui.1.gz

Ta linijka stworzy link ansifilter-gui.1.gz do pliku /usr/share/man/man1/ansifilter.1.gz w wynikowym pakiecie.

debian/*.lintian-overrides i debian/source/lintian-overrides

Te dwa pliki są wykorzystywane przez narzędzie lintian , które po przeskanowaniu paczek/źródeł wyrzuca komunikaty w przypadku, gdy coś odbiega od ustalonego standardu. Niemniej jednak, czasem się zdarza tak, że lintian będzie miał buga, albo też jego błąd tak naprawdę nie będzie żadnym błędem i będzie można cały komunikat zakwalifikować jako false-positive.

W obu przypadkach mamy możliwość ukrycia tych komunikatów, tak by nas już więcej nie niepokoiły. Jeśli problem dotyczy źródeł, będziemy wykorzystywać plik debian/source/lintian-overrides . Natomiast jeśli lintian ma problem z jakąś paczką wynikową, wtedy trzeba będzie stworzyć osobny plik z nazwą paczki i sufiksem .lintian-overrides .

Źródła powinny zostać zweryfikowane, a to zwykle odbywa się przez sprawdzenie sygnatury GPG. Nie każdy serwis daje nam taką możliwość i nie mamy tak naprawdę żadnego pola manewru w sytuacji, gdy twórca źródeł nie podpisał swojego dzieła cyfrowo. Jeśli napotkamy podobny problem, to jako że komunikat dotyczy źródeł pakietu, wrzucamy poniższą linijkę do pliku debian/source/lintian-overrides :

light-locker source: debian-watch-may-check-gpg-signature

Spora część komunikatów będzie jednak dotyczyć paczek wynikowych. Poniżej jest przykład ukrycia błędu w paczce libssr-glinject . W katalogu debian/ tworzymy plik libssr-glinject.lintian-overrides i wrzucamy do niego np. coś takiego:

libssr-glinject: shlib-without-versioned-soname usr/lib/*/libssr-glinject.so libssr-glinject.so
libssr-glinject: pkg-has-shlibs-control-file-but-no-actual-shared-libs

By stworzyć takie pliki musimy znać komunikat błędu, nazwę paczki oraz nazwę pliku, którego błąd dotyczy. Wszystkie te informacje wypisze nam lintian po przeskanowaniu paczek. Poniżej jest przykład:

W: libssr-glinject: shlib-without-versioned-soname usr/lib/x86_64-linux-gnu/libssr-glinject.so libssr-glinject.so
E: libssr-glinject: pkg-has-shlibs-control-file-but-no-actual-shared-libs

Jak widać, praktycznie niczym się te linijki z błędami nie różnią od tego co wpisaliśmy wyżej w pliku, no za wyjątkiem początkowych W: i E: . Struktura obu powyższych plików jest taka sama i dokładnie opisana jest tutaj.

source/format

Ten plik określa format źródeł pakietu i z reguły wpisujemy w nim 3.0 (native) dla natywnych debianowych pakietów, oraz 3.0 (quilt) dla wszystkiego innego i zwykle będziemy budować właśnie ten drugi rodzaj, gdzie wszystkie zmiany dokonywane przez nas na źródłach będą rejestrowane i zapisywane w plikach w katalogu debian/patches/ (o nim później). Takie zmiany po wypakowaniu źródeł będą automatycznie nakładane przez dpkg-source .

source/local-options i source/options

W tych plikach możemy zdefiniować opcje dla narzędzia dpkg-source wywoływanego z parametrami -b lub --print-format . Użyteczne mogą się okazać --compression czy --compression-level , z tym, że dodajemy je do pliku bez tych dwóch myślników na początku, przykładowo:

compression = "bzip2"
compression-level = 9

Jedyną różnicą między tymi dwoma plikami jest to, że source/local-options nie będzie dołączany w zbudowanych źródłach. Wszystkie dostępne opcje dla dpkg-source są przystępnie opisane w manualu.

debian/patches/ i debian/patches/series

Jako, że nie możemy bezpośrednio zmieniać upstream'owych źródeł, musimy robić łatki, by przy ich pomocy nanieść pożądane zmiany. Jeśli zmienimy pewnie pliki w źródłach, po czym spróbujemy zbudować taki pakiet, zostanie nam wypisany poniższy komunikat:

...
dpkg-source -b monitorix-3.6.0
dpkg-source: info: using source format '3.0 (quilt)'
dpkg-source: info: building monitorix using existing ./monitorix_3.6.0.orig.tar.gz
dpkg-source: info: local changes detected, the modified files are:
 monitorix-3.6.0/monitorix
dpkg-source: error: aborting due to unexpected upstream changes, see /tmp/monitorix_3.6.0-1.diff.6zxg54
dpkg-source: info: you can integrate the local changes with dpkg-source --commit
dpkg-buildpackage: error: dpkg-source -b monitorix-3.6.0 gave error exit status 2
debuild: fatal error at line 1376:
dpkg-buildpackage -rfakeroot -d -us -uc -S -sa failed

By zbudować taki pakiet musimy stworzyć łatę przy pomocy dpkg-source --commit .

Jest też jeszcze inne narzędzie, które ułatwia zarządzanie uprzednio stworzonymi patch'ami i jest to quilt . Pomoże nam on nie tylko w zakładaniu/ściąganiu łat ale również w ich edycji.

Musimy wiedzieć jak działa quilt , by sprawnie operować na łatach. Przede wszystkim, mamy do dyspozycji plik debian/patches/series i to w nim są zawarte informacje na temat tego jakie patch'e i w jakiej kolejności mają być założone na źródła. Poniżej przykładowy plik:

disable-update-check
#proper-tempfiles
assure-quit-keybinding
fix_desktop_file.patch

W katalogu debian/patches/ mamy zaś:

morfik:~/debian_build/minitube-2.3.1$ ls -al debian/patches/
total 28K
drwxr-xr-x 2 morfik morfik 4.0K 2015-02-12 20:13:12 ./
drwxr-xr-x 4 morfik morfik 4.0K 2015-02-15 04:55:45 ../
-rw-r--r-- 1 morfik morfik  968 2014-09-05 23:02:33 assure-quit-keybinding
-rw-r--r-- 1 morfik morfik  427 2014-09-05 23:02:33 disable-update-check
-rw-r--r-- 1 morfik morfik  396 2015-02-12 20:13:12 fix_desktop_file.patch
-rw-r--r-- 1 morfik morfik 2.5K 2014-09-05 23:02:33 proper-tempfiles
-rw-r--r-- 1 morfik morfik   85 2015-02-12 19:34:19 series

Patch'e są zakładane w kolejności od góry do dołu z perspektywy pliku debian/patches/series . Zatem pierw zostanie założony patch disable-update-check , potem by został założony proper-tempfiles ale, że jest wykomentowany, to zostanie pominięty, następnie jest zakładany assure-quit-keybinding i tak dalej, aż do końca pliku.

By założyć kolejny patch, korzystamy z quilt push, by ściągnąć obecny patch, dajemy quilt pop . Parametr -a dopisany do obu z powyższych poleceń, odpowiednio założy i ściągnie wszystkie łaty. Jeśli przez przypadek usunęliśmy pliki z patch'ami ale przy tym uprzednio ich nie ściągnęliśmy, będziemy musieli posłużyć się opcją -f . By sprawdzić, jaki patch jest aktualnie założony, wydajemy quilt applied . Możemy także przeprowadzać różne operacje na samych łatach, np. zmieniać ich nazwy. Dobrze jest rzucić okiem na manual quilt'a, gdzie znajdziemy opis wszystkich przydatnych funkcji. Można również zajrzeć pod ten adres, gdzie znajdziemy opis praktycznego zastosowania tego narzędzia.

Nagłówek łaty

Z bardziej przydatnych rzeczy musimy jeszcze wiedzieć co nieco o edycji nagłówków plików łat. Każdy patch musi bowiem spełniać standard DEP-3, który definiuje szereg pół wykorzystywanych w nagłówkach. Nagłówek wygenerowany przez dpkg-source --commit jest dość rozbudowany. Jeśli nie chce nam się wypełniać wszystkich linijek albo zwyczajnie nie jesteśmy w posiadaniu informacji, które moglibyśmy tam umieścić, to możemy ograniczyć się do dwóch obowiązkowych pól, czyli Description: oraz Author (opis i autor). Resztę natomiast można bez problemu skasować.

Spotkamy się także z sytuacjami, gdzie ktoś stworzy lub podeśle patch ale nie poda w nim żadnego nagłówka lub informacje w nim będą błędne. Być może nawet sami zapomnimy zmienić powyższy nagłówek, lub zwyczajnie go przeoczymy i nie uwzględnimy w łacie. W takich przypadkach możemy oczywiście dokonać edycji nagłówka, z tym, że dobrze jest to zrobić via quilt header --dep3 -e . Spowoduje to utworzenie domyślnego nagłówka, co ułatwi nam nieco robotę.

debian/*.examples (dh_installexamples)

Pozycje w tym pliku będą instalowane pod /usr/share/docs/nazwa_paczki/examples/ i generalnie będziemy tutaj umieszczać pliki zawierające przykłady, np. konfiguracji.

debian/*.templates

Ten plik jest używany do interakcji z użytkownikiem przy instalacji pakietu i odbywa się to przez zadawanie pytań, na które trzeba udzielić odpowiedzi i w oparciu o nie można skonfigurować pakiet. Generalnie rzecz biorąc nie spotkałem się z tym plikiem i za bardzo nic na jego temat nie napiszę. Za to, pod tym linkiem jest przykład jego wykorzystania.

debian/conffiles (dh_installdeb)

Debian automatycznie oznacza wszystkie pliki w katalogu /etc/ jako pliki konfiguracyjne i ich domyślnie ani nie usuwa przy pozbywaniu się pakietu z systemu ani też nie nadpisuje ich przy instalacji/aktualizacji pakietu. Jeśli się przyjrzymy procesowi budowania paczki, dostrzeżemy tam coś takiego:

...
   dh_installdeb
...
        find debian/monitorix/etc -type f -printf '/etc/%P' | LC_ALL=C sort >> debian/monitorix/DEBIAN/conffiles
        chmod 644 debian/monitorix/DEBIAN/conffiles
...

Dlatego też, nie musimy tworzyć pliku debian/conffiles , by powpisywać tam wszystkie pliki konfiguracyjne z katalogu /etc/ . Natomiast, jeśli zajrzymy do paczki wynikowej, zobaczymy tam, że został utworzy plik debian/connfiles i jest tam lista plików, np:

/etc/init.d/monitorix
/etc/logrotate.d/monitorix
/etc/monitorix/conf.d/debian.conf
/etc/monitorix/monitorix.conf
/etc/sysconfig/monitorix

By usunąć te pliki trzeba korzystać z opcji purge (np. przy aptitude). Z kolei jeśli chodzi o nowsze wersje tych plików, to sprawdzane są sumy kontrolne i jeśli się różnią, wtedy dostajemy stosowne powiadomienie i podejmujemy akcję dotyczącą ewentualnego nadpisania tego pliku, choć i tak stary (ew. nowy) plik będzie backup'owany. Jeśli sumy są takie same, pliki nadpisywane są bez jakiegokolwiek powiadamiania nas o tym, no bo w sumie i tak nie dokonywaliśmy żadnych zmian w tych plikach.

Plik debian/conffiles znajduje jedynie zastosowanie w przypadku, gdy konfiguracja budowanego pakietu znajduje się poza katalogiem /etc/ , co zwykle nie powinno mieć miejsca.

debian/.cron. (dh_installcron)

Czasami zdarzy się tak, że pewna funkcjonalność dostarczana wraz z budowanym pakietem będzie wymagać okresowego wywoływania jakichś operacji. Możemy to osiągnąć przez stworzenie jednego z pięciu (ew. kilku) plików: debian/*.cron.hourly , debian/*.cron.daily , debian/*.cron.weekly , debian/*.cron.monthly , debian/*.cron.d . Każdy z tych plików powędruje w określone miejsce w systemie, odpowiednio będzie to katalog /etc/cron.hourly/ , /etc/cron.daily/ , /etc/cron.monthly/ , /etc/cron.weekly/ oraz /etc/cron.d/ . Do pierwszych czterech wrzuca się skrypty, zaś do ostatniego plik, który musi ma być w formacie określonym przez crontab.

debian/*.logrotate (dh_installlogrotate)

Jeśli nasza paczka będzie zawierać demony, które będą logować zdarzenia, dobrze jest także zadbać o rotację logów, tak by jeden plik nie rozrastał się w nieskończoność. Wszystkie pliki określone w debian/*.logrotate powędrują do /etc/logrotate.d/ , zaś sam format tych plików określony jest w manie.

debian/.postinst, debian/.postrm, debian/.preinst, debian/.prerm

Te cztery pliki to skrypty maintainera i nie biorą one udziału przy budowaniu pakietu. Za to, potrafią robić pewne rzeczy przy instalowaniu, usuwaniu i aktualizowaniu paczki. To właśnie tutaj dodajemy, np. nowego użytkownika, czy nadajemy uprawnienia pewnym plikom. Zwykle nie będziemy musieli tworzyć tych skryptów. Jeśli jednak zdecydujemy się na nie, to musimy bardzo uważać, by czasem nie skasować sobie połowy systemu.

Skrypt preinst jest wywoływany przed rozpakowaniem paczki, z kolei skrypt postinst po jej rozpakowaniu. Podobnie z dwoma pozostałymi skryptami. prerm jest wywoływany przed usunięciem pakietu, a postrm po usunięciu.

Dokładny proces instalowania/deinstalowania/aktualizowania pakietu jest opisany tutaj. Są tam wyszczególnione wszystkie akcje podejmowane podczas powyższych czynności. Z kolei zaś, pod tym linkiem można znaleźć przykłady samych skryptów. Dobrze jest też zajrzeć w katalog debian/ innych pakietów, by podejrzeć jak inni maintainerzy tworzą te skrypty.

*.desktop

Ten plik nie jest bezpośrednio związany z katalogiem debian/ ale jest bardzo użyteczny w przypadku aplikacji graficznych, bo na jego podstawie mogą zostać utworzone skróty w menu, w które można kliknąć myszą. Format tego pliku jest opisany tutaj. Warto wiedzieć, że istnieje także narzędzie, które może pomóc nam w weryfikacji składni tego pliku. Jest to desktop-file-validate (pakiet desktop-file-utils ).

Hardening pakietów

Jeśli nasz pakiet zawierać będzie pliki wykonywalne, trzeba będzie je skompilować i trzeba będzie to zrobić z pewnymi określonymi flagami. Większość projektów ma już ustawione pewne flagi domyślne. Czasem może się zdarzyć tak, że akurat to są te flagi, których my potrzebujemy. Niemniej jednak, mi się przytrafił taki projekt, którego nie szło z początku niczym ruszyć. W przypadku jednej z paczek trzeba było edytować plik makefile , z kolei w przypadku drugiej trzeba było odprawić większe czary ale o tym za moment.

Na sam początek prześledźmy część pliku makefile:

...
###### Compiler, tools and options

CC            = gcc
CXX           = g++
DEFINES       = -DO2 -DQT_NO_DEBUG -DQT_GUI_LIB -DQT_CORE_LIB -DQT_SHARED
CFLAGS        = -pipe -march=x86-64 -mtune=generic -O2 -pipe -fstack-protector --param=ssp-buffer-size=4 -Wall -W -D_REENTRANT $(DEFINES)
CXXFLAGS      = -pipe -march=x86-64 -mtune=generic -O2 -pipe -fstack-protector --param=ssp-buffer-size=4 -Wall -W -D_REENTRANT $(DEFINES)
INCPATH       = -I/usr/share/qt4/mkspecs/linux-g++ -I. -I/usr/include/qt4/QtCore -I/usr/include/qt4/QtGui -I/usr/include/qt4 -I. -I.. -I. -I.
LINK          = g++
LFLAGS        = -Wl,-O1,--sort-common,--as-needed,-z,relro -Wl,-O1
LIBS          = $(SUBLIBS)  -L/usr/lib -lQtGui -lQtCore -lpthread
AR            = ar cqs
RANLIB        =
QMAKE         = /usr/bin/qmake-qt4
TAR           = tar -cf
COMPRESS      = gzip -9f
COPY          = cp -f
SED           = sed
COPY_FILE     = $(COPY)
COPY_DIR      = $(COPY) -r
STRIP         = strip
INSTALL_FILE  = install -m 644 -p
INSTALL_DIR   = $(COPY_DIR)
INSTALL_PROGRAM = install -m 755 -p
DEL_FILE      = rm -f
SYMLINK       = ln -f -s
DEL_DIR       = rmdir
MOVE          = mv -f
CHK_DIR_EXISTS= test -d
MKDIR         = mkdir -p
...

Mamy tam takie linijki jak CFLAGS , CXXFLAGS oraz LFLAGS . To właśnie te pozycje musimy zmienić. To jakie flagi musimy ustawić, możemy odnaleźć na wiki debiana, do poczytania tutaj, tutaj i tutaj . Nie zagłębiając się w szczegóły, zwykle wystarczy dopisać na początku pliku debian/rules , te poniższe linijki:

export DEB_BUILD_MAINT_OPTIONS = hardening=+all
DPKG_EXPORT_BUILDFLAGS = 1
include /usr/share/dpkg/buildflags.mk

Po zbudowaniu paczki, możemy sprawdzić czy powyższe rozwiązanie zadziałało i wykorzystujemy do tego celu polecenie hardening-check (pakiet hardening-includes ), przykładowo:

$ hardening-check  /usr/bin/ansifilter-gui
/usr/bin/ansifilter-gui:
 Position Independent Executable: no, normal executable!
 Stack protected: no, not found!
 Fortify Source functions: no, only unprotected functions found!
 Read-only relocations: no, not found!
 Immediate binding: no, not found!

Jeśli widzimy log jak powyżej, oznacza to, że flagi nie zostały poprawnie ustawione i trzeba kombinować jak je poprawić. Najłatwiejszym sposobem jest oczywiście edycja flag w plikach źródłowych, z tym, że nie zawsze to działa, np. projekty, które są budowane przy pomocy qmake generują sobie plik makefile i co z tego, że przepiszemy flagi, jak podczas budowania, ten plik zostanie nadpisany. Na wiki debiana jest kilka przykładów opisujących min. qmake czy cmake i możemy z nich skorzystać. W tym przypadku ( qmake ) trzeba było edytować plik .pro i ustawić w nim:

QMAKE_CPPFLAGS *= -g -O2 -fPIE -fstack-protector-strong -Wformat -Werror=format-security -D_FORTIFY_SOURCE=2 -fPIE -pie -Wl,-z,relro -Wl,-z,now
QMAKE_CFLAGS   *= -g -O2 -fPIE -fstack-protector-strong -Wformat -Werror=format-security -D_FORTIFY_SOURCE=2 -fPIE -pie -Wl,-z,relro -Wl,-z,now
QMAKE_CXXFLAGS *= -g -O2 -fPIE -fstack-protector-strong -Wformat -Werror=format-security -D_FORTIFY_SOURCE=2 -fPIE -pie -Wl,-z,relro -Wl,-z,now
QMAKE_LFLAGS   *= -g -O2 -fPIE -fstack-protector-strong -Wformat -Werror=format-security -D_FORTIFY_SOURCE=2 -fPIE -pie -Wl,-z,relro -Wl,-z,now

W każdej linijce zostały zawarte flagi, które zwracał dpkg-buildflags przy ustawieniu hardening=+all, przykładowo:

dpkg-buildflags --status
dpkg-buildflags: status: environment variable DEB_BUILD_OPTIONS=parallel=2
dpkg-buildflags: status: environment variable DEB_HOST_ARCH=amd64
dpkg-buildflags: status: vendor is Debian
dpkg-buildflags: status: hardening features: bindnow=no format=yes fortify=yes pie=no relro=yes stackprotector=yes stackprotectorstrong=yes
dpkg-buildflags: status: qa features: bug=no canary=no
dpkg-buildflags: status: reproducible features: timeless=no
dpkg-buildflags: status: CFLAGS [vendor]: -g -O2 -fstack-protector-strong -Wformat -Werror=format-security
dpkg-buildflags: status: CPPFLAGS [vendor]: -D_FORTIFY_SOURCE=2
dpkg-buildflags: status: CXXFLAGS [vendor]: -g -O2 -fstack-protector-strong -Wformat -Werror=format-security
dpkg-buildflags: status: FCFLAGS [vendor]: -g -O2 -fstack-protector-strong
dpkg-buildflags: status: FFLAGS [vendor]: -g -O2 -fstack-protector-strong
dpkg-buildflags: status: GCJFLAGS [vendor]: -g -O2 -fstack-protector-strong
dpkg-buildflags: status: LDFLAGS [vendor]: -Wl,-z,relro
dpkg-buildflags: status: OBJCFLAGS [vendor]: -g -O2 -fstack-protector-strong -Wformat -Werror=format-security
dpkg-buildflags: status: OBJCXXFLAGS [vendor]: -g -O2 -fstack-protector-strong -Wformat -Werror=format-security

Sprawdzając plik wykonywalny przy pomocy hardening-check , log zmienił się nieco:

$ hardening-check  /usr/bin/ansifilter-gui
/usr/bin/ansifilter-gui:
 Position Independent Executable: yes
 Stack protected: yes
 Fortify Source functions: yes (some protected functions found)
 Read-only relocations: yes
 Immediate binding: yes

I dokładnie o takie coś nam chodzi.

Jeśli mamy problem z ustaleniem jakich flag brakuje, pomocne może okazać się narzędzie blhc . Wszystko czego nam potrzeba by z niego skorzystać, to log z budowania pakietu. Jeśli dysponujemy takowym, brakujące flagi sprawdzamy w poniższy sposób:

$ blhc --color --build build_log
W-dpkg-buildflags-missing|CPPFLAGS 16 (of 32) missing|

$ blhc --color build_log
CXXFLAGS missing (-g): g++ -c -O2 -fstack-protector-strong -Wformat -Werror=format-security arg_parser.cpp -o arg_parser.o
CPPFLAGS missing (-D_FORTIFY_SOURCE=2): g++ -c -O2 -fstack-protector-strong -Wformat -Werror=format-security arg_parser.cpp -o arg_parser.o
CXXFLAGS missing (-g): g++ -c -O2 -fstack-protector-strong -Wformat -Werror=format-security stringtools.cpp -o stringtools.o
...

Jak widzimy wyżej, kilku obiektom brakuje flagi -g oraz -D_FORTIFY_SOURCE=2 . Trzeba mieć też na uwadze fakt, że przez taki hardening można nieco popsuć funkcjonalność pakietu.

Budowanie źródeł

Jeśli już dostosowaliśmy wszystkie pliki w katalogu debian/ , przyszedł czas na zbudowanie źródeł. W tym celu przechodzimy do głównego katalogu ze źródłami i wydajemy poniższe polecenie:

morfik:~/debian_build/monitorix-3.6.0$ debuild -S -sa -d -i --lintian-opts --profile debian

Skrypt debuild , w zależności od konfiguracji, zainicjuje dpkg-buildpackage oraz kilka dodatkowych narzędzi Podczas całego procesu, wszystkie łaty, które założyliśmy, zostaną automatycznie ściągnięte ( quilt ), a sam katalog debian/ zostanie oddzielony od źródeł i upchnięty w osobnej paczce ( dpkg-source ). Takie rozwiązanie ma na celu zmniejszenie ruchu sieciowego repozytorium, no bo jeśli aktualizujemy paczkę, to wystarczy pobrać tylko katalog debian/ i nie ma potrzeby przy tym pobierać samych źródeł z repozytorium, które czasami mogą ważyć nawet i dziesiątki czy setki MiB. Same źródła zostaną sprawdzone pod kątem ewentualnych błędów ( lintian ). Zostaną także wygenerowane trzy dodatkowe pliki: .build , .dsc oraz changes . Po całym procesie pliki .dsc i .changes zostaną podpisane cyfrowo ( gpg ).

W pliku .build będzie umieszczony log z operacji budowania źródeł, czyli dokładnie to samo co zostanie wydrukowane w terminalu. Plik .dsc zawiera min. sumy kontrole spakowanych źródeł i katalogu debian . Na tym etapie, plik .changes zbytnio się nie różni od pliku .dsc ale to w nim będą zawarte sumy kontrole wszystkich wyprodukowanych przez nas paczek .deb . Jest tam również kilka dodatkowych informacji, takich jak np. numery błędów, które ten pakiet ma zamykać.

Potrzebne zależności

Czasami może zdarzyć się tak, że podczas budowania źródeł będziemy musieli doinstalować w systemie kilka dodatkowych zależności. W takim przypadku można je naturalnie zainstalować ręcznie przy pomocy apt-get/aptitude . Można też skorzystać z apt-get build-dep w przypadku aktualizowania paczki, która już w repozytoriach Debiana się znajduje. Niemniej jednak, takie ręczne instalowanie zależności sprawi, że będziemy mieli kłopot z ich odinstalowaniem w późniejszym czasie (będziemy musieli zwracać uwagę na to co instalujemy). Dlatego też lepszym rozwiązaniem jest skorzystanie z narzędzia mk-build-deps , który jest w stanie stworzyć pusty pakiet .deb , w którym to znajdą się jedynie zależności wymagane podczas budowania źródeł. Później w dowolnym czasie ten pakiet możemy usunąć, a wszystkie zależności w nim określone (o ile nie wymagane przez inne pakiety) zostaną automatycznie usunięte z systemu.

Budowanie pakietu

Po zbudowaniu źródeł, przyszedł czas na zbudowanie pakietu. W tym celu przechodzimy do katalogu nadrzędnego (tam gdzie jest plik .dsc ) i zaprzęgamy pbuilder'a do roboty:

$ sudo pbuilder --build ./monitorix_3.6.0-1.dsc

W tej chwili pbuilder wypakowuje przygotowanego wcześniej chroot'a. Po wypakowaniu są sprawdzane i instalowane zależności. Po tym etapie, pliki źródłowe są kopiowane do środowiska chroot, po czym są one wypakowywane. Jeśli robiliśmy jakieś łaty, to zostaną one następnie automatycznie założone na źródła. Po tym procesie rozpoczyna się właściwa faza budowania pakietu. Dalej już tylko są wywoływane poszczególne targety debhelper'a (linijki z dh_ ) i jeśli proces zakończy się bez błędów, zbudowane w ten sposób paczki wędrują do katalogu docelowego. Jest także generowany plik .changes . Po zakończeniu, środowisko chroot jest usuwane. Paczki powinny się znaleźć w tym katalogu, który określiliśmy w konfiguracji pbuilder'a.

W pliku .changes powinny być teraz uwzględnione paczki .deb :

...
Checksums-Sha1:
...
 aed14e43b9604dc0bdfdc54ea05df91edb33d50b 156398 monitorix_3.6.0-1_amd64.deb
Checksums-Sha256:
...
 951ddd2ace33cac27cd1c02ec695ffce9cd5e6e2d6758676851003e72969ae83 156398 monitorix_3.6.0-1_amd64.deb
Files:
...
 adede69324183ba2248c093263e06934 156398 net optional monitorix_3.6.0-1_amd64.deb

Niemniej jednak, ten plik .changes nie został jeszcze podpisany. Musimy go podpisać ręcznie przy pomocy tego poniższego polecenia:

$ debsign /media/Kabi/pbuilder/result/monitorix_3.6.0-1_amd64.changes

Została nam jeszcze ostania rzecz, czyli zweryfikowanie czy stworzona przez nas paczka zawiera jakieś błędy. Wołamy zatem lintian'a by sprawdził wszystkie paczki, które znajdzie w wygenerowanym przed chwilą pliku .changes :

$ rm -R /media/Kabi/pbuilder/lintian/*

$ lintian --setup-lab

$ lintian /media/Kabi/pbuilder/result/monitorix_3.6.0-1_amd64.changes

Jeśli chodzi o samego lintian'a jeszcze, to dobrze jest usunąć poprzednie katalogi robocze, bo śmieci tam zgromadzone, mogą wpłynąć na wyniki skanowania.

W tym przypadku nie ma żadnych błędów ani ostrzeżeń, zatem paczka została zbudowana pomyślnie i pewnie spełnia wszystkie standardy debiana, co otwiera drogę do umieszczenia jej w oficjalnym repozytorium dystrybucji.

Manualne budowanie pakietów

Prawdopodobnie zdarzy się tak, że z jakiegoś powodu utkniemy i trzeba będzie przetestować szereg rzeczy, tak by doprowadzić proces budowania pakietu do końca. Jeśli będziemy z każdą drobną poprawką wywoływać pbuilder'a, to nas szlag trafi w oczekiwaniu na zainstalowanie wszystkich tych zależności w środowisku chroot. Nie chcemy też sobie zaśmiecać systemu tymi zbędnymi pakietami używanymi jedynie przy budowaniu paczki. Mamy do wyboru dwie opcje, z których jedna została opisana już na początku. Druga opcja to skorzystanie z hook'a, który jest dostępny w katalogu /usr/share/doc/pbuilder/examples/ . Mowa o C10shell . Umożliwia on przerwanie operacji przy ewentualnych błędach podczas budowania paczki i zrzucenie nas do shell'a wewnątrz środowiska chroot. Standardowo pbuilder by przerwał akcję i posprzątał po sobie. Linkujemy ten hook:

$ ln -s /usr/share/doc/pbuilder/examples/C10shell /media/Kabi/pbuilder/hooks/

Od tego momentu, jeśli pbuilder napotka błąd, doinstaluje w chroot min. edytor vim i da nam szansę sprawdzić czemu pakiet się nie zbudował.

Musimy umieć się poruszać wewnątrz środowiska chroot. Przede wszystkim, znajdziemy się w katalogu ze źródłami (tam gdzie jest katalog debian/ ), z tym, że nasz katalog roboczy zostanie zmieniony nieco:

root@morfikownia:~/monitorix-3.6.0# pwd
/tmp/buildd/monitorix-3.6.0

Sama edycja plików nie powinna przysporzyć problemów, jedynie co, to trzeba opanować vim'a, choć można też i to obejść ale o tym za moment. Jeśli problemy będą tkwić w ścieżkach plików, zawsze możemy porównać je z tym co wypisał nam debhelper . Przykładowo:

...
dh_install --
   cp -a ./docs/debian.conf debian/monitorix/etc/monitorix/conf.d/
...

Powyższy wycinek z logu mówi, że plik debian.conf z folderu ./docs/ (wszystko odbywa się względem naszego katalogu roboczego), czyli w sumie jest to /tmp/buildd/monitorix-3.6.0/docs/debian.conf ma zostać przekopiowany do debian/monitorix/etc/monitorix/conf.d/ . Zajrzyjmy zatem do tego tajemniczego katalogu debian/monitorix/ :

root@morfikownia:~/monitorix-3.6.0# ls -al debian/monitorix
total 24
drwxr-xr-x 6 morfik morfik 4096 Feb 22 12:50 .
drwxr-xr-x 5 morfik morfik 4096 Feb 22 12:50 ..
drwxr-xr-x 6 morfik morfik 4096 Feb 22 12:50 etc
drwxr-xr-x 3 morfik morfik 4096 Feb 22 12:50 lib
drwxr-xr-x 5 morfik morfik 4096 Feb 22 12:50 usr
drwxr-xr-x 3 morfik morfik 4096 Feb 22 12:50 var

Widzimy tutaj, że jest to część struktury folderów, która przypomina tę z naszego systemu operacyjnego. To tutaj właśnie się buduje cały projekt, czyli w odpowiednie miejsca są kopiowane dostarczane z projektem pliki. Jeśli z jakiegoś powodu coś nie gra, to tu zaczynamy poszukiwania, oczywiście podpierając się logiem debhelper'a.

Gdy już ustalimy w czym tkwił problem, budujemy testowo paczkę .deb , z tym, że korzystamy już bezpośrednio z narzędzia dpkg-buildpackage :

root@morfikownia:~/monitorix-3.6.0# dpkg-buildpackage -uc -us -b
...

Jeśli paczka się zbuduje, znaczy, że możemy opuścić ten chroot.

Powyższe środowisko to chroot, zatem mamy dostęp do jego plików z maszyny hosta, wystarczy znaleźć miejsce gdzie się ulokował ten chroot. Miejsce gdzie zaczynamy poszukiwania jest określone w pliku konfiguracyjnym pbuilder'a. Jako, że środowiska są wypakowywane za każdym razem, tworzone są foldery z numerkami pid'ów procesu pbuilder'a, przykładowo /media/Kabi/pbuilder/build/69865/ . Jeśli chcielibyśmy w trybie graficznym edytować, np. plik debian/rules , to używamy ścieżki /media/Kabi/pbuilder/build/69865/tmp/buildd/monitorix-3.6.0/debian/rules .

Zgubione pliki (dh_missing)

Budując pojedynczą paczkę nie ma w zasadzie obaw, że jakieś pliki nam się gdzieś zawieruszą, bo wszystkie pliki wynikowe są pakowane do tej jednej paczki. Niemniej jednak, w przypadku bardziej zaawansowanych projektów, z jednego źródła będziemy w stanie wyprodukować kilka paczek. W takim przypadku trzeba będzie rozdzielić pliki. Może się zatem zdarzyć tak, że przez naszą nieuwagę pewne pliki nie trafią na swoje miejsce lub zostaną pominięte i zwyczajnie nieuwzględnione w żadnym pakiecie, który zostanie zbudowany. By tego typu sytuacjom przeciwdziałać dobrze jest w pliku reguł debian/rules zawrzeć poniższe przepisanie targetu dh_missing :

override_dh_missing:
    dh_missing --list-missing --fail-missing

W tym powyższym nadpisaniu mamy dwa dodatkowe parametry, z których --list-missing wypisuje wszystkie pliki, które nie zostaną uwzględnione w żadnym wynikowym pakiecie. Jeśli dodatkowo zostanie podany --fail-missing , to proces budowy pakietu zostanie przerwany jeśli taki plik zostanie odnotowany.

Instalowanie zbudowanego pakietu

Jeśli mamy obawy co do tego jak zachowa się paczka podczas jej instalacji w systemie, możemy skorzystać z narzędzia piuparts , które w środowisku chroot przetestuje wszystkie kombinacje instalacji/deinstalacji i aktualizacji pakietu i zwróci obszerny log na temat modyfikowanych plików. Na końcu zostanie także przedstawione podsumowanie. Poniżej przykładowe sprawdzenie paczki ansifilter-gui_1.11-1_amd64.deb w środowisku chroot upchniętym w base.tgz .

# piuparts /media/Kabi/pbuilder/result/ansifilter-gui_1.11-1_amd64.deb -b /media/Kabi/pbuilder/base.tgz

Aktualizowanie pakietu

Aktualizacja pakietu .deb, który jest dostępny w debianie i ma zdebianizowane źródła, sprowadza się do pobrania tych źródeł przy pomocy apt . Trzeba pamiętać by wpis z deb-src był dodany do pliku /etc/apt/sources.list . Źródła pobieramy w poniższy sposób:

$ apt-get source wpasupplicant
Reading package lists... Done
Building dependency tree
Reading state information... Done
Picking 'wpa' as source package instead of 'wpasupplicant'
Selected version '2.2-1' (testing) for wpa
NOTICE: 'wpa' packaging is maintained in the 'Svn' version control system at:
svn://anonscm.debian.org/pkg-wpa/wpa/trunk/
Need to get 1,801 kB of source archives.
Get:1 http://ftp.pl.debian.org/debian/ sid/main wpa 2.2-1 (dsc) [2,483 B]
Get:2 http://ftp.pl.debian.org/debian/ sid/main wpa 2.2-1 (tar) [1,725 kB]
Get:3 http://ftp.pl.debian.org/debian/ sid/main wpa 2.2-1 (diff) [74.3 kB]
Fetched 1,801 kB in 1s (1,458 kB/s)
gpgv: Signature made Wed 17 Sep 2014 09:53:09 PM CEST using RSA key ID C2B35520
gpgv: Can't check signature: public key not found
dpkg-source: warning: failed to verify signature on ./wpa_2.2-1.dsc
dpkg-source: info: extracting wpa in wpa-2.2
dpkg-source: info: unpacking wpa_2.2.orig.tar.xz
dpkg-source: info: unpacking wpa_2.2-1.debian.tar.xz
dpkg-source: info: applying 01_use_pkg-config_for_pcsc-lite_module.patch
dpkg-source: info: applying 02_dbus_group_policy.patch
dpkg-source: info: applying 06_wpa_gui_menu_exec_path.patch
dpkg-source: info: applying 07_dbus_service_syslog.patch
dpkg-source: info: applying 12_wpa_gui_knotify_support.patch
dpkg-source: info: applying wpa_gui_desktop_add-keywords-entry.patch
dpkg-source: info: applying wpa_supplicant-MACsec-fix-build-failure-for-IEEE8021.patch
dpkg-source: info: applying ap_config_c_fix-typo-for-capabilities.patch

Paczki budowane ze źródeł nie zawsze mają nazwy takie same jak ich źródła ale to nie stanowi problemu dla apt i ten sobie odszuka odpowiednie pakiety, tak jak nam to wyżej oznajmił: Picking 'wpa' as source package instead of 'wpasupplicant' . Dalej w logu widzimy, że z repozytorium zostały pobrane trzy pliki: .dsc , .tar oraz .diff .

Plik .dsc zawiera szereg informacji opisujących pliki, źródła i ludzi, którzy opiekują się daną paczką. Są tam też zawarte linki, pod którymi można znaleźć dany projekt. Dodatkowo, znajdują się tam też informacje na temat sum kontrolnych źródeł oraz katalogu debian/ . Całość jest podpisana cyfrowo, co gwarantuje integralność danych, które właśnie pobraliśmy. Jeśli uważnie przejrzeliśmy log pobierania źródeł, możemy tam zauważyć, że apt nie może zweryfikować sygnatury. By zweryfikować podpis, potrzebny nam jest klucz publiczny osoby, która podpisała powyższy plik. Pobranie klucza i weryfikację podpisu możemy przeprowadzić ręcznie przy pomocy poniższych linijek:

$ gpg --verify wpa_2.2-1.dsc
$ gpg --search-keys 0xFF914AF0C2B35520
$ gpg --verify wpa_2.2-1.dsc

Zwykle plik zostanie poprawnie zweryfikowany, ale jako że nie mamy określonego zaufania do osoby, której klucz publiczny posiadamy, to zostanie nam wyświetlone ostrzeżenie: WARNING: This key is not certified with a trusted signature!. Samo ostrzeżenie nie wpływa jednak na weryfikację podpisu, a ten jest w porządku, zatem możemy przejść dalej.

Poza plikiem .dsc , pobrał się także plik .tar . Zawiera on spakowane źródła aplikacji, zwykle te udostępnianie przez jej dewelopera. W pliku .dif zaś są umieszczone wszelkie zmiany jakie muszą być poczynione, by zdebianizować oryginalne źródła. Po pobraniu tych plików, źródła są wypakowywane i dostosowywane, wliczając w to też nakładanie łat, jakie opiekun pakietu przygotował. Po tym zabiegu mamy przygotowany katalog wpa/ .

W katalogu z wypakowanymi źródłami znajduje się podkatalog debian/ i to w nim będziemy dokonywać praktycznie wszelkich zmian. Sam katalog, jak możemy wywnioskować z tych wszystkich powyższych informacji, jest odrębną całością i jest pakowany oddzielnie. Można go też przenosić między kolejnymi wersjami źródeł. Jeśli zajrzymy do katalogu gdzie pobraliśmy źródła, możemy dostrzec również spakowany plik wpa_2.2-1.debian.tar.xz . To tam właśnie jest ulokowany katalog debian/ .

Aktualizacja długo nieaktualizowanego pakietu

Jakiś czas temu, próbowałem sobie zaktualizować pakiet minitube , bo w repozytorium była dostępna wersja sprzed chyba 2 lat. Co w takim przypadku należałoby zrobić, by zaktualizować ten pakiet? Na początek pobieramy stare źródła przy pomocy apt-get source . Po ich pobraniu, będziemy mieli dostęp do pliku minitube_2.0-1.debian.tar.xz . Pobieramy ze strony projektu najnowsze źródła i zapisujemy je w tym samym katalogu. Zmieniamy nazwę pliku z nowymi źródłami, na taką według wzoru: program_versja.orig.tar.{gz,bz2,xz} , po czym wypakowujemy te dwa pliki. Następnie kopiujemy katalog debian/ do folderu z nowszą wersją źródeł i przechodzimy do tego katalogu. Poniżej praktyczny przykład:

$ apt-get source minitube
$ wget http://flavio.tordini.org/files/minitube/minitube.tar.gz
$ mv minitube.tar.gz minitube_2.2.orig.tar.gz
$ tar xpf minitube_2.2.orig.tar.gz
$ tar xpf minitube_2.0-1.debian.tar.xz
$ cp -a debian/ minitube

Teraz pozostaje nam edycja poszczególnych plików. Pierwszym, na który musimy rzucić okiem, jest plik debian/control . Na stronie projektu danej aplikacji, zwykle przy nowszych wersjach, będą publikowane informacje na temat zależności, które muszą być spełnione, by dana aplikacja działała w naszym systemie bez zarzutu. Także jeśli podczas budowania nowszej wersji pakietu, system narzeka na brak pewnych bibliotek, znaczy to, że musimy uaktualnić pole Build-Depends: . Nie trzeba też zwykle definiować zależności, które muszą być spełnione przy instalacji pakietu w systemie, bo one powinny zostać wygenerowane automatycznie w oparciu o ${shlibs:Depends}, ${misc:Depends}, .

Kolejnym miejscem gdzie prawdopodobnie będziemy musieli zajrzeć przy aktualizacji pakietu jest katalog debian/patches/ , który zawiera wszystkie modyfikacje źródeł wprowadzone przez opiekuna pakietu. Zwykle przy nowszej wersji, część z łatek zwyczajnie staje się zbędna lub też zbudowanie pakietu za ich sprawą może okazać się niemożliwe. W takim przypadku, w logu będzie informacja odnośnie łaty, która stwarza problemy i należy ją wyłączyć przez zakomentowanie odpowiedniej linijki w pliku debian/patches/series .

Przy aktualizacji paczki, dobrze jest też za sprawą dch uzupełnić wpisy w changelog'u odnośnie tego co zostało zmienione. Zwykle będziemy korzystać z opcji -n , -i oraz -r . Jeśli byśmy dokonywali aktualizacji czyjejś paczki, wtedy posługujemy się parametrami -n oraz -r . W przypadku aktualizacji swojej, wykorzystujemy -i oraz -r . Opcja -n uzupełnia wpis w changelog'u o Non-maintainer upload :

 minitube (2.2-1.1) UNRELEASED; urgency=medium

  * Non-maintainer upload.
  *

  -- Mikhail Morfikov <morfik@nsa.com>  Fri, 26 Sep 2014 17:30:33 +0200

Tam gdzie są gwiazdki, definiujemy zmiany. Natomiast opcja -r zmienia UNRELEASED , widoczny wyżej, na unstable , przykład:

 minitube (2.2-1.1) unstable; urgency=medium

   * Non-maintainer upload.
   * New upsteram version.

  -- Mikhail Morfikov <morfik@nsa.com>  Fri, 26 Sep 2014 17:33:19 +0200

I to jest w zasadzie wszystko czego od nas wymaga proces przygotowywania źródeł, przynajmniej w przypadku aktualizowanych paczek. Musimy teraz zbudować osobno źródła i paczkę .deb , tak jak to zostało opisane wyżej w rozdziale 8 . Dobrze jest także zajrzeć sobie do tego poradnika i przejrzeć poszczególne podrozdziały. Więcej bardziej zaawansowanych informacji można znaleźć w podręczniku dla deweloperów oraz w dokumencie poświęconym polityce debiana. Na wiki debiana jest też kilka obszerniejszych wpisów min. ten dotyczący wprowadzenia do pakietowania, który można obrać jako punkt wyjścia.

Aktualizacja pakietu z repozytorium git

Jako, że spora ilość projektów jest utrzymywana w jakimś systemie kontroli wersji (CVS), to przydałoby się poruszyć temat ich aktualizacji. Oczywiście, gdy zostaje wypuszczona nowa wersja danej aplikacji, to nie ma problemu z aktualizacją takiego oprogramowania. Niemniej jednak, deweloperzy rozwijając dany projekt niekoniecznie chcą co każdy kolejny commit wypuszczać nową wersję programu. Sporo problemów i/lub błędów jest naprawianych chwilę po ich zgłoszeniu, co owocuje kolejnymi commit'ami. Gdy tych commit'ów zbierze się jakaś większa ilość (lub będą łatać krytyczne dziury), to wtedy dopiero autor projektu decyduje się na wydanie nowej wersji appki. Przez ten cały czas od jednego wydania do drugiego zwykle korzystamy ze starszej wersji programu do momentu, aż zostanie wypuszczony nowy release, mimo, że pewne zmiany w projekcie już się dokonały. Jeśli nie chce nam się czekać na moment wypuszczenia kolejnego release, to możemy pobrać wersję git danej aplikacji i to z niej zbudować pakiet .deb .

Najnowsze źródła zawsze pobieramy za pomocą git clone (lub git pull , jeśli repozytorium mamy już na dysku). Następnie tak uzyskany katalog trzeba spakować. Poniżej przykład:

$ git clone https://github.com/mhogomchungu/ussd-gui/
$ tar --exclude='.git*' --exclude='.pc' -cf - ussd-gui | xz -9 -c - > ussd-gui_1.2.0+git20160426.orig.tar.xz

W ten sposób uzyskujemy paczkę z oryginalnymi źródłami niezbędnymi w dalszym procesie budowania pakietu .deb . Nazwa tego archiwum zawiera w sobie +git20160426 . Standardowo po numerze wersji ostatniego release jest wskazanie, że mamy do czynienia z wersją git ( +git ). Następnie jest określona data najświeższego commit'a, który został przepchnięty do repozytorium, w formie YYYYMMDD.

W changelog'u ( dch -i ) uwzględniamy tą wersję oraz dorzucamy informacje na temat tego commit'a. Można ją wyciągnąć z logu git'a w poniższy sposób:

$ git log --graph --decorate --oneline
* 84b2c13 (HEAD -> master, origin/master, origin/HEAD) minor code improvements
...

Poniżej zaś przykładowy wpis w pliku changelog'a:

ussd-gui (1.2.0+git20160426-2) unstable; urgency=medium

  * New upstream snapshot (84b2c13)

 -- Mikhail Morfikov <morfik@nsa.com>  Tue, 26 Apr 2016 21:00:59 +0200

Tak przygotowane źródła można zbudować standardową metodą.

Mikhail Morfikov avatar
Mikhail Morfikov
Po ponad 10 latach spędzonych z różnej maści linux'ami (Debian/Ubuntu, OpenWRT, Android) mogę śmiało powiedzieć, że nie ma rzeczy niemożliwych i problemów, których nie da się rozwiązać. Jedną umiejętność, którą ludzki umysł musi posiąść, by wybrnąć nawet z tej najbardziej nieprzyjemniej sytuacji, to zdolność logicznego rozumowania.