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.