FreshSourcing. Čerstvé nápady. Kreatívne riešenia.

» Domov » Auto Tools » 2. časť

2. časť: Hello World, parametre programu make

V dnešnej časti si vylepšíme program z minulej časti. Ďalej si preberieme prepínače (parametre), s ktorými sa program make môže spustiť.

Ahoj v. 2

Program z minulej časti zmeníme takto:

  1. pridáme dva zdrojové a dva hlavičkové súbory (ahoj1.cpp, ahoj1.h, ahoj2.cpp, ahoj2.h).
  2. pôvodný súbor premenujeme na main.cpp.
  3. zmeníme súbor Makefile.
Celý program dokopy nič nerobí - vypisuje hlásenia z rôznych častí programu, na ukážku však stačí. Tu je prvý pár súborov:
ahoj1.cpp:

#include "ahoj2.h"
#include <iostream>

using namespace std;

void ahoj1()
{
cout << "Ahoj, svet! (ahoj1)" << endl;
ahoj2();
}


ahoj1.h:

void ahoj1();
Funkcia ahoj1() vypíše na štandardný výstup pozdrav a svoje meno, aby sme mali prehľad o tom, ktorá časť programu práve prebieha. Druhý pár súborov je podobný.
ahoj2.cpp:

#include <iostream>

using namespace std;

void ahoj2()
{
cout << "Ahoj, svet! (ahoj2)" << endl;
}


ahoj2.h:

void ahoj2();
Teraz nasleduje funkcia main(), ktorá ma na svedomí volanie ostatných funkcií.
main.cpp:

#include "ahoj1.h"
#include <iostream>

using namespace std;

void main()
{
cout << "Ahoj, svet! (main)" << endl;
ahoj1();
}
Teraz nasleduje súbor Makefile. Je viacero možností, ako urobiť tú istú vec - skompilovať celý projekt. Začneme najjednoduchšie - zmeníme súbor z minulej časti seriálu tak, že pridáme názvy súborov do premennej SRC.
Makefile:

CC = g++
TARGET = ahoj
SRC = ahoj1.cpp ahoj2.cpp main.cpp

all: $(TARGET)

$(TARGET): $(SRC)
$(CC) -o $(TARGET) $(SRC)
Pozor, v poslednom riadku musí byť prvý znak tab! Spustením príkazu make sa najprv skontroluje cieľ all, ktorý vyžaduje kontrolu cieľa $(TARGET) (po substitúcii názvov premenných za ich hodnoty to bude vlastne pravidlo ahoj). Cieľ ahoj skontroluje zoznam zdrojových súborov. Ak sa aspoň jeden z nich zmenil, prekompiluje sa celý program. Je to naozaj najjednoduchšie riešenie, pretože nekontroluje, či treba pri zmene hlavičkového súboru prekompilovať aj príslušné zdrojové súbory. Druhý nedostatok je tiež veľmi závažný - pri zmene aspoň jedného súboru sa prekompilujú všetky súbory, a to je práve to, čo nechceme.

Riešenie obidvoch problémov je pomerne jednoduché. Ak si spomínate, v prvej časti sme hovorili o pravidlách. Pre zopakovanie: ak chceme vytvoriť cieľ (pred dvojbodkou), skontrolujú sa zdroje (za dvojbodkou). Ak boli zmenené, vykonajú sa príkazy (na ďalších riadkoch. Tento mechanizmus pracuje rekurzívne, teda ak uvedieme zdroj, program sa pokúsi nájsť také ďalšie pravidlo, ktorého cieľom je vytvoriť spomínaný zdroj. To využijeme aj v našom prípade. Pre zmenu však nevymenujeme všetky zdrojové súbory (v našom prípade *.cpp), ale všetky objekty, teda súbory s príponou .o . Premennú SRC premenujeme na OBJECTS (objekty). Takisto pridáme pre každý takýto súbor jedno pravidlo, ktoré ho vytvorí (skompiluje, ale nezlinkuje). Cieľom týchto pravidiel budú objektové súbory, zdrojom budú všetky súbory, ktoré sú potrebné na skompilovanie daného cieľa. Pre súbor ahoj1.o to bude zdrojový súbor ahoj1.cpp a hlavičkový súbor ahoj2.h, ktorý je do zdrojového súboru vkladaný pomocou direktívy prekladača #include "ahoj2.h". Na linkovanie použijeme samostatné pravidlo.

Makefile:

CC = g++
TARGET = ahoj
OBJECTS = ahoj1.o ahoj2.o main.o


# Všeobecné pravidlo, ktoré sa vykoná po spustení
# samotného príkazu make bez parametrov
all: $(TARGET)

# Zlinkovanie projektu
$(TARGET): $(OBJECTS)
$(CC) -o $(TARGET) $(OBJECTS)

# Pravidlá pre vytvorenie jednotlivých objektových súborov

ahoj1.o: ahoj1.cpp ahoj2.h
$(CC) -c ahoj1.cpp

ahoj2.o: ahoj2.cpp
$(CC) -c ahoj2.cpp

main.o: main.cpp ahoj1.h
$(CC) -c main.cpp
Program make teraz bude pracovať takto:
  1. Nájde pravidlo all.
  2. Skontroluje jeho zdroje (v našom prípade je to premenná $(TARGET) - po substitúcii to bude ahoj).
  3. Nájde pravidlo, ktorého cieľ sa zhoduje so zdrojom predchádzajúceho pravidla: $(TARGET).
  4. Skontroluje jeho zdroje (ahoj1.o, ahoj2.o, main.o) takým istým spôsobom ako vyššie.
  5. Nájde prvý zdroj predchádzjúceho pravidla: ahoj1.o.
  6. Skontroluje zdroje tohto pravidla - súbory ahoj1.cpp a ahoj2.h. Ak bol aspoň jeden z nich zmenený, vykoná nasledujúci príkaz (skompiluje súbor ahoj1.cpp) a informuje predchádzajúce pravidlo ($(TARGET) = ahoj), že došlo k zmene jej cieľa ahoj1.o.
  7. Podobne fungujú aj nasledujúce dve pravidlá.
  8. Ak aspoň jeden zo zdrojov pravidla $(TAGET) bol zmenený, teda aspoň jedno z pravidiel ahoj1.o, ahoj2.o a main.o oznámilo zmenu svojho cieľa, vykoná sa nasledujúci príkaz - zlinkujú sa objekty a vznikne spustiteľný program.

Ako ste si mohli všimnúť, program pracuje rekurzívnym spôsobom do teoreticky neobmedzenej hĺbky. Možno ste uvažovali nad tým, prečo sme namiesto zdrojových súborov .cpp vymenovali objektové súbory .o a zmenili celý súbor Makefile. Dôvod je jednoduchý: ak chceme, aby sa daný cieľ označil za zmenený, naozaj sa musí zmeniť! V našom príklade ho vytvárame kompilátorom, teda čas jeho poslednej zmeny sa určite zaktualizuje. Ak by sme predsa len chceli vymenovať zdrojové súbory namiesto objektov, museli by sme ako príkaz uviesť napríklad:

touch ahoj1.cpp
Treba sa jednoducho postarať o zmenu časovej známky (time stamp). Keď sa však nad tým zamyslíte hlbšie, prídete na to, že takto je to logickejšie - cieľ, teda niečo, čo vznikne, sa vytvorí zo zdrojov - nejakých "polotovarov". Ak by sme ako cieľ chceli uviesť zdrojový súbor a zároveň zachovať spomínanú logiku, mali by sme zdrojový súbor vytvárať príkazmi - a to sa deje v špeciálnych prípadoch, napríklad pri použití programov typu flex, alebo pri generovaní formulárov a pod.

Ak chcete explicitne určiť, ktorý cieľ sa má vytvoriť, spustite make s názvom cieľa ako parametrom, napríklad make main.o. Aj teraz sa však skontrolujú všetky jeho zdroje (a prípadné zdroje jeho zdrojov atď.) - program pracuje takisto rekurzívne. Spomínaná vlastnosť (spúštanie make s parametrom - cieľom) sa používa dosť často. Do nášho projektu by sme mohli napríklad pridať možnosť vyčistenia od vygenerovaných objektových súborov (to by mal mať každý správny Makefile). Zvykne sa tiež zmazať aj prípadný súbor core a zlinkovaný (výsledný) program. Do súboru Makefile pridáme toto pravidlo:

clean:
rm -f $(OBJECTS) $(TARGET) core
Toto pravidlo neobsahuje zdroje, vykoná sa teda vždy, keď spustíme make clean. Pravidlá môžu byť viac-menej v ľubovoľnom poradí, teda naše nové pravidlo môžeme pridať aj niekde uprostred súboru. Zvykom však je takéto pravidlá dávať na koniec.

Parametre programu make

-C adresár
Program make implicitne operuje v lokálnom adresári. Týmto parametrom to môžete zmeniť.

-d
Zahltí vám terminál rôznymi výpismi o činnosti programu.

-e
Systémové premenné, nastavenia a pravidlá budú mať prednosť pred lokálnymi (neprekryjú sa tak, ako to býva zvykom - za normálnych okolností majú lokálne nastavenia vždy väčšiu prioritu ako systémové nastavenia).

-f súbor
Namiesto lokálneho súboru Makefile sa načíta definovaný súbor.

-h
Vypíše stručné informácie o používaní programu.

-i
Ignoruje prípadné chyby pri vykonávaní príkazov.

-I adresár
Ak vkladáte súbory (preberieme neskôr, ekvivalent direktívy #include v C/C++), budú sa hľadať aj v adresári, ktorý definujete (štandardne sa hľadajú vo vašom lokálnom adresári).

-j číslo
Nastavuje, koľko procesov môže bežať naraz. Ak číslo nedefinujete (ale použijete parameter -j), nastaví sa nekonečno. Ak by sme náš príklad prekladali spustením

make -j

začali by sa kompilovať všetky tri súbory naraz, potom by sa zlinkovali.

-k
Pokračuje v behu programu, aj keď sa niektoré cieľe nedajú vytvoriť.

-l číslo
Nespustí viacero procesov naraz (opak parametra -j), ak je vyťaženosť procesora vyššia, ako zadané číslo.

-n
Príkazy sa nevykonajú, iba sa vypíšu (veľmi užitočné na odstraňovanie chýb v Makefile).

-o súbor
Súbor považuje za "starý" (old) a nebude kontrolovať, či bol zmenený.

-p
Vypíše internú databázu pravidiel. Väčšinou sú to často používané pravidlá (napr. na kompilovanie), ktoré sú natoľko bežné, že si ich make dokáže "domyslieť" aj sám (s pomocou spomínanej databázy). Ak nechcete, aby zároveň načítal súbor Makefile, spustite ho napríklad takto:

make -p -f /dev/null

Takto sa nenačíta súbor Makefile (načíta sa nulový súbor).

-q
"Question" mód. Nevykonajú sa žiadne príkazy, nevypíšu sa žiadne hlásenia. Ak je výsledný program "up to date", vráti nulovú hodnotu, inak vráti nenulovú hodnotu.

-r
Nebudú sa používať žiadne vstavané pravidlá (viď parameter -p).

-s
Tichý (silent) mód. Nebudú sa vypisovať žiadne hlásenia.

-t
Zmení informáciu o poslednej zmene súborov (time stamp), nevykonajú sa príkazy.

-v
Vypíše informáciu o verzii programu make, copyright atď.

-w
Vypíše názov pracovného adresára pred vykonaním akéhokoľvek pravidla. Užitočné, ak váš projekt pozostáva z viacerých podadresárov a v každom z nich sa nachádza Makefile (nič nezvyčajné).

-W súbor
Presvedčí make, že zadaný súbor bol zmenený, a teda že má vykonať všetky príslušné akcie (skontrolovať zdroje a podobne).

Záver

Prebrali sme zopár nových vecí, mali by ste byť schopní písať jednoduché Makefile. Samozrejme, ešte toho máme pred sebou veľmi veľa; nabudúce si preberieme všeobecné pravidlá a niektoré špeciálne premenné.


Oto Komiňák


Článok bol uverejnený v magazíne PC Revue 02/2002.