Teraz przejdę do nieco innego, mniej typowego zastosowania. Najpierw
nakreślę nieco tła: otóż są pewne programy, które aktualizuję sobie poprzez
CVS. Z różnych powodów, te są teraz nieistotne. Jeśli ktoś używa
oprogramowania z CVS, to zwykle kompiluje je nieco częściej niż ludzie
używający tego softu z ,,normalnych'' paczek. I byłoby bardzo fajnie, gdyby
cały ten proces dało się jakoś zautomatyzować. I niespodzianka - da się
:)
Tutaj do gry wchodzi RPM - jak zwykle chodzi o skompilowanie kodu i złożenie
gotowej do instalacji paczki. W zasadzie to prawie nic się nie zmienia
w porównaniu ze ,,zwykłą'' paczką RPM, ale myślę że taki przykład będzie
dosyć użyteczny. Dobra, padło na ,,mc''. Więc do rzeczy:
1. Źródła. Mam sobie w katalogu domowym taki katalog CVS.
I w katalogu tym mam różne takie inne podkatalogi, w których siedzą sobie
różne projekty pobierane regularnie z sieci. Jednym z tych projektów jest
mc. Szczegóły dotyczące pobierania można sobie oszczędzić, procedura jest
identyczna w przypadku wszystkich danych z CVS.
A co jest ważne? No cóż, na pewno mam katalog ~/CVS/mc. Zawierający
źródła. Więc warto się im przyjrzeć. Najpierw kopiuję sobie ~/CVS/mc do
/shm, potrafię być bardzo nieostrożny a nie chcę sobie uszkodzić
oryginalnych źródeł. Więc oglądam kopię... i wygląda normalnie. Brak skrypty
./configure, ale to normalne dla CVS. Zamiast tego jest ./autogen.sh,
którego zadaniem jest stworzenie skryptu ./configure. Uruchamiam
./autogen.sh i mam tylko cichą nadzieję, że nie wypluje zbyt wielu błędów
:)
autogen.sh działa, i działa, i działa... zupełnie jak króliczki Energizera.
Na dodatek uruchomił od razu stworzone przez siebie ./configure Ech.
Wreszcie skończył.
Pierwsze co robię, to przeglądam autogen.sh czy nie znajdę wewnątrz jakiegoś
przełącznika który może mi posłużyć do wyłączenia tego automatycznego
uruchamiania ./configure. Czasem się taki znajdzie. Ale nie tym razem. No
dobra, pół biedy. Na szczęście parametry przekazane skryptowi ./autogen.sh
zostaną podane skryptowi ./configure, więc nie jest tak źle. Po prostu nie
chciałbym, żeby autogen.sh uruchomił ./configure na darmo i żebym musiał
potem to samemu jeszcze raz uruchamiać. Przebieg ./configure dosyć długo
trwa. Wiem, że to pewnie tylko mój problem, ale...
...o czym to ja mówiłem? Aha, już wiem:
2. Próbna instalacja. No więc jest ten skrypt ./configure. Po
obejrzeniu ,,./configure --help'' mam już zestaw swoich ulubionych opcji:
Opcje są proste do wyjaśnienia: najpierw wyłączam wbudowany edytor, bo i tak
używam vima. Potem wyłączam dla pewności ext2undel, bo i tak używam XFS.
Wyłączam, co ciekawe, obsługę myszy - bo nie będzie mi w mc potrzebne
klikanie na przyciski ani przeciąganie przycisków. A zaznaczanie tekstu oraz
wklejanie i tak są od tego niezależne.
Wyłączam też mcfs oraz sambę, bo nie mam zamiaru z tego korzystać. Wymuszam
używanie ncurses zamiast libslang (nie lubię libslang :)
I na koniec, tradycyjnie już, wyłączam wsparcie dla polskich tłumaczeń
i oszczędzam nieco czasu kompilacji na --disable-dependency-tracking. Te
wszystkie flagi będę potem musiał przekazać skryptowi ./autogen.sh.
Uruchomienie ,,make'' się udaje, podobnie instalacja z DESTDIR=/shm.
Od razu oglądam listę plików - nic specjalnego:
Binarki, manuale, wykonywalny plik cons.saver w usr/lib/mc. O dziwo, zainstalowały się też puste katalogi
usr/sbin i usr/man/man8.
Pewnie jakiś błąd w skrypcie instalacyjnym. Dobra, tym akurat nie muszę
sobie głowy zaprzątać - rpm powinien zignorować pusty katalog jeśli go
pominę w pliku .spec. Interesujący za to jest katalog usr/share/mc. Jest tam trochę luźnych plików... widzę
pliki mc.hint.*, które zawierają ,,porady'' mc w różnych językach. Te pliki
usunę z wynikowego pakietu, nie będą mi potrzebne. Zachowam sobie jedynie
anglojęzyczny ,,mc.hint''. Katalogi syntax i term... hmm, term będzie mi
niepotrzebny, on zawiera różne pliki których można użyć do połatania baz
terminali w systemie, ale ja nie mam problemów z terminalami, więc i tak bym
niczego nie musiał poprawiać. Więc term nie trafi
do końcowego pakietu. syntax też mi nie jest
potrzebny - przecież ja kompiluję mc bez wbudowanego edytora, więc
podświetlanie składni jest mi niepotrzebne. Od razu odpadają też związane
z edycją pliki łapiące się na wzorzec ,,*edit*''
Katalog usr/share/mc/bin zawiera jakieś owijki dla
sh/csh - nigdy nie używałem i nie mam zamiaru zacząć. Więc też
wypada.
Po tych operacjach zostaje mi tylko kilka plików oraz podkatalog extfs. Ten jest jednak potrzebny, bo zawiera skrypty
obsługujące różne wirtualne filesystemy. Są to np. skrypty, które pozwalają
,,zajrzeć'' do pliku .rpm. To musi zostać. Trochę kłopotliwe będzie
nadawanie praw do plików - wszystkie pliki w tym katalogu będą musiały być
wykonywalne, oprócz pliku README i dwóch plików *.ini. To oznacza, że nie
będę mógł po prostu ,,globalnie'' załatwić tego katalogu jedną linijką
w sekcji %files. Ale tym to się później zajmę.
3. Plik spec.
Summary: Midnight Commander
Name: mc
Version: %(date +%Y%m%d)
Release: 1
License: GPL
Group: Pliki
BuildRoot: /var/tmp/%{name}-%{version}
%description
Midnight Commander to rozbudowany filemanager, pracujący w trybie tekstowym.
Można go obsługiwać myszą, radzi sobie z połączeniami FTP, potrafi też
podglądać różne rodzaje wirtualnych systemów plików, jak np. paczki RPM czy
archiwa TAR. Poza tym, jest bardzo konfigurowalny. O jego jakości najlepiej
mówi chyba fakt, że jak do tej pory nie wymyślono w tej klasie programów nic
lepszego dla trybu tekstowego.
%prep
%setup -q -c -T
cp -a /home/grzegorz/CVS/mc/* .
./autogen.sh --without-edit --without-ext2undel --without-gpm-mouse \
--with-subshell --without-mcfs --without-samba --with-screen=ncurses \
--disable-nls --disable-dependency-tracking --prefix=%{_prefix} \
--mandir=%{_mandir} --libdir=%{_libdir} --datadir=%{_datadir} \
--bindir=%{_bindir}
%install
rm -rf %{buildroot}
make install DESTDIR=%{buildroot}
rm -rf %{buildroot}%{_datadir}/mc/{bin,syntax,term,*edit*,mc.hint.*}
%files
%defattr(0644, root, root, 0755)
%attr(0755,root,root) %{_bindir}/*
%{_mandir}/man*/*.gz
%attr(0755,root,root) %{_libdir}/mc
%dir %{_datadir}/mc
%{_datadir}/mc/mc.*
%dir %{_datadir}/mc/extfs
%{_datadir}/mc/extfs/README
%{_datadir}/mc/extfs/*.ini
%attr(0755,root,root) %{_datadir}/mc/extfs/a*
%attr(0755,root,root) %{_datadir}/mc/extfs/b*
%attr(0755,root,root) %{_datadir}/mc/extfs/d*
%attr(0755,root,root) %{_datadir}/mc/extfs/h*
%attr(0755,root,root) %{_datadir}/mc/extfs/l*
%attr(0755,root,root) %{_datadir}/mc/extfs/m*
%attr(0755,root,root) %{_datadir}/mc/extfs/p*
%attr(0755,root,root) %{_datadir}/mc/extfs/r*
%attr(0755,root,root) %{_datadir}/mc/extfs/t*
%attr(0755,root,root) %{_datadir}/mc/extfs/u*
%clean
rm -rf %{buildroot} %{_builddir}/%{buildsubdir}
Uff. I znowu parę rzeczy do wyjaśnienia: Najpierw ta konstrukcja w tagu
,,Version:'':
Version: %(date +%Y%m%d)
Nie używam tutaj stałego numeru wersji, bo pakiet będzie
zawierał program z CVS. Jednak jakiś numerek powinienem wstawić. W przypadku
programów z CVS zwykle jako wersję podaję datę, dzień z którego pochodzą
źródła (lub dzień zbudowania pakietu, to zwykle to samo). Lubię używać daty
w formacie rok-miesiąc-dzień, tyle że bez łączników. Czyli np. '20030428' na
oznaczenie '28 kwietnia 2003r'. Teraz mógłbym oczywiście po prostu wpisać
ten numerek na stałe, i zmieniać go przy każdej kolejnej budowie pakietu,
ale to by było męczące. Mogę zmusić do tego maszynę... Używam do tego
polecenia ,,date'', które może zwrócić datę w ,,moim'' formacie, jeśli
wywołać je jako ,,date +%Y%m%d''. Teraz trzeba tylko zmusić plik .spec do
pobierania wyjścia tego polecenia. A robi się to w plikach spec
konstrukcją %(...) - zachowuje się ona podobnie do shellowego $(...)
lub `...` - zawartość wyrażenia zostanie wykonana, a to, co zostanie
zwrócone zostanie podstawione zamiast %(...). Jest to prosty sposób na
umieszczanie w plikach .spec treści, które mają być generowane przez
zewnętrzne programy. Podsumowując: dzięki temu %(date +%Y%m%d) będę miał
automagicznie ustawiany tag Version: na zakodowaną datę bieżącego dnia.
Następna warta opisu linijka to występujące w sekcji %prep makro
%setup -q -c -T
Zgodnie z tym, co napisałem wcześniej przy opisie %setup, makro to będzie
się zachowywało następująco: będzie siedziało cicho, założy katalog o nazwie
%{name}-%{version} i wejdzie do niego, ale pominie
rozpakowywanie źródeł. O, właśnie - w preambule w ogóle nie wymieniłem tagu
Source: - prawie zapomniałem o tym powiedzieć. Skoro nie korzystam z żadnej
paczki ze źródłami, to nie muszę tego robić. Więc nie robię. Bo źródła wezmę
z innego miejsca - z katalogu ~/CVS/mc, gdzie mam
kopię repozytorium CVS Midnight Commandera. A zrobi to linijka
cp -a /home/grzegorz/CVS/mc/* .
Jej działanie jest proste: skoro jesteśmy w katalogu ~/rpm/BUILD/%{name}-%{version}, to wystarczy przekopiować
tutaj zawartość repozytorium. Jedno wywołanie ,,cp'' i po wszystkim.
Potem następuje wywołanie skryptu autogen.sh, który wygeneruje plik
./configure. autogen.sh gdy tylko wykona swoje zadanie uruchomi skrypt
./configure i przekaże mu wszystkie parametry jakie sam otrzymał, więc ja
daję mu od razu wszystkie te opcje, które otrzymać ma ./configure. Ponieważ
nie używam makra %configure, muszę samodzielnie zadbać o przekazanie
zmiennych konfigurujących potrzebne katalogi.
jest to polecenie usunięcia z zainstalowanych plików tych wszystkich rzeczy
w usr/share/mc, o których mówiłem że nie będą mi
potrzebne. Żeby uniknąć wpisywania wielu linijek i podawania wszędzie tych
samych katalogów połączyłem wszystko w jedno wywołanie, polegając na
mechaniźmie ,,brace expansion''. Trzeba sobie ułatwiać życie...
Ostatnie co muszę omówić to sekcja %install. Na początku są normalne wpisy
dotyczące binarek i manuali. Potem jest wpis katalogu usr/lib/mc (wraz z zawartością) - tam leży binarka
cons.saver, więc dlatego nadaję bit wykonywalności. Następnie załączam sam
katalog usr/share/mc, potem wszystkie pliki mc.*
z jego wnętrza. Po tym zabiegu w tym katalogu do załączenia pozostaje mi
tylko podkatalog extfs. Więc załączam i jego,
a potem w dwóch linijkach jego pliki *.info i README. Pozostały mi te
skrypty z jego wnętrza, muszą być wykonywalne. Tutaj zauważam, że mogę je
wychwycić po ich pierwszych literach (te pliki *.info i README nie załapią
się, bo szczęśliwym zbiegiem okoliczności mają ,,unikatowe'' pierwsze litery
nazw) - co też robię. Staram się na jak najszerszą skalę używać globbingu
(,,*'', niestety w sekcji %files nie mogę użyć ,,brace expansion'' bo nie
jest interpretowana przez shell, tylko przez samego rpm-a), to pozwoli mi
zamortyzować ew. pojawienie się nowych skryptów w przyszłych wersjach. Przy
odrobinie szczęścia. Jednocześnie konstrukcja którą stworzyłem jest na tyle
precyzyjna, by zareagować na pojawienie się nowych plików .info lub
pojawienie się konfliktu (rpmbuild mnie wtedy powiadomi o podwójnie
opisanych wpisach w
%files).
Uwaga końcowa: nie musiałem się tak męczyć. Mogłem ,,odgórnie'' walnąć
plikom w /usr/share/mc prawa do wykonywania, co
mnie obchodzi, że trzy z nich będą niepotrzebnie wykonywalne. Mogłem też
wyluzować w inny sposób - mc zainstalował te pliki w 100% poprawnie, tzn. te
trzy pliki nie miały bitu '+x' - więc ja mogłem poinstruować rpm-a, by
przejął zastane atrybuty i nie forsował własnych
%attr(-,root,root) %{_datadir}/mc
tak, tak też mogłem zrobić. I byłoby to bardzo ładne rozwiązanie, dużo
bardziej elastyczne od tego, które pokazałem w pliku .spec, bo zastąpiłoby ono linijki
Ale pociąga ono za sobą jedno pytanie - czy ufasz instalatorowi programu?
Jesteś pewien, że nałoży zawsze odpowiednie prawa? Akurat w tym przypadku
nawet gdyby coś zepsuł, to nie miałoby to wielkiego wpływu na system czy
pakiet, bo ,,przymknąłbym oko'' tylko na jeden odosobniony katalog. Ale ja
generalnie staram się uniknąć pozwalania pakietowi na zbyt wiele bez mojej
kontroli. Wolę mieć rękę na pulsie, nawet jeśli oznacza to dopisanie
dodatkowych linijek w sekcji %files.
4. Budowanie pakietu Co tu dużo gadać - udało się za pierwszym razem
:) Aha, jeszcze coś - w przypadku ,,mc'' warto zwrócić uwagę na plik
cons.saver. Jest on używany do ,,zapamiętywania'' zawartości terminala przy
przełączaniu ekranów za pomocą C-o lub wykonywaniu zewnętrznych poleceń. Do
tego celu musi mieć dostęp do niektórych plików odwzorowania terminali
w katalogu /dev. Na niektórych systemach pliki te
mają bardzo ostro okrojone prawa dostępu i cons.saver musi mieć odpowiedni
suid, a jeszcze lepiej sgid i grupę by się do tych plików dobrać. Jeśli twój
mc zawiesza się przy przełączaniu C-o, uruchamianiu zewnętrznych poleceń lub
nawet jeszcze w trakcie startu, to na 90% twój cons.saver potrzebuje
dodatkowych uprawnień.