Produkty Novinky Články Návody Kontakty

Syntaxe makefile

Syntaxe souboru makefile popisuje jaké konstrukce, jaký jazyk můžeme při zápisu procesu sestavení aplikace použít. Jinými slovy jak deklarujeme proměnné, jak vkládáme komentáře a jak zapisujeme pravidla, která program make interpretuje. Zjistíme, že ve své základní syntaxi je psaní souboru makefile snadné a srozumitelné.

Proměnné

Proměnnou definujeme uvedením jejího jména. Obvykle používáme velká písmena. Výjimkou jsou pouze jména těch proměnných, které slouží jen pro vnitřní účely (uživatel by je neměl měnit při volání make). Ty jsou psány malými písmeny. Názvy proměnných jsou citlivé na velikost písmen (case sensitive), takže Flags je jiná proměnná než FLAGS.
Hodnota proměnné se nastavuje jejím přiřazením. Přiřazení probíhá pouze na textové úrovni, každá hodnota je proto vždy řetězcem. Na hodnotu proměnné se odkazujeme pomocí zápisu $(NazevPromenne), u proměnných s názvem tvořeným jedním znakem lze zapisovat i jako $N. To je ale doporučeno pouze pro tzv. vnitřní proměnné. Náhrada odkazu $(NazevPromenne) za hodnotu proměnné probíhá pouze při vyhodnocení příkazu v pravidlu makefile.
Přiřazení hodnoty do proměnné je možné několika způsoby:
# proměnná CFLAGS bude mít hodnotu ’-O2’
CFLAGS  = -O2
​
# proměnná CFLAGS bude mít hodnotu ’$(include) -O2’
# pozn.: proměnná include nemusí být zatím definována, 
#        protože v tomto místě nedojde k vyčíslení její hodnoty
CFLAGS = $(include) -O2
​
# proměnná CFLAGS bude mít hodnotu ’include-myheaders -O2’
# pozn.:proměnná include musí být už definována, 
#       protože dojde k vyčíslení její hodnoty
CFLAGS := $(include) -O2
​
# přidání hodnoty ’-g’ k hodnotě ’-O2’
# výsledná hodnota bude ’-O2 -g’
CFLAGS += -g
Hodnoty proměnných nastavených v makefile lze přepsat hodnotou zadanou na příkazovém řádku při volání programu make, např.:
$ make CFLAGS=’-O3’
To způsobí, že proměnná CFLAGS bude mít hodnotu -O3 bez ohledu na to, že v makefile jsme jí přiřadily jinou hodnotu. Tak může uživatel bez změny v makefile upravit proces sestavení dle svých představ.
Kromě proměnných, které jsme nadefinovali my, jsou k dispozici i vnitřní proměnné, kterým se také někdy říká automatické proměnné (auto variables). Hodnoty vnitřních proměnných nastavuje a mění program make. Nejčastěji se používají tyto vnitřní proměnné:
@, resp. její hodnota $@, která obsahuje cíl pravidla
​
<, resp. její hodnota $<, která obsahuje první komponentu pravidla
Jejich použití je nejčastěji v tzv. implicitních pravidlech nebo v šablonách pravidel.

Komentáře

Jakýkoliv komentář musí být uvozen znakem #. Znaky následující po tomto znaku jsou do konce řádku považovány za součást komentáře.
Příklad:
# normální komentář od začátku řádku
​
CFLAGS_ARCH=-march=armv5te #komentář v přiřazení

Pravidla

Zápis pravidel
Pravidlo je v makefile obecně zadáno takto:
cil : komponenta1 komponenta2
<tab>prikaz1
<tab>prikaz2
kde:
  • cil - je název souboru, který má být výsledkem/výstupem pravidla. Cílů může být více v jednom pravidlu, ale obyčejně je jeden cíl na jedno pravidlo. Oddělují se mezerami.
  • komponenta - je název souboru, z kterého se skládá cíl nebo na kterém cíl závisí. Může jich být více vzájemně oddělených mezerami.
  • prikaz - je postup/návod jak z komponent vytvořit cíl, pokud cíl neexistuje, nebo jak cíl aktualizovat, pokud některá z komponent je novější jak cíl (porovnává se čas a datum poslední změny).
Několik důležitých poznámek:
  • Pravidlo nemusí obsahovat žádný příkaz.
  • Cíle nemusí mít závislost na žádné komponentě.
  • Komponentou může být pouze jiný cíl.
  • Makefile může obsahovat více pravidel. Na pořadí jejich uvedení nezáleží. Ovšem platí, že pravidlo uvedené jako první od začátku makefile, je výchozím pravidlem. To znamená pravidlem, které je vyhodnoceno jako první, když je make zavolán bez výchozího pravidla uvedeného jako parametr.
  • Výchozí pravidlo obvykle slouží pro sestavení celého programu nebo všech programů, proto se jeho cíl pojmenovává all.
  • Příkazy se zadávají každý na jednu řádku.
  • K rozdělení dlouhého příkazu na více řádek nebo naopak pro spojení více příkazů do jednoho se používá zpětné lomítko \.
  • Odsazení příkazu od začátku řádku pomocí tabelátorem je povinné! Pozor na editory, které nahrazují tabelátory mezerami!
  • Pro každý příkaz spustí make jednu instanci shellu, v případě GNU make je to bash.
Vyhodnocení pravidel
Program make vyhodnotí pravidlo následujícím způsobem:
  • Porovná čas poslední změny všech komponent s časem poslední změny cíle.
  • Pokud je jedna nebo více komponent novějších jak cíl nebo pokud cíl neexistuje, tak vykoná příkazy daného pravidla.
Ukažme si to na příkladu jednoduchého makefile:
1all:     hello
23hello:        modul1.o modul2.o
4      gcc modul1.o modul2.o -o hello
56modul1.o:     modul1.c
7      gcc -c -o modul1.o modul1.c
89modul2.o:     modul2.c
10     gcc -c -o modul2.o modul2.c
1112.PHONY: all
Příklad jednoduchého makefile
Program make při zpracovávání souboru makefile (výpis 2) nejprve vyhodnotí první, resp. výchozí pravidlo (řádek 1). Zjistí, že soubor hello neexistuje, a proto zavolá pravidlo, kde je soubor hello cílem (řádek 3). Toto pravidlo říká, že soubor hello se skládá ze dvou souborů - modul1.o a modul2.o. Program make zkusí ověřit čas poslední změny souboru modul1.o, který ovšem také neexistuje. Proto rekurzívně pokračuje vyhodnocením pravidla pro soubor modul1.o (řádek 4). Zde zjistí, že soubor modul1.c existuje a proto jej zkompiluje (vykoná příkaz pravidla), čímž vznikne soubor modul1.o. Make se tak vrátí zpět k pravidlu pro soubor hello (řádek 3) a vyhodnotí čas změny souboru modul2.o. Jelikož ten neexistuje, tak jej zkompiluje a zase se vrátí zpět. Nyní má již všechny komponenty souboru hello. Může tedy provést příkaz pravidla - vytvořit slinkováním souborů modul1.o a modul2.o spustitelný soubor hello. Pak se vrátí zpět na začátek k výchozímu pravidlu (řádek 1), který žádný příkaz nemá. Proto zde práce programu make skončí.
Podobně make vyhodnocuje i změnu např. jednoho zdrojového souboru. Je tak schopen aktualizovat (zkompilovat, slinkovat) jen případné závisející soubory a nikoliv celou aplikaci. To přináší značnou časovou úsporu hlavně u větších projektů.
Pomocné cíle a .PHONY
Kromě cílů, které jsou skutečnými soubory, rozeznáváme i pomocné cíle, které jsou jen názvy pravidel nikoliv skutečné soubory. Jako například cíl clean, který se používá pro vymazání všech souborů, jenž vznikly činností programu make. Co se ale stane, když bude přeci jen existovat stejnojmenný soubor? Jelikož pomocné cíle obvykle nemají závislost na žádných komponentách, tak make dojde k závěru, že soubor clean je vždy aktuální a naše pravidlo pro úklid přestane fungovat. Tuto možnost snadno ošetříme pomocí speciálního cíle/značky .PHONY. Tím řekneme make, že takto označený cíl není skutečným souborem a nemá s ním žádnou souvislost (pokud existuje). Úklidové pravidlo tak bude fungovat vždy.
Příklad:
clean:
        rm -f *.o
​
.PHONY: clean
Šablona pravidel
Vezměme si příklad jednoduchého makefile z výpisu 2. Pro každý objektový soubor musíme napsat extra pravidlo, které zajistí jeho kompilaci z příslušného zdrojového souboru. Představte si, že máme stovky objektových souborů ... A tehdy s výhodou využijeme šablonu pravidel. Přepišme uvedený příklad makefile (výpis 2) s využitím šablony pravidel:
1all:     hello
23hello:        modul1.o modul2.o
4      gcc modul1.o modul1.o -o hello
56%.o:  %.c
7      gcc -c -o $@ $<
89.PHONY: all
Upravený příklad jednoduchého makefile
Pravidlo na řádku 6 je onou šablonou, která definuje, že každý objektový soubor (s příponou .o) se skládá ze stejnojmenného zdrojového souboru (s příponou .c). Make tak toto pravidlo vykoná vždy, když potřebuje vytvořit nebo aktualizovat libovolný objektový soubor.
Na řádku 7 je vlastní příkaz, který zajistí kompilaci libovolného objektového souboru, pokud je tento starší než jeho zdrojový soubor nebo vůbec neexistuje. Je zde elegantně využito vnitřních proměnných: hodnota $@ obsahuje název objektového souboru (cíle pravidla), zatímco hodnota $< název zdrojového souboru.