Bash

Konfigurační soubor configure.ac obsahuje makra preprocessoru m4, která se nahradí bash skriptem. Cokoliv co je v tomto souboru a není makro m4 se přenese do výsledného configure souboru beze změny. Je tedy možné psát do configure.ac přímo příkazy bashe? Odpověď zní: Ano, ale …

Přenositelnost

Pokud jste v linuxu noví, přečtěte si nejdříve něco o programování v bashi. Pod předchozím odkazem se skrývájí články, které představují několik programů, příkazů a programovacích konstrukcích bashe. Do nějakého, pro začátečníky vhodného, tutoriálu mají daleko, tak se kdyžtak poohlédněte i jinde.

Pokud už něco o příkazové řádce Linuxu víte, máte vyhráno jen na půl. Velké „ale“ je totiž v tom, že tam venku existuje spousta verzí bashe, které se na první pohled tváří stejně, ale ve skutečnosti jsou v nich malé, leč smrtící rozdíly.

Velký kus dokumentace autools se věnuje Portable Shell Programming technikám. Tato dokumentace probírá asi všechny problémy, na které kdy autoři autotools v souvislosti s přenositelností narazili. A přiznám se bez mučení, že půlce z toho ani nerozumím.

Bát se toho ale nemusíte. Vy byste měli používat bash tak málo, jak to jen půjde. Při psaní configure.ac si vystačíte v 99% s makry m4, která už obsahují přenositelný zdrojový kód shellu.

V této kapitole vám ukáži pár věcí, které je bezpečné v configure.ac použít.

Příkazy, programy a proměnné

Úplným základem je práce s proměnnými a příkazy bashe.

Následující je seznam programů a příkazů, které by měli být dostupné na každém systému. Ale i tyto programy a příkazy se systém od systému trochu liší, takže si při jejich použití musíte dát pozor:

cat
Nepoužívejte žádný přepínač tohoto programu. Ne na všech systémech je dostupný, nebo dělá totéž.
cp
Nepoužívejte volbu -r, používejte místo toho -R
date
Některé verze tohoto programu nerozumí direktivám s %
basename
Není přenositelný na všechny systémy.
dirname
Ne na všech systémech tento příkaz existuje. Použijte místo toho makro AS_DIRNAME.
grep
jediné spolehlivé přepínače jsou -c, -l, -n a -v.
ln
Ne na všech systémech lze vytvářet symbolické odkazy. Místo volby -s použijte $(LN_S)
ls
Přenositelné volby jsou: -acdilrtu
mkdir
Žádné volby tohoto příkazu nejsou přenositelné. Zvláště ne -p. Používejte AS_MKDIR_P(file-name), případně AC_PROG_MKDIR_P.
mv
Přenositelné volby jsou: -f a -i
rm
Přenositelné volby jsou: -f a -r
sed
Program sed sice můžete použít, ale přenositelnost jeho vzorů je problematická. Před jeho použitím si dobře nastudujte autotools dokumentaci.
sleep
Přenositelný. Pozastaví běh shellu na zadaný počet sekund.
sort
Přenositelný, ale výsledek je ovlivněn nastaveným locale. Raději používejte LC_ALL=C sort
touch
V zásadě přenositelný.
echo
Nespoléhejte na žádné volby. Raději používejte AS_ECHO, nebo AS_ECHO_N.

Toto byl stručný výběr programů a příkazů, které můžete používat. Podrobnější informace najdete v dokumentaci k autoconf: Limitations of Usual Tools a Limitations of Shell Builtins.

A teď něco málo k programování v bashi.

Proměnné můžete vytvořit přiřazením hodnoty k názvu proměnné. Používají se za pomoci názvu s dolarem ($) na jeho začátku:

$ POZDRAV="Jó napot kívánok!"
$ echo $POZDRAV
Jó napot kívánok!

Pokud chcete uložit do proměnné výstup z nějakého programu, můžete to udělat pomocí zpětných uvozovek:

$ DATUM=`LC_ALL=C date`
$ echo $DATUM
Fri Nov 17 20:15:48 CET 2027

Proměnné uvozené v dvojitých uvozovkách se expandují, v jednoduchých uvozovkách nikoliv.

$ POZDRAV="Jó napot kívánok!"
$ echo "$POZDRAV"
Jó napot kívánok!
$ echo '$POZDRAV'
$POZDRAV

Podmínky a cykly

Jediná správná forma příkazu if je za použití test:

CISLO=0

if test x"$CISLO" != x0
then
        echo "Cislo neni 0"
else
        echo "Cislo je 0"
fi

Pokud by v testu nebylo na obou stranách rovnice přidáno x, hrozilo by, že v případě prázdné proměnné CISLO by bash vyhodnotil řádku s ifem tatko: if test != 0. A to není syntakticky správné.

Na některých systémech stačí uzavřít proměnnou do uvozovek. Bash pak řádku s ifem interpretuje jako if test "" != 0. Problém může nastat, pokud proměnná $CISLO obsahuje něco jako přepínač (text začínající pomlčkou). Pak by se to interpret bashe mohl pokusit interpretovat jako přepínač příkazu test. x na začátku tomu zabrání.

Proto při práci s autoconf často narazíte na podmínky, které vypadají nějak takto: if test x"$nejaka_promenna" = xyes then ... .

V configure.ac, nebo při psaní vlastních maker, můžete použít toto makro:

AS_IF(test1, [run-if-true1], ..., [run-if-false])

Například:

  AS_IF([test "x$foo" = xyes], [HANDLE_FOO([yes])],
      [test "x$foo" != xno], [HANDLE_FOO([maybe])],
      [echo foo not specified])

Všiměte si, že jsou všechny argumenty makra AS_IF uzavřena v uvozovkách (m4 uvozovky jsou ty hranaté závoky). Jedním takovým argumentem je HANDLE_FOO([yes]), tj. volání makra, které má také argument uzavřený v uvozovkách.

Argumenty AS_IF jsou vždy nejdříve podmínka, pak nějaká akce spuštěná při splnění podmínky. Posledním argumentem (nepovinným) je akce, která se spustí, když žádná předchozí podmínka není pravdivá („else“ větev).

Jako switch můžete použít makro AS_CASE:

AS_CASE(word, [pattern1], [if-matched1], ..., [default])

Například:

AS_CASE($foo, ["yes"], [echo "foo=YES!"], ["no"], [echo "foo=NO"], [echo "WTF?"])

Pro cyklus přes numerické hodnoty můžete použít m4 makro m4_for:

m4_for(var, first, last, [step], expression)

Například:

m4_for(x, 1, 6, [], [echo $x])

Pro cyklus přes hodnoty v proměnné oddělené mezerami můžete použít m4_foreach_w:

m4_foreach_w(var, list, expression)

Například:

m4_foreach_w(x, [raz dva tri], [echo $x])
VAR="raz dva tri"
m4_foreach_w(x, [$VAR], [echo $x])

Programování v M4sh

Dokumentace autoconf věnuje programování shellu velkou kapitolu nazvanou Programming in M4sh.

V podstatě jde o programování v m4 obohacené o nějaká makra dodávaná s autotools. Makra začínají na AS_. Pokud makra definují někjaké proměnné, pak tyto proměnné začínají na $as_.

Krom AS_ maker m4sh (vyslovuje se [mash]) přináší pár dalších m4 maker, jako je m4_for, m4_foreach_w atp.

Nejdůležitější jsou:

# vytiskne text obklopený boxem ze znaků char
AS_BOX(text, [char = ‘-’])            

# vrátí jméno adresáře souboru
AS_DIRNAME(file-name)

# vypíše text a znak nového řádku.
# Text musí být bez mezer nebo v uvozovkách
AS_ECHO([text])                      

# vypíše text bez znaku nového řádku na konci.
# Text nesmí obsahovat znaky nového řádku
AS_ECHO_N([text])

# vytvoří adresář a všechny předchozí adresáře, pokud neexistují
AS_MKDIR_P(file-name)

Příklad

Testovací soubor configure.ac:

# 03Bash/example1/configure.ac

AC_INIT([example], [0.1], [sallyx@example.org], [],
        [http://www.sallyx.org/sally/c/autotools/bash])

AS_BOX(["Test if:"],[=])

CISLO=0

if test x"$CISLO" != x0
then
        echo "Cislo neni 0"
else
        echo "Cislo je 0"
fi

m4_pattern_allow([AS_IF|AS_CASE|m4_for|m4_foreach_w])

AS_BOX([[["Test AS_IF:"]]])

AS_IF([test "x$foo" = xyes], [echo yes],
      [test "x$foo" != xno], [echo maybe],
      [echo foo not specified])

AS_BOX([[["Test AS_CASE:"]]])

foo=no
AS_CASE([x"$foo"], ["xyes"], [echo "foo=YES"], ["xno"], [echo "foo=NO"], [echo "WTF?"])

AS_BOX([[["Test m4_for:"]]])

m4_for(x, 1, 6, , [
        AS_ECHO([x])
])

AS_BOX([[["Test m4_foreach_w:"]]])

m4_foreach_w([x], [raz dva tri], [echo x;])
VAR="raz dva tri"
m4_foreach_w([x], [$VAR], [echo "{ x }";])

AC_OUTPUT

Pokud se vám do výstupu dostane název nějakého známého makra, autoconf si postěžuje a ukončí se s chybou. Obvykle to chyba je, ale v příkladu se snažím dostat názvy maker do configu záměrně (abych je mohl vypsat v boxu). Proto volám makro m4_pattern_allow. Pokud nějaké podezřelé makro odpovídá regulárnímu výrazu, jež má m4_pattern_allow za argument, pak si autoconf stěžovat nebude.

Preprocessor m4 funguje tak, že nahrazuje makro jeho definicí. Výsledek pak ale znovu projde preprocessorem a znovu nahradí všechny nalezená makra. A tak stále dokola, dokud je co nahrazovat.

Takže když se například snažím vypsat text AS_IF, hrozí, že se m4 pokusí tento text nahradit tělem makra.

Aby k expanzi nedošlo, stačí text uzavřít do hranatých závorek.

Jenže, kdykoliv se zavolá nějaké makro s argumentem, tak se jedny uvozovky kolem argumentu odstraní. Makro AS_BOX interně volá další makro, kterému předá svůj argument. Tím pádem se odstraní dvoje hranaté závorky. proto musím s AS_BOX použít troje závorky. Ty poslední zabraňují expanzi makra, které je v textu argumentu.

Naproti tomu takové makro AS_ECHO interně žádné makro nevolá. Proto při použití AS_ECHO místo AS_BOX stačí použít dvoje hranaté závorky.

Co z toho plyne? Pokud použijete závorek moc, neexpanduje se makro, které chcete (na to se snaží autoconf upozornit a proto je nutné použít m4_pattern_allow). Když použijete závorek málo, expanduje se makro, které nechcete (nebo dříve, než chcete). Ne nadarmo se m4 přezdívá závorkové peklo.

Mezi jménem makra a (kulatou) závorkou ohraničující jeho argumenty nesmí být mezera.
Toto je špatně: m4_foreach_w ([x],[raz dva tri],[echo x;])!

Pokud chcete vynechat nějaký nepovinný argument, prostě jej vynechejte – zapiště dvě čárky za sebou (viz příklad s m4_for).

Poslední důležitá věc, kterou je třeba si při použití maker uvědomit, je, jakým způsobem pracují. Například makro m4_foreach_w je nahrazeno několika výskyty svého posledního argumentu. V configure tedy nebude nějaký bash for-each cyklus nad raz dva tri, ale toto:

echo raz;echo dva;echo tri;

Všiměte si, jak důležitou roli tu hraje středník.

Druhé volání m4_foreach_w se expanduje takto:

VAR="raz dva tri"
echo "{ $VAR }";

Takto vypadá výstup volání ./configure.

## ========== ##
## "Test if:" ##
## ========== ##
Cislo je 0
## ------------- ##
## "Test AS_IF:" ##
## ------------- ##
maybe
## --------------- ##
## "Test AS_CASE:" ##
## --------------- ##
foo=NO
## -------------- ##
## "Test m4_for:" ##
## -------------- ##
1
2
3
4
5
6
## -------------------- ##
## "Test m4_foreach_w:" ##
## -------------------- ##
raz
dva
tri
{ raz dva tri }

Pokud si nejste jistí, co je výsledkem nějkého makra, nejjednodušší je podívat se do výsledného configure skriptu.

Makra AC_ARG_ENABLE a AC_ARG_WITH

Makro AC_ARG_ENABLE přidá novou konfigurační volbu pro skript ./configure. První argument makra je název featury, kterou makro umožňuje zapnout/vypnout. Druhým argumentem je nápověda.

Makro AS_HELP_STRNIG je makro, které vytvoří takový help string. Postará se o to, aby byl druhý argument správně odsazen a případně zalomen. První argument by měl být něco jako --enable-feature, nebo --disable-feature.

Pozor! Jedná se skutečně jen o text nápovědy. Nemá vliv na funkci volby configure skriptu. Pokud v prvním argumentu neuvedete stejný název featury jako je první argument AC_ARG_ENABLE, akorát tím zmatete uživatele.

# 03Bash/example2/configure.ac

AC_INIT([example], [0.2], [sallyx@example.org], [],
        [http://www.sallyx.org/sally/c/autotools/bash])

AC_ARG_ENABLE([pozdrav],
        AS_HELP_STRING([--enable-pozdrav],
               [Zapne zdravici hlasku pri spusteni programu.]))

AS_IF([test "x$enable_pozdrav" = "xyes"], [
        AC_DEFINE([POZDRAV],["Hello world"],[Pokud je definovan, vypise se pri startu programu])
])

AC_CONFIG_HEADERS([config.h])

AC_OUTPUT

Ukázka použití:

$ autoconf
$ autoheader
$ ./configure
configure: creating ./config.status
config.status: creating config.h
$ cat config.h
...
/* Pokud je definovan, vypise se pri startu programu */
/* #undef POZDRAV */
$ ./configure --enable-pozdrav
configure: creating ./config.status
config.status: creating config.h
$ cat config.h
...
/* Pokud je definovan, vypise se pri startu programu */
#define POZDRAV "Hello world"

Makro AC_ARG_ENABLE se používá jak pro volby --enable-feature, tak pro --disable-feature. Pokud chcete použít --disable-feature, stačí jen změnit AS_IF podmínku. A taky příslušný text nápovědy:

# 03Bash/example3/configure.ac

AC_INIT([example], [0.3], [sallyx@example.org], [], [http://www.sallyx.org/sally/c/autotools/bash])

AC_ARG_ENABLE([pozdrav],
        AS_HELP_STRING([--disable-pozdrav],
               [Vypne zdravici hlasku pri spusteni programu.]))

AS_IF([test "x$enable_pozdrav" = "xno"], [], [
        AC_DEFINE([POZDRAV],["Hello world"],[Pokud je definovan, vypise se pri startu programu])
])

AC_CONFIG_HEADERS([config.h])

AC_OUTPUT

Všiměte si, že tentokrát je AC_DEFINE až třetí argument makra AS_IF.

Pokud uživatel zavolá configure s --enable-feature, bude proměnná $enable_feature rovna yes.
Pokud uživatel zavolá configure s --disable-feature, bude proměnná $enable_feature rovna no.
Pokud uživatel zavolá configure bez těchto voleb, bude $enable_feature prázdné.

Makro AC_ARG_WITH

Makro AC_ARG_WITH funguje stejně, jako AC_ARG_ENABLE. Pouze proměnná se bude jmenovat $with_feature namísto $enable_feature.

Kdy použít AC_ARG_ENABLE a kdy AC_ARG_WITH je jen fylozofická otázka. AC_ARG_WITH se obvykle používá, když chcete zkompilovat program s nějakou externí knihovnou. AC_ARG_ENABLE obvykle zapíná nějakou featuru programu.

Závěr

Při troše štěstí nebudete programování v bashi potřebovat. Vystačíte si s makry dodávanými s autoconf, jako jsou AC_ARG_ENABLE, AC_ARG_WITH, nebo AS_IF. Nakonec, můžete se i rozhodnout, že váš configurační skript bude fungovat jenom na těch systémech, na kterých si je sami odzkoušíte. A pokud to někde jinde někomu nepůjde, ať vám pošle opravu :-).

Znalost přenositelnosti, používání externích programů nebo maker jako je m4_for se hodí spíše těm, kteří makra píší.

Pokud se o tuto problematiku zajímáte více, pak GNU Autoconf Archive pro vás může být téměř nevyčerpatelným zdrojem inspirace.

Komentář Hlášení chyby
Created: 24.12.2018
Last updated: 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..