Automake
V této kapitole vám ukáži trochu blíže fungování automake. Budu pokračovat v projektu z předchozí kapitoly, vytvořím Makefile.am soubory a detailněji popíši jeho souboru.
Příprava configure.ac
Při použití automake je potřeba přidat do configure.ac několik maker. Znáte je už z kapitoly GNU Build System, ale opakování je matka moudrosti.
AC_CONFIG_AUX_DIR
Makro AC_CONFIG_AUX_DIR
označuje adresář, kam má automake
instalovat pomocné skripty (které se budou distribuovat s vaším projektem).
Jedná se o skripty, které pomáhají s instalací a překladem projektu.
Nahrazují různé funkce systému, které nejsou všude
dostupné, nebo se chovají odlišně.
Adresář se obvykle pojmenovává build-aux
(auxiliary – pomocný).
Potřebné soubory se nainstalují příkazem automake -i
,
nebo autoreconf -i
. Adresář musí existovat,
tak jej vytvořte.
AM_INIT_AUTOMAKE
Makro AM_INIT_AUTOMAKE
inicializuje automake. Popis tohoto makra
viz GNU Build System - automake.
Nezapomeňte odstranit -Werror
než vypustíte svůj projekt do světa!
Jinak i obyčejné varování o deprecated makru znemožní uživatelům váš balíček
zkonfigurovat.
AM_SILENT_RULES
Tímto makrem můžete nastavit defaultní úroveň ukecanosti vygenerovaných
makefile souborů. Pokud mu předáte za hodnotu argumentu yes
,
bude úroveň upovídanosti make jako s volbou V=0
.
Já jsem do configure.ac přidal toto makro s volbou no
, protože
během ladění chci vidět vše, co make dělá. (Stejného výsledku bych dosáhl,
kdybych makro AM_SILENT_RULES
vůbec neuvedl.)
AC_PROG_RANLIB a AM_PROG_AR
Tyto makra přidám za makro AC_PROG_CC
. Kontrolují existenci
programů ranlib a ar, které make potřebuje pro svou
práci. Pokud byste tyto makra neuvedli, automake by si postěžoval a vypsal by
vám varování, že je máte do configure.ac přidat.
$ automake src/Makefile.am:7: error: library used but 'RANLIB' is undefined src/Makefile.am:7: The usual way to define 'RANLIB' is to add 'AC_PROG_RANLIB' src/Makefile.am:7: to 'configure.ac' and run 'autoconf' again. ...
Automake vás upozorňuje jen na ty makra, která hledají programy, jež aktuálně potřebuje. Takže při úpravě/rozíšření Makefile.am souborů se může stát, že bude požadovat další makra. Například tyto:
AC_PROG_INSTALL
AC_PROG_LN_S
AC_PROG_MAKE_SET
AC_CHECK_LIB
Makro AC_CHECK_LIB
se používá k testování existence nějaké
knihovny. Pokud knihovna existuje, přidá do proměnné LIBS
volbu -lknihovna
.
Proměnné se do Makefile.am předávají voláním
AC_SUBST. V Makefile se takto
definované proměnné používají ve formátu @nazev_promenne@
.
Nicméně, @LIBS@ využívá automake automaticky, takže se o to nemusíte starat.
Posledním argumentem jsou knihovny, které jsou potřeba pro překlad programu
s hledanou knihovnou (z prvního argumentu). Například -lXt -lX11
.
Funkce může být main
, nebo nějaká konkrétní funkce z knihovny,
kterou testujete.
Pokud je knihovna s funkcí nalezena, definuje se v config.h cpp makro
HAVE_LIBKNIHOVNA
.
V mém příkladu potřebuji získat -l
volby pro
curses
, threads
a m
knihovny. O Curses se stará makro
AX_WITH_CURSES.
O zbylé dvě se postará AC_CHECK_LIB
:
AC_CHECK_LIB([m], [isgreater]) # -lm
V knihovně pthread hledám funkci pthread_create()
(jedna náhodně
vybraná z mnoha, kterých v programu používám). V matematické knihovně libm
hledám isgreater()
(jediná „funkce“ z math.h, kterou
používám).
A tady je malý chyták. Funkce isgreater()
totiž není funkce, ale
makro. Takže výsledkem AC_CHECK_LIB([m], [isgreater])
bude
„no“ a volba -lm
se do LIBS nepřidá.
Protože isgreater()
je definované jako makro v math.h a žádnou
reálnou funkci z knihovny m
v programu nepoužívám,
tak to vlastně nevadí :-).
Pokud bych v libm hledal třeba funkci cos
, už by se knihovna
s touto funkcí našla a volba -lm
přidala. Mějte to na paměti
…
AC_CHECK_LIB nelze moc dobře použít s C++ knihovnou. Metody z tříd jsou totiž
pojmenovány způsobem, který se může lišit systém od systému, překladač od
překladače. Například z X::App::Model::Chess::Position::init() se stane
_ZN1X3App5Model5Chess8Position4initEv, nebo taky něco jiného.
Jediné v co můžete doufat je nějaká funkce deklarovaná uvnitř extern "C" {
.
Viz Name mangling.
AC_CONFIG_FILES
Posledním, nicméně neméně důležitým makrem je AC_CONFIG_FILES
,
jehož argumentem je seznam Makefile souborů, které se budou z Makefile.am
souborů generovat.
Makefile
src/Makefile
src/thnc/Makefile
])
Tolik k souboru configure.ac Teď je tedy třeba vytvořit soubory Makefile.am,
src/Makefile.am a src/thnc/Makefile.am. Mohou být i prázdné.
Spuštěním autoreconf -i
si můžete ověřit, že máte vše správně
nastaveno.
Výsledný config.ac soubor
Bez komentáře:
# Process this file with autoconf to produce a configure script.
AC_PREREQ([2.69])
AC_INIT([example1], [0.2], [sallyx@example.org])
AC_CONFIG_SRCDIR([src/wposition.c])
AC_CONFIG_HEADERS([src/config.h])
AC_DEFINE([RELEASE_YEAR],["2018"],[Define the year of release])
CONF_OPT=$@
AC_DEFINE_UNQUOTED([CONF_OPT], ["$CONF_OPT"], [Define configure options])
AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_AUX_DIR([build-aux])
AM_INIT_AUTOMAKE([-Wall -Werror])
AM_SILENT_RULES([no])
AX_WITH_CURSES
if test "x$ax_cv_ncursesw" != xyes && test "x$ax_cv_ncurses" != xyes; then
AC_MSG_ERROR([requires either NcursesW or Ncurses library])
fi
if test "x$ax_cv_curses_color" != xyes; then
AC_MSG_ERROR([requires an (N)Curses library with color])
fi
# Checks for programs.
AC_PROG_CC
AC_PROG_RANLIB
AM_PROG_AR
# Checks for libraries.
AC_CHECK_LIB([pthread], [pthread_create]) # -lpthread
AC_CHECK_LIB([m], [isgreater]) # -lm
# Checks for header files.
AC_CHECK_HEADERS([locale.h stdlib.h unistd.h], [],[AC_MSG_ERROR([Unable to find the header.])])
# Checks for typedefs, structures, and compiler characteristics.
AC_CHECK_HEADER_STDBOOL
AC_TYPE_SIZE_T
AC_CHECK_TYPE([double_t],[have_double_t=yes;],[],[[@%:@include <math.h>]])
AS_IF([test x"$have_double_t" == xyes],[
AC_DEFINE([HAVE_DOUBLE_T],[1],[Defined if double_t is defined in math.h])
])
# Checks for library functions.
AC_CHECK_FUNCS([atexit setlocale],[],[AC_MSG_ERROR([Unable to find function atexit or setlocale.])])
AC_CONFIG_FILES([
Makefile
src/Makefile
src/thnc/Makefile
])
AC_OUTPUT
Formát souboru Makefile.am
Formát souboru Makefile.am je poměrně jednoduchý. Pouze se do proměnných se speciálním významem přiřazují hodnoty, nebo můžete psát vlastní make pravidla. V následujícím textu popíši, jaké speciální proměnné máte k dispozici a k čemu se používají.
Podívejte se na soubor Makefile.am, který je umístěn v kořeni (rootu) projektu:
ACLOCAL_AMFLAGS = -I m4
include data/Makefile.soubory
indent:
cd src;$(MAKE) indent
ACLOCAL_AMFLAGS
viz K čemu je ACLOCAL_AMFLAGS?.
Zde je jedna speciální proměnná, SUBDIRS
.
Automake prochází všechny
adresáře zapasné v této proměnné (oddělené mezerou) a spouští
v nich make. Nakonec spustí make v aktuálním adresáři.
(Pokud byste chtěli spustit nejdříve make v aktuálním adresáři,
přidejte do SUBDIRS
na první místo tečku.)
include
includuje pravidla ze zadaného souboru. Tato pravidla jsou
includována před zpracováním automake programem, takže mohou obsahovat
speciální automake proměnné. Cokoliv, co byste mohli zapsat do Makefile.am můžete
zapsat do extra souboru a takto includovat.
Na konci jsem přidal vlastní make cíl (target) indent:
. V tomto případě
jen přejde do adresáře src a spustí make indent
(toto pravidlo
používám k formátování zdrojových souborů programem indent). Výsledný makefile tedy bude
obsahovat cíl indent a všechny cíle, které vytváří automake (install, distcheck, clean atd.).
Pokud definujete cíl, který vytváří automake, takt ten váš vyhrává. Automake
jej nechá, jak je. Pokud chcete něco přidate ke generovanému cíli,
můžete použít *-local
variantu:
- $(RM) *~ *.gch
Při spuštění make clean
se nejdříve spustí clean-local
a pak cíl clean
, vygenerovaný automake programem.
Existuje ještě -hook
varianta, která se spouští po
automake cíli. Tato varianta je podporována jen u těchto cílů:
install-data
, install-exec
, uninstall
,
dist
a distcheck
.
Vygenerovaný Makefile má cíle pro vygenerování sám sebe. Je závislý na Makefile.in a config.status. A config.status je závislý na configure skriptu. Takže když znovu vygenerujete configure skript, automake jej znovu spustí a sám se znovu vygeneruje :-).
Primaries
Názvy nejdůležitějších speciálních proměnných se skládají z tzv. primaries a prefixu, který určuje adresář, kam se „primaries“ budou instalovat.
Automake podporuje tyto primaries:
- PROGRAMS
- Seznam programů, které se mají přeložit.
- LIBRARIES
- Seznam knihoven
- LISP
- PYTHON
- JAVA
- SCRIPTS
- Seznam bash skriptů
- DATA
- Seznam dat, která se stanou součástí distribučního balíčku
- HEADERS
- Hlavičkové soubory, jež se budou instalovat.
- MANS
- Manuálové stránky.
- TEXINFOS
- LTLIBRARIES
- Tento „primary“ přidává libtools, které budu popisovat až v kaptiole Libtool.
Programy napsané v jazycích lisp, python, java a vlastně i scripty se instalují a spouštějí jinak, než klasické binární programy, proto mají vlastní primaries. Těmto jazykům se věnovat nebudu.
Před primaries se dává prefix určující adresář, kam se mají soubory
instalovat. Automake poskytuje tyto prefixy:
bindir
,
sbindir
,
libexecdir
,
datarootdir
,
datadir
,
sysconfdir
,
sharedstatedir
,
localstatedir
,
includedir
,
oldincludedir
,
docdir
,
infodir
,
htmldir
,
dvidir
,
pdfdir
,
psdir
,
libdir
,
lispdir
,
localedir
,
mandir
a
manNdir
.
K názvu proměnné se připojují bez dir
. Nejpoužívanější primaries
budou ve tvaru bin_PROGRAMS
, data_DATA
,
include_HEADERS
nebo lib_LIBRARIES
.
Můžete si vytvořit dokonce vlastní prefix pro adresář. Jde o libovolnou
proměnnou, která končí na dir
.
Například:
soubory_DATA= ...
Automake definuje mnoho užitečných proměnných, které můžete používat. Jejich přehled získáte nejsnadněji tím, že si otevřete vygenerovaný Makefile. Najdete v něm takovéto definice:
exec_prefix = $(prefix)
bindir = $(exec_prefix)/bin
sbindir = $(exec_prefix)/sbin
datarootdir = $(prefix)/share
datadir = $(datarootdir)
includedir = $(prefix)/include
pkgdatadir = $(datadir)/example1
pkgincludedir = $(includedir)/example1
pkglibdir = $(libdir)/example1
pkglibexecdir = $(libexecdir)/example1
bindir = $(exec_prefix)/bin
mandir = $(datarootdir)/man
...
Obsah proměnných prefix
, exec_prefix
atp. je přesně to,
co vám umožňuje změnit svými volbami skript configure. A dají se ještě změnit
i při spuštění make.
Další speciální prefixy jsou tyto:
- check
- Označuje primaries, které se mají překládat jen kvůli testování.
- noinst
- Označuje primaries, které se nemají instalovat. Takové primaries samozřejmě nemají prefix označující instalační adresář. Můžete chtít přeložit program, který použijete při překladu/instalaci/testování jiného programu, ale více ho již nepotřebujete. Tak nač jej instalovat. Ze stejného důvodu můžete chtít distribuovat nějaký bash skript.
- EXTRA
- EXTRA je prefix pro programy, které se překládají podmíněnně.
Některé programy můžete chtít překládat jen pokud uživatel o ně požádá nějakou volbou configure skriptu, na základě proměnných prostředí, nebo pokud byly nalezeny potřebné knihovny.
V takovém případě názvy těchto programů musíte uvést v EXTRA, aby automake věděl, že existují a že je (jejich zdrojové soubory) má zahrnout do distribučního balíčku, a že má vytvořit pravidla pro jejich sestavení v Makefile. V PROGRAMS primaries pak místo názvu programu uvedete název proměnné, která se podmíněně nastavuje v configure skriptu. Například:
bin_PROGRAMS = prog1 @extra_programy@
Proměnná @extra_programy@ se bude nastavovat v configure.ac pomocí makra
AC_SUBST a to na hodnotu prog2
, prog3
,
prog2 prog3
nebo nic.
Poslední speciální prefixy, které lze přidat k některým primaries, jsou:
- dist
- Označuje soubory, které se mají zahrnout do distribučního balíčku. Tato volba je obvykle defaultní, takže ji není třeba uvádět. Vyjímkou je např. primary DATA.
- nodist
- Označuje soubory, které se nemají zahrnout do distribučního balíčku. Například zdrojové soubory, které se generují pomocí configure skriptu.
- nobase
- automake obvykle odstraňuje adresáře ze jmen souborů, se kterými pracuje. Pokud chcete nainstalovat hlavičkový soubor xxx/soubor.h, automake ho nainstaluje do příslušného adresáře pro instalaci jako soubor.h. S volbou nobase jej ale nainstaluje do podadresáře xxx příslušného adresáře pro instalaci.
Zdrojové kódy a volby překladu
S každým programem jsou asociované různé pomocné proměnné. Tyto proměnné jsou nepovinné a mají nějaké defaultní hodnoty.
Součástí názvu takovýchto proměnných je název programu. Nejdůležitější je
proměnná nazev_prgoramu_SOURCES
, která obsahuje seznam
zdrojových souborů.
bflmpsvz_SOURCES=threads.c wposition.h
Pokud není proměnná *_SOURCES definována, defaultní hodnota pro ni je
nazev_programu.c
.
Proměnné nemohou obsahovat některé speciální znaky, které může obsahovat název programu nebo knihovny (jako například pomlčka, tečka atp.). Tyto speciální znaky se v názvu proměnné nahrazují podtržítkem. Například:
libinit_a_SOURCES=init.c init.h config.h curses-local.h
libwposition_a_SOURCES=wposition.c wposition.h config.h curses-local.h
Další s programem asociované proměnné jsou:
- nazev_programu_CPPFLAGS
- Přepínače pro preprocessor (např. -Dmakro=hodnota -I..)
- nazev_programu_CFLAGS
- Přepínače pro překladač (např. -g -Wall)
- nazev_programu_LDADD
- Seznam knihoven, které se mají k programu přilinkovat (např. libinit.a)
- nazev_programu_LDFLAGS
- Volby pro linker (např -lm -L/xxx)
Pro vytváření knihoven můžete ještě použít volbu:
- knihovna_LIBADD
- Seznam knihoven, které se mají přidat do vytvářené knihovny.
libcrack_a_SOURCES =
libcrack_a_LIBADD = filecrack.o inetcrack.o
Do _LIBADD
přidávám (již někde něčím vygenerované) knihovny .o.
Default
pro libcrack_a_SOURCES
by bylo crack.c.
Protože takový soubor nemám (a nepotřebuji), uvádím libcrack_a_SOURCES =
prázdné.
Knihovna libcrack.a bude jen obsahovat knihovny filecrack.o a inetcrack.o.
Pro nastavení přepínačů pro všechny programy v Makefile souboru slouží
proměnné AM_CFLAGS
, AM_CPPFLAGS
a
AM_LDFLAGS
.
threads_CFLAGS=@CURSES_CFLAGS@ # pouze pro program threads
AM_* volby se pro program použijí, jen pokud pro něj není specifikována program_* volba. Pokud chcete použít jak AM_*, tak specifickou volbu, lze to provést třeba takto:
threads_CFLAGS=$(AM_CFLAGS) -lthreads # pouze pro program threads
Standardní konfigurační přepínače
Automake také rozumí standardním proměnným z make, jako jsou
CC
, CFLAGS
, CXX
, CXXFLAGS
,
LDFLAGS
a CPPFLAGS
.
Hodnoty těchto proměnných se nastavují na příkazové řádce při spuštění configure nebo make. Nikdy je nenastavujte přímo v Makefile.am! Nechte je uživatelům, ať si je nastaví, jak chtějí (pokud potřebují).
Makefile.am z příkladu
Makefile.am z kořene projektu už jste viděli. V tomto souboru se includuje soubor data/Makefile.soubory. Ten vypadá takto:
dist_soubory_DATA= data/soubory/x1 data/soubory/x2\
data/soubory/x3 data/soubory/x4 data/soubory/x5
Soubory z adresáře data/soubory chci nainstalovat do adresáře
$(pkgdatadir)/soubor
a chci, aby se přidaly do distribučního
balíčku. Primary DATA se defaultně nedistribuje, proto musím uvést na začátku
dist
. (Automake asi předpokládá, že se data ve většině případů
generují configure skriptem.)
Do proměnné dist_soubory_DATA
se musejí vypsat všechny soubory.
Nelze do ní vložit cestu k adresáři a doufat, že automake nainstaluje adresář
s jeho obsahem.
Důvodem je to, že automake se chce vyhnout tomu, aby se omylem nainstalovaly dočasné nebo záložní soubory (takové ty s příponou ~, nebo swap soubory editoru vim atp.). Navíc díky tomu může vytvořit uninstall pravidlo, které pozná, že v adresáři je nějaký soubor, který nebyl instalován a omylem tak nesmaže něco, co by neměl.
Seznam všech souborů může být hodně dlouhý, proto ho rád dávám do extra souboru, který v Makefile.am includuji (ale není to nutné).
Dlouhý seznam suborů lze rozdělit na více řádků, pokud před znak nového řádku dáte zpětné lomítko (viz příklad výše).
Soubor src/thnc/Makefile.am obsahuje toto:
noinst_LIBRARIES=libthnc.a
libthnc_a_SOURCES = thread-ncurses.c thread-ncurses.h
indent:
indent *.c *.h
.PHONY: indent
Vytváří se v něm knihovna libthnc.a ze souborů thread-ncurses.c a
thread-ncurses.h. Tato knihovna se nemá instalovat, použije se při
linkování threads
programu. (Takovýmto knihovnám se v autoconf
dokumentaci říká convenient libraries.)
Posledním souborem je src/Makefile.am:
bin_PROGRAMS=threads
noinst_PROGRAMS=math
noinst_LIBRARIES=libinit.a libwposition.a
threads_CFLAGS=@CURSES_CFLAGS@
math_CFLAGS=-DPKGDATADIR="$(pkgdatadir)"
libinit_a_SOURCES=init.c init.h config.h curses-local.h
libwposition_a_SOURCES=wposition.c wposition.h config.h curses-local.h
threads_SOURCES=threads.c wposition.h
threads_LDADD=libinit.a libwposition.a thnc/libthnc.a @CURSES_LIBS@
math_SOURCES=math.c config.h xstr.h
indent:
indent *.c *.h
for i in $(SUBDIRS); do cd $$<?php ?>{<?php ?>i<?php ?>}<?php ?>; $(MAKE) indent; cd -; done
clean-local:
- $(RM) *~ *.gc
Není v něm nic nového. Vytváří program threads
, který se bude
instalovat do bin adresáře a program math
, který se instalovat
nebude. Dále se vytvářejí dvě „convinient libraries“ libinit.a a
libwposition.a. Tyto knihovny se přidají k programu threads
(viz
proměnná threads_LDADD
).
Hodnota $(pkgdatadir) se nemůže generovat configure skriptem
do config.h souboru, protože se může změnit ještě příkazem make! To platí
i pro další „dir“ proměnné. Proto ji předávám skrze math_CFLAGS
.
Všiměte si, že nemusíte nikdy definovat závislost .c souborů na .h. Vygenerované Makefile se už o zjištění této závislosti postarají. Funguje to tak, že při prvním překladu – kdy se tak jako tak musí přeložit všecko – překladač vytvoří .deps soubory, kam závislosti zapíše. Pak už make překládá jen v závislosti na změnách souborů.
Závěr
Teď už znáte většinu z toho, co potřebujete k úspěšnému vytvoření Makefile.am
souborů. Příklad, který jsem uvdel, byl záměrně složitý, abych na něm mohl ukázat
jak lze vytvářet několik Makefile souborů, které se navzájem volají, jak v projektu
vytvářet několik programů, nebo jak vytvářet a používat convinient knihovny.
Takže se ničeho nebojte, vyhrňte si rukávy a hurá do práce :-). Zkuste si
vytvořit jednoduchý projekt s jedním zdrojovým souborem – váš Makefle.am
možná nebude potřebovat nastavit nic jiného, než bin_PROGRAMS
!
A nezapomeňte si vždy svůj výtvor zkontrolovat příkazem make
distcheck
!