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
datadir=@datadir@
przeszła w
datadir=$(DESTDIR)@datadir@
Możesz obejrzeć całą łatkę.

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.

Konfiguruję ponownie, za pomocą
./configure --prefix=/usr --bindir=/usr/bin --sbindir=/usr/sbin \
--libexecdir=/usr/lib --datadir=/usr/share --sysconfdir=/etc \
--localstatedir=/usr/var --libdir=/usr/lib --includedir=/usr/include \
--infodir=/usr/share/info --mandir=/usr/share/man --exec-prefix=/usr
(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):
usr/
 |-bin/
 |  |-addftinfo
 |  |-afmtodit
 |  |-[...]
 |  |-tfmtodit
 |  \-troff
 |-lib/
 |  \-groff/
 |      \-site-tmac/
 \-share/
     |-doc/
     |  \-groff/
     |      \-1.18.1/
     |          |-examples/
     |          |    |-README.mom
     |          |    |-elvis_syntax
     |          |    |-grnexmpl.g
     |          |    |-grnexmpl.me
     |          |    |-[...]
     |          |    |-typewrite.ps
     |          |    |-webpage.ms
     |          |    \-webpage.ps
     |          |-html/
     |          |  \-momdoc/
     |          |      |-appendices.html
     |          |      |-cover.html
     |          |      |-[...]
     |          |      |-typesetting.html
     |          |      \-using.html
     |          |-meintro.me
     |          |-meintro.ps
     |          |-meref.me
     |          |-meref.ps
     |          |-pic.ms
     |          \-pic.ps
     |-groff/
     |   |-1.18.1/
     |   |   |-eign/
     |   |   |-font/
     |   |   |   \-[...]
     |   |   \-tmac/
     |   |       \-[...]
     |   \-site-tmac/
     |          |-man.local
     |          \-mdoc.local
     |-info/
     |  |-dir
     |  |-groff
     |  |-groff-1
     |  |-groff-10
     |  |-[...]
     |  |-groff-8
     |  \-groff-9
     \-man/
        |-man1/
        |  |-addftinfo.1
        |  |-afmtodit.1
        |  |-[...]
        |  |-tfmtodit.1
        |  \-troff.1
        |-man5/
        |  |-groff_font.5
        |  |-groff_out.5
        |  \-groff_tmac.5
        \-man7/
           |-ditroff.7
           |-groff.7
           |-[...]
           |-groff_www.7
           \-roff.7
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
BuildRequires: /usr/bin/makeinfo
PreReq: /sbin/install-info
Patch0: groff-destdir.patch
Patch1: groff-nroff.patch
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.