2. diel - Céčko a Linux - Makefile
V minulej lekcii, Céčko a Linux - Úvod , sme sa pozreli na to, ako používať
prekladač, aké sú jeho možnosti a ako ich môžeme prakticky využiť. Čo
ale, keď budeme mať viac súborov? Museli by sme každý preložiť zvlášť
a nakoniec je slinkovat dohromady. A keď vyvíjame, často prekladáme aj
niekoľkokrát počas desiatich minút ... Určite budete súhlasiť, že
písať zakaždým príkazy znovu by bolo minimálne trošku nepraktické.
Našťastie tu máme program make, ktorý toto všetko urobí za nás. Jeho
použitie je veľmi jednoduché - v zložke s naším projektom si vytvoríme
súbor Makefile a napíšeme, čo chceme, aby robil. Ako, to si dnes ukážeme.

Jednoduchý príklad
Vytvoríme nasledujúci program - hello.c
#include <stdio.h> #include <stdlib.h> int main(void) { printf("Hello world!\n"); return 0; }
A do rovnakého priečinka súbor s menom Makefile a týmto obsahom.
# Projekt: Hello world!
# Autor: David Novák
# Datum: 16.2.2015
CC=gcc
CFLAGS= -std=c99 -pedantic -Wall -Wextra -g
hello: hello.c
$(CC) $(CFLAGS) hello.c -o hello
Náš jednoduchý program všetci dôverne poznáte, takže o ňom hovoriť
nebudem. Keď sa pozrieme na Makefile, vidíme ako prvá vec hlavičku - meno
projektu, autor, dátum, prípadne poznámky alebo čokoľvek uznáme za
vhodné. Je to dobrý zvyk (kdekoľvek nielen v Makefile) a jeho dodržiavaním
uľahčíte ostatným ľuďom prácu s vašim kódom. 
Ako písať Makefile
Komentáre začínajú znakom #, ako ste si iste všimli a môžu byť kdekoľvek (napríklad za príkazom). Čokoľvek za znakom # až do konca riadku je brané ako komentár. Hneď po hlavičke si môžeme všimnúť deklarácie premenných CC (skratka pre C Compiler) a CFLAGS (názvy si samozrejme môžete zvoliť ľubovoľné). Tieto príkazy samozrejme môžeme napísať zakaždým znova (prípadne pre každý súbor môžu byť iné), ale väčšinou ich budeme chcieť pre jednoduchú zmenu deklarovať na začiatku súboru.
Poslednou časťou je definícia pravidlá hello. "Hello" je názov pravidla
(ciele) - väčšinou sa jedná o názov generovaného súboru alebo akcie,
ktorú chceme vykonať (napríklad clean). Nasleduje dvojbodka a zoznam
závislostí. Závislosti sú súbory alebo iné pravidlá, ktoré pravidlo
vyžaduje. Na ďalšom riadku (MUSÍ začínať tabulátorom!)
Uvedieme príkaz, ktorý bude vykonaný. Príkaz končí koncom riadku a
pravidlo je ukončené riadkom, ktorý nezačína tabulátorom. Príkazov
môžeme napísať koľko chceme (každý začína tabulátorom). Pravidlo sa
vykoná len ak sú k dispozícii všetky súbory vypísané v závislostiach. Ak
máme v závislostiach ďalšie pravidlá, prvýkrát sa všetky vykonajú a až
potom sa vykoná naše pravidlo. Tak ... To bola nudná teória a teraz sa
pozrieme, ako to prakticky použiť. 
Otvoríme si terminál v zložke s naším projektom a napíšeme príkaz make. Ak ste Makefile opísali správne, zobrazí sa vám niečo také.

Vidíme, že týmto krátkym, jednoduchým príkazom sme vykonali to isté, ako keby sme napísali "gcc -std = C99 -pedantic -Wall -Wextra -g hello.c -o hello". Pekné, že? Ale to zďaleka nie je všetko, čo make vie! Ďalšia príjemná vlastnosť je, že pravidlo sa vykoná len vtedy, ak je čas vytvorenia cieľového súboru staršie než čas niektorého súboru v zozname závislostí. V praxi to znamená, že sa preloží iba tie súbory, u ktorých sme vykonali zmenu. To je u väčších projektov značná úspora času.
Ďalšie možnosti
Náš Makefile si trochu rozšírime. Hlavičku pre skrátenie nebudem uvádzať.
CC=gcc
CFLAGS= -std=c99 -pedantic -Wall -Wextra -g
NAME=hello
$(NAME): hello.c
$(CC) $(CFLAGS) hello.c -o $(NAME)
clean-bin:
rm -f $(NAME) # smaže binární soubor
clean-bck:
rm -f *~ *.bak # smaže všechny záložní soubory
clean: clean-bin clean-bck # smaže binární soubor i zálohy
Ako vidíte, do pravidiel v Makefile môžeme napísať aj klasické príkazy Bash. Ďalej si môžete všimnúť, že som vytvoril premennú NAME s názvom generovaného súboru. Používame ju už na troch miestach a keby sme sa rozhodli ju zmeniť, bolo by to otravné prepisovať. Nakoniec som vytvoril tri ďalšie pravidlá, pričom najzaujímavejšie je posledný pravidlo clean, ktoré vykoná clean-exe a clean-bck.
Pravidlá máme. Ako ich ale použiť? Make funguje tak, že keď ho zavoláme bez parametrov, vykoná prvé pravidlo a potom skončí. V našom prípade to znamená, že príkazom make spustíme kompiláciu súboru hello.c. Ak chceme zavolať iné pravidlo, je to veľmi jednoduché - stačí ho napísať ako parameter (argument).

Čo urobiť, keď chceme napríklad použiť vlastný hlavičkový súbor?
Stačí ho len pripísať do závislostí .. Ukážeme si ako. 
Na účely tohto tutoriálu si vytvoríme jednoduchý hlavičkový súbor my_config.h
#ifndef __MY_CONFIG__ #define __MY_CONFIG__ #define MOJE_CISLO 42 #endif
Program upravíme nasledovne:
#include <stdio.h> #include <stdlib.h> #include "my_config.h" int main(void) { printf("Hello world!\n" "Moje číslo je: %d\n", MOJE_CISLO); return 0; }
A Makefile bude vyzerať takto:
# Projekt: Hello world!
# Autor: David Novák
# Verze: 1.1
# Datum: 16.2.2015
CC=gcc
CFLAGS= -std=c99 -pedantic -Wall -Wextra -g
NAME=hello
$(NAME): $(NAME).c my_config.h # zde jsme připsali „my_config.h“
$(CC) $(CFLAGS) $(NAME).c -o $(NAME)
clean-exe:
rm -f $(NAME) # smaže binární soubor
clean-bck:
rm -f *~ *.bak # smaže všechny záložní soubory
clean: clean-exe clean-bck # smaže binární soubor i zálohy
Keď teraz spustíme make a následne program, výstup bude niečo takéto:

Pre zaujímavosť si ešte ukážeme, čo by sa stalo, keby my_config.h chýbal.

Makefile pre väčší projekt
Jednoduché, že? Čo ale, keď má náš projekt viac modulov? Všeobecne sa
dá povedať, že modul je samostatná jednotka, ktorá je do istej miery
nezávislá na hlavnom programe (a napríklad mu poskytuje nejaké služby).
Možno by sa dal trochu prirovnať k objektu (ale toto berte prosím voľne ...
). Každopádne sa skladá z
.ca .h súboru. Ak teda máme hlavný súbor ak tomu ešte ďalší modul, máme
dva súbory, ktoré musíme preložiť a slinkovat. Minule sme si povedali o
prepínačmi -ca toto je presne prípad, kedy sa bez neho nezaobídeme.
Takto bude vyzerať náš jednoduchý modul.c
#include "modul.h" #include <stdio.h> #include <stdlib.h> int vypocet(int a, int b) { return (a + b)*a; }
Takto jeho hlavičkový súbor modul.h
#ifndef __MY_MODUL__ #define __MY_MODUL__ extern int vypocet(int a, int b); #endif
Len by som podotkol, že pre vytváranie modulov a hlavičkových súborov platia určité konvencie a oplatí sa ich dodržiavať. Tieto príklady sú iba na účely tutoriálu a pre reálny vývoj sú minimálne nekompletné. O modulárnym programovanie si možno v budúcnosti povieme niečo viac. Každopádne, ak vás to zaujíma, na internete sa dá nájsť množstvo informácií na túto tému.
A takto náš upravený hlavný súbor.
#include <stdio.h> #include <stdlib.h> #include "my_config.h" int main(void) { printf("Hello world!\n" "Moje číslo je: %d\n", MOJE_CISLO); printf("Výpočet: %d\n", vypocet(2,5)); return 0; }
Nakoniec musíme upraviť náš Makefile.
CC=gcc
CFLAGS= -std=c99 -pedantic -Wall -Wextra -g
NAME=hello
$(NAME): modul.o $(NAME).o # slinkování přeložených souborů
$(CC) $(CFLAGS) $(NAME).o modul.o -o $(NAME)
$(NAME).o: $(NAME).c my_config.h # překlad hlavního souboru
$(CC) $(CFLAGS) -c $(NAME).c -o $(NAME).o
modul.o: modul.c modul.h # překlad modulu
$(CC) $(CFLAGS) -c modul.c -o modul.o
Nie je to moc pekné, že? A keď budeme mať projekt o desiatkach modulov,
nebude to ani príliš praktické. Keď by sme si vytvorili nový hlavičkový
súbor, museli by sme otvoriť Makefile a vykonať zmeny .. Ide to ale lepšie

NAME=hello
OBJFILES=$(NAME).o modul.o # zde napíšeme všechny moduly
CC=gcc
CFLAGS= -std=c99 -pedantic -Wall -Wextra -g
# vzorové pravidlo pro generování všech objektových souborů
%.o : %.c
$(CC) $(CFLAGS) -c $<
# Startovací pravidlo - pro přehlednost
all: $(NAME)
# Generování závislostí
# při změně souborů spustíme 'make dep'
dep:
$(CC) -MM *.c >dep.list
-include dep.list
# závěrečné slinkování
$(NAME): $(OBJFILES)
$(CC) $(CFLAGS) $(OBJFILES) -o $@
Vyzerá to inak, že? Generovanie závislostí prenecháme prekladači -
takhle stačí pri každej zmene (napr. Pridáme hlavičkový súbor) len
zavolať make dep. Prepínač -MM je špecialita k tomuto určená - prekladač
len vygeneruje závislosti (include). $ <A $ @ sú tzv. Automatické
premenné. $ <Obsahuje názov zdrojového súboru a $ @ názov cieľového
súboru. Získajú ich z hlavičky pravidlá (pre nás je dôležité, že to
funguje ...
). Nakoniec by som
mal asi ešte vysvetliť vzorové pravidlo .. Znak% je tzv. Žolík (angl.
Wildcard) a zastupuje ľubovoľný text. Vzorové pravidlo funguje tak, že v
zozname skutočných pravidiel (to nám vygeneroval prekladač) nájde tie,
ktoré mu zodpovedajú - cieľ je súbor s koncovkou .o, ktorý má v
závislostiach aspoň jeden súbor s koncovkou .c.
Tak .. To je pre dnešok všetko ..
Nabudúce, v lekcii Céčko a Linux - Debugging , sa pozrieme na debugging a ladiace
nástroje pod Linuxom. Inak tento Makefile môžete použiť na väčšinu
projektov a ak by ste chceli, môžete sa pozrieť do dokumentácie alebo na
internet a určite ho aj rozšíriť o ďalšie možnosti ako napríklad
generovanie dokumentácie pomocou doxygen (trochu zložitejšie) alebo zabalenie
celého projektu do archívu.
Ako vždy uvítam akékoľvek pripomienky alebo otázky 
