This is an old revision of the document!
În această secțiune vom urmări să obținem o mai bună înțelegere a procesului de compilare/build precum și a utilității și utilizării fișierelor Makefile.
Pentru această secțiune trebuie să vă asigurați că sunteți în directorul potrivit. Rulați comanda
cd ~/uso.git/labs/04-appdev/support/
După cum a fost precizat și înainte, compilatorul cel mai folosit pentru programele scrise în C este gcc, iar pentru C++, g++.
Să presupunem că avem următorul fișier sursă, print.c; în urma rulării comenzii gcc print.c, procesul de compilare trece prin toate cele patru faze intermediare rezultând fișierul executabil a.out.
student@uso:~$ ls print.c student@uso:~$ gcc print.c student@uso:~$ ls a.out print.c
a.out
este denumirea default în situația în care nu este specificat care să fie
numele executabilului. Pentru aceasta folosim parametrul [-o outputFileName]
la rularea comenzii. Parametrul poate să se găsească la orice poziție în cadrul
comenzii, dar este obligatoriu să fie urmat întotdeauna de numele dorit.
student@uso:~$ gcc print.c -o print student@uso:~$ ls a.out print print.c student@uso:~$
Procesul de compilare are patru etape: preprocesare, compilare, asamblare și link-editare. La o rulare normală a comenzii gcc se trece prin toate aceste faze, însă există și posibilitatea de a întrerupe procesul la finalul uneia anume.
asamblare și să nu linkeze codul.
student@uso:~$ gcc -c print.c student@uso:~$ ls print.c print.o student@uso:~$ cat print.o ELF>�@@ UH��H�=��]�USO Rules! <3GCC: (Debian 8.2.0-7) 8.2.0zRx R �A�C �� $print.cmain_GLOBAL_OFFSET_TABLE_puts�������� �������� .symtab.strtab.shstrtab.rela.text.data.bss.rodata.comment.note.GNU-stack.rela.eh_frame @@0 &WW1W90eB�W�R@@ � �)Xaroot@ebp:~/Documents/uso/lab_04/support#
Se observă că rezultatul final este un fișier cu cod obiect.
student@uso:~$ gcc -S print.c student@uso:~$ ls print.c print.s student@uso:~$ cat print.s .file "print.c" .text .section .rodata .LC0: .string "USO Rules! <3" .text .globl main .type main, @function main: .LFB5: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 leaq .LC0(%rip), %rdi call puts@PLT movl $0, %eax popq %rbp .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE5: .size main, .-main .ident "GCC: (Debian 8.2.0-7) 8.2.0" .section .note.GNU-stack,"",@progbits student@uso:~$
(HINT: man gcc)
Utilitarul de automatizare cel mai folosit pentru aplicațiile C/C++ este make. În general numele unui fișier makefile este Makefile, însă te poți afla în situația în care ai nevoie de mai multe astfel de fișiere, moment în care pentru a alege unul dintre ele putem rula comanda make cu parametrul -f, urmat de numele makefile-ului pe care dorim să îl folosim. Exemplu: make -f makefile.win
Regulă: dependențe
<TAB> comandă
Fișierul poate să conțină una sau mai multe astfel de linii.
student@uso:~$ cat Makefile rule1: print.c gcc -Wall print.c -o print clean: rm print student@uso:~$ student@uso:~$ student@uso:~$ make gcc -Wall print.c -o print student@uso:~$ ls Makefile print print.c student@uso:~$
Regulă reprezintă numele unei instrucțiuni. La simpla rulare a comenzii make prima regulă din fișier este cea care va fi executată. În schimb, dacă vom scrie make raccoon, se va executa comanda aferentă regulii raccoon.
Dependințele pot să lipsească sau nu. Ele reprezintă de fapt fișiere și/sau reguli necesare pentru a putea rula o altă regulă. Practic, la rularea unei reguli se verifică dacă fișierul din dependintă există. Dacă acesta există, putem rula regula, dacă nu, se caută o altă regulă cu acel nume și se va rula acea regulă (dacă dependințele ei sunt îndeplinite, dacă nu, se continuă lanțul de dependințe). Să considerăm următorul makefile:
build: utils.o hello.o help.o gcc utils.o help.o hello.o -o hello all: gcc simple_hello.c -o simple utils.o: utils.c gcc -c utils.c hello.o: hello.c gcc -c hello.c help.o: help.c gcc -c help.c clean: rm -f *.o hello
Prima regulă care va fi rulată este “build”. Când se încearcă rularea ei se verifică existența fișierelor obiect utils.o, hello.o și help.o. Cum ele nu au fost încă generate se caută în fișier o regulă cu numele lor. După cum se observă, aceste reguli se găsesc mai jos în makefile și vor fi executate deoarece ele au ca dependințe doar fișiere cod sursă (despre care știm deja că există), comenzile aferente lor generând codul obiect necesar rulării regulii “build”.
De reținut este și faptul că make verifică dacă o dependință a fost sau nu modificată după momentul în care a fost creată. În caz afirmativ aceasta va fi regenerată, altfel, pentru eficientizare, aceasta va fi reutilizată.