Tworzymy paczkę
Budowanie pakietów (nie tylko) dla początkujących

Tekst ukazał się w numerze specjalnym Linux+ wraz z dystrybucją Debian GNU/Linux (Woody). W roku 2006 w dalszym ciągu jest w miarę aktualny.

Kiedy ponad rok temu przygotowywałem swój pierwszy w życiu pakiet dla Debiana, rzucił mi się w oczy brak polskiego tekstu opisującego cały proces budowy paczki debianowej. Konkretnie - brak tekstu dla początkujących, gdyż w poświęconym Debianowi Potato numerze Linux+ pojawił się artykuł przeznaczony raczej dla zaawansowanych użytkowników dpkg. Mam nadzieję, że ten tekst pomoże zapełnić tę lukę w literaturze.

W katalogu /usr/share/docs/ można znaleźć kilka dokumentów, które przydadzą się przy tworzeniu oficjalnych pakietów i z częścią nich powinni się zapoznać wszyscy przyszli developerzy Debiana. Jeśli chcecie stworzyć pakiet dla własnego użytku, poniższy artykuł powinien wam wystarczyć, jednak mimo wszystko dobrze jest zapoznać się z następującymi dokumentami (w nawiasie podaję pakiet, w którym znajduje się każdy tekst):

Debian New Maintainer's Guide (maint-guide) - chyba najlepszy istniejący podręcznik tworzenia pakietów dla Debiana. Napisany z myślą o początkującym developerze, prowadzi go praktycznie krok po kroku od momentu przygotowywania źródeł do spakowania, aż do uploadu gotowego pakietu na serwer oraz jego późniejszych aktualizacji.

Debian Policy Manual (debian-policy) jak również wszystkie dokumenty z pakietu debian-policy są opisem standardów stosowanych przez wszystkich twórców Debiana i każdy pakiet, który ma trafić do dystrybucji, powinien być zgodny z opisanymi w tych dokumentach założeniami.

Debian Packaging Manual (packaging-manual) - opis technicznej części budowy pakietów, w którym można znaleźć specyfikację plików kontrolnych, a także instrukcja obsługi niektórych narzędzi niezbędnych przy pracy nad tworzeniem plików .deb.

Debian Developer's Reference (developers-reference) - dokument przeznaczony głównie dla developerów i kandydatów na developerów, w którym można znaleźć informacje na temat sposobu uploadowania gotowych pakietów do dystrybucji, informacje na temat obsługi błędów, sposób zarządzania kluczem gpg itp.

Jak głosi rzymskie przysłowie (przytoczone zresztą przez Josepha Rodina we wstępie do Debian New Maintainer's Guide), najprościej uczyć na przykładach. W tym celu stworzyłem prosty pakiet składający się z jednego pliku wykonywalnego, hello, makefile-a oraz krótkiej dokumentacji -- strony manuala, pliku README oraz changelog. Oznacza to, że w jego skład wchodzą wszystkie podstawowe elementy spotykane w zaawansowanych programach.

Do tworzenia pakietu Debiana wykorzystamy narzędzia debhelper oraz dh_make, które zostały stworzone z myślą o ułatwieniu całego procesu. Są one wykorzystywane również przez twórców Debiana, tak więc nie ma obawy, że pakiet wykonany przy ich pomocy mógłby mieć problemy ze znalezieniem się w oficjalnej wersji Debiana.

Pierwszym narzędziem, po które sięgniemy, jest dh_make - narzędzie, które rozpakuje nam kod źródłowy opracowywanego przez nas oprogramowania, a także założy strukturę plików wymaganą podczas budowania pakietu. Załóżmy, że oprogramowanie skopiowane przez nas ze stron producenta jest spakowane do pliku hello_0.02.tar.gz. Zakładamy sobie katalog ~/debian, w którym będziemy trzymać nasze pakiety, a w nim podkatalog hello-0.02. Jego nazwa powinna składać się z samych małych liter, a numer wersji musi być oddzielony znakiem minus. Jeśli autor oryginalnego oprogramowania nie numeruje wersji w sposób XX.YY.ZZ, lecz na przykład wstawia tam datę, musimy przekształcić proponowaną przez niego nazwę w coś o kształcie nazwa-0.0.20011218. Do tak utworzonego podkatalogu rozpakowujemy plik ze żródłami.
Do katalogu debian/ kopiujemy plik z kodem źródłowym, wchodzimy do uprzednio założonego podkatalogu i wykonujemy komendę dh_make -e moj_adres@mojekonto.pl -f ../hello_0.02.tar.gz. Zostaniemy zapytani o to, czy pakiet będzie biblioteką, programem z jedną lub wieloma binarkami. Najczęstszą odpowiedzią będzie tu s, nawet jeśli nasze oprogramowanie udostępnia więcej niż jeden plik wykonywalny - możliwość m (multiple binary) przydaje się przy tworzeniu dużych pakietów, które będą się później składać z wielu plików .deb.
Po potwierdzeniu naszego wyboru, w katalogu debian/hello-0.02 zostanie utworzony podkatalog debian, w którym znajdują się wszystkie pliki wykorzystywane przy budowie pakietu .deb. Jak się za chwilę okaże, część z nich można usunąć bez obaw o konsekwencje - dh_make tworzy ich tyle, ile możemy potrzebować, rzadko jednak zdarza wszystkie wykorzystamy.

Pierwszą rzeczą, na którą musimy zwrócić uwagę, jest lokalizacja plików powstałych po kompilacji oprogramowania. Zwykle - choć nie zawsze - są one skonfigurowane tak, by pliki należące do programu lądowały w podkatalogach /usr/local/. Pakiety Debiana powinny kopiować pliki binarne do /usr/bin/, manuale do /usr/share/man/, a dokumentację - do /usr/share/doc/.
Dlatego też musimy poprawić plik Makefile naszego przykładowego pakietu w taki sposób, żeby był dopasowany do wymagań stawianych przez Debian Policy. W tym celu modyfikujemy wszystkie ścieżki w sekcji install. Trzeba pamiętać, że w przypadku programów wykorzystujących programy typu automake czy autoconf, edycja wynikowego Makefile na wiele nam się nie przyda, gdyż będzie on za każdym razem generowany na nowo. Dlatego do przygotowywania pakietów z takim oprogramowaniem przydaje się znajomość struktury plików Makefile.am i Makefile.in.

W chwili, kiedy rozpoczynamy generowanie pakietu, na dysku zakładany jest katalog tymczasowy, w którym odwzorowana jest struktura plików tworzonych podczas kompilacji. Dlatego w wykorzystywanym później do budowy pakietu pliku Makefile przed każdą ścieżką powinniśmy dodać zmienną ustawianą przez builder. Zmienna o nazwie DESTDIR wymagana jest tylko przy programach nie korzystających z autoconfa, gdyż w ich przypadku odpowiednie modyfikacje zostaną automatycznie wprowadzone przez dh_make.

Tak więc na początku naszego przykładowego Makefile-u pojawi się teraz nowa linijka: DESTDIR =, a przed każdą ścieżką docelową - $(DESTDIR). Ostateczny wygląd pliku Makefile przedstawiony jest na rysunku 2.

Bardzo ważne jest pamiętanie, że zmiana katalogu z danymi, z których obrabiany przez nas program korzysta dopiero podczas uruchomienia, wymaga nie tylko zmian w Makefile-u, lecz również ingerencji w sam kod źródłowy, ewentualnie pliki konfiguracyjne. Nie jest do tego wprawdzie potrzebna doskonała znajomość języka, w którym stworzono dane oprogramowanie, jednak jeśli nie jesteśmy pewni, czy sobie dobrze poradziliśmy z modyfikacją źródeł warto poświęcić więcej niż zwykle czasu na testowanie finalnego pakietu.

Przyszedł czas na rzecz najważniejszą - edycję plików umieszczonych przez dh_make w podkatalogu debian/. Pierwszym z nich jest control, w którym umieszczane są informacje o autorze pakietu, jego nazwie, architekturach, na których działa, a także jego krótki opis. Pierwotnie plik ten wygląda w następujący sposób:

Source: hello
Section: unknown
Priority: optional
Maintainer: Debian User <honey@debian.org>
Standards-Version: 3.0.1

Package: hello
Architecture: any
Depends: ${shlibs:Depends}
Description: <insert up to 60 chars description>
 <insert long description, indented with spaces>

Pierwsza linijka jest nazwą pakietu źródłowego, następnie znajduje się miejsce na wpisanie sekcji, w której pakiet zostanie umieszczony. Może to być main, non-free lub contrib. W związku z tym, że nasz pakiet jest rozprowadzany na zasadach licencji GNU GPL, może on trafić do sekcji main. Gdyby jego zainstalowanie pociągało za sobą konieczność instalacji oprogramowania z sekcji non-free, trafiłby on do sekcji contrib. Następnie trzeba podjąć decycję co do podsekcji - wśród możliwych do wpisania są x11, base, devel itp. Nasz program kwalifikuje się do kategorii misc, co też wpisujemy w odpowiednie miejsce.

Opcja Priority określa, jak ważny jest nasz program dla całości dystrybucji - w związku z tym, że nie straci ona na stabilności i użyteczności bez niego, zostawiamy tam wpis optional.
Pod Priority powinniśmy wpisać oprogramowanie, które nie znajduje się w standardowym Debianie, a które jest wymagane do kompilacji naszego pakietu. Podczas jego budowy wykorzystamy możliwości dawane przez pakiet debhelper, dlatego dodajmy tam linijkę o treści Build-Depends: debhelper.

Kolejne pola są miejscem na wpisanie naszych danych, wersji Debian-Policy z którą nasz pakiet jest zgodny, nazwy pakietu binarnego oraz architekturę (zwykle any). Ostatnie trzy pola posłużą nam na ustawienie zależności (wpis ${shlibs:Depends} zwykle wystarczy), krótkiego opisu wyświetlanego np podczas listowania pakietów komendą dpkg -l oraz dłuższego opisu, który pojawi się na przykład podczas przeglądania dostępnych pakietów programem dselect.

Oczywiście nie są to wszystkie możliwe opcje, które mogą się znaleźć w tym pliku, jednak te wystarczają do stworzenia większości pakietów. Wygląd pliku control po wprowadzeniu odpowiednich modyfikacji znajdziecie na rysunku 3.

Innym dość ważnym dla dystrybucji i samego pakietu jest copyright, który zwykle zastępuje plik z licencją dołączaną do oprogramowania. Umieszcza się w nim dane dotyczące autora pakietu, autora oryginalnego oprogramowania, a także adres pakietu źródłowego oraz krótką informację o licencji - ewentualnie, jeśli jest to licencja niestandardowa (nieznajdująca się w /usr/share/common-licenses/) - jej pełną treść. Dzięki temu unika się istnienia na dysku kilku tysięcy jednakowych kopii pliku z treścią GNU GPL, a pojawia się standardowe miejsce, w którym można znaleźć informacje o sposobie licencjonowania danego pakietu.

Kiedy tworzymy pakiet, często nieuniknione są poprawki w jego źródła - czy to zmiany w Makefile, czy też poprawione drobne błędy, które w nim się pojawiły. Do tego, a także do informowania o różnicy pomiędzy kolejnymi wersjami pakietów, służy plik changelog. Umieszczany później pod nazwą changelog.Debian, jest nierzadko podstawowym źródłem informacji przy podejmowaniu decyzji o ewentualnej aktualizacji. Jeśli tworzymy nową wersję pakietu tylko dlatego, że pojawiła się nowa wersja oprogramowania, umieszczamy tu linijkę w stylu New upstream release, jeśli jest to jego pierwsza wersja - pierwszym wpisem powinno być Initial Release..
Informacje zawarte w tym pliku - a konkretnie numer wersji pakietu umieszczony w nawiasach przy jego nazwie - służy do ustalenia ostatecznej nazwy pakietu .deb. Ostateczna zawartość debian/changelog nie jest tu prezentowana, jednak zmodyfikowany plik znajdziecie wraz z resztą pakietu na stronie, której adres podano na rysunku 1.

Jeśli nasz pakiet - co nie zachodzi w tym przypadku - posiada jakiekolwiek pliki konfiguracyjne, powinniśmy je wpisać wraz z pełną ścieżką do debian/conffiles. Dzięki temu podczas aktualizacji pakietu zostaniemy zapytani, czy chcemy zastąpić poprzednią konfigurację - nową, a przy usuwaniu pakietu komendą dpkg --remove ustawiona przez nas konfiguracja nie zostanie stracona.

Jak już uprzednio wspomniałem, podczas budowy pakietu na dysku tworzony jest katalog tymczasowy mający odwzorować późniejszą strukturę plików w pakiecie. Do automatycznego stworzenia wszystkich katalogów, które nie są budowane przez Makefile, wykorzystywana jest zawartość pliku debian/dirs. Standardowo znajdziecie w nim wpis zakładający usr/bin i usr/sbin, który powinien wystarczyć w większości mało zaawansowanych programów.
W związku z tym, że z pliku Makefile nie usunęliśmy komend kopiujących strony manuala oraz dokumentację (choć pakiet debhelper daje możliwość automatyzacji tych procesów), musimy tu dodać wpisy zakładające usr/share/doc oraz usr/share/man/man1

Jeśli chcemy, żeby nasz pakiet po instalacji pojawił się w menu po uruchomeniu X Window System, musimy zmienić nazwę debian/menu.ex na debian/menu, a także odpowiednio go zmodyfikować. W tym celu warto zapoznać się z dokumentacją do pakietu menu, a także stanowiącym część pakietu debian-policy dokumentem Debian Menu sub-policy.

Najważniejszym plikiem, który zarządza budową pakietu, jest debian/rules, który jest niczym innym jak skryptem wykonywanym przez program make. Znajdziecie w nim wywołanie wszystkich poleceń używanych do stworzenia pakietu. Postaram się tu pokrótce opisać te najważniejsze, pełną instrukcję ich obsługi możecie znaleźć wśród stron manuala.

Sekcja build odpowiada za kompilację pakietu - tu znajduje się wywołanie make, tu też możecie dopisać informację o komendach, które powinny być wydane w celu skompilowania pakietu

Poniżej znajduje się clean, który jest wywoływany zawsze przed budową pakietu. Sekcja ta odpowiada za usunięcie wszystkich plików mogących być pozostałością po poprzednich konfiguracjach, podobnie jak install, zwykle nie wymaga większych modyfikacji.

Kolejna sekcja - install - jest dokładnym zapisem kolejnych etapów kompilacji - tu znajdują się wywołania programów odpowiedzialnych za założenie katalogu debian/tmp/ (a w nim całego drzewa katalogów używanych przez tworzony właśnie pakiet), wyczyszczenie kodu źródłowego z niepotrzebnych plików i wreszcie samego make z odpowiednimi parametrami.

Wreszcie - binary-arch, czyli oprogramowanie odpowiedzialne za poskładanie zainstalowanego chwilę wcześniej do katalogu debian/tmp/ programu w jeden pakiet. Każde z nich posiada własną stronę manuala, ja opiszę tylko najważniejsze:

dh_testroot - sprawdza, czy procedura tworzenia pakietu została uruchomiona z konta root. Pakiety Debiana muszą być tworzone przez użytkownika z uprawnieniami administratora... lub przez osobę korzystającą z programu rfakeroot, który potrafi oszukać dużą część programów sprawdzających prawa uruchamiającego.
dh_installdocs - kopiuje dokumentację programu - w tym pliki debian/changelog oraz - ewentualnie - debian/README.Debian i debian/TODO.Debian do katalogu /usr/share/doc/nazwapakietu/. Jeśli utworzyliśmy plik debian/docs, w którym znajduje się lista innych plików należących do dokumentacji, również zostaną one skopiowane.
dh_installmanpages odpowiada za skopiowanie wszystkich stron manuala, które znajdą się w katalogu źródłowym, do odpowiednich katalogów w /usr/share/man/. Może być zastąpiony odpowiednim wpisem do pliku Makefile.
dh_md5sums generuje sumy kontrolne wszystkich plików wchodzących w skład pakietu.
dh_gencontrol tworzy katalog DEBIAN, w którym umieszcza opis pakietu i sumę kontrolną wszystkich plików w nim zawartych. Te informacje można obejrzeć wchodząc do dowolnego pakietu .deb przy pomocy Midnight Commandera.
dh_builddeb buduje pakiet.

Wśród innych programów, które mogą tu być wywołane, są odpowiadające za aktualizację crona (dh_installcron), menu X Window System (dh_installmenu) czy tworzenie linków symbolicznych (dh_link). Warto się zapoznać z opisem wszystkich programów wchodzących w skład pakietu debhelper, gdyż często mogą one znacznie ułatwić pracę przy tworzeniu pakietu.

Kiedy już będziemy gotowi do zbudowania naszego pakietu, cofnijmy się o jeden katalog do góry (czyli po prostu do głównego katalogu ze źródłami) i wydajmy komendę dpkg-buildpackage -rfakeroot. Jesli chcemy, żeby część plików była podpisana naszym kluczem gpg, dodajmy do tego jeszcze opcję -sgpg. Po chwili - jeśli nie zrobiliśmy żadnych błędów - w katalogu nadrzędnym do aktualnego pojawi się plik o nazwie hello_0.02-1_i386.deb. Możemy go już zainstalować w systemie, jednak lepszym pomysłem jest jego uprzednie sprawdzenie na zgodność ze standardami Debiana. Służy do tego pakiet Lintian.

Lintiana wywołujemy podając jako parametr plik .deb, co zaowocuje wygenerowaniem raportu tylko dla gotowego pakietu, albo .changes - wówczas Lintian sprawdzi wszystkie pliki, które są w nim wymienione. Przy budowaniu pierwszych pakietów, kiedy jeszcze nie jesteśmy nie mamy wprawy ich przygotowywaniu, pomocna może się okazać opcja -i, która spowoduje, że Lintian poza wyświetleniem suchej informacji o niezgodności z Policy spróbuje naprowadzić nas na rozwiązanie problemu.

Kiedy nasz pakiet jest na takim etapie, że Lintian nie wyświetla w odpowiedzi żadnego komunikatu, prawdopodobnie jest on zbudowany z zachowaniem wszelkich reguł sztuki. Oznacza to też, że pora na jego dokładne przetestowanie. W tym celu spróbujmy kilkukrotnie zainstalować i odinstalować nasz pakiet, jeśli jest to już jego kolejna wersja - próby upgrade'u również nie zaszkodzą. Dopiero kiedy mamy pewność, że nie ma w nim żadnego błędu, możemy go wysłać na serwer Debiana, ewentualnie - jeśli nie jest on przygotowywany do oficjalnej wersji tej dystrybucji - rozpocząć jego rozpowszechnianie. Niestety, jak się okazuje nie wszyscy przestrzegają tej prostej, wydawałoby się, reguły, a przekonali się o tym wszyscy użytkownicy wersji unstable, którzy przez błąd w jednym z pakietów nie mieli możliwości zalogowania się na własne komputery. No, ale to już są uroki korzystania z niestabilnych wersji Debiana ;)

Po pewnym czasie, który możemy przeznaczyć albo na pracę nad nowymi pakietami, albo na robieniu tego, co robić lubimy najbardziej, znienacka dopadnie nas informacja o pojawieniu się nowej wersji oprogramowania, którym się opiekujemy. Nie oznacza to oczywiście, że pora na panikę - nie, nie czeka nas już tyle pracy, co nad stworzeniem pakietu od podstaw. Życie ułatwi nam należący do pakietu devscripts program uupdate. Chociaż do dobrych nawyków należy czytanie dokumentacji, to jednak w jego wypadku możecie tego nie robić - cała jego obsługa sprowadza się do wydania jednego polecenia.

Nim to jednak nastąpi, musimy skopiować nowy plik ze źródłami (na przykład hello_0.03.tar.gz) do katalogu ~/debian/. Następnie wchodzimy do katalogu, w którym stworzyliśmy poprzednią wersję pakietu (czyli wydajemy komendę cd ~debian/hello-0.02/ i wpisujemy polecenie uupdate -u hello_0.03.tar.gz. Trzeba pamiętać, żeby nazwa pliku podana jako parametr była identyczna, jak nazwa pliku z nowymi źródłami, który jest umieszczony katalog wyżej. Po chwili na dysku pojawi się ~debian/hello-0.03/, a w nim wszystkie pliki potrzebne do budowy pakietu. Spróbujmy go teraz zbudować poleceniem dpkg-buildpackage -rfakeroot -sgpg, a pliki wynikowe sprawdzić podobnie jak sprawdzaliśmy poprzednią wersję pakietu - przy pewnej dozie szczęścia aktualizacja ograniczy się do konieczności wydania tylko tych kilku poleceń i skopiowaniu nowej wersji pakietu na serwer.

Ramki towarzyszące artykułowi:

rys. 1
Potrzebne narzędzia
pakiet debhelper
pakiet dh-make
pakiet lintian
pakiet fakeroot
pakiet devscripts

rys. 2

DESTDIR=
CC=gcc
CFLAGS=

all:	hello	hello.c

install:
	cp hello $(DESTDIR)/usr/bin/
	cp hello.1 $(DESTDIR)/usr/share/man/man1/
	mkdir $(DESTDIR)/usr/share/doc/hello
	cp docs/* $(DESTDIR)/usr/share/doc/hello/

clean:
	rm hello

rys. 3
Source: hello
Section: misc
Priority: optional
Maintainer: Lukasz Jachowicz <honey@debian.org>
Build-Depends: debhelper
Standards-Version: 3.5.6.0

Package: hello
Architecture: any
Depends: ${shlibs:Depends}
Description: Pakiet z nieprawidlowym - bo polskim - opisem
 Krociutki pakiet stworzony przez honeja po to, zeby
 pokazac czytelnikom miesiecznika Linux+ sposob tworzenia
 plikow .deb. A warto je robic, bo sa fajne i juz.