make
je skvelý nástroj na zostavovanie projektov v Linuxe. Miestami je síce značne obskúrny, ale čuduj-sa-svete, dá sa použiť aj veľmi jednoduchým spôsobom, a to aj v prípade, že chceme zostavoať len smiešne zápočtoidné súbory.
Ukážeme si, ako možno ľahko vytvoriť zopár zostavovacích súborov (makefileov) pre tieto prípady, ba dokonca, že si možno veľmi dlho vystačiť aj bez nich a napriek tomu kompilovať céčka.
Do práce!
Vzorový súbor
Na veselenie sa s make
om použime klasický “Ahoj svet” súbor:
#include<stdio.h>
int main(void) {
printf("Hello World!");
return 0;
}
Zostavenie len tak, bez nastavení
Mnohí si ani nevedia predstaviť, že make
funguje aj bez špeci nastavení. Ale súbor hello.c
môžeme naozaj zmake
ovať len tak!
make hello
Zo zostavenia automaticky vypadne súbor hello
, ktorý môžeme spustiť:
./hello
Čo sa vlastne stalo? Na mnohých moderných systémoch sa uskutočnilo toto:
gcc hello.c -o hello
Pre pokročilých: čo sa stalo na pozadí?
Make
sme spustili s cieľom hello
. Vďaka zabudovaným nastaveniam sa na základe mena cieľa hello
vyhľadá súbor hello.c
, skompiloval sa kompilátorom cc
a vypadla z neho binárka s názvom hello
.
V skutočnosti sa vykoná:
cc hello.c -o hello
Na vzorovom Debiane je cc
nalinkovaný na gcc
:
novotnyr@s:~/$ which cc
/usr/bin/cc
novotnyr@s:~/$ ls /usr/bin/cc -l
lrwxrwxrwx 1 root root 20 Sep 29 2011 /usr/bin/cc -> /etc/alternatives/cc
novotnyr@s:~/$ ls /etc/alternatives/cc
lrwxrwxrwx 1 root root 12 Sep 29 2011 /etc/alternatives/cc -> /usr/bin/gcc
Pre veľmi pokročilých: Ako prebehne vyhľadávanie?
Nahliadnime do špecifikácie POSIX:
Ak cieľ, ktorý sa má zostaviť, neobsahuje príponu a pre tento cieľ neexistuje žiadne pravidlo, overia sa jednopríponové odvodzovacie pravidlá. […] Jednopríponové pravidlá určujú ako zostaviť cieľ, za predpokladu, že existuje súbor s menom tvoreným z mena cieľa a prípony z pravidla.
Pri našom make hello
máme cieľ, ktorý naozaj neobsahuje príponu a dokonca preň neexistuje žiadne pravidlo (písali sme ho niekde? nie). Prejdú sa tak všetky zabudované odvodzovacie pravidlá (inference rules), pre ktoré existuje v systéme súbor s menom hello
a príponou definovanou v pravidle. Jediný kandidát je pravidlo pre céčkarské súbory:
.c:
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<
Vďaka tomuto pravidlu:
- zavolá sa štandardný kompilátor
cc
, čo je hodnota premennejCC
. - využijú sa štandardné parametre kompilátora definované v premennej
CFLAGS
. V GNU kompilátore je táto premenná prázdna, v POSIXovej verzii má hodnotu-O
. - využijú sa štandardné hodnoty pre linker, definované v premennej
LDFLAGS
, ktorá je v POSIXovej verzii prázdna - premenná
<
obsahuje názov súboru, vďaka ktorému sa vybralo toto pravidlo, čižehello.c
- premenná
$@
obsahuje zase meno aktuálneho cieľa (tedahello
). Podľa neho sa teda určí meno binárky na výstupe z kompilátora.
Zmena kompilátora v makefile
Čo ak chceme doladiť nastavenia kompilátora? Napríklad chceme vždy a zakaždým používať gcc
a nespoliehať sa na fakt, že zhodou okolností bude v systéme gcc
aliasom cc
?
Vytvorme si makefile, teda súbor popisujúci zostavenie projektu. Aká náhoda, bude sa volať makefile
a bude obsahovať jediný riadok:
CC=gcc
Skúsme opäť spustiť:
make hello
Môžeme vidieť dva prípady. Asi už existuje binárka z predošlého zostavenia a dostaneme hlášku, že nie je čo zostavovať, lebo všetko je už hotové:
make: `hello' is up to date.
Zbavme sa jej (tej binárky), aby sme ju zostavili nanovo:
rm hello
A spusťme make
nanovo:
make hello
make
prevezme existujúci makefile a na zostavenie využije gcc
gcc hello.c -o hello
V makefile sme totiž nastavili premennú CC
, ktorá udáva názov kompilátora pre céčkarske zdrojáky, teda súbory s príponou .c
.
Pre pokročilých: Čo sa stalo na pozadí?
Premenná CC
sa použila v zabudovanom odvodzovacom pravidle z predošlej úlohy.
Zmena parametrov pre kompilátor
Ak začíname s céčkom, je dobré si pri kompilovaní dodať mnoho parametrov, ktoré upozornia na problémy v zdrojákoch. Jeden z kompilačných úsloví:
gcc -Wall -Wextra -Werror -pedantic -ansi hello.c -o hello
Aj toto vieme nastaviť v makefile, stačí nastaviť premennú CFLAGS
˙:
CC=gcc
CFLAGS=-Wall -Wextra -ansi -pedantic -Werror
Skúsme si opäť zostaviť projekt cez make hello
:
novotnyr@s:~/$ make hello
gcc -Wall -Wextra -ansi -pedantic -Werror hello.c -o hello
Cieľ pre upratanie binárok a poradie cieľov
Často sa hodí Lojzo Čistič, teda cieľ clean
, pomocou ktorého upraceme vykompilované binárky. V demonštračnom projekte upraceme ručne jednoducho:
rm hello
Celý makefile môže vyzerať nasledovne. Pozor však, je to pasca!
CC=gcc
CFLAGS=-Wall -Wextra -ansi -pedantic -Werror
clean:
rm hello
Zavolanie make hello
bude bežať bez problémov. Skúste však omylom zavolať len samostatný make
! Buď si zmažete binárky, alebo dôjde k chybe!
novotnyr@s:~/$ make
rm hello
rm: cannot remove `hello': No such file or directory
make: *** [clean] Error 1
Ak zabudnete v make
uviesť názov cieľa, vykoná sa prvý cieľ uvedený v makefile… v našom prípade čistenie.
To teda nie je ktoviečo.
Opraviť to môžeme cieľom-necieľom, ktorý bude v makefile uvedený ako prvý, ale v skutočnosti len bude kričať, že takto sa makefile používať nemá. Tradícia káže tento prvý cieľ pomenovať all
:
CC=gcc
CFLAGS=-Wall -Wextra -ansi -pedantic -Werror
all: hello
clean:
rm hello
Na vykonanie cieľa all
treba vykonať cieľ hello
(hello
je prerekvizita all
), a to prebehne podľa pravidiel z predošlých ukážok (ak ste dávali pozor).
Ak teraz zavoláme make
bez cieľa, uvidíme klasickú hlášku, ktorú čakáme
gcc -Wall -Wextra -ansi -pedantic -Werror hello.c -o hello
Zovšeobecnenie!
Ak narýchlo vyrábame kopu nezávislých céčkarských súborov (lebo napríklad chodíme na UINF/JAC1), hodí sa univerzálny nástroj: teda univerzálny makefile, ktorý sa strčí do adresára s kopou zdrojákov a dá sa použiť podľa potreby na ten zdroják, ktorý potrebujeme.
Oprava implicitnieho cieľa
Najprv opravme cieľ all
, lebo už sa nemôžeme spoliehať na zadrôtovaný názov cieľa. Po novom nech make all
, či čistý make
vypíše len hlášku o chýbajúcom cieli:
CC=gcc
CFLAGS=-Wall -Wextra -ansi -pedantic -Werror
all:
echo Chyba meno ciela
clean:
rm hello
Ak teraz zavoláme make
bez cieľa, uvidíme:
novotnyr@s:~/$ make
echo Chyba meno ciela
Chyba meno ciela
Prekáža dvojitý výpis? Využime špeciálny cieľ .SILENT
, ktorému do prerekvizít nasekajme názvy cieľov, pri ktorých má byť make
ticho — teda nemá vypisovať ich príkazy.
CC=gcc
CFLAGS=-Wall -Wextra -ansi -pedantic -Werror
.SILENT: all
all:
echo Chyba meno ciela
clean:
rm hello
Čistič verzie 2.0
Podobne môžeme opraviť čistenie. Spôsobov je viacero, podľa toho, ako šialene robustné a blbuvzdorné má čistenie byť. Jedno z nebezpečných riešení: vymažme všetky binárky, ktoré nájdeme v adresári. Predpokladáme, že iné binárky, než tie, ktoré vypadli z kompilátora nebudú a teda ich môžeme rm
núť všetky.
clean:
find . -executable -type f -exec rm {} \;
Využijeme vyhľadávacieho klasika find
, ktorým nájdeme všetky spustiteľné (-executable
) súbory (-type f
), ktoré postupne nasúkame do rm
.
Kompletný univerzálny makefile
CC=gcc
CFLAGS=-Wall -Wextra -ansi -pedantic -Werror
.SILENT: all
all:
echo Chyba meno ciela
clean:
find . -executable -type f -exec rm {} \;
Ako spustiť projekt?
Celý projekt nanovo vymažeme a skompilujeme cez:
make clean hello
Binárku spustíme klasickým spôsobom:
./hello
Do kompilovania!