Może to zabrzmi patetycznie, ale ta sekcja jest sercem pliku spec.
A przynajmniej ja tak myślę. Tutaj określa się, jakie pliki w końcu wylądują
w pakiecie, jakie będą miały uprawnienia, i inne takie.
Pakiet RPM będzie zawierał tylko te pliki, które wymieni się w tej sekcji.
Zwykle trzeba tutaj tylko wylistować pliki/katalogi znajdujące się po fazie
%install w ,,fake root''. Teoretycznie RPM mógłby to zrobić za użytkownika
i można by uznać tę konieczność za bardzo denerwującą. Ale mimo wszystko
lepiej jest jednak, gdy autor pakietu świadomie poustawia te rzeczy. Często
będzie tu szło o szczegóły, ale będą to szczegóły bardzo ważne.
Załóżmy, że mamy w ,,fake root'' po instalacji tylko dwa pliki:
czyli jakiś program i jego stronę manuala. Wiem, nieczęsta sytuacja, ale nie
chcę od razu przegiąć z pierwszym przykładem :) Prosta sekcja %files dla
tego przykładu mogłaby wyglądać tak:
Proste, prawda? Jeśli ktoś jednak czyta z uwagą, ten zauważył pojawienie się
,,znikąd'' rozszerzenia .gz przy pliku manuala. To konieczność, bowiem rpm
w magiczny sposób kompresuje manuale i dokumentację info w standardowych dla
tych plików ,,lokacjach'' (czyli /usr/share/info, /usr/X11R6/man itp.).
Nawet jeśli ,,make install'' zainstalowało dokumentację w formie
nieskompresowanej, to nie gra roli - rpmbuild wykona jeszcze przed
interpretowaniem sekcji %files jeden przebieg po całym ,,fake
root'', kompresując manuale. Fajne :) Dlatego w %files zakładam, że manual
jest zgzipowany. Muszę tak robić. Ale można pójść krok dalej - makro
%configure używało zamiast ,,jawnych'' ścieżek takich jak /usr czy
/usr/lib specjalnych makr. I tutaj można zrobić tak samo. Powinno się
w każdym razie.
W zmodyfikowanej postaci ta sama sekcja może wyglądać tak:
(Notka na boku: jeśli ktoś do tej pory nie domyślił się, dlaczego podczas
budowania pakietów RPM tak silny nacisk kładzie się na unikanie podawania
jawnych ścieżek, to tutaj łopatologiczne wyjaśnienie na przykładzie
z historii linuksa:
Kiedyś standardowo kładło się strony manuala do /usr/man/man*/. Ale potem
nastąpiły zmiany w standardzie lokowania plików i manuale zaczęto umieszczać
w /usr/share/man/man*/. Podobne zmiany mogą zachodzić jeszcze w przyszłości
- albo w ramach ogólnolinuksowych standardów, albo w ramach pojedynczej
dystrybucji, albo w ramach normalnego dostosowywania pakietów do
konkretnego, pojedynczego systemu. Ale do czego dążę? To proste: Wyobraźmy
sobie plik .spec, w którym wszędzie są odwołania ,,proste'', w stylu
/usr/man. Zarówno przy ./configure, jak i w %files. Jeśli teraz zajdzie
potrzeba skompilowania pakietu z przeniesieniem manuali do /usr/local/man
(nie, nie pytaj ,,po co'' - bo to tylko abstrakcyjny przykład), no więc
jeśli trzeba będzie wprowadzić takie zmiany, to taki plik .spec trzeba
będzie zmienić, w dwóch miejscach poprawić. I tak trzeba będzie postąpić
z każdym jednym rekompilowanym pakietem. Niezbyt to miłe. A co się stanie,
jeśli będzie się używać makra %{_mandir}? Proste - wystarczy zmienić to
makro raz, globalnie, w ~/.rpmmacros. Pliki .spec nie zmienią się, ale
wynikowe, zrekompilowane pakiety będą umieszczały swoje manuale
w /usr/local. Co jest prostsze - zmienić jedną globalną definicję, czy też
zmieniać dziesiątki wpisów w plikach .spec? Zalet takiego podejścia jest
więcej - np. większa zgodność między systemami, bo wystarczy że na danym
systemie te kluczowe zmienne będą zdefiniowane prawidłowo. Dodatkowo te
makra można zmieniać w linii poleceń, przy wywołaniu rpmbuild - wystarczy
użyć opcji --define aby ,,w locie'' zmienić ustawienie jakiegoś
katalogu, np. ,,--define="_prefix /opt"'' Proste, bo nie zmienia ani
globalnych ustawień systemu, ani pliku .spec, a działa :)
A po ludzku: używając takich różnych owijek w postaci różnych zmiennych czy
makrodefinicji jest później łatwiej nad tym zapanować, kiedy trzeba będzie
coś zmienić. Mam nadzieję, że to jasne?
Dobra, teraz dodajmy jeszcze parę plików. Dodajmy do naszego przykładu jakąś
drugą binarkę, jej manual, jakąś bibliotekę z której to wszystko korzysta
i parę nowych stron manuala. Czyli będziemy bliżej tego, co normalnie spotka
się w życiu. O, mam pomysł - weźmy naprawdę przykład z życia - fragment
plików z paczki gpm:
no, już tego więcej. Świetnie. Jak widać, mamy tu wszystkiego po trochę
- plik konfiguracyjny w /etc, trzy programy w /usr/bin, jeden plik
nagłówkowy, bibliotekę, jeden program w /usr/sbin, parę stron manuala
i jeden plik info. Strony manuala i plik info muszą mieć w tej sekcji
rozszerzenie .gz - bo jak mówiłem, zostały automagicznie skompresowane.
Teraz jeszcze przeróbmy to na formę używającą makr zamiast sztywnych
katalogów:
W sekcji %files mogę się posługiwać normalnym, shellowym dopasowywaniem,
więc jeśli napiszę ,,katalog/*'' to wzorzec ten zostanie zinterpretowany
jako ,,wszystkie pliki w tym katalogu''. Bardzo fajnie. No to jeszcze trochę
to poprawmy:
Jeszcze bardziej to uprościłem, zwłaszcza część z manualami jest bardzo ładna.
Tutaj uwaga: w ten sposób sam plik .spec stał się bardziej uniwersalny
- jeśli w następnej wersji gpm dojdzie np. jakiś dodatkowy plik nagłówkowy
w /usr/include, to ta sekcja %files nie będzie potrzebowała modyfikacji
i zadziała sama z siebie. Muszę szybko powiedzieć, że nie popełnia się przez
to żadnego błędu - bo możnaby co prawda pomyśleć, że robiąc taki przesycony
,,wildcards'' plik .spec może dojść do tego, że jakieś ważne zmiany przejdą
niezauważone, że coś będzie nie tak z pakietem jeśli w którejś wersji coś
się bardzo pozmienia - ale bez obaw. Jeśli w wyniku ewolucji gpm zniknie np.
plik /etc/gpm-root.conf, to rpmbuild powie o tym (powie, że ten plik nie
istnieje i odmówi zbudowania paczki). Podobnie będzie jeśli dojdzie coś
nowego, co nie jest uwzględnione obecnie w sekcji %files - rpmbuild powie
wtedy, że znalazł jakieś przeoczone pliki. Tak więc zachowując zdrowy
rozsądek można napisać w miarę uniwersalne pliki .spec, których będzie można
używać normalnie zmieniając tylko wersję źródeł, i to bez narażania się na
powstanie ,,zdeformowanych'' pakietów.
OK, więc mamy już w miarę sensowną sekcję %files.
Porozmawiajmy teraz o uprawnieniach. RPM ma jakieś tam uprawnienia domyślne,
które będą nakładane na wszystkie pliki z sekcji %files. Ale ja nigdy nie
pamiętam jakie one są, zresztą mogą znienacka się nam zmienić przy następnej
wersji RPM, więc lepiej jest te domyślne uprawnienia ustawić samemu. Robi
się to makrem %defattr(perm_file,user,group,perm_dir). Dziwna
notacja, ale już wyjaśniam: makro to wywołuje się z opcjami w nawiasie,
trochę jak wywołuje się funkcje z parametrami. ,,user'' i ,,group'' powinny
być jasne - to będzie właściciel i grupa nadawane plikom. Ale o co chodzi
z ,,perm_file'' i ,,perm_dir''? Pierwsza z tych opcji oznacza uprawnienia
jakie należy nałożyć na zwykłe pliki, druga uprawnienia jakie należy nałożyć
na katalogi. Tak, o tym trzeba decydować osobno, choćby z tego powodu, że
katalogi zawsze muszą mieć bit wykonywalności by użytkownik mógł do nich
wejść, za to zwykłe pliki zwykle wykonywalne nie muszą być. Przykładowy
(chyba najczęstszy) zapis będzie wyglądał tak:
%defattr(0644,root,root,0755)
ustawi to sensowne domyślne prawa - root może wszystko, inni mogą sobie co
najwyżej popatrzeć, ale nie wykonywać ani zapisywać. Wyjątkiem są katalogi,
które mają zawsze ustawiony bit wykonywalności - po to, by każdy mógł do
nich zajrzeć.
Mały bonus: mogę nie podać praw dla katalogów, wtedy zostaną użyte prawa dla
plików. Zamiast praw czy też właściciela/grupy mogę też po prostu wpisać
,,-'' (minus), wtedy rpm pozostawi takie uprawnienia, jakie ustawił plikom
proces instalujący je w fake root. Ale to akurat niezbyt szczęśliwy pomysł,
tak pozwalać jakiemuś przygodnemu ,,make install'' mieszać nam
w uprawnieniach które przecież potem mogą nam namieszać w całym systemie.
Lepiej trzymać rękę na pulsie i nie dać się skusić Mrocznemu Minusowi.
No dobra, tak naprawdę to RPM użyje wtedy swoich domyślnych
(fabrycznych) ustawień %defattr, ale to się do tego samego
sprowadza.
Te ustawienia domyślne są bardzo fajne i sprawdzają się w 80% przypadków
- z jednym wyjątkiem. Gdy idzie o pliki wykonywalne. Bo na tych
,,defaultach'' wszystkie pliki by się stały niewykonywalne - 0644 w końcu
swoje robi. A my w naszym przykładzie przecież mieliśmy binarki... i co
teraz? Zmienić %defattr? Ale to bez sensu, bo jeśli zacznę nadawać bit
wykonywalny wszystkim plikom, to skończę np. z ,,wykonywalnymi'' plikami
manuala itp. Mógłbym co prawda skusić się i użyć minusa jako uprawnień,
wtedy rpm by wziął to, co dostał od ,,make install'' - ale ja tak nie chcę,
bo nie wiem czy ,,make install'' nie ustawił np. suida na jakiejś binarce
albo nie zrobił czegoś jeszcze gorszego... Nie, to trzeba zrobić inaczej.
Trzeba zmienić uprawnienia pojedynczym plikom z listy, bez zmieniania
atrybutów. I temu (a także innym rzeczom) służy makro
%attr(perm,owner,group). Wystarczy, jeśli zaznaczę
pliki w %{_bindir} i %{_sbindir} nieco innymi flagami...
Co zmieniłem? Dodałem linię %defattr (najlepiej wstawić ją zaraz po %files),
no i doszły te dwa wywołania %attr. Jak widać, używa się ich w linii
z plikami, które mają być zmienione. Po prostu wpisuje się %attr() przed
ścieżką do pliku. Tutaj zmieniłem prawa binarkom na 0755, przy okazji
zmieniłem grupę na ,,bin''. Ot, tak sobie :)
Tutaj, w wywołaniu %attr bezpiecznym już jest użycie Minusa, bo ustawienia
będą dziedziczone z naszych ustawień %defattr - a więc to my wybieramy.
Użyłem minusa, więc ,,owner'' zostanie odziedziczony z poprzednich ustawień.
Czyli tutaj będzie to ,,root''.
Zaczyna to wyglądać coraz bardziej złożenie, ale mam nadzieję że nadal jest
to zrozumiałe.
A teraz jeszcze sprawa plików konfiguracyjnych.
Z plikami konfiguracyjnymi jest tak, że jeśli je sobie ręcznie
zmodyfikujemy, to zwykle chcemy by te zmiany zostały zachowane ,,dla
potomnych''. Trzeba rpm-owi powiedzieć, który plik jest dla nas tak ważny.
Robi się to makrem %config:
Jak widać, makra %config używa się podobnie jak %attr - czyli wpisuje przed
ścieżką do pliku. To taki system :) Jeśli trzeba by połączyć więcej makr na
jednym pliku, to się je po prostu dopisuje. Aby np. zmienić od razu
uprawnienia tego pliku, trzeba by wpisać:
po ,,załapaniu'' podstaw okazuje się to być całkiem prostym, prawda? Ot,
budowanie z klocków. Kinderspiel, jak mawiają Francuzi ;)
Dobrze, ale nie powiedziałem jeszcze co daje to makro %config. Działanie
jest w sumie proste: plik tak oznaczony nie będzie w razie czego usuwany
przy odinstalowywaniu pakietu - i gdy przyjdzie nam jednak na myśl na nowo
zainstalować jakiś usunięty onegdaj pakiet, to będziemy mieć stary, ręcznie
rzeźbiony konfig. Wygodne i proste. Makro %config ma też swoje opcje,
podawane w nawiasie, nieco podobnie jak w przypadku %attr - opcje te to
,,noreplace'' i ,,missingok''.
%config(noreplace) %{_sysconfdir}/gpm-root.conf
spowoduje, że ten plik nigdy nie zostanie automatycznie nadpisany przy
upgrade-owaniu pakietu. Pozostanie niezmieniony, zamiast tego ten nowy plik,
który powinien wylądować normalnie na jego miejscu, zostanie zapisany obok,
z rozszerzeniem .rpmnew. Tej opcji używa się bardzo często, bo rpm nie
ingeruje wtedy automatycznie w pliki konfiguracji. Zamiast tego kładzie
swoje propozycje obok, z rozszerzeniem .rpmnew, a administrator będzie mógł
przejrzeć je i zadecydować ,,co dalej?''.
%config(missingok) %{_sysconfdir}/gpm-root.conf
Ta opcja z kolei wpływa na sposób w jaki działa weryfikowanie poprawności
pakietów (rpm -V). Pliki oznaczone tą flagą można spokojnie usunąć, a rpm
nie uzna tego potem za ,,uszkodzenia pakietu''. Inaczej: tą flagą oznacza
się pliki konfiguracyjne, które wolno usunąć i nie wpłynęłoby to w negatywny
sposób na pracę programu. Przykładu zastosowania nie podam, to dla mnie zbyt
abstrakcyjne ;)
Oprócz makr %defattr, %attr oraz %config w sekcji %flags
może wystąpić także kilka innych konstrukcji. Jedną z nich jest makro
%dir, którego używa się podobnie jak makra %config (tyle że nie ma
żadnych opcji). Tutaj znowu trzeba wyjaśnić kilka spraw - jeśli w sekcji
%files podam np. po prostu
%files
/usr/bin
to w skład pakietu wejdą wszystkie pliki zawarte w /usr/bin oraz sam katalog
/usr/bin. A makro %dir oznacza ,,dołącz do pakietu tylko katalog, nie
włączaj automatycznie jego zawartości''.
Ale żeby nie było w ogóle żadnych niejasności: Załóżmy, że mamy
w $RPM_BUILD_ROOT katalog /usr/bin, i w tym katalogu różne pliki. Teraz mamy
trzy sposoby na oznaczenie tego w sekcji %files:
1. Do pakietu będzie należał katalog /usr/bin wraz zawartością.
%files
/usr/bin
2. Do pakietu będzie należała tylko zawartość /usr/bin, ale bez samego
katalogu. Na czym polega różnica? Jeśli katalog by należał do pakietu,
jak ma to miejsce w pierwszym punkcie, to rpm przy odinstalowywaniu pakietu
będzie próbował usunąć sam katalog (o ile będzie pusty).
%files
/usr/bin/*
3. Do pakietu będzie należał tylko katalog /usr/bin, bez zawartości.
%files
%dir /usr/bin
Teoria teorią, ale kiedy stosuje się taki, a nie inny sposób zapisu?
Pomyślmy... Jeśli instaluje się jakiś program który umieszcza swoje binarki
w /usr/bin, to zwykle załącza się tylko pliki, bez katalogu (bo /usr/bin ma
pozostać, i pozostanie, na swoim miejscu nawet po odinstalowaniu
programu).
Jeśli jednak instaluje się program który tworzy sobie jakiś katalog
z własnymi danymi w /usr/share/foo, to zwykle po usunięciu takiego pakietu
oczekuje się, że /usr/share/foo zniknie z dysku - bo używał go tylko jeden
program, który właśnie usunęliśmy. I w takim wypadku załącza się do pakietu
zarówno zawartość /usr/share/foo jak też sam katalog /usr/share/foo. Gdyby
nie załączyć tego katalogu, to po usunięciu pakietu na dysku by pozostał
pusty katalog /usr/share/foo - zawartość by wyparowała, ale katalog
pozostał. A to by było brzydkie, prawda?
A trzeci sposób, czyli załączenie samego katalogu można stosować np.
w przypadku pakietu zawierającego szkielet katalogów na dysku. Niektóre
dystrybucje mają takie pakiety, które zawierają tylko siatkę katalogów /usr,
/usr/bin, /usr/share/man/man5 itp. I tutaj zwykle użyje się tego makra. Albo
w sytuacjach, gdy sekcja %files wymienia każdy plik pojedynczo, wtedy też
makro %dir znajdzie zastosowanie. Ale to bardzo rzadkie sytuacje...
Następnym makrem jest makro %doc. To makro jest
nieco dziwne, bo typowy przykład użycia tego makra to wpis w rodzaju:
%files
%doc README LICENSE ChangeLog
I działanie jest nieco odmienne niż by można oczekiwać - makro to pobierze
pliki README, LICENSE, ChangeLog z katalogu z rozpakowanymi źródłami,
a nie z $RPM_BUILD_ROOT, następnie umieści te pliki w fake root
w podkatalogu %{_docdir}/%{name}-%{version}, i pooznacza je jako ,,pliki
dokumentacji''. Czyli po prostu weźmie kilka luźnych plików z katalogu ze
źródłami, założy im w razie czego katalog /usr/share/doc/nazwa-wersja,
i skopiuje je tam. To taki sposób na dodanie do pakietu dodatkowej
dokumentacji z pakietu źródłowego, dokumentacji która normalnie by nie
została zainstalowana (bo ,,make install'' jej nie kopiowało).
Jest jeszcze jeden sposób użycia tego makra, można go jednak faktycznie użyć
w odniesieniu do plików zainstalowanych w $RPM_BUILD_ROOT, wtedy zostaną one
oflagowane jako ,,pliki dokumentacji''. Użycie przypomina wtedy nieco
korzystanie z makra %config. Zastanawiające jest w jaki sposób rpmbuild
rozpoznaje, czy makro odwołuje się do pliku z $RPM_BUILD_ROOT czy też jest
rozkazem przekopiowania pliku z katalogu ze źródłami. Nie byłem w stanie
nigdzie tego wyszperać, a w źródłach rpm-a nie chce mi się już grzebać, ale
przypuszczam że rozpoznawane jest to na podstawie wyglądu ścieżki - jeśli
ścieżka zaczyna się od ukośnika, to oznacza plik w $RPM_BUILD_ROOT. Jeśli
jednak podawana jest sama nazwa pliku, bez początkowego ukośnika (czyli tzw.
ścieżka względna), to chodzi o plik z katalogu ze źródłami.
W komplecie do makra %dir istnieje jeszcze makro
%docdir. Oznacza się nim katalog, a wtedy cała jego zawartość
zostanie oflagowana jako ,,pliki z rodzaju dokumentacji''. Ważna uwaga
- makro to nie załącza plików do pakietu, to znaczy że trzeba
najpierw w jakiś sposób ,,normalnie'' włączyć katalog w listę %files,
a potem dodatkowo, w osobnej linii, oznaczyć ten katalog za pomocą
%docdir.
Ale ja tutaj tak gadam i gadam o dokumentacji, ale nie powiedziałem
najważniejszego - o co w ogóle chodzi z tym oznaczaniem dokumentacji. Cóż,
cel jest tylko jeden - przy instalacji pakietów rpm można za pomocą
specjalnego przełącznika (lub ustawienia w ~/.rpmrc) pomijać instalowanie
dokumentacji. To znaczy, że pakiet się zainstaluje ale pliki oznaczone jako
,,dokumentacja'' nie zostaną zainstalowane. Przydatne jest to np. przy
instalowaniu pakietów na małym serwerze, gdzie cenny będzie każdy megabajt
dysku. Przydaje się to też użytkownikom - bowiem polecenie ,,rpm -qd
nazwapakietu wyświetli listę dokumentacji w danym pakiecie
- i użytkownik od razu będzie wiedział, jakie pliki można
przeczytać.
RPM oznacza niektóre pliki automatycznie jako dokumentację. Będą to pliki
lądujące w różnych %{_mandir}, %{_infodir}, %{_docdir}. Czyli tylko
sporadycznie trzeba będzie samemu używać flag %doc/%docdir.
Powoli zbliżamy się do końca opisywania sekcji %files,
zostało nam w sumie tylko jedno ważne makro do opisania - makro
%verify. RPM posiada bardzo fajną możliwość weryfikowania pakietów po
instalacji. Oznacza to po prostu, że RPM porównuje stan plików na dysku
z tym, co ma zapisane w swoich bazach danych. Jeśli coś się różni, to rpm to
zgłasza. A więc wykryje zmiany dat, rozmiaru, uprawnień, sum kontrolnych
itp. Fajne. A makro %verify służy do sterowania zachowaniem rpm-a właśnie
przy przeprowadzaniu weryfikacji. Makro %verify wywołuje się podobnie jak
%config, to makro również przyjmuje opcje w nawiasach. A jakie są to opcje?
md5
Pod weryfikację podpada sprawdzanie sumy kontrolnej md5
size
Wykrywane będą zmiany wielkości pliku
link
Sprawdza, czy plik jest linkiem, a jeśli jest, to czy nie zmieniła się lokacja na którą wskazuje
user
Sprawdza, czy zmianie nie uległ właściciel pliku
group
Sprawdza grupę do której przynależy plik
mtime
Sprawdza czas ostatniej modyfikacji pliku
mode
Wykrywa zmiany w prawach dostępu
Makra używa się np. w taki sposób
%files
%{_bindir}/*
%verify(mode user group) %{_datadir}/foo/plik
co spowoduje, że rpm będzie w przypadu pliku ,,%{_datadir}/foo/plik''
wykrywał tylko zmiany właściciela/grupy i praw dostępu. Zmiany rozmiaru,
daty modyfikacji itp. będą ignorowane.
Jeśli opcje poprzedzi się pojedynczym słowem ,,not'', to zostaną one zanegowane. Np.
oznacza ,,sprawdzaj wszystko, oprócz rozmiaru, sumy kontrolnej
i daty modyfikacji''.
W pewnych wypadkach rpm automatycznie wyłączy
pewne rodzaje weryfikacji w zależności od typu pliku, np. zliczanie sumy md5
na urządzeniach (np. wpisy w /dev).
I na deser została nam tylko jedna cecha sekcji %files, nie jest to co
prawda makro, ale raczej opcja dla samej sekcji. Normalnie sekcję rozpoczyna
się deklaracją ,,%files'', po której następuje lista plików wraz
z ew. makrami %attr, %verify itp. Jeśli jednak sekcję rozpocznie się w taki
sposób:
%files -f plik_z_listą
to rpmbuild odczyta plik ,,plik_z_listą'' i użyje go tak, jakby jego
zawartość wpisać w sekcji %files. Oznacza to, że można nadal normalnie
umieścić listę plików w %files, opcja -f działa po prostu trochę jak
dyrektywa ,,#include'' w C. Zwykle używa się jej w połączeniu z makrem
%find_lang, ale o tym przy innej okazji.
I to by było wszystko co mam do powiedzenia o sekcji %files.