Agora
Media
www.agora.ro
Libraria Byblos



AgoraNews  





PC Magazine Ro  




NET Report   




Ginfo   




agora ON line   





PC Concrete   





Liste de discuții   




Cartea de oaspeți   




Mesaje   





Agora   








Clic aici
PC Report - ultimul numar aparut


Soluții - PC Magazine Romania, Mai  2002

Make, un utilitar nu doar pentru programatori


Iulian Radu

Deși în prima pagină a manualului programului GNU make se specifică faptul că: "utilitarul determină automat care parte dintr-un program mare trebuie recompilată și execută comenzile necesare recompilării", acest program poate fi folosit ori de câte ori avem de gestionat fișiere al căror conținut trebuie actualizat când conținutul altor fișiere s-a modificat. Soluțiile oferite în acest articol nu sunt aplicabile doar cu GNU make, ci sunt valabile pentru orice altă variantă a programului make.

Cum lucrează programul make
Programul make execută o serie de comenzi aflate într-un fișier text pe baza argumentelor furnizate în linia de comandă. De aceea, make are nevoie să știe două lucruri: care este numele scopului pentru care trebuie executate comenzi și în ce fișier găsește descris acest scop. Dacă nu este furnizat nici un nume de fișier, se caută în directorul curent un fișier care se numește GNUmakefile sau makefile sau Makefile (căutarea se face în această ordine; oricare din aceste fișiere sau cel/cele specificat/e implicit cu -f îl voi denumi în continuare Makefile). Când a fost găsit unul dintre ele, se întrerupe căutarea. Dacă se specifică mai multe fișiere cu ajutorul argumentului -f, atunci toate acestea vor fi privite ca un singur fișier ce conține reguli. În lipsa specificării unui scop, se va considera primul scop care nu începe cu punct, găsit în fișierul Makefile. În secțiunea "Structura unui fișier Makefile" veți găsi explicat cum arată conținutul unui astfel de fișier.

Argumentele cele mai utilizate
În acest articol nu vom face o prezentare completă a tuturor argumentelor pe care le poate primi programul make și nici a tuturor aspectelor legate de el. Pentru astfel de informații cea mai bună sursă rămâne documentația în format .info aflată în kit-ul programului (se poate vizualiza cu "info make" după instalarea pachetului). Iată o listă cu cele mai utilizate argumente:

1 -C nume_director (înainte de a face orice altceva make schimbă directorul la cel specificat)
2 -f nume_fișier_makefile (poate apărea de mai multe ori pentru a crea în mod transparent un singur fișier Makefile din mai multe fișiere reale)
3 -k (continuă executarea scopurilor chiar dacă a apărut eroare la actualizarea unuia)
4 -i (se ignoră erorile produse la executarea comenzilor definite în cadrul regulilor; se consideră că un program nu s-a terminat cu succes în cazul în care codul de terminare a lui a fost diferit de 0)
5 -n (nu se execută nici o comandă, doar le afișează; util pentru a vedea ce va executa make în realitate)
6 -q (nu execută nimic, doar întoarce codul de terminare care ne arată starea în care se află targetul: este sau nu nevoie să-l actualizăm)
7 -r (dezactivează regulile implicite)
8 -R (dezactivează variabilele implicite)
9 -s (nu afișează comenzile pe care le execută sau orice alt mesaj, cu excepția celor de eroare)
10 -t (actualizează timpul ultimei modificări a fișierelor în loc să le actualizeze conținutul).

Structura unui fișier Makefile
Deoarece un model va fi mai simplu de înțeles decât o prezentare în cuvinte, vom da un exemplu pe care apoi îl vom analiza. Acesta este făcut special pentru a putea fi prezentate anumite caracteristici ale fișierelor Makefile, nefiind un model luat din lumea reală.

1. .PHONY all compile-all do-exe instal-all test clean
2. .SILENT clean test
3.
4. include"common.mak"
5.
6. VPATH = src man doc tests
7.
8. prefix = /usr/local
9. exec_prefix = $(prefix)
10. bindir = $(prefix)/bin
11. libdir = $(prefix)/lib
12. includedir = $(prefix)/include
13.
14. objects = foo.o bar.o
15. files := $(wildcard *_exe)
16. files += $(objects)
17.
18. all: compile-all install-all
19. compile-all: $(objects) mylib.a do-exe
20. $(objects): %.o : %c
21. $(CC) -c $(CFLAGS) $< -o $@
22. mylib.a: mylib.c mylib.h
23. @$(CC) -c $(CFLAGS) $^ -o $(patsubst %.c,%.o,$^)
24. ar -r $@ $(patsubst %.c,%.o,$^)
25. do-exe: $(filter %_exe,$(files))
26. +echo "Executie terminata !"
27. $(filter %_exe,$(files)): %_exe: %
28. ifneq(,$(findstring "/bin",$PATH))
29. $*.sh
30. else
31. $*.exe
32. endif
33.
34. instal-all: $(filter %_unix.exe,$(objects))
35. $(MAKE) -C $(addsuffix ".dir",$(dir "/usr/local/lib/mylib.so"))
36. $(shell echo "Instalarea s-a facut cu succes!")
37.
38. clean:
39. -rm -rf $(files)
40.
41. test: $(wildcard tests/*.c)
42. $(MAKE) -C test $(word $(words $<)-1,$<)
În linia 1, folosind directiva .PHONY, anunțăm programul make că scopurile all, compile-all, do-exe, install-all, test și clean nu sunt fișiere reale și deci scopurile și comenzile asociate lor trebuie executate de fiecare dată când sunt evaluate. În linia 2, folosind directiva .SILENT, se precizează că la executarea comenzilor asociate scopurilor clean și test programul make să nu afișeze comenzile pe care le execută. Implicit, make afișează comenzile pe care le execută și intrările/ ieșirile din directoare. În linia 4, folosind directiva include, se intercalează conținutul fișierului common.mak în cadrul fișierului Makefile curent. Fișierul common.mak trebuie să respecte structura unui fișier de tip Makefile. Programul make știe să caute automat un fișier aflat într-un scop atât în directorul curent cât și în directoarele specificate în variabila VPATH. În linia 6, indicăm programului make să caute fișierele și în directoarele ./src, .-/man, ./doc și ./tests dacă nu le găsește în directorul curent (./.). În liniile 8-12 definim 5 variabile, din care 4 depind de valoarea primeia. Este o practică comună să se definească o variabilă denumită prefix în care să se memoreze rădăcina directoarelor implicate în prelucrarea datelor. Astfel, dacă se dorește prelucrarea unor date aflate în locuri diferite dar care respectă aceeași structură de directoare și fișiere se poate apela make cu diferite valori ale variabilei prefix (valoarea variabilei este stabilită în cadrul shellului înainte de a apela programul make). În linia 14 este definită o variabilă denumită objects care primește numele a două fișiere. Folosirea semnului egal (=) pentru atribuire înseamnă că variabila primește exact valoarea din partea dreaptă a egalului. Orice expandare va avea loc la folosirea variabilei. În acest fel, este posibil să definim variabile care depind de valorile altor variabile, valori care se pot modifica de-a lungul execuției programului make și deci care vor modifica în mod dinamic și valoarea primelor variabile. În linia 15 definim o variabilă denumită files și care primește valoarea funcției $(wildcard *_exe). Această funcție generează o listă a tuturor fișierelor din directorul curent care respectă modelul dat, în cazul nostru *_exe. Apoi, în linia 16 îi adăugam valoarea $(objects) care va fi expandată la folosirea valorii variabilei files. În linia 18 definim primul scop din acest fișier Makefile. Numele lui este all și depinde de alte două scopuri denumite compile-all și install-all, dar nu are definită nici o comandă. În linia 19 definim scopul compile-all necesar scopului all. Scopul compile-all depinde de valoarea variabilei $(objects), care după expandare reprezintă timpul modificării fișierelor foo.o și bar.o, a timpului modifcării fișierului mylib.a și a rezultatului evaluării scopului do-exe. Când este evaluat un scop, întâi se evaluează dependențele lui (în cazul nostru fișierele foo.o, bar.o și mylib.a și rezultatul scopului do-exe) și apoi comenzile asociate lui (dacă este necesară actualizarea). În linia 20 definim un scop generic care spune că pentru fiecare cuvânt din $(objects) se aplică modelul %.o și apoi transformarea %.c (% înseamnă orice număr de caractere). Se consideră cuvânt orice succesiune de caractere diferite de spațiile albe (spațiu, tab, linie nouă etc.). Traducerea acestui scop generic este: fiecărui cuvânt din $(objects) i se schimbă extensia din .o în .c și se generează câte un scop pentru generarea fișierelor cu extensie .o din fișierele corespunzătoare cu extensia .c. În linia 21 avem comanda asociată scopului generic. Aceasta face apel la variabilele definite implicit de către programul make: $(CC) și $(CFLAGS). De asemenea, apar variabilele speciale $<, care semnifică lista tuturor dependențelor (în cazul nostru, fișierul cu extensia .c), și $@, care semnifică numele scopului (în cazul nostru fișierul cu extensia .o). În linia 22 este definit un scop care indică necesitatea actualizării fișierului mylib.a dacă fișierul mylib.c sau mylib.h este mai nou decât el. Linia 23, care este comanda asociată scopului mylib.a, începe cu caracterul @ ce indică programului make să nu afișeze comanda pe care o execută (cea care urmează după @), așa cum face în mod implicit dacă din linia de comandă lipsește argumentul -s sau acel scop apare în cadrul unui scop .SILENT. În linia 23 întâlnim variabila specială $^, care semnifică primul cuvânt din lista de dependențe (în cazul nostru mylib.c), și funcția $(patsubst %.c, %.o,$^), care în această formă semnifică înlocuirea extensiei .o a primului argument din lista de dependențe cu .c. În linia 24 valoarea variabilei speciale $@ este mylib.a. În linia 25 apare funcția $(filter %_exe,$(files)) care are ca efect, în acest context, extragerea din valoarea variabilei $(files) doar a cuvintelor care au sufixul _exe. Comanda din linia 26 apare cu un semn plus (+) înaintea ei, ceea ce semnifică faptul că această comandă va fi executată chiar dacă programul make a fost apelat cu unul din următoarele argumente: -n, -q sau -t. În linia 27 este definit un scop generic care are o comandă ce depinde de mediul în care este lansat programul make. Funcția $(findstring "/bin",$PATH), apelată astfel, întoarce șirul vid dacă nu există șirul "/bin" în valoarea variabilei de sistem $PATH și "/bin" altfel.

Astfel, dacă există șirul "/bin" în valoarea lui $PATH atunci se va executa ramura cu $*.sh, altfel se va executa ramura cu $*.exe. Se observă prezența variabilei speciale $* a cărei valoare este înlocuită cu partea din cuvânt care corespunde în model lui %. În cazul nostru, partea din numele fișierelor care se află înaintea sufixului _exe. În linia 35 este apelat programul make prin intermediul variabilei speciale $(MAKE) urmat de argumentul -C și numele directorului în care se află un fișier denumit în una din cele trei forme indicate la începutul acestui articol. Funcția specială $(dir "/usr/local/ lib/mylib.so") extrage partea de director din șirul furnizat ca argument, în cazul nostru "/usr/local/lib", iar funcția $(addsuffix ".dir","cale") adaugă sufixul ".dir" la șirul cale, în cazul nostru obținem "/usr/ local/lib.dir". Există și funcțiile: $(notdir "path/file"), care extrag din șir partea care corespunde numelui fișierului, $(basename "nume"), opusul funcției $(suffix), și $(addprefix prefix,șir), care adaugă un prefix în fața lui șir. În linia 36 este lansat un shell folosindu-se funcția $(shell comandă). Linia 38 conține un exemplu de scop fără dependințe. Comanda din linia 39 este precedată de semnul minus (-) pentru a indica programului make să continue execuția următoarelor comenzi asociate scopului curent chiar dacă această comandă se termină cu eroare
(de exemplu, dacă vrem să ștergem niște fișiere este mai simplu să punem semnul minus în fața comenzii care va șterge fișierele decât să testăm dacă acestea există pe disc). În linia 42 apar funcțiile $(words $<), care întorc numărul de cuvinte conținute
(în cazul nostru numărul de dependințe), și $(word poziție,$<), care întorc poziția cuvântului (indexul primului cuvânt este 1). În cazul nostru este extras penultimul nume de fișier care are extensia .c din directorul tests.

Pentru o listă completă de funcții puteți consulta nodul "Quick reference" din documentația în format .info a programului GNU make.


PC Magazine Ro | CD ROM | Redactia | Abonamente | CautareArhive

Copyright © 1999-2002 Agora Media.

[email protected]

LG - LifeŽs Good

www.agora.ro

deltafri

Concurs de Grafica Digitala si Web Design

www.agora.ro

www.agora.ro