OK, weźmy się teraz za groffa. Groff to system formatowania dokumentów.
Przyjmuje specjalny rodzaj dokumentów źródłowych składający się z tekstu
i znaczników, a następnie produkuje z tego dokument wyjściowy, przeznaczony
do pokazania na urządzeniu znakowym (jako ascii/ansi/html), ekranie X, czy
też wydrukowaniu na drukarkach zgodnych z PCL lub PostScript. Choć i tak
pewnie 95% zastosowań groffa to formatowanie stron manuala na potrzeby
programu ,,man''.
1. Źródła są do pobrania z dowolnego serwera FTP zawierającego kopię
zasobów GNU (czyli praktycznie z każdego większego)
2. Próbna instalacja
Hmm, skrypt ./configure nie ma żadnych dodatkowych opcji, więc przekazuję mu
na potrzeby testu tylko ,,--prefix=/usr''. Kod jest chyba w całości napisany
w C++, ale groff poprawnie użył flag pochodzących z $CXXFLAGS, więc wszystko
jest OK. Standardowa kompilacja trochę potrwa... o, udało się. Teraz
make install DESTDIR=/shm
i... błąd. groff próbuje usunąć plik /usr/bin/groff (pewnie próbuje usunąć
starą wersję przed instalacją), co mu się nie udaje (właśnie
dlatego, dla takich sytuacji nie warto bawić się
w kompilowanie/budowanie pakietów na uprawnieniach roota). Nie wiem czy
groff w ogóle ignoruje $DESTDIR, czy też może brakuje mu przekierowania
jedynie we fragmentach które mają oczyścić mu pole operacyjne. Nieważne
- trzeba go poprawić, tak czy siak. Dla pewności robię
grep -r DESTDIR .
i... nic. Zero wyników. To znaczy, że groff po prostu pojęcia nie ma
o ustrojstwie zwanym $DESTDIR. Dobra, po przeglądaniu plików Makefile, wyników
make -k install DESTDIR=/shm
(chcę zobaczyć wszystkie instalowane pliki, nawet jeśli proces
instalacji się nie powiedzie) i zwyczajowej w takich sytuacjach grzebaninie
w różnych plikach, po paru nieudanych zmianach dochodzę do wniosku, że
wystarczy zmienić w głównym pliku Makefile.in linie rozłożone w różnych
miejscach, definicje: @prefix@, @exec_prefix@, @bindir@, @libdir@,
@datadir@, @infodir@ i @mandir@, poprzedzając je odpowiednio prefiksem
$(DESTDIR), np. linia
Notka: gdy ingeruje się w takie zmienne, to powinno się uruchamiać
./configure z takimi parametrami, jakich będziemy potem używać wewnątrz
rpm-a. Nie chodzi o konkretne wartości, tzn. nie gra większej roli czy użyje
się /usr/man czy /usr/share/man, chodzi o samą obecność przełączników
--mandir, --libdir itd. Skrypty Makefile.in zwykle pisane są tak, że zmienne
tworzą sieć względnych definicji - jedna zmienna jest zdefiniowana tak,
druga wykorzystuje jej definicję i dodaje jakiś swój sufiks, itd. Niektóre
zmienne jednak są niezależne. Czasem jeszcze większe zamieszanie powodują
,,wartości domyślne''. Np. załóżmy, że @mandir@ jest w pliku Makefile.in
zdefiniowane jako @mandir@. Czyli żadnej zależności od innych wartości,
bierze to, co się mu poda. A przynajmniej tak to by mogło wyglądać, bowiem
jeśli użyję --mandir=/usr/share/man, to @mandir@ będzie miało wartość /usr/share/man, faktycznie. Jeśli jednak pominę ten
argument, to ./configure zdefiniuje tę wartość jako @mandir@=@prefix@/man,
bo taki ma ,,default'', o czym zresztą mówi ,,./configure --help''. Chodzi
o to, że robi się z tego deklaracja łańcuchowa ,,po cichu'', chociaż
początkowo wcale na taką to nie wyglądało. Jeśli ja zmienię tylko linię
definicji zmiennej @prefix@ na $(DESTDIR)@prefix@ w Makefile.in, to @prefix@
rozwinie się po zdefiniowaniu DESTDIR przy instalacji np. w /shm/usr, a @mandir@ w /shm/usr/man. Czyli świetnie. Jeśli jednak użyję opcji
--mandir=/usr/share/man, to @mandir@ rozwinie się w /usr/share/man (bo nie ma już zależności od @prefix@, ta
zależność istniała tylko wtedy, gdy ./configure uzupełniało zmienne swoimi
domyślnymi wartościami).
Trzeba pamiętać, że makro %configure używa ,,rozwiniętych'' wartości opcji
--prefix, --bindir, --mandir itp., czyli np. '--bindir=/usr/bin' a nie
'--bindir=%{_prefix}/bin'. Co prawda definicje w pliku ~/.rpmmacros mają
postać wiązaną, łańcuchową, ale w momencie użycia makra w pliku .spec rpm
rozwija makro do jego postaci końcowej. Jeśli rpm używa wartości dosłownych,
to skrypt ./configure dostanie definicje wszystkich katalogów, nic nie
otrzyma definicji ,,domyślnej''. W plikach Makefile prawdopodobnie znikną
wszystkie łańcuchowe odwołania, zostaną zastąpione dosłownymi
opisami.
W przypadku groffa wystarczy załatać linijkę z @prefix@ by po
skonfigurowaniu jako
./configure --prefix=/usr
pakiet dał się zainstalować w $DESTDIR. Taka poprawka by jednak zawiodła
przy późniejszym budowaniu pakietu rpm, bo rpm by przekazał nie tylko
--prefix, ale i cały komplet opcji. Co by spowodowało, że zmienne @infodir@
itp. przestałyby się odwoływać do @prefix@, przez co utraciłyby jakikolwiek
związek z $DESTDIR, przez co by znowu próbowały się zainstalować
bezpośrednio w /usr... skomplikowane to, ale daje się sprowadzić do jednej
prostej reguły - jeśli zmieniasz zmienne w Makefile.in, to sprawdzaj to
potem używając opcji jak najbardziej zbliżonych do tych, jakie wystąpią
w pliku .spec. Nie chodzi zbytnio o wartości, ale o samą obecność
przełączników w linii poleceń. Warto używać jednak wartości docelowych, bo
dzięki temu czasem odkryjesz, że jakiś pakiet np. nie kieruje się według
podanych mu opcji lub ma inne wady.
Poprawki które wprowadziłem do Makefile.in dadzą dziwne wyniki jeśli
nie poda się explicite ważniejszych opcji. Ale że jest to patch tylko
na potrzeby budowy pakietu, to wszystko jest w porządku.
(tutaj doskonale sprawdza się zsh i jego dopełnianie parametrów skryptów
./configure - oszczędza wklepywanie do minimum i eliminuje możliwość
popełnienia błędu/literówki)
Kompiluję, próbuję zainstalować... błąd:
The prefix directory `/shm/usr' doesn't exist
OK, najwidoczniej zakłada, że katalog /usr będzie
istniał (to trochę jak z tym katalogim bin
w przypadku xwelltris, tyle że tutaj prawidłowo proces się wyłożył zamiast
kombinować za plecami). Dobra, jeszcze raz:
mkdir /shm/usr
make install DESTDIR=/shm
I wreszcie się udaje. No. Wynikowe drzewko plików (z obciętymi gałęziami):
Nie ma tu nic, co by zasługiwało na większą uwagę. Na pewno wywalę
z końcowego pakietu zawartość katalogu /usr/share/doc, bo tam są tylko informacje dla ludzi
chcących pisać dokumenty. Do odczytywania wystarczą mi informacje
zawarte w stronach manuala. Strony info standardowo są podzielone na wiele
plików, więc to też będę musiał zwalczyć. Ale poza tym nie powinno tu być
już więcej kłopotów.
A, nie, moment. Przypomniało mi się coś. W serii 1.18.x groffa zmieniło się
zachowanie systemu - wyjdzie to przy oglądaniu stron manuali. Starszy groff
prawdopodobnie automatycznie reformatował strony tak, by wypełniały całą
szerokość strony. Nowy tego nie robi, należy mu podać odpowiednie opcje.
Problem polega na tym, że ,,man'' się do tego jeszcze nie dostosował. Albo
używa starego sposobu zmuszania groffa do współpracy, albo nawet nie próbuje
tego robić - w efekcie strony manuala będą formatowane tak, aby mieć
szerokość 80 znaków. Istnieje proste rozwiązanie: ,,man'' uruchamia skrypt
,,nroff''. A skrypt ,,nroff'' jest niczym innym, jak prostą owijką na
program ,,groff'' (który z kolei uruchamia ,,troff'', ale to nie jest tutaj
ważne :). Nroff jest na tyle prosty, że można go zmodyfikować i dodać opcje,
które spowodują dobieranie szerokości dokumentu do szerokości terminala.
Wystarczy zmodyfikować jedną linię. Możesz obejrzeć tego prostego patcha którego zestawiłem. Nie wiem czy ten patch jest
,,ideowo poprawny'', w zasadzie należałoby to naprawiać pewnie w źródłach
programu ,,man'', ale łatwiej jest to załatać w ten sposób.
3. Plik .spec
Summary: groff - frontend systemu formatowania dokumentów groff
Name: groff
Version: 1.18.1
Release: 1
License: GPL
Group: Tekst
Source: %{name}-%{version}.tar.bz2
BuildRoot: /var/tmp/%{name}-%{version}
BuildRequires: /usr/bin/makeinfo
PreReq: /sbin/install-info
Patch0: groff-destdir.patch
Patch1: groff-nroff.patch
%description
"groff" jest frontendem do systemu formatowania dokumentów groff. Normalnie
uruchamia on program troff i odpowiedni dla wybranego urządzenia
postprocesor (zawarte już w tym pakiecie).
%prep
%setup -q
%patch0 -p0
%patch1 -p0
%build
%configure
make
%install
rm -rf %{buildroot}
mkdir -p %{buildroot}%{_prefix}
make install DESTDIR=%{buildroot}
rm -rf %{buildroot}/%{_docdir}
rm -f %{buildroot}/%{_infodir}/{dir,groff*}
cd %{buildroot}/%{_infodir}
makeinfo --no-split %{_builddir}/%{buildsubdir}/doc/groff.texinfo \
-I %{_builddir}/%{buildsubdir}/doc -o groff.info
%files
%defattr(0644,root,root,0755)
%attr(0755,root,root)%{_bindir}/*
%{_mandir}/man*/*.gz
%{_libdir}/*
%{_datadir}/groff
%{_infodir}/groff.info.gz
%clean
rm -rf %{buildroot} %{_builddir}/%{buildsubdir}
%post
install-info %{_infodir}/groff.gz %{_infodir}/dir
%preun
install-info --delete %{_infodir}/groff.gz %{_infodir}/dir
Nie ma tu specjalnie ciekawych rzeczy - w preambule doszły linie
oznaczające zależności wymagane na etapie budowania i instalacji pakietu.
Deklaruję też dwa patche, które potem aplikuję w sekcji %prep.
Sekcja %install przed wykonaniem instalacji najpierw tworzy katalog usr/, a po instalacji wywala dokumentację z usr/share/doc, oczyszcza katalog usr/share/info, wchodzi do niego, a następnie generuje na
nowo jeden duży plik info. Przy okazji zmieniam mu nazwę na bardziej
standardową, z rozszerzeniem .info.
Sekcja %files jest prosta - binarki, manuale, zawartość %{_libdir}, katalog usr/share/groff oraz strona info. Stronę info wymieniam
,,z imienia'' - gdyby któregoś dnia do pakietu groff doszły inne strony,
zapewne też podzielone na wiele plików, to wolałbym o tym wiedzieć (i je
również poprawić). Jeśli deklaruję w katalogu usr/share/info tylko jeden plik .info, to gdy dojdą inne
(jeśli kiedyś dojdą w ogóle) mechanizm rpmbuild mnie zaalarmuje.
4. Budowanie pakietu kończy się stuprocentowym sukcesem.