Vydělávej až 160.000 Kč měsíčně! Akreditované rekvalifikační kurzy s garancí práce od 0 Kč. Více informací.
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

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é.

Makefile v Linuxe - Linux a programovanie v jazyku C

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).

Príkaz clean v Linuxe - Linux a programovanie v jazyku C

Č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:

Príkaz make v Linuxe - Linux a programovanie v jazyku C

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

chýbajúce config - Linux a programovanie v jazyku C

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 ;)


 

Predchádzajúci článok
Céčko a Linux - Úvod
Všetky články v sekcii
Linux a programovanie v jazyku C
Preskočiť článok
(neodporúčame)
Céčko a Linux - Debugging
Článok pre vás napísal David Novák
Avatar
Užívateľské hodnotenie:
1 hlasov
Autor se zajímá především o nízkoúrovňové programování (C/C++, ASM) a návrh hardwaru (VHDL).
Aktivity