Správa projektů pomocí programu Make

[  Program make  |  Makefile  |  Komentáře  |  Proměnné  |  Pravidla  |  Vyhodnocení pravidel  |  Složitější makefile  |  Překlad projektu s moduly  ]

Program make

Make je program pro automatické kompilování programů, které jsou složeny z více souborů. Využijeme jej však i u jednoduchých projektů, které jsou tvořeny jediným zdrojovým souborem. Program make totiž umí daleko více než jen překládat. Můžeme pomocí něj spravovat celý projekt. Ukážeme si, že s jeho pomocí hravě vyčistíme celý projekt od pomocných souborů vytvořených překladačem nebo záložních souborů vytvořených editorem. Také si ukážeme, jak všechny důležité soubory projektu zabalit do archívu a další užitečné věci.

Podrobnější informace o programu make najdete na jeho manuálové stránce (man make), na info stránce (info make) nebo na webu. V tomto dokumentu jsou popsány pouze základní vlastnosti, které jsou potřebné pro začátečníky - zejména při řešení projektů kurzu IZP. Podrobněji se používání programu make probírá v kurzu IJC.

Existuje více verzí programu make, zejména na různých platformách. Program make je součástí distribucí GCC (ve Windows MinGW, Cygwin), ale i komerčních překladačů (např Borland). Dokonce i většina vizuálních prostředí umožňuje pro každý projekt vygenerovat makefile. Základní principy všech verzí jsou však stejné.

Makefile

Aby program make věděl, co má s projektem dělat, potřebuje k tomu soubor se jménem makefile. Je to obyčejný textový soubor, který je nejlépe umístit v hlavním adresáři projektu. Celý makefile se skládá ze specifikace pravidel a proměnných. Na následujícím příkladu si ukážeme makefile, pro překlad programu skládajícího se z jediného zdrojového souboru helloworld.c (čísla řádků jsou zde jenom proto, abych se na ně mohl odkazovat).

01 #
02 # Projekt: Hello world!
03 # Autor:   David Martinek
04 # Datum:   11.12.2012
05 # 
06 
07 CC=gcc                              # překladač jazyka C
08 CFLAGS=-std=c99 -Wall -pedantic -g  # parametry překladače
09
10 hello: helloworld.c
11 	$(CC) $(CFLAGS) helloworld.c -o hello
12

Komentáře

Jak je vidět, komentáře se uvozují znakem # (mřížka). Komentář končí na konci řádku. Je docela dobré na začátek souboru makefile vložit hlavičku s podpisem, datem vytvoření a pokud je to složitější makefile, tak i s popisem nejdůležitějších cílů.

Proměnné

Na řádcích 7 a 8 je specifikace proměnných. V tomto případě jsem si takto definoval překladač a parametry překladu. Proměnné je možné definovat kdekoli v souboru. Hodnotu proměnné získáme tak, že napíšeme $(JMENOPROMENNE), jak je vidět na řádku 11.

Pravidla

Nejdůležitější částí souboru makefile je specifikace pravidel. V našem souboru je jednoduché pravidlo na řádcích 10 a 11. Každé pravidlo se skládá z cíle, závislostí a kódu. V našem případě se cíl jmenuje hello (řádek 10), závislost je v tomto případě pouze jediná - hello.c. Název cíle musí být ukončen dvojtečkou. Závislostí může být libovolný počet (tedy i nulový) a jsou vzájemně odděleny mezerami. Jako závislosti se používají jména souborů nebo názvy jiných cílů. Kód našeho pravidla najdeme na řádku 11. Kód musí následovat bezprostředně za specifikací cíle. Není možné vkládat prázdné řádky. Řádky s kódem musí začínat tabulátorem! Blok kódu končí řádkem, který nezačíná tabulátorem. Obecně vypadá pravidlo takto:

cíl: [závislosti]
  [příkaz]
  [příkaz]
  ...

Pokud je cíl jméno souboru, znamená to, že pravidlo bude generovat soubor s tímto jménem. Potom se v závislostech musí uvést jména všech souborů, které jsou potřeba ke zkonstruování cíle. V případě projektů v jazyce C, je potřeba zde uvést jméno zdrojového souboru (.c) a jména všech lokálních hlavičkových souborů (.h), které tento zdrojový soubor používá.

Vyhodnocení pravidel

Program make jde spouštět dvěma způsoby. V obou případech je nutné spouštět program make v adresáři, kde se nachází soubor makefile. První způsob je spouštění make bez parametru

$ make
gcc -std=c99 -Wall -pedantic -g helloworld.c -o hello

Vyhodnocení potom probíhá tímto způsobem:

  1. Program make vyhledá pravidlo, které se v souboru makefile vyskytuje na prvním místě. Program make spuštěný bez dalších parametrů spouští vždy první pravidlo. Na pořadí ostatních pravidel potom nijak nezáleží.
  2. Pokud se v závislostech tohoto pravidla (část za dvojtečkou v hlavičce pravidla) vyskytují cíle jiných pravidel, nejprve se vyhodnotí všechna tato pravidla v pořadí v jakém jsou vyjmenována za dvojtečkou. Vyhodnocení ostatních pravidel nezáleží na pořadí jejich uvedení v souboru, ale na pořadí, v jakém jsou uvedeny v závislostech právě zpracovávaných pravidel.
  3. Pokud je cíl jméno souboru a v závislostech se také vyskytují jména souborů, pravidlo se vykoná pouze tehdy, je-li čas vytvoření cílového souboru starší než čas některého souboru v seznamu závislostí. Jinými slovy, pravidlo se vykoná jen když je to potřeba, tj. když se zdrojový kód změnil od posledního překladu.

V našem případě se spustí překlad souboru helloworld.c a výsledkem bude spustitelný soubor hello. V prostředí Windows bychom museli makefile upravit tak, aby se generoval soubor hello.exe.

Druhý způsob spouštění programu make si vysvětlíme vzápětí.

Složitější makefile

Překlad není to jediné, co jde s programem make dělat. Následující Makefile umí nejenom překládat, ale také pakovat do archívu, odstraňovat zbytečné soubory a spouštět debugger.

#
# Projekt: Hello world!
# Autor:   David Martinek
# Datum:   10.10.2012
# 
# Použití:
#   - překlad:      make
#   - ladit:        make debug
#   - zabalit:      make pack
#   - vyčistit:     make clean
#   - vyčistit vše: make clean-all
#

NAME=hello

CC=gcc                                # překladač jazyka C
CFLAGS=-std=c99 -Wall -pedantic -g # parametry překladače

ALLFILES=helloworld.c makefile        # obsah projektu

$(NAME): helloworld.c
	$(CC) $(CFLAGS) helloworld.c -o $(NAME)

.PHONY: debug pack clean clean-exe clean-all

debug: $(NAME)
	export XEDITOR=gvim;ddd $(NAME)

pack:
	tar cvzf $(NAME).tar.gz $(ALLFILES)
	zip $(NAME).zip $(ALLFILES)

clean:
	rm -f *~ *.bak
  
clean-exe:
	rm -f $(NAME)

clean-all: clean-exe clean

Pokud nyní spustím program make bez parametru, bude se chovat způsobem popsaným výše. Pokud jako parametr použijeme cíl některého z pravidel, vykoná se toto zadané pravidlo.

Zvláštní pozornost zaslouží cíl .PHONY. Toto pravidlo slouží pro označení tzv. falešných cílů - tedy cílů, které nemají význam "cílový soubor", ale spíše "návěští". Tyto falešné cíle neprodukují soubory shodné se svým jménem. Tyto cíle se používají právě k tomu, aby se spouštěly přes parametr programu make. Pokud by cíl .PHONY v souboru makefile nebyl, pravděpodobně by to nemělo vliv na jeho funkčnost. Problémy by mohly nastat, kdyby se v našem adresáři objevil soubor, jehož jméno se shoduje s některým falešným cílem. Program make by se potom pokoušel porovnávat čas jeho vytvoření a pravidlo by přestalo fungovat. Proto je dobré cílem .PHONY označit všechny falešné cíle, abychom se vyhnuli případným problémům.

Překlad projektu s moduly

Všechny předchozí příklady sloužily k překladu programu, který je tvořen jediným zdrojovým souborem. Program make ovšem především slouží k překladu projektů, které jsou tvořeny více moduly (viz kapitolu Moduly a knihovny). U těchto projektů je potřeba vytvořit v Makefile pravidlo pro generování objektového souboru každého modulu a také pravidlo pro jejich slinkování. Pokud přidáme na začátek souboru Makefile pomocné startovací pravidlo, nezáleží na pořadí jednotlivých pravidlel pro překlad modulů.

# pomocné startovací pravidlo, nemá žádné tělo, slouží jenom jako odkaz
all: program

modul1.o: modul1.c modul1.h
	$(CC) $(CFLAGS) -c modul1.c -o modul1.o
modul3.o: modul3.c modul3.h
	$(CC) $(CFLAGS) -c modul2.c -o modul2.o
modul2.o: modul2.c modul1.h modul2.h
	$(CC) $(CFLAGS) -c modul2.c -o modul2.o
program.o: program.c modul1.h modul2.h modul3.h
	$(CC) $(CFLAGS) -c program.c -o program.o
program: modul1.o modul2.o modul3.o program.o
	$(CC) $(CFLAGS) modul1.o modul2.o modul3.o program.o -o program

Protože tyto činnosti jsou velmi časté, lze si ušetřit práci a využitím tzv. univerzálních pravidel a proměnných.

NAME=program
OBJFILES=$(NAME).o modul1.o modul2.o modul3.o

CC=gcc
CFLAGS=-std=c99 -Wall -pedantic -W -g

# univerzální pravidlo pro generování všech objektových souborů
%.o : %.c
	$(CC) $(CFLAGS) -c $<

# Startovací pravidlo
all: $(NAME)

## ## ## 
# pravidla bez těla - to se sem doplní z univerzálního pravidla
modul1.o: modul1.c modul1.h
modul2.o: modul2.c modul1.h modul2.h
modul3.o: modul3.c modul3.h
program.o: program.c modul1.h modul2.h modul3.h
## ## ## 

# Slinkování všech objektových souborů do jednoho spustitelného programu.
$(NAME): $(OBJFILES)
	$(CC) $(CFLAGS) $(OBJFILES) -o $@

Vytváření části předchozího souboru Makefile, která je označena řádky s textem ## ## ##, lze generovat automaticky. Je to mnohem spolehlivější, než vytvářet závislosti ručně.

## ## ## 
# Generování závislostí
dep: 
	$(CC) -MM *.c >dep.list

## vloží vygenerované závislosti
-include dep.list
## ## ## 

Vždy, když se závislosti změní (tj. vytvoříte nový modul nebo hlavičkový soubor), je potřeba ručně zavolat pravidlo make dep a vytvořit tak soubor dep.list, který bude obsahovat všechna potřebná pravidla pro generování modulů i s jejich skutečnými závislostmi. Poté, co je tento soubor vytvořen, se překlad spouští obvyklým způsobem pomocí volání programu make.


Autor: David Martinek. Poslední modifikace: 11. December 2012. Pokud v tomto dokumentu narazíte na chybu, dejte mi prosím vědět.