This shows you the differences between two versions of the page.
uso:laboratoare:new:04-appdev:demo [2018/10/25 17:07] vlad_mihai.corneci [Compilarea codului în C/C++] |
uso:laboratoare:new:04-appdev:demo [2019/10/24 09:59] (current) adrian.zatreanu [Demo] |
||
---|---|---|---|
Line 1: | Line 1: | ||
===== Demo ===== | ===== Demo ===== | ||
- | În această secțiune vom urmări să obținem o mai bună înțelegere a | + | În această secțiune vom urmări să obținem o mai bună înțelegere asupra |
- | procesului de compilare/build precum și a utilității și utilizării fișierelor Makefile. | + | procesului compilării precum și asupra 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 | Pentru această secțiune trebuie să vă asigurați că sunteți în directorul potrivit. Rulați comanda | ||
- | <code> | + | |
- | cd ~/uso.git/labs/04-appdev/support/ | + | <code bash> |
+ | student@uso:~$ cd ~/uso-lab/04-appdev/support/demo/ | ||
</code> | </code> | ||
Line 14: | Line 15: | ||
scrise în C este gcc, iar pentru C++, g++. | 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 | + | Să presupunem că avem următorul fișier sursă ''print.c''. |
- | gcc print.c, procesul de compilare trece prin toate cele patru faze intermediare | + | |
- | rezultând fișierul executabil a.out. | + | <code bash> |
+ | #include <stdio.h> | ||
+ | #include <stdlib.h> | ||
+ | |||
+ | int main(void) { | ||
+ | printf("USO Rules! <3\n"); | ||
+ | |||
+ | return 0; | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | Î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. | ||
<code bash> | <code bash> | ||
Line 24: | Line 37: | ||
student@uso:~$ ls | student@uso:~$ ls | ||
a.out print.c | a.out print.c | ||
+ | root@ebp:~$ | ||
</code> | </code> | ||
''a.out'' este denumirea default în situația în care nu este specificat care să fie | ''a.out'' este denumirea default în situația în care nu este specificat care să fie | ||
- | numele executabilului. Pentru aceasta folosim parametrul [-o outputFileName] | + | numele executabilului. Pentru aceasta folosim parametrul ''[-o output_filename]'' |
la rularea comenzii. Parametrul poate să se găsească la orice poziție în cadrul | 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. | comenzii, dar este obligatoriu să fie urmat întotdeauna de numele dorit. | ||
Line 36: | Line 50: | ||
a.out print print.c | a.out print print.c | ||
student@uso:~$ | student@uso:~$ | ||
+ | </code> | ||
+ | |||
+ | Observăm ca fișierul ''a.out'' este un fișier binar folosind comanda ''file''. | ||
+ | |||
+ | <code bash> | ||
+ | student@uso:~$ file a.out | ||
+ | a.out: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=c5ad78cfc1de12b9bb6829207cececb990b3e987, not stripped | ||
</code> | </code> | ||
Line 41: | Line 62: | ||
link-editare. La o rulare normală a comenzii gcc se trece prin toate aceste faze, | 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. | însă există și posibilitatea de a întrerupe procesul la finalul uneia anume. | ||
- | * folosirea parametrului -c spune compilatorului să se oprească după faza de | + | * folosirea parametrului -c spune compilatorului să se oprească după faza de asamblare și să nu linkeze codul. |
- | asamblare și să nu linkeze codul. | + | |
<code bash> | <code bash> | ||
Line 49: | Line 69: | ||
print.c print.o | print.c print.o | ||
student@uso:~$ cat print.o | student@uso:~$ cat print.o | ||
- | ELF>�@@ | + | ELF>�@@ |
- | UH��H�=��]�USO Rules! <3GCC: (Debian 8.2.0-7) 8.2.0zRx | + | UH��H�=��]�USO Rules! <3GCC: (Debian 8.2.0-7) 8.2.0zRx |
- | R �A�C | + | R �A�C |
- | �� $print.cmain_GLOBAL_OFFSET_TABLE_puts�������� | + | �� $print.cmain_GLOBAL_OFFSET_TABLE_puts�������� |
- | + | ||
- | �������� .symtab.strtab.shstrtab.rela.text.data.bss.rodata.comment.note.GNU-stack.rela.eh_frame @@0 | + | �������� .symtab.strtab.shstrtab.rela.text.data.bss.rodata.comment.note.GNU-stack.rela.eh_frame @@0 |
- | &WW1W90eB�W�R@@ | + | &WW1W90eB�W�R@@ |
- | � | + | � |
- | �)Xaroot@ebp:~/Documents/uso/lab_04/support# | + | �)Xaroot@ebp:~/Documents/uso/lab_04/support# |
</code> | </code> | ||
+ | |||
Se observă că rezultatul final este un fișier cu cod obiect. | Se observă că rezultatul final este un fișier cu cod obiect. | ||
* folosirea parametrului -S instruiește compilatorul să nu asambleze codul. | * folosirea parametrului -S instruiește compilatorul să nu asambleze codul. | ||
Line 97: | Line 118: | ||
* folosirea parametrului -E oprește procesul de compilare după faza de preprocesare. | * folosirea parametrului -E oprește procesul de compilare după faza de preprocesare. | ||
- | (HINT: man gcc) | + | <note> |
+ | Toate opțiunile utilitarului ''gcc'' pot fi regăsite în pagina de manual (''man gcc''). | ||
+ | </note> | ||
==== Makefile ==== | ==== Makefile ==== | ||
- | Utilitarul de automatizare cel mai folosit pentru aplicațiile C/C++ este make. | + | 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 | Î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 | 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 | pentru a alege unul dintre ele putem rula comanda make cu parametrul -f, urmat | ||
de numele makefile-ului pe care dorim să îl folosim. | de numele makefile-ului pe care dorim să îl folosim. | ||
- | Exemplu: make -f makefile.win | + | |
+ | <code> | ||
+ | student@uso:~$ ls | ||
+ | Makefile Makefile.win | ||
+ | student@uso:~$ make -f Makefile.win | ||
+ | (..) | ||
+ | </code> | ||
<note> | <note> | ||
Formatul unui fișier makefile este următorul: | Formatul unui fișier makefile este următorul: | ||
- | Regulă: dependințe\\ | + | Regulă: dependențe\\ |
<TAB> comandă | <TAB> comandă | ||
Line 117: | Line 146: | ||
</note> | </note> | ||
- | <code bash> | + | Avem fișierul sursă ''print.c''. Un exemplu de Makefile care autmatizeaza procesul de compilare |
- | student@uso:~$ cat Makefile | + | al acestuia este următorul: |
- | rule1: print.c | + | |
+ | <code> | ||
+ | build: print.c | ||
gcc -Wall print.c -o print | gcc -Wall print.c -o print | ||
clean: | clean: | ||
rm print | rm print | ||
+ | </code> | ||
+ | |||
+ | Acest fișier conține 2 reguli: una de **build** prin care se compilează fișierul ''print.c'' și una de ''clean'' | ||
+ | pe care o putem folosi să ștergem fișierele generate în urma compilării (cum ar fi fișierele executabile, obiect, etc.). | ||
+ | |||
+ | Pentru a rula regula numită ''build'', rulăm comanda ''make build''. | ||
+ | |||
+ | <code bash> | ||
+ | student@uso:~$ make build | ||
+ | gcc -Wall print.c -o print | ||
+ | student@uso:~$ ls | ||
+ | Makefile print print.c | ||
student@uso:~$ | student@uso:~$ | ||
+ | </code> | ||
+ | |||
+ | Pentru a șterge fișierul generat (''print''), rulăm comanda ''make clean''. | ||
+ | |||
+ | <code bash> | ||
+ | student@uso:~$ make clean | ||
+ | rm print | ||
+ | student@uso:~$ ls | ||
+ | Makefile print.c | ||
student@uso:~$ | student@uso:~$ | ||
+ | </code> | ||
+ | |||
+ | Dacă nu se precizează niciun argument comenzii ''make'', atunci aceasta va rula prima regulă întâlnită în fișier, adică regula ''build''. | ||
+ | |||
+ | <code bash> | ||
student@uso:~$ make | student@uso:~$ make | ||
gcc -Wall print.c -o print | gcc -Wall print.c -o print | ||
Line 133: | Line 190: | ||
</code> | </code> | ||
- | Regulă reprezintă numele unei instrucțiuni. La simpla rulare a comenzii make prima | + | <note> |
- | regulă din fișier este cea care va fi executată. În schimb, dacă vom scrie make raccoon, | + | Regulile din Makefile pot avea și ''dependențe''. Aceastea sunt opționale, însă sunt încurajate. |
- | se va executa comanda aferentă regulii raccoon. | + | 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 dependență există. | ||
+ | Dacă acesta există, putem rula regula, dacă nu, se caută o altă regulă cu acel nume și se va | ||
+ | rula acea regulă (dacă dependențele ei sunt îndeplinite, dacă nu, se continuă lanțul de dependențe). | ||
+ | </note> | ||
- | Dependințele pot să lipsească sau nu. Ele reprezintă de fapt fișiere și/sau reguli | + | Să considerăm următorul Makefile: |
- | 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: | + | |
<code bash> | <code bash> | ||
Line 164: | Line 220: | ||
</code> | </code> | ||
- | Prima regulă care va fi rulată este "build". Când se încearcă rularea ei se verifică | + | 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ă | 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 | 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 | + | 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 | + | au ca dependențe doar fișiere cod sursă (despre care știm deja că există), comenzile |
- | aferente lor generând codul obiect necesar rulării regulii "build". | + | 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ă | + | De reținut este și faptul că ''make'' verifică dacă o dependență a fost sau nu modificată |
după momentul în care a fost creată. În caz afirmativ aceasta va fi regenerată, | după momentul în care a fost creată. În caz afirmativ aceasta va fi regenerată, | ||
- | altfel, pentru eficientizare, aceasta va fi reutilizată. | + | altfel, pentru eficientizare, aceasta va fi reutilizată. |
+ | |||
+ | Spre exemplu, dacă avem fișierul Makefile de mai sus, am rulat deja o dată regula ''make build'' | ||
+ | și după modificăm fișierul ''utils.c'', atunci la următoarea rulare a regulii ''make build'' se | ||
+ | va rula mai întâi (automat) regula ''utils.o'' și abia dupa ''make build''. | ||