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

» Domov » Auto Tools » 7. časť

7. časť: Premenné

Premenná je v prostredí programu make definovaná ako meno (názov premennej), reprezentujúce nejaký textový reťazec (hodnotu premennej). Tieto mená sú v prípade použitia v tvare

$(meno premennej)

nahradené hodnotou danej premennej. Substitúcia prebieha už pri čítaní okrem prípadu shell príkazov (príkazová časť pravidla), pri použití príkazu define a v pravej časti priradenia (operátor =). Premenné môžu, ako už vieme, obsahovať akýkoľvek text, napríklad zoznam súborov, názov prekladača, verziu programu, parametre linkovača... Názov premennej je tvorený postupnosťou znakov okrem dvojbodky, znaku "rovná sa" a mriežky (hash, #). Takisto sa nemôžu použiť medzery (na začiatku i na konci názvu), teda príklad:

   premenna = text...
echo $( premenna )

je chybný - v druhom riadku nemôžete použiť medzery (prvý riadok je správny, tieto medzery sa ignorujú). Mali by ste sa vyhnúť používaniu názvov premenných obsahujúcich iné znaky ako písmená, číslice a podškrtovník (underscore, _). Make je citlivý na veľkosť písmen, teda:

Premenna
PREMENNA
premenna
pReMEnNa

sú štyri rôzne premenné. Zvyknú sa používať veľké písmená, malé môžete použiť pre interné premenné. O premenných, ktorých názov pozostáva iba z jedného znaku (napríklad $<, $@) sme si už kedysi rozprávali.

Premenná sa, ako už iste viete, používa takto:

$(PREMENNA)
${PREMENNA}

Druhý spôsob sa mi zdá menej bežný, ale môžete sa s ním kde-tu stretnúť. Pretože dolár ($) má tento špeciálny význam, na zapísanie tohto znaku v príkaze alebo v názve súboru ho musíte zdvojiť:

echo $$PATH

Ak za dolárom nasleduje iný znak ako ďalší dolár, zložená alebo obyčajná zátvorka, ako názov premennej sa berie iba jeden nasledujúci znak, teda príklad

P=text

all:
echo $PREMENNÁ

vypíše

textREMENNÁ

Ak by ste používali jednoznakové názvy premenných, mohli by ste ich zapísať aj takto - určite to však nie je odporúčaný spôsob. Výnimkou sú spomínané automatické premenné.

Premenné možete použiť v ľubovoľnom kontexte - v príkaze, názve súboru, v zdroji pravidiel a podobne.

V prostredií Makefile súborov sa niekdy zvykne hovoriť o dvoch druhoch premenných, resp. o dvoch spôsoboch ich používania. V podstate ide o to, ako premennú definujeme a kedy dôjde k jej expanzii (k náhrade zápisu premennej jej hodnotou). Najprv si povieme niečo o prvom type, s ktorým sme sa stretávali doteraz. Príklad:

AAA=$(BBB)
BBB=$(CCC)
CCC=ahoj

all:
echo $(AAA)

Na terminál sa vypíše text "ahoj". Všimnite si, že premennej AAA priradíme hodnotu BBB, hoci v tom čase takáto premenná ešte neexistuje. To nám pomôže utvoriť si predstavu o tom, kedy a ako dochádza k expanzii - až pri použití v príkaze. Oproti "normálnym" jazykom sa to môže zdať veľmi výhodné, ale aj tento spôsob má svoje nevýhody. Napadlo vás, ako by ste na koniec premennej mohli pridať nejaký text? Skúsili ste toto:

PREMENNÁ=text1
PREMENNÁ=$(PREMENNÁ) text2

all:
echo $(PREMENNÁ)

Takto by ste nepochodili. Make by sa mal teoreticky zacykliť, vďaka jeho inteligencii však dostanete iba chybové hlásenie:

Makefile:5: *** Recursive variable `PREMENNA' references itself
(eventually). Stop.

Na piatom riadku máme chybu - rekurzívny odkaz. Ďalšia nevýhoda je v tom, že ak používate funkcie na prácu s textovými reťazcami (niektoré sme už spomínali), pri každom zavolaní takejto funkcie sa musí premenná expandovať, čo je časovo dosť náročná operácia. Takisto môžete dosiahnuť nepredvídateľné výsledky volaní funkcií shell a wildcard.

Jednoducho expandované premenné

Aby sme sa vyhli týmto možným ťažkostiam, môžeme použiť jednoducho expandované premenné. Ako už z pomenovania vyplýva, pri priradzovaní hodnoty premennej dôjde k jednoduchej expanzii - všetky premenné sa nahradia svojou hodnotou a výsledok sa uloží ako obyčajný text do tejto novej premennej. Na tento účel použijeme špeciálny operátor priradenia. Namiesto dosiaľ používaného znaku = zapíšeme priradenie := . Skúste toto:

AAA:=predtým
BBB:=$(AAA) nejaký text
AAA:=potom

all:
echo $(BBB)

Premenná BBB bude obsahovať "predtým nejaký text" a nie "potom nejaký text", ako by sa stalo, keby sme použili obyčajný znak =. Ak by sme pri prvom aj treťom priradení použili = , nič by sa, samozrejme, nestalo, pretože pri premennej AAA vôbec nedochádza k žiadnej expanzii. Ide teda iba o druhý riadok. Tento druhý typ premenných nám môže zjednodušiť komplikovanejšie Makefile. Upozorňujem, že tento spôsob vám pobeží iba v GNU make, v iných verziách by ste s ním mohli mať problémy (resp. vaša verzia make vypíše chybu). Pri tomto type ešte ukážem jedno potenciálne riziko pri tvorbe Makefile. Predstavte si, že komentujete svoj kód:

DIR := /usr/local    # štandardný adresár inštalácie...

Všimnite si tých pár medzier za "/usr/local". Pri tomto priradení sa medzery neignorujú, a tak sa môže stať, že príkazom

cp subor $(DIR)/bin

nespravíte to, čo ste očakávali...

Existuje aj ďalší priradzovací operátor. Je to podmienený operátor a zapisuje sa takto:

PREMENNÁ ?= text

Do PREMENNEJ sa priradí "text" iba vtedy, ak ešte PREMENNÁ nebola definovaná. Pojem "definovaná" neznamená nenulová alebo neprázdna. Aj priradením prázdnej hodnoty premennú definujeme.

Teraz si ukážeme jednu sofistikovanú metódu, s ktorou sa až tak ľahko v Makefile nestretnete, ale možno sa práve vám bude v projekte hodiť. Jeden nikdy nevie... Ide o rekurzívnu expanziu premenných, ktorá je však založená na trošku inom princípe, ako tá spomínaná vyššie:

AAA=BBB
BBB=CCC
CCC=ahoj

all:
echo $($($(AAA)))

Všimnite si, že premenná AAA má hodnotu BBB a nie $(BBB) ako v predchádzajúcom rekurzívnom príklade. Posledný riadok funguje takto:

$($($(AAA)))

sa expanduje na

$($(BBB))

ďalej na:

$(CCC)

a to sa nakoniec expanduje na text "ahoj". Tento princíp nie je v make jedinečný, takisto ho môžete použiť napríklad v PHP:

$AAA=BBB;
$BBB=CCC;
$CCC=ahoj;

echo $$$AAA;

Ostáva na vás, ako túto vymoženosť využijete. Napadá ma napríklad toto:

a_SRC=a.c b.c c.c
b_SRC=p.c q.c r.c
c_SRC=x.c y.c z.c

ABC=a

SRC=$($(ABC)_SRC)

all: program

program: $(SRC)
cc -o $@ $<

Nadefinovali sme si tri skupiny zdrojových súborov. Premenná ABC obsahuje názov použitej skupiny, premenná SRC zase názvy konkrétnych súborov. Ak by sme chceli zameniť skupinu, stačí editovať premennú ABC, nemusíme prepisovať nič viac. Takto máme navyše pokope všetky ostatné skupiny.

Pridávanie za premennú

Ak chcete pridať nejaký text za premennú, môžete to urobiť dvoma spôsobmi:

PREMENNÁ:=$(PREMENNÁ) text

O tomto spôsobe sme si pred chvíľkou rozprávali. Tu je nový spôsob:

PREMENNÁ+=text

Tento spôsob by mal byť jasný. Ak PREMENNÁ predtým nebola definovaná, tento nový operátor sa správa ako obyčajnýá znak =. Ak PREMENNÁ predtým definovaná bola, správanie += závisí od typu premennej. Ak je to jednoducho expandovaná premenná, tak vyššie uvedené príklady sú funkčne zhodné, inak dochádza k rekurzívnej expanzii ako pri obyčajnej premennej definovanej pomocou znaku =.

Prepisovanie premenných

Ak na príkazovom riadku definujeme nejaké premenné, tak za normálnych podmienok prepíšeme premenné definované v súbore Makefile. Nie vždy je to však vhodné. Na prepísanie premenných definovaných na príkazovom riadku použijeme príkaz override napríklad takto:

override CC=gcc
...

Teraz môžeme spustiť

make CC=c_compiler

stále však budeme používať naše gcc. override však nebolo vymyslené na vedenie vojen medzi Makefile a definíciami z príkazového riadka. Tu je lepší príklad jeho použitia:

override PREMENNÁ+=text

all:
echo $(PREMENNÁ)

Spustíme:

make PREMENNÁ=nejaký

a dostaneme výsledok: "nejaký text". Teda override ... += ... pridá za dafinovanú premennú (definovanú na príkazovom riadku) ďalší text.

Definovanie premenných

Existuje ďalšia alternatíva priradeniu hodnoty premennej operátormi += a =. Je ňou dvojica príkazov define ... endef. Používajú sa takto:

define PRÍKAZY
echo "nejaký text"
ls
cd ..
ls
endef

Premenná PRÍKAZY obsahuje päť riadkov, ekvivalentých tomuto jedinému:

PRIKAZ=echo "nejaky text"; ls; cd ..; ls

Samozrejme aj tam dochádza k expanzii premenných. Rovnako ako pri bežnom spôsobe definovania môžete použiť príkaz override.

Premenné prostredia

Tieto premenné určite poznáte (napríklad $PATH, $HOME...). Pozná ich aj make a to natoľko, že ich môžete normálne používať vo svojich Makefile. Pozor, majú najnižšiu prioritu, prepíšete ich teda obyčajným priradením alebo parametrom na príkazovom riadku. Ak sa vám to nepáči, môžete použiť parameter -e, ktorý spôsobí, že tieto premenné prepíšu tie, ktoré su definované vo vašom súbore. V žiadnom prípade vám však nemôžem odporúčať, aby vaše Makefile nejakým spôsobom záviseli na týchto premenných prostredia (napríklad použitie premennej CFLAGS bez jej definovania s tým, že sa spoľahnete na skúsenosti cieľového užívateľa...). Pri spoliehaní sa na environmentálne premenné ešte spomeniem, že takisto môžete mať problémy s premennou SHELL. Tá je na každom slušnom systéme definovaná, avšak v MS DOSe a vo Windows by ste ju márne hľadali... Preto make túto premennú radšej vždy ignoruje.


Oto Komiňák


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