This is an old revision of the document!


Demo

Î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/

Compilarea codului în C/C++

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.

  • folosirea parametrului -c spune compilatorului să se oprească după faza de

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.

  • folosirea parametrului -S instruiește compilatorul să nu asambleze codul.
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:~$
  • folosirea parametrului -E oprește procesul de compilare după faza de preprocesare.

(HINT: man gcc)

Makefile

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

Formatul unui fișier makefile este următorul:

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ă.

uso/laboratoare/new/04-appdev/demo.1540744764.txt.gz · Last modified: 2018/10/28 18:39 by elena.stoican
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0