Wskazówki
dla deweloperów PLD
Dodając speca nie bazowanego na template*.spec należy go potraktować
skryptem adapter.awk (więcej o używaniu tego skryptu można znaleźć
(po angielsku) na http://www.pld-linux.org/ w sekcji dla deweloperów).
Spece bazowane na template*.spec także można przepuścić przez
adapter,
aby poprawić formatowanie linii w preambułach i opisach.
Pakiet musi się budować z konta zwykłego użytkownika bez dodatkowych
kombinacji. Jedynym wyjątkiem jest pakiet dev (który musi być budowany
przy użyciu programu fakeroot - nie z konta roota!). Wymusza to
korzystanie z BuildRoot.
Polskie opisy powinny być w języku polskim.
W szczególności dotyczy to ortografii, stosowania znaków diakrytycznych
oraz apostrofów i odmiany nazw własnych i skrótowców. W skrócie:
- apostrofów używa się tylko do oddzielenia końcówki, jeżeli przed nią
jest samogłoska niema lub samogłoska
niema z niemym "s" lub "x"
(czyli np. Apache'a; ale NIE
Blackbox'a czy IceWM'a)
- w przypadku skrótowców końcówkę oddzielamy łącznikiem (czyli np.
IceWM-a)
- "zwykłe" nazwy oraz skrótowce o charakterze rzeczownika (typu
"Cepelia") odmieniamy
bez łącznika przed końcówką (czyli np.
AfterStepa, Sawfisha)
- nazwy typu "Hortex", "Pewex" odmieniamy -ex, -eksu, -eksem,
-ksie
itd. (czyli np. Blackboksa,
Postfiksa)
Polskie znaki diakrytyczne w polskich opisach w specach obecnie muszą
być kodowane w ISO-8859-2 (natomiast w plikach .desktop obowiązuje
kodowanie UTF-8).
Nazwy pakietów:
W przypadku niektórych klas pakietów stosujemy ujednolicone nazewnictwo.
W szczególności:
- dla modułów Perla: perl-nazwa (dla modułów obiektowych zazwyczaj
nazwa jest postaci
Klasa[-Klasa[-Klasa...]]
- dla modułów Pythona: python-nazwa
- dla modułów Apache'a: apache-mod_nazwa
- dla modułów PEAR-a: php-pear-nazwa (gdzie nazwa zazwyczaj jest postaci
Klasa[_Klasa[_Klasa...]]
- dla modułów binarnych PHP zawierających klasy dla PEAR-a: php-pecl-nazwa
- dla modułów jądra: kernel-typ-nazwa (typ jest taki sam, jak podkatalog
w drivers/ w którym znalazłby się
moduł - np. char, net itd.)
Podział na podpakiety:
Pakiety zawierające biblioteki dzielone standardowo dzielimy na:
podstawowy (zawierający pliki m.in. lib*.so.*.*, podstawową dokumentację
przydatną
dla użytkownika i wywołanie /sbin/ldconfig w skryptach
%post/%postun)
pakiet -devel (pliki nagłówkowe, dowiązanie lib*.so, ewentualnie pliki
typu *.la,
*.m4, *.pc, skrypty *-config oraz dokumentację dla
programistów)
pakiet -static (zawierający tylko biblioteki statyczne (lib*.a) mające
swoje
odpowiedniki współdzielone (czasem pod nieco inną nazwą -
liczy się
ABI); biblioteki występujące tylko w wersji statycznej
powinny
znaleźć się w pakiecie -devel)
Uwaga: W niektórych aplikacjach (gnome kde ...) pliki *.la muszą
znajdować
się w pakiecie podstawowym i _są_ konieczne do pracy aplikacji - wtedy
należy _koniecznie_ zaznaczyć komentarzem ten fakt w specu.
Wersje pakietów:
Dla pełnych wydań pole Version zawiera wersję pakietu.
Dla wersji rozwojowych lub testowych (snapshotów z CVS, wszelkich alpha,
beta, gamma, pre, rc), aby uniknąć ciągłego podbijania Epoch, w polu
Version wpisujemy tylko podstawowy (docelowy) numer wersji, natomiast
w Release umieszczamy ciąg 0.wersja-pre.rzeczywisty-release (np. dla wersji
rc2 i beta będą to 0.rc2.1, dla snapshotów ciąg typu 0.20030303.1).
Przykładowy opis w specu (wersja rzeczywista: 2.0rc1)
Version:
2.0
%define
_rc rc1
Release: 0.%{_rc}.1
Grupy pakietów:
Pakiet musi posiadać zdefiniowane Group: zgodnie z SOURCES/rpm.groups
Nie umieszczamy "BuildRequires: kernel-headers" w pakietach z
samymi
programami user-space (sam pakiet glibc-devel wymaga właściwych
nagłówków jądra). Taka zależność powinna pojawiać się tylko
w pakietach zawierających moduły jądra, i to warunkowo, czyli jako:
%{!?_without_dist_kernel:BuildRequires:
kernel-headers}
Podobnie
nie umieszczamy zależności od jądra w pakietach nie
zawierających modułów; w pakietach z modułami umieszczamy ją warunkowo:
%{!?_without_dist_kernel:%requires_releq_kernel_up}
(dla modułów jądra UP)
lub
%{!?_without_dist_kernel:%requires_releq_kernel_smp}
(dla modułów jądra SMP)
Dodając wywołania narzędzi auto* i *ize należy pamiętać o
dopisywaniu
zależności:
autoconf,autoheader BuildRequires: autoconf
automake,aclocal BuildRequires:
automake
libtoolize
BuildRequires:
libtool
gettextize
BuildRequires:
gettext-devel
autopoint
BuildRequires:
gettext-autopoint
intltoolize BuildRequires:
intltool
xml-i18n-toolize BuildRequires: intltool
Dobrze jest sprawdzić wymagane wersje narzędzi -
oprócz dokumentacji
często można je znaleźć w następujących miejscach:
autoconf - w makrze AC_PREREQ(wersja) w configure.{ac,in} lub *.m4
automake
- w AUTOMAKE_OPTIONS w Makefile.am
gettext - w makrze AM_GNU_GETTEXT_VERSION w configure.{ac,in}
libtool:
- jeśli
pakiet zawiera biblioteki dzielone i przynajmniej jedna
jest linkowana z inną z tego samego pakietu - wymagana jest
wersja >= 0:1.4.2-9
- jeśli
pakiet zawiera bibliotekę dzieloną w C++ (korzystającą
z biblioteki standardowej libstdc++), wymagana jest wersja
>= 2:1.4d
- jeśli oba
powyższe warunki są spełnione, to wersja >= 2:1.4d-3
- jeśli w
pliku configure.ac (nie configure.in) znajduje się
makro AC_CONFIG_AUX_DIR, to
wersja >= 1:1.4.3 (lub 2:1.4e
w przypadku bibliotek w C++)
O ile to możliwe, programy te uruchamiamy używając makr (dla
informacji
z prawej podane są rozwinięcia tych makr):
%{__autoconf}
= autoconf
%{__autoheader}
= autoheader
%{__automake}
= automake -a -c -f --foreign
%{__aclocal}
= aclocal
%{__libtoolize}
= libtoolize --copy --force
%{__gettextize}
= gettextize --copy --force [--intl]
(+inne
operacje zależne od użytej wersji gettexta)
%{__autopoint}
= autopoint --force
Oczywiście w
razie potrzeby można podawać za makrem dodatkowe parametry
do programu (z wyjątkiem gettextize).
Zdarzają się problemy z automake, jeśli uruchamiane jest w podkatalogu
z opcją --force (zawartą w makrze %{__automake}) - wtedy zamiast makra
należy użyć samego:
automake
-a -c --foreign
(opcja --foreign
w wielu przypadkach jest istotna, ponieważ bez niej
automake nadpisuje pliki typu COPYING czy INSTALL własnymi wersjami)
Zależności przy budowaniu pakietów:
Wszystkie pakiety potrzebne do zbudowania danego pakietu P (oprócz
nagłówków jądra) powinny być wymagane jawnie - w jednym z trzech miejsc:
- w BuildRequires w specu do pakietu P
- w Requires pakietu rpm-build (globalne wymagania - pakiety potrzebne
przy budowaniu zdecydowanej większości
pakietów)
- w Requires pakietów wymienionych na dwóch ww. listach.
Staramy się nie duplikować wymagań - jeśli coś jest wymagane (także
pośrednio) przez rpm-build lub któryś pakiet z listy BuildRequires
pakietu P, nie dopisujemy go już do BuildRequires pakietu P - chyba, że
trzeba zaostrzyć wymaganie co do wersji.
W przypadku bibliotek dzielonych (i modułów np. perlowych) linkowanych
dynamicznie w czasie kompilacji trzeba pamiętać, żeby oprócz
"BuildRequires: cośtam[-devel] >= wersja" dopisywać także
"Requires: cośtam >= wersja" - chyba że zależność taka wynika
już
z SONAME (w przypadku bibliotek dzielonych) lub jest generowana
automatycznie (w przypadku modułów perlowych).
Standardowo (bez bcondów, --nodeps itp.) zależności przy budowaniu
pakietu P, nakładane łaty oraz polecenia w specu (opcje do %configure,
zmienne make itp.) powinny jednoznacznie określać możliwości
i zależności budowanego pakietu. Sytuacja, gdy w obecności jakiegoś
innego pakietu Q (nie wymienionego w BuildRequires ani BuildConflicts)
pakiet P zaczyna wymagać pakietu Q lub sam z siebie buduje się
z jakimiś nowymi opcjami, jest niepoprawna.
Standardem jest linkowanie dynamiczne (i BuildRequires: biblioteka-devel).
Linkowania statycznego (i BuildRequires: biblioteka-static) można używać
tylko w dobrze uzasadnionych przypadkach. Stwierdzenie "bo tak było
łatwiej" albo "program tak robi domyślnie" nie jest wystarczającym
uzasadnieniem.
Nie używamy makra %{_sysconfdir} w ścieżkach do stałych,
ogólnosystemowych katalogów:
/etc/cron.*
/etc/crontab.d
/etc/logrotate.d
/etc/pam.d
/etc/profile.d
/etc/rc.d
/etc/security
/etc/skel
/etc/sysconfig
Sekcja budownia (%build):
W tej sekcji należy zawrzeć wszystko co spowoduje zbudowanie pakietu w
katalogu rpm/BUILD w drzewie budowania rpma. W sekcji tej katalog
$RPM_BUILD_ROOT nie powinien być używany.
Sekcja instalacji (%install):
Służy do zawarcia komend instalujących to co zostało wybudowane w sekcji
budowania w katalogu $RPM_BUILD_ROOT. Powinna się rozpoczynać od
wyczyszczenia owego katalogu:
%install
rm -rf $RPM_BUILD_ROOT
Budownie rpmów w PLD musi kończyć się sekcją
czyszczenia (%clean) czyszczącą
$RPM_BUILD_ROOT:
%clean
rm -rf $RPM_BUILD_ROOT
Ten podział
nabiera większego sensu przy tworzeniu specy dla dużych pakietów
gdzie czas budowania jest znaczny. Wtedy rozsądnie jest posiłkować się w
budowaniu rpm -bc/-bi... --short-circuit , co wraz z opcjami budowania pozwala
omijać poszczególne sekcje budowania.
Lista plików:
Kompletując listę plików nie można zapominać o katalogach. Każdy
katalog
powinien należeć do dokładnie jednego pakietu spośród tych, które można
zainstalować naraz. Katalog nie należący do żadnego pakietu jest błędem -
tworzy się z uprawnieniami zależnymi od umask i rpm nie sprawdza jego
własności przy rpm -Va. Do wykrywania tego typu błędów dobrze jest ustawić
umask 077 dla roota.
Uaktualniając pakiet należy przejrzeć nakładane patche. Łaty są nakładane
nie bez powodu i niedopuszczalne jest ich usuwanie tylko dlatego, że "bez
niej też się kompiluje". Usuwać należy tylko te łaty, które zostały
już
uwzględnione w oryginalnych źródłach, lub problem został rozwiązany
inaczej.
Nie może być tak, że w nowej wersji pakietu znika jakaś poprawka błędu
lub dodatkowa funkcjonalność, którą zapewniała dodana łata.
Oczywiście dotyczy to także standardowych możliwości oprogramowania - nie
można wyłączać funkcjonalności obecnej w poprzedniej wersji "bo się
nie
kompilowało".
Jeśli w danym momencie nie jesteś w stanie uaktualnić którejś łaty lub
poprawić budowania z opcją włączoną w poprzedniej wersji, zostaw speca
z niecałkowitym release i dopisaną pozycją w TODO; możesz dodatkowo
napisać na listę z prośbą o uaktualnienie. Natomiast taki pakiet NIE MA
PRAWA pojawić się na ftp poza katalogami /test.
Dodanie do pakietu łaty dodającej jakąś funkcjonalność, o ile taki pakiet
trafi na ftp poza /test, powinno być traktowane jako zobowiązanie do
utrzymywania tej funkcjonalności w przyszłych wersjach. Do usunięcia
dodanej kiedyś nowej funkcjonalności trzeba mieć odpowiednio ważny powód.
Pozycje TODO umieszcza się zaraz pod linią # $Revision x.xx $ ... ):
#
$Revision: 1.12 $ ....
# TODO:
# - fix that and that.
W przypadku
źródeł z sourceforge aktualnie najlepsze są adresy w postaci:
http://dl.sourceforge.net/nazwa/plik
Nie należy używać odnośników do prdownloads.sourceforge.net, ponieważ pod
tymi adresami zamiast źródeł znajduje się strona w HTML do wyboru mirrora.
ftp://ftp.sourceforge.net/ jest o tyle niedobre, że tylko niektóre
z serwerów wskazywanych przez adres ftp.sourceforge.net obsługują protokół
FTP.
Branche w CVS.
Często pakiet rozwija się w wielu gałęziach (np. bo jest to nowa wersja,
bo inny developer też rozwija i nie chcemy mu przeszkadzać, bo pakiet jest
przeznaczony dla innej wersji PLD (ra/ac/...)
Aby zrobić gałąź (branch) najwygodniej posłużyć się skryptem builder
i kolejno:
Ściągnać wersje która ma być podstawą gałęzi:
$
./builder -g -r <revision> <pakiet>.spec
Stworzyć gałaź o nazwie:
$ ./builder -B <nazwa_brancha> <pakiet>.spec
Gdzie nazwa gełęzi to np.:
DEVEL - wersja rozwojowa pakietu
RA-branch - wersja dla PLD Ra.
(TODO - inne i kiedy)
Ściągnąć wersje z nowej (właśnie stworzonej) gałęzi:
$ ./builder -g -r <nazwa_brancha> <pakiet>.spec
Teraz można już wprowadzać zmiany i commitować, a powinny pojawić się one
w nowo stworzonej gałęzi.
Nie należy tworzyć gałęzi bez racjonalnego powodu (słuszne jest istenienie
pakietu przeznaczonego dla wielu rodzajów PLD (RA/AC..) w gałęzi głównej
(HEAD) o ile nie mają one sporych różnic.
Warto przeczytać początek zawartości skryptu builder - zawiera ona opis
dostępnych opcji.
Tagowanie.
NIE WOLNO TAGOWAĆ NUMEREM WERSJI (ani tworzyć gałęzi), czyli tagować
nazwą
składającą się z nazwy pakietu (np. : bind-9_2_2-1). To kompetencja ludzi
puszczających pakiet na buildery. Niestosowanie się grozi odebraniem R/W.
TODO:
Kiedy i jak używać konstrukcji if/else w specach gdy spec musi być inny dla
różnych wersji PLD.