next up previous
Next: GDB (GNU Debugger) Up: Programarea in Unix/Linux Previous: GCC (GNU C Compiler,

Make

Utilitarul make este utilizat pentru a determina dependentele dintre modulele unui program si de a genera comenzi de recompilare. Spre exemplu, daca am compilat fisierele server.c, sock.c si handle.c si modificam fisierul handle.c, utilitarul isi va da seama de acest lucru si va recompila numai handle.c pentru obtinerea noului executabil.

Inca de la aparitia lui, make a devenit un standard de facto in lumea Unix; in ciuda mai multor deficiente, el este inca folosit pe scara larga, mai ales datorita altor tool-uri din proiectul GNU: autoconf, automake.

La baza utilitarului sta un fisier makefile care contine reguli de compilare. Desi sunt recunoscute implicit mai multe nume (Makefile, GNUMakefile si makefile - pentru GNU make, versiunea cea mai utilizata), se prefera folosirea Makefile, deoarece apare proeminent la listarea continutului unui director.

Un fisier Makefile este format din reguli si definiri; o regula este, la randul ei formata dintr-o linie de dependente si 0 sau mai multe actiuni (comenzi). Pentru o mai buna intelegere a acestora vom utiliza urmatorul exemplu:

Dorim asadar obtinerea executabilelor client si server pentru rularea celor doua entitati. Structura fisierului Makefile este prezentata mai jos:

#
# exemplu Makefile
#

CC = gcc                        # compilatorul folosit
CFLAGS = -Wall -pedantic -ansi  # optiunile pentru compilare
LDFLAGS = -lefence              # optiunile pentru linking

# creeaza executabilele client si server
all: client server

# leaga modulele client.o user.o sock.o in executabilul client
client: client.o user.o sock.o log.o
        $(CC) $(LDFLAGS) -o $@ $^

# leaga modulele server.o cli_handler.o sock.o in executabilul server
server: server.o cli_handler.o sock.o log.o
        $(CC) $(LDFLAGS) -o $@ $^

# compileaza fisierul client.c in modulul obiect client.o
client.o: client.c sock.h user.h log.h
        $(CC) $(CFLAGS) -c $<

# compileaza fisierul user.c in modulul obiect user.o
user.o: user.c user.h
        $(CC) $(CFLAGS) -c $<

# compileaza fisierul sock.c in modulul obiect sock.o
sock.o: sock.c sock.h
        $(CC) $(CFLAGS) -c $<

# compileaza fisierul server.c in modulul obiect server.o
server.o: server.c cli_handler.h sock.h log.h
        $(CC) $(CFLAGS) -c $<

# compileaza fisierul cli_handler.c in modulul obiect cli_handler.o
cli_handler.o: cli_handler.c cli_handler.h
        $(CC) $(CFLAGS) -c $<

# compileaza fisierul log.c in modulul obiect log.o
log.o: log.c log.h
        $(CC) $(CFLAGS) -c $<

clean:
        rm -fr *~ *.o server client

Pe acest exemplu, vom descrie in detaliu structura unui fisier Makefile.

Comentariile intr-un fisier Makefile incep de la caracterul # si dureaza pana la sfasitul liniei.

Dupa liniile cu comentariu urmeaza trei linii de definire; aici se specifica compilatorul (definit in variabila CC), optiunile pentru compilare (CFLAGS) si cele pentru linking (LDFLAGS). Compilatorul folosit este gcc, optiunile sunt -Wall -pedantic -ansi, iar pentru legare folosim biblioteca libefence. Electric Fence este un utilitar care ajuta la prevenirea erorilor de alocare sau eliberare a zonelor de memorie; intrucat este usor de folosit (trebuie doar legat la executabil), se recomanda folosirea lui.

Ceea ce urmeaza acum sunt regulile. Regulile au o sintaxa de forma:

TARGET: PREREQUISITES
        COMMANDS

TARGET este in general numele unui fisier (executabil, obiect) care se doreste obtinut. PREREQUISITES sunt fisiere care sunt necesare pentru obtinerea fisierului specificat de target. COMMAND este comanda (comenzile) executate de make pentru obtinerea fisierului indicat de TARGET. IMPORTANT: fiecare comanda incepe cu caracterul TAB.

In lipsa specificarii unei tinte la rularea make, se va considera prima regula intalnita. De aceea se obisnuieste folosirea unui ``phony target'' cu numele all ca prima regula; acesta va avea ca dependente executabilele ce se doresc obtinute. Obtinerea executabilului client necesita existenta modulelor obiect client.o, sock.o, user.o, log.o; actiunea necesara pentru obtinerea executabilului este legarea acestor module. Linia

        $(CC) $(LDFLAGS) -o $@ $^
va fi translatata in

        gcc -lefence -o client client.o sock.o user.o log.o 
O variabila definita este utilizata in formatul $(nume_var) (dupa cum se poate observa pentru variabilele CC si LDFLAGS). Constructia $@ este inlocuita cu TARGET, iar $^ este inlocuita cu intreaga lista de PREREQUISITES. Acestea sunt denumite variabile automate (automatic variables). O alta variabila automata este $< care este inlocuita cu primul element din lista de PREREQUISITES. Executabilul server se obtine la fel ca si executabilul client.

Pentru obtinerea modulelor obiect, sintaxa este aceeasi: TARGET este numele fisierul obiect iar PREREQUISITES contine fisierul sursa care va fi compilat si fisierele header folosite. Comanda

        $(CC) $(CFLAGS) -c $< 
va fi translatata in

        gcc -Wall -pedantic -ansi -c client.c 
Tinta clean este un ``phony target''. Aceasta inseamna ca ea nu este accesibila decat explicit in Makefile, adica prin rularea comenzii

        $ make clean 
Singura modalitate in care ar putea fi accesata implicit este prezenta ei la inceputul fisierului Makefile (lucru care nu este dorit). clean este o tinta clasica si este folosita pentru ``curatarea'' directorului curent (stergerea fisierelor temporare, obiect si executabile).

Daca la un moment de timp, se va modifica fisierul user.c, atunci pasii urmati de make vor fi urmatorii (la rularea comenzii $ make):

        $(CC) $(CFLAGS) -c $< 
        $(CC) $(CFLAGS) -o $@ $^
Dimensiunea unui fisier Makefile poate fi foarte usor diminuata daca se tine cont de faptul ca utilitarul make foloseste multe reguli si definiri implicite. Dintre acestea amintim:

client.o: client.c user.h sock.h log.h 
folosesc linia de actiuni (comanda) implicita

        $(CC) $(CFLAGS) -c $< -o $@
deci nu mai este practic nevoie de prezenta liniei de reguli in cadrul fisierului Makefile.

client.o: user.h sock.h log.h 
aceasta este forma obisnuita de scriere a fisierelor Makefile. Se pot face si alte simplificari, dar se poate pierde din claritate.

Fisierul Makefile in forma simplificata va fi asadar:

#
# exemplu Makefile - forma uzuala (simplificata)
#

CFLAGS = -Wall -pedantic -ansi
LDFLAGS = -lefence

all: client server

client: client.o sock.o user.o log.o
        $(CC) $(LDFLAGS) -o $@ $^

server: server.o sock.o cli_handler.o log.o
        $(CC) $(LDFLAGS) -o $@ $^

client.o: sock.h user.h log.h
user.o: user.h
sock.o: sock.h
log.o: log.h
server.o: sock.h cli_handler.h log.h
cli_handler.o: cli_handler.h

clean:
        rm -fr *~ *.o client server

Informatii exhaustive despre utilitarul make gasiti accesand paginile de documentatie info

        $ info make 
sau prin accesarea pagini http://www.gnu.org/software/make/manual/make.html.


next up previous
Next: GDB (GNU Debugger) Up: Programarea in Unix/Linux Previous: GCC (GNU C Compiler,
Razvan Adrian Deaconescu 2005-10-01