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ě.

AC_CONFIG_AUX_DIR([build-aux])

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.

AM_INIT_AUTOMAKE([-Wall -Werror])

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.)

AM_SILENT_RULES([no])

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:

AM_PROG_CC_C_O
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.

AC_CHECK_LIB(knihovna, funkce, [action-if-found], [action-if-not-found], [dalsi knihovny])

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([pthread], [pthread_create])       # -lpthread
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_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.

AC_CONFIG_FILES([
        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:

#                                               -*- Autoconf -*-
# 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:

SUBDIRS = src

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:

clean-local:
        - $(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:

souborydir=$(pkgdatadir)/soubory
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:

prefix = /usr/local
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:

EXTRA_PROGRAMS = prog2 prog3

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ů.

bin_PROGRAMS=bflmpsvz
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:

noinst_LIBRARIES=libinit.a  libwposition.a
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.
noinst_LIBRARIES = libcrack.a
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.

AM_CFLAGS=@CURSES_CFLAGS@             # pro vsechny programy v aktualnim Makefile
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:

AM_CFLAGS=@CURSES_CFLAGS@             # pro vsechny programy v aktualnim Makefile
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:

souborydir=$(pkgdatadir)/soubory
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:

AM_CFLAGS=@CURSES_CFLAGS@

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:

SUBDIRS=thnc

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!

Komentář Hlášení chyby
Vytvořeno: 24.12.2018
Naposledy upraveno: 24.12.2018
Tato stánka používá ke svému běhu cookies, díky kterým je možné monitorovat, co tu provádíte (ne že bych to bez cookies nezvládl). Také vás tu bude špehovat google analytics. Jestli si myslíte, že je to problém, vypněte si cookies ve vašem prohlížeči, nebo odejděte a už se nevracejte :-). Prohlížením tohoto webu souhlasíte s používáním cookies. Dozvědět se více..