FreshSourcing. Čerstvé nápady. Kreatívne riešenia.

» Domov » Auto Tools » 10. časť

10. časť: Knižnice

Minule sme si zdokonalili náš program Hello a dopracovali sme sa k verzii 1.1. Tentoraz ho prepracujeme trošku viac a zároveň si ukážeme, ako môžeme použiť autotools pri programovaní knižníc.

Knižnice

Čo sú knižnice, to asi netreba nikomu pripomínať. Zjednodušene by sme ich mohli opísať ako zbierku nejakých funkcií, ktoré môžeme opätovne použiť bez toho, aby sme ich museli znova kompilovať. Teda stačí ich k nášmu programu iba prilinkovať. Ďalšiu výhodu objavíme, keď budeme mať viac programov používajúcich tú istú knižnicu - stačí, aby sa na disku nachádzala iba raz - ušetríme diskový priestor, aj pamäť RAM (ak ide o dynamickú knižnicu). Navyše si uľahčíme život aj vtedy, ak v kóde knižnice nájdeme chybu - stačí nahradiť jeden súbor, a všetky závislé programy začnú fungovať bezchybne. My si ukážeme, ako vytvoriť tak dynamické, ako aj statické knižnice.

Možno sa pýtate, ako môžeme v tak jednoduchom programe, ako je náš Hello, používať knižnice. Na ilustráciu použijem nie veľmi užitočný (ale ľahko pochopiteľný) príklad. Knižnica hello bude obsahovať rovnomennú funkciu, ktorá vypíše známy text hello, ... (namiesto troch bodiek vypíše svoj jediný argument - char* name). V samostatnom adresári test budú dva programy, ktoré používajú našu knižnicu. Jeden z nich je v podstate totožný z príkladom z minulej časti. Druhý z nich "pozdraví" každému parametru z príkazového riadka (viď nižšie). Úvod by sme mali za sebou, pusťme sa teda do práce!

Hello, world! 2.0

Po prečítaní predchádzajúcich riadkov asi budete súhlasiť s tým, že zmeny v našom programe sú dostatočne "revolučné" na to, aby sme zvýšili aj hlavné číslo verzie na 2. Bude totiž jednoduchšie napísať celý program (resp. knižnicu) odznova, ako opravovať a meniť starý program. Najprv si teda vytvorme prázdny adresár, ktorý bude obsahovať celý projekt. V ňom vytvoríme tieto podadresáre:

doc
src
test

doc

Najjednoduchšie to bude s prvým adresárom - to by ste mali po preštudovaní minulej časti zvládnuť aj sami. Vytvoríme nejakú dokumentáciu (napríklad hello.html) - môže to byť aj prázny súbor, zatiaľ to veľký zmysel nemá. Ďalej vytvoríme súbor Makefile.am, ktorý je v podstate totožný s tým, ktorý sme si uviedli minule:

Makefile.am:

## Process this file with automake to produce Makefile.in

EXTRA_DIST = hello.html

src

V tomto adresári sa budú nachádzať zdrojové kódy a aj neskôr aj výsledná knižnica (dynamická aj statická). Vytvoríme tieto súbory:

hello.c
hello.h
Makefile.am

Prvé dva súbory sú podobné tým z minulej časti.

hello.c

hello.c:

#include <hello.h>

void hello(char* name)
{
printf("hello, %s\n", name);
}

Nasleduje hello.h, ktorý sa neskôr pri inštalácii knižnice (make install) nakopíruje do adresára s ostatnými hlavičkovými súbormi (napríklad do /usr/local/include).

hello.h:

#include <stdio.h>

void hello(char* name);

Zatiaľ by vám malo byť všetko jasné. Teraz však prichádza zmena - súbor Makefile.am, ktorý sa od minulej časti výrazne líši:

Makefile.am:

## Process this file with automake to produce Makefile.in

lib_LTLIBRARIES = libhello.la
libhello_la_SOURCES = hello.c

include_HEADERS = hello.h

DISTCLEANFILES = ./.deps/* ./.deps/.P

Uvedený súbor nemá s tým minulým spoločné skoro nič. Tak teda vysvetlime si jednotlivé riadky. Prvý (okrem komentára) dáva vedieť programu automake, ako sa bude volať výsledná knižnica (LT = libtool; k tomu tiež dôjdeme). Nasleduje zoznam zdrojových súborov. Názov tejto premennej vytvoríme podobne ako v prípade bežných programov (bodky, pomlčky a iné znaky zameníme za podškrtovníky a na koniec pridáme _SOURCES). Naša knižnica sa skladá z jediného zdrojového súboru, ktorý sme uviedli. V nasledujúcom riadku je zoznam všetkých hlavičkových súborov, ktoré sa majú nainštalovať (teda opak noinst_HEADERS pri programoch). Posledný riadok zabezpečí zmazanie adresára .deps pri spustení make distclean (samozrejme, môžeme ho používať aj pri programoch, ak by sme to potrebovali). Tento adresár obsahuje súbory s informáciami o závislostiach jednotlivých zdrojákov.

./

Teraz sa pustíme do ďalšieho (hlavného) adresára nášho projektu a do súborov, ktoré v ňom budú sídliť. Najprv výpis súborov, ktoré musíme vytvoriť sami:

AUTHORS
bootstrap
COPYING
ChangeLog
INSTALL
Makefile.am
NEWS
README
configure.in

Obsah prvého súboru (AUTHORS) nechám na vás. Skúste si všimnúť, ako vyzerá vo väčších projektoch. Aj obsah ChangeLog-u by ste mohli zvládnuť sami, podobne na tom je aj INSTALL, NEWS, README. Obsah súboru s licenciou (COPYING) záleží samozrejme od toho, pre ktorú licenciu ste sa rozhodli. Ostali nám teda tri súbory, od ktorých záleží, či sa náš projekt bude správať ako knižnica alebo program.

Libtoolizing

Najprv načrtnem, ako prebieha proces "knižnicovania". Najprv spustíme aclocal, ktorý prehľadá configure.in a zapíše potrebné m4 makrá do súboru aclocal.m4. Ďalej prichádza na rad libtoolize pre nás nový program, ktorý pripraví projekt na použitie ďalšieho programu - libtool. Okrem toho do projektu nakopíruje (s parametrom --copy) tieto súbory:

config.guess
config.sub
ltconfig
ltmain.sh

Prvé dva súbory majú na starosti identifikáciu cieľového stroja. Výstup prvého z nich môže vyzerať napríklad takto:

i586-pc-linux

Ak spustíte druhý program napríklad takto:

./config.sub i586-pc-linux

dostanete niečo také:

i586-pc-linux-gnu

Ďalší z programov (ltconfig) má za úlohu pri svojom spustení vytvoriť libtool, špecifický pre vaše prostredie. Spustí sa až niekde na konci behu configure (všimnite si výstup a uvidíte).

Za použitím libtoolize nasleduje klasika: autoheader, automake, autoconf. Všetko to môžeme pekne "zlepiť" do nášho súboru bootstrap, ktorý poznáme aj z predchádzajúcich častí.

bootstrap:

aclocal && \
libtoolize --force --copy && \
autoheader && \
automake --gnu --add-missing --copy && \
autoconf

Ak by vám nebol zrejmý význam parametrov už z ich prekladu, skúste si pozrieť --help.

Makefile.am

Tento súbor sa viac-menej nezmenil, zmenili sme ho iba preto, aby si "všimol" aj nový adresár (test).

Makefile.am:

## Process this file with automake to produce Makefile.in

SUBDIRS = . src test doc

Poradie adresárov je veľmi dôležité.

configure.in

Tento súbor sa zmenil asi najviac - zachytáva všetky zmeny knižnice oproti programu. Možno sa vám zmena dvoch-troch riadkov nebe zdať až taká veľká, avšak skúste si porovnať výstup programu configure. Teraz bude kontrolovať oveľa viac vecí, z ktorých mnohé majú význam iba pri knižniciach. Porovnajte si ich.

configure.in:

dnl Process this file with autoconf to produce a configure script.

AC_INIT(src/Makefile.am)
AM_INIT_AUTOMAKE(hello, 2.0)

AC_PREFIX_DEFAULT(/usr/local)
AM_CONFIG_HEADER(config.h)

AC_PROG_INSTALL
AC_PROG_LN_S
AM_PROG_LIBTOOL

AC_OUTPUT(Makefile src/Makefile test/Makefile doc/Makefile)

Prvé dva riadky by vám mali byť zrejmé. Tretí nastavuje štandardný adresár, kam sa program/knižnica nainštaluje. V prípade programu sa napríklad binárky inšatlujú do adresára $PREFIX/bin, ak robíme knižnicu, používajú sa adresáre ako $PREFIX/lib na knižnice a $PREFIX/include na hlavičkové súbory. Samozrejme, vo všetkých prípadoch si môžete zmeniť či už prefix, alebo iba konkrétny adresár (napríklad hlavičkové súbory chcete mať v /usr/local/include, ale dokumentáciu, knižnice a podobne chcete mať priamo v podadresároch /usr. Ďalší riadok by vám takisto mal byť známy. Nasledujúce riadky oboznamujú príslušné programy, že budete používať programy install a libtool. Makro AC_PROG_LN_S zistí, či v aktuálnom adresári fungujú symbolické odkazy/linky (ln -s); ak nie, bude sa používať ln. No a dostávame sa k poslednému riadku, ktorý zabezpečí vytvorenie všetkých Makefile zo súborov Makefile.in.

test

Práve sa dostávame do posledného adresára, v ktorom sú umiestnené programy, ktorých cieľom je overiť funkčnosť knižnice, prípadne ukázať jej možnosti (napríklad pri grafickej knižnici by to mohli byť nejaké demá, jednoduché hry a pod.). V prvom prípade by bolo dobré, ak by sme tak urobili pred prípadnou inštaláciou (môžeme do príslušných súborov Makefile.am pridať napríklad pravidlo tests, ktoré by spustilo všetky testovacie programy v tomto adresári - to by pre vás už vôbec nemal byť problém). Najprv si uvedieme zdrojové súbory obidvoch testov.

test1.c:

#include <hello.h>

int main(int argc, char* argv[])
{
if (argc < 2) {
printf("Usage: %s \"text\"\n", argv[0]);
return 1;
}

hello(argv[1]);
return 0;
}

Tento súbor je veľmi podobný súboru main.c z minulej časti; jediná zmena je vlastne v prvom riadku - úvodzovky sme zamenili za lomené zátvorky, pretože hlavičkové súbory budeme odteraz môcť nájsť v niektorom zo "štandardných" adresárov s hlavičkovými súbormi. Ideme na druhý test:

test2.c:

#include <hello.h>

int main(int argc, char* argv[])
{
int i;

if (argc < 2) {
printf("Usage: %s name [name]...\n", argv[0]);
return 1;
}

for (i = 1; i < argc; i++)
hello(argv[i]);

return 0;
}

Tento test "pozdraví" každé meno, zadané na príkazovom riadku. Napríklad:

$ ./test2  "Janko Hrasko"  "Juro Janosik"  Neo

hello, Janko Hrasko
hello, Juro Janosik
hello, Neo

Zdrojáky by sme teda mali, ostáva nám len "zlepiť" to dohromady:

Makefile.am:

## Process this file with automake to produce Makefile.in

noinst_PROGRAMS = test1 test2

test1_SOURCES = test1.c
test1_LDADD = ../src/libhello.la

test2_SOURCES = test2.c
test2_LDADD = ../src/libhello.la

CFLAGS = -I../src

V prvom riadku vymenujeme všetky programy, ktoré by sme chceli kompilovať (ale nie nainštalovať, pretože to sú iba testy; inak by sme nahradili noinst slovkom bin). Nasledujú dve dvojice, ktoré opisujú, ako dané programy kompilovať/linkovať. Pre každý program musíme uviesť dva takéto riadky. Prvý obsahuje zoznam zdrojových súborov, druhý z nich knižnice, ktoré potrebujeme pridať. Všimnite si relatívnu cestu ku knižnici. Nemôžeme teda použiť niečo ako -lhello, teda aspoň v testoch nie - ak by sme robili aj nejaký samostatný program, ktorý by túto knižnicu používal, tam to už urobiť môžeme - ale o tom až nabudúce. Posledný riadok pridá adresár ../src medzi tie, v ktorých bude preprocesor jazyka C hľadať hlavičkové súbory.

Teraz už je na rade iba klasická kombinácia:

./bootstrap

./configure
make
make install

Posledný riadok samozrejme nie je nutný, ale môžete si vyskúšať, čo sa kde nainštaluje (ak sa bojíte neporiadku v systéme, tak vedzte, že funguje aj make uninstall, ktorý vás zbaví starostí). Môžete si takisto vyskúšať príkazy make dist a make distcheck, ktoré vám vytvoria balíček hello-2.0.tar.gz.

Teraz sa ešte pozrime na súbory test1 a test2 v adresári test. Skúste ich otvoriť vo vašom obľúbenom textovom editore. Že načo by ste mali otvárať binárne súbory? Skúste! Ste prekvapení? Prečo je to tak?

V čase kompilácie testov ešte naša knižnica nie je nainštalovaná, čo je celkom logické. Keďže používame dynamické knižnice, systém potrebuje vedieť, odkiaľ ich má v prípade potreby načítať do pamäte. Normálne je táto cesta uložená v globálnej premennej $LD_LIBRARY_PATH. Tieto shell skipty, ktoré sa používajú dočasne namiesto normálnych bináriek, sa starajú o správne načítanie ešte nenainštalovanej knižnice. V čase inštalácie sa nahradia skutočnými binárkami (pred inštaláciou samotných testov - ak ich chceme inštalovať). Skúste si pozrieť obsah adresára test/.libs.


Oto Komiňák


Článok bol uverejnený v magazíne PC Revue 11/2002.