Articles

GCC e Make

GCC (GNU Compiler Collection)

Breve storia e introduzione a GCC

Il compilatore GNU C originale (GCC) è sviluppato da Richard Stallman, il fondatore del progetto GNU. Richard Stallman ha fondato il progetto GNU nel 1984 per creare un sistema operativo completo simile a Unix come software libero, per promuovere la libertà e la cooperazione tra utenti e programmatori di computer.

GCC, precedentemente per “GNU C Compiler”, è cresciuto nel tempo per supportare molti linguaggi come C (gcc), C++ (g++), Objective-C, Objective-C++, Java (gcj), Fortran (gfortran), Ada (gnat), Go (gccgo), OpenMP, Cilk Plus, e OpenAcc. È ora indicato come “GNU Compiler Collection”. Il sito madre di GCC è http://gcc.gnu.org/. La versione attuale è GCC 7.3, rilasciata il 2018-01-25.

GCC è un componente chiave della cosiddetta “GNU Toolchain”, per sviluppare applicazioni e scrivere sistemi operativi. La GNU Toolchain include:

  1. GNU Compiler Collection (GCC): una suite di compilatori che supporta molti linguaggi, come C/C++ e Objective-C/C++.
  2. GNU Make: uno strumento di automazione per compilare e costruire applicazioni.
  3. GNU Binutils: una suite di strumenti di utilità binaria, inclusi linker e assemblatore.
  4. GNU Debugger (GDB).
  5. GNU Autotools: Un sistema di compilazione che include Autoconf, Autoheader, Automake e Libtool.
  6. GNU Bison: un generatore di parser (simile a lex e yacc).

GCC è portatile e funziona su molte piattaforme operative. GCC (e GNU Toolchain) è attualmente disponibile su tutti gli Unix. Sono anche portati su Windows (da Cygwin, MinGW e MinGW-W64). GCC è anche un compilatore incrociato, per produrre eseguibili su piattaforme diverse.

Versioni di GCC

Le varie versioni di GCC sono:

  • GCC versione 1 (1987): Versione iniziale che supporta il C.
  • GCC versione 2 (1992): supporta il C++.
  • GCC versione 3 (2001): incorpora ECGS (Experimental GNU Compiler System), con una migliore ottimizzazione.
  • GCC versione 4 (2005):
  • GCC versione 5 (2015):
  • GCC versione 6 (2016):
  • GCC versione 7 (2017):
Supporto standard C++

Ci sono vari standard C++:

  • C++98
  • C++11 (aka C++0x)
  • C++14 (aka C++1y)
  • C++17 (aka C++1z)
  • C++2a (prossimo standard previsto nel 2020)

La modalità predefinita è C++98 per le versioni GCC precedenti alla 6.1, e C++14 per GCC 6.1 e superiori. Puoi usare il flag della linea di comando -std per specificare esplicitamente lo standard C++. Per esempio,

  • -std=c++98, o -std=gnu++98 (C++98 con estensioni GNU)
  • -std=c++11, o -std=gnu++11 (C++11 con estensioni GNU)
  • -std=c++14, o -std=gnu++14 (C++14 con estensioni GNU), modalità predefinita per GCC 6.1 e superiori.
  • -std=c++17, o -std=gnu++17 (C++17 con estensioni GNU), sperimentale.
  • -std=c++2a, o -std=gnu++2a (C++2a con estensioni GNU), sperimentale.

Installazione di GCC su Unix

GNU Toolchain, incluso GCC, è incluso in tutti gli Unix. È il compilatore standard per la maggior parte dei sistemi operativi Unix-like.

Installazione di GCC su Mac OS X

Apri un Terminale, e inserisci “gcc --version“. Se gcc non è installato, il sistema vi chiederà di installare gcc.

$ gcc --version......Target: x86_64-apple-darwin14.5.0 // 64-bit target codesThread model: posix

Installare GCC su Windows

Per Windows, potete installare Cygwin GCC, MinGW GCC o MinGW-W64 GCC. Leggi “Come installare Cygwin e MinGW”.

  • Cygwin GCC: Cygwin è un ambiente Unix-like e un’interfaccia a riga di comando per Microsoft Windows. Cygwin è enorme e include la maggior parte degli strumenti e delle utilità Unix. Include anche la shell Bash comunemente usata.
  • MinGW: MinGW (Minimalist GNU for Windows) è un port della GNU Compiler Collection (GCC) e GNU Binutils per l’uso in Windows. Include anche MSYS (Minimal System), che è fondamentalmente una shell Bourne (bash).
  • MinGW-W64: un fork di MinGW che supporta sia 32-bit che 64-bit windows.
Varie GCC sotto Cygwin

Ci sono molte GCC sotto Cygain/MinGW. Per differenziare queste variazioni, devi capire quanto segue:

  • Windows/Intel usa questi set di istruzioni: x86 è un set di istruzioni a 32 bit; i868 è una versione migliorata a 32 bit di x86; x86_64 (o amd64) è un set di istruzioni a 64 bit.
  • I compilatori/programmi a 32-bit possono girare su Windows a 32-bit o 64-bit (retrocompatibile), ma i compilatori a 64-bit possono girare solo su Windows a 64-bit.
  • I compilatori a 64-bit possono produrre target di 32-bit o 64-bit.
  • Se usi GCC di Cygwin, il target potrebbe essere Windows nativo o Cygwin. Se il target è Windows nativo, il codice può essere distribuito ed eseguito sotto Windows. Tuttavia, se il target è Cygwin, per distribuire, è necessario distribuire l’ambiente di runtime Cygwin (cygwin1.dll). Questo perché Cygwin è un emulatore Unix sotto Windows.
MinGW-W64 Target 32/64-bit Native Windows

Il MinGW-W64 (un fork di MinGW, disponibile a http://mingw-w64.org/doku.php) supporta il target di Windows nativo sia a 32-bit che a 64-bit. Potete installare “MinGW-W64” sotto “Cygwin” selezionando questi pacchetti (sotto la categoria “devel”):

  • mingw64-x86_64-gcc-core: compilatore C a 64 bit per il target di Windows nativo a 64 bit. L’eseguibile è “x86_64-w64-mingw32-gcc“.
  • mingw64-x86_64-gcc-g++: compilatore C++ a 64 bit per target di Windows nativo a 64 bit. L’eseguibile è “x86_64-w64-mingw32-g++“.
  • mingw64-i686-gcc-core: compilatore C a 64 bit per il target di Windows nativo a 32 bit. L’eseguibile è “i686-w64-mingw32-gcc“.
  • mingw64-i686-gcc-g++: compilatore C++ a 64 bit per target di Windows nativo a 32 bit. L’eseguibile è “i686-w64-mingw32-g++“.

Note:

  • Suggerisco di installare “mingw64-x86_64-gcc-core” e “mingw64-x86_64-gcc-g++” per fornire codici nativi di Windows a 64 bit, ma saltate “mingw64-i686-gcc-core” e “mingw64-i686-gcc-g++“, a meno che non abbiate bisogno di produrre applicazioni Windows a 32 bit.
  • Per JNI (Java Native Interface) in Java a 64 bit, dovete usare “x86_64-w64-mingw32-gcc” o “x86_64-w64-mingw32-g++” per produrre codice Windows nativo a 64 bit.

Esegui gli eseguibili e controlla le versioni:

// Target 64-bit native Windows$ x86_64-w64-mingw32-gcc --version
x86_64-w64-mingw32-gcc (GCC) 6.4.0$ x86_64-w64-mingw32-gcc -v
Using built-in specs.
COLLECT_GCC=x86_64-w64-mingw32-gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-w64-mingw32/6.4.0/lto-wrapper.exe
Target: x86_64-w64-mingw32
Configured with: .....
Thread model: posix
gcc version 6.4.0 (GCC)$ x86_64-w64-mingw32-g++ --version
x86_64-w64-mingw32-g++ (GCC) 6.4.0// Target 32-bit native Windows$ i686-w64-mingw32-gcc --version
i686-w64-mingw32-gcc (GCC) 6.4.0
$ i686-w64-mingw32-g++ --version
i686-w64-mingw32-g++ (GCC) 6.4.0
Altri GCC in Cygwin

Altri pacchetti GCC in Cygwin sono:

  • gcc-core, gcc-g++: Compilatore C/C++ di base a 64 bit per Cygwin. Probabilmente dovreste installare anche questi due pacchetti. Tuttavia, per distribuire il codice prodotto, è necessario distribuire Cygwin Runtime Environment (cygwin1.dll). Questo perché Cygwin è un emulatore Unix sotto Windows.
  • cygwin32-gcc-core, cygwin32-gcc-g++: Vecchio compilatore C/C++ a 32-bit per Cygwin a 32-bit (Obsoleto da gcc-code e gcc-g++?).
  • mingw-gcc-core, mingw-gcc-g++: Vecchio compilatore MinGW 32-bit C/C++ per Windows 32-bit (Obsoleto dai pacchetti MinGW-W64?).

Post Installazione

Versioni

Si potrebbe visualizzare la versione di GCC tramite l’opzione --version:

$ gcc --versiongcc (GCC) 6.4.0$ g++ --version g++ (GCC) 6.4.0

Maggiori dettagli possono essere ottenuti tramite l’opzione -v, per esempio,

$ gcc -vUsing built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-pc-cygwin/6.4.0/lto-wrapper.exe
Target: x86_64-pc-cygwin
Configured with: ......
Thread model: posix
gcc version 6.4.0 (GCC)$ g++ -v
Using built-in specs.
COLLECT_GCC=g++
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-pc-cygwin/6.4.0/lto-wrapper.exe
Target: x86_64-pc-cygwin
Configured with: ......
Thread model: posix
gcc version 6.4.0 (GCC)
Help

È possibile ottenere il manuale di aiuto tramite l’opzione --help. Per esempio,

$ gcc --help
Pagine man

È possibile leggere le pagine di manuale di GCC (o pagine man) tramite l’utilità man:

$ man gcc// or$ man g++// Press space key for next page, or 'q' to quit.

Leggere le pagine man sotto CMD o la shell Bash può essere difficile. Si potrebbe generare un file di testo tramite:

$ man gcc | col -b > gcc.txt

L’utilità col è necessaria per rimuovere il backspace. (Per Cygwin, è disponibile in “Utils”, pacchetto “util-linux”.)

In alternativa, si potrebbe cercare una pagina man online, ad es, http://linux.die.net/man/1/gcc.

Le pagine man di GCC si trovano sotto “usr/share/man/man1“.

$ whereis gccgcc: /usr/bin/gcc.exe /usr/lib/gcc /usr/share/man/man1/gcc.1.gz

Inizio

I compilatori GNU C e C++ si chiamano gcc e g++, rispettivamente.

Compilare/Link un semplice programma C – hello.c

Di seguito è riportato il programma Hello-world C hello.c:

1234567
// hello.c#include <stdio.h> int main() { printf("Hello, world!\n"); return 0;}

Per compilare il hello.c:

> gcc hello.c // Compile and link source file hello.c into executable a.exe (Windows) or a (Unixes)

L’eseguibile di output predefinito si chiama “a.exe” (Windows) o “a.out” (Unix e Mac OS X).

Per eseguire il programma:

// (Windows) In CMD shell> a// (Unixes / Mac OS X) In Bash Shell - include the current path (./)$ chmod a+x a.out$ ./a.out

Note per Unix e Bash Shell:

  • Nella shell Bash, il PATH predefinito non include la directory di lavoro corrente. Quindi, è necessario includere il percorso corrente (./) nel comando. (Windows include la directory corrente nel PATH automaticamente; mentre Unix non lo fa – è necessario includere la directory corrente esplicitamente nel PATH, “./a.out“.
  • In Unix, il file di output potrebbe essere “a.out” o semplicemente “a“. Inoltre, è necessario assegnare il file-mode eseguibile (x) al file eseguibile “a.out“, tramite il comando “chmod a+x filename” (aggiungere il file-mode eseguibile “+x” a tutti gli utenti “a+x“).

Per specificare il nome del file di output, utilizzare l’opzione -o:

// (Windows) In CMD shell> gcc -o hello.exe hello.c // Compile and link source file hello.c into executable hello.exe> hello // Execute hello.exe under CMD shell// (Unixes / Mac OS X) In Bash shell$ gcc -o hello hello.c$ chmod a+x hello$ ./hello

NOTE per Unix:

  • In Unix, tipicamente omettiamo l’estensione .exe del file (intesa solo per Windows), e nominiamo semplicemente l’eseguibile in uscita come hello (tramite il comando “gcc -o hello hello.c“.
  • È necessario assegnare la modalità di file eseguibile tramite il comando “chmod a+x hello“.
Compilare/Link un semplice programma C++ – hello.cpp

12345678
// hello.cpp#include <iostream>using namespace std; int main() { cout << "Hello, world!" << endl; return 0;}

È necessario utilizzare g++ per compilare il programma C++, come segue. Usiamo l’opzione -o per specificare il nome del file di output.

// (Windows) In CMD shell> g++ -o hello.exe hello.cpp // Compile and link source hello.cpp into executable hello.exe> hello // Execute under CMD shell// (Unixes / Mac OS X) In Bash shell$ g++ -o hello hello.cpp$ chmod a+x hello$ ./hello
Altre opzioni del compilatore GCC

Alcune opzioni del compilatore GCC comunemente usate sono:

$ g++ -Wall -g -o Hello.exe Hello.cpp
  • -o: specifica il nome del file eseguibile in uscita.
  • -Wall: stampa “all” messaggi di avvertimento.
  • -g: genera ulteriori informazioni simboliche di debug per l’uso con gdb debugger.
Compilare e collegare separatamente

Il comando precedente compila il file sorgente in file oggetto e link con altri file oggetto e librerie di sistema in eseguibile in un unico passaggio. Potete separare la compilazione e il collegamento in due passi come segue:

// Compile-only with -c option> g++ -c -Wall -g Hello.cpp// Link object file(s) into an executable> g++ -g -o Hello.exe Hello.o

Le opzioni sono:

  • -c: Compila nel file oggetto “Hello.o“. Per default, il file oggetto ha lo stesso nome del file sorgente con estensione “.o” (non è necessario specificare l’opzione -o). Nessun collegamento con altri file oggetto o librerie.
  • Il collegamento viene eseguito quando i file di input sono file oggetto “.o” (invece di file sorgente “.cpp” o “.c“). GCC usa un programma linker separato (chiamato ld.exe) per eseguire il collegamento.
Compilare e collegare più file sorgente

Supponiamo che il tuo programma abbia due file sorgente: file1.cppfile2.cpp. Si potrebbero compilare tutti in un unico comando:

> g++ -o myprog.exe file1.cpp file2.cpp 

Tuttavia, di solito compiliamo ogni file sorgente separatamente in un file oggetto, e li colleghiamo insieme nella fase successiva. In questo caso, i cambiamenti in un file non richiedono la ricompilazione degli altri file.

> g++ -c file1.cpp> g++ -c file2.cpp> g++ -o myprog.exe file1.o file2.o
Compilare in una libreria condivisa

Per compilare e collegare un programma C/C++ in una libreria condivisa (".dll" in Windows, ".so" in Unix), usate l’opzione -shared. Leggete “Java Native Interface” per esempio.

Processo di compilazione di GCC

GCC compila un programma C/C++ in eseguibile in 4 passi come mostrato nel diagramma sopra. Per esempio, un “gcc -o hello.exe hello.c” viene eseguito come segue:

  1. Pre-elaborazione: tramite il preprocessore GNU C (cpp.exe), che include le intestazioni (#include) ed espande le macro (#define
    > cpp hello.c > hello.i

    Il file intermedio risultante “hello.i” contiene il codice sorgente espanso.

  2. Compilazione: Il compilatore compila il codice sorgente pre-elaborato in codice assembly per un processore specifico.
    > gcc -S hello.i

    L’opzione -S specifica di produrre codice assembly, invece di codice oggetto. Il file assembly risultante è “hello.s“.

  3. Assembly: L’assemblatore (as.exe) converte il codice assembly in codice macchina nel file oggetto “hello.o
    > as -o hello.o hello.s
  4. Linker: Infine, il linker (ld.exe) collega il codice oggetto con il codice della libreria per produrre un file eseguibile “hello.exe
    > ld -o hello.exe hello.o ...libraries...
Modalità Verbosa (-v)

Puoi vedere il processo di compilazione dettagliato abilitando l’opzione -v (verbose). Per esempio,

> gcc -v -o hello.exe hello.c
Definizione della macro (-D)

Puoi usare l’opzione -Dname per definire una macro, o -Dname=value per definire una macro con un valore. Il value dovrebbe essere racchiuso tra doppi apici se contiene spazi.

Headers (.h), Librerie Statiche (.lib, .a) e Librerie Condivise (.dll, .so)

Biblioteca Statica vs. Libreria Condivisa

Una libreria è una collezione di file oggetto precompilati che possono essere collegati nei vostri programmi tramite il linker. Esempi sono le funzioni di sistema come printf() e sqrt().

Ci sono due tipi di librerie esterne: libreria statica e libreria condivisa.

  1. Una libreria statica ha estensione di file “.a” (file di archivio) in Unix o “.lib” (libreria) in Windows. Quando il vostro programma è collegato a una libreria statica, il codice macchina delle funzioni esterne usate nel vostro programma viene copiato nell’eseguibile. Una libreria statica può essere creata tramite il programma di archivio “ar.exe“.
  2. Una libreria condivisa ha estensione di file “.so” (oggetti condivisi) in Unix o “.dll” (libreria a collegamento dinamico) in Windows. Quando il tuo programma è collegato a una libreria condivisa, solo una piccola tabella viene creata nell’eseguibile. Prima che l’eseguibile inizi a funzionare, il sistema operativo carica il codice macchina necessario per le funzioni esterne – un processo noto come collegamento dinamico. Il collegamento dinamico rende i file eseguibili più piccoli e risparmia spazio su disco, perché una copia di una libreria può essere condivisa tra più programmi. Inoltre, la maggior parte dei sistemi operativi permette che una copia di una libreria condivisa in memoria sia usata da tutti i programmi in esecuzione, risparmiando così memoria. I codici della libreria condivisa possono essere aggiornati senza la necessità di ricompilare il programma.

A causa del vantaggio del collegamento dinamico, GCC, per default, si collega alla libreria condivisa se è disponibile.

È possibile elencare il contenuto di una libreria tramite “nm filename“.

Ricerca di file di intestazione e librerie (-I, -L e -l)

Quando si compila il programma, il compilatore ha bisogno dei file di intestazione per compilare il codice sorgente; il linker ha bisogno delle librerie per risolvere i riferimenti esterni da altri file oggetto o librerie. Il compilatore e il linker non troveranno le intestazioni/librerie a meno che non si impostino le opzioni appropriate, il che non è ovvio per il primo utente.

Per ciascuna delle intestazioni usate nel tuo sorgente (tramite le direttive #include), il compilatore cerca i cosiddetti include-paths per queste intestazioni. Gli include-path sono specificati tramite l’opzione -Idir (o la variabile d’ambiente CPATH). Poiché il nome del file dell’intestazione è noto (ad esempio, iostream.hstdio.h), il compilatore ha bisogno solo delle directory.

Il linker cerca nei cosiddetti library-path le librerie necessarie per collegare il programma in un eseguibile. Il library-path è specificato tramite l’opzione -Ldir (maiuscolo 'L' seguito dal percorso della directory) (o variabile d’ambiente LIBRARY_PATH). Inoltre, devi anche specificare il nome della libreria. In Unix, la libreria libxxx.a è specificata tramite l’opzione -lxxx (lettera minuscola 'l', senza il prefisso “lib” e l’estensione ".a“). In Windows, fornite il nome completo come -lxxx.lib. Il linker ha bisogno di conoscere sia le directory che i nomi delle librerie. Quindi, è necessario specificare due opzioni.

Include-paths, Library-paths e Librerie predefinite

Prova a elencare gli include-paths predefiniti nel tuo sistema usati dal “GNU C Preprocessor” tramite “cpp -v“:

> cpp -v......#include "..." search starts here:#include <...> search starts here: /usr/lib/gcc/x86_64-pc-cygwin/6.4.0/include
/usr/include
/usr/lib/gcc/x86_64-pc-cygwin/6.4.0/../../../../lib/../include/w32api

Provare ad eseguire la compilazione in modalità verbosa (-v) per studiare i percorsi di libreria (-L) e le librerie (-l) utilizzate nel vostro sistema:

> gcc -v -o hello.exe hello.c......-L/usr/lib/gcc/x86_64-pc-cygwin/6.4.0-L/usr/x86_64-pc-cygwin/lib-L/usr/lib-L/lib-lgcc_s // libgcc_s.a-lgcc // libgcc.a-lcygwin // libcygwin.a-ladvapi32 // libadvapi32.a-lshell32 // libshell32.a-luser32 // libuser32.a-lkernel32 // libkernel32.a

Eclipse CDT: In Eclipse CDT, è possibile impostare i percorsi di inclusione, i percorsi di libreria e le librerie cliccando con il tasto destro del mouse sul progetto ⇒ Proprietà ⇒ C/C++ General ⇒ Paths and Symbols ⇒ Sotto le schede “Includes”, “Library Paths” e “Libraries”. Le impostazioni sono applicabili solo al progetto selezionato.

Variabili d’ambiente GCC

GCC usa le seguenti variabili d’ambiente:

  • PATH: Per la ricerca degli eseguibili e delle librerie condivise run-time (.dll.so).
  • CPATH: Per la ricerca degli include-paths per le intestazioni. Viene cercato dopo i percorsi specificati nelle opzioni -I<dir>C_INCLUDE_PATH e CPLUS_INCLUDE_PATH possono essere usati per specificare gli header C e C++ se il particolare linguaggio è stato indicato nel pre-processing.
  • LIBRARY_PATH: Per cercare i percorsi delle librerie per le librerie di collegamento. Viene cercato dopo i percorsi specificati nelle opzioni –L<dir>.

Utilità per esaminare i file compilati

Per tutte le utilità GNU, si può usare “command --help” per elencare il menu di aiuto; o “man command” per visualizzare le pagine man.

Utilità “file” – Determina il tipo di file

L’utilità “file” può essere usata per visualizzare il tipo di file oggetto e di file eseguibili. Per esempio,

$ gcc -c hello.c$ gcc -o hello.exe hello.o $ file hello.c
hello.c: C source, ASCII text, with CRLF line terminators$ file hello.ohello.o: data > file hello.exehello.exe: PE32 executable (console) x86-64, for MS Windows
Utility “nm” – Elenca la tabella dei simboli dei file oggetto

L’utility “nm” elenca la tabella dei simboli dei file oggetto. Per esempio,

$ nm hello.o0000000000000000 b .bss
0000000000000000 d .data
0000000000000000 p .pdata
0000000000000000 r .rdata
0000000000000000 r .rdata$zzz
0000000000000000 t .text
0000000000000000 r .xdata
U __main
0000000000000000 T main
U puts$ nm hello.exe | grep main
00000001004080cc I __imp___main
0000000100401120 T __main
00000001004010e0 T main
......

“nm” è comunemente usato per controllare se una particolare funzione è definita in un file oggetto. Un 'T' nella seconda colonna indica una funzione che è definita, mentre un 'U' indica una funzione che è indefinita e dovrebbe essere risolta dal linker.

Utilità “ldd” – Elenca le librerie a collegamento dinamico

L’utilità “ldd” esamina un eseguibile e mostra una lista delle librerie condivise di cui ha bisogno. Per esempio,

> ldd hello.exentdll.dll => /cygdrive/c/WINDOWS/SYSTEM32/ntdll.dll (0x7ff9ba3c0000)
KERNEL32.DLL => /cygdrive/c/WINDOWS/System32/KERNEL32.DLL (0x7ff9b9880000)
KERNELBASE.dll => /cygdrive/c/WINDOWS/System32/KERNELBASE.dll (0x7ff9b6a60000)
SYSFER.DLL => /cygdrive/c/WINDOWS/System32/SYSFER.DLL (0x6ec90000)
ADVAPI32.dll => /cygdrive/c/WINDOWS/System32/ADVAPI32.dll (0x7ff9b79a0000)
msvcrt.dll => /cygdrive/c/WINDOWS/System32/msvcrt.dll (0x7ff9b9100000)
sechost.dll => /cygdrive/c/WINDOWS/System32/sechost.dll (0x7ff9b9000000)
RPCRT4.dll => /cygdrive/c/WINDOWS/System32/RPCRT4.dll (0x7ff9b9700000)
cygwin1.dll => /usr/bin/cygwin1.dll (0x180040000)

GNU Make

L’utilità “make” automatizza gli aspetti mondani della costruzione di eseguibili dal codice sorgente. “make” utilizza un cosiddetto makefile, che contiene le regole su come costruire gli eseguibili.

È possibile emettere “make --help” per elencare le opzioni della riga di comando; o “man make” per visualizzare le pagine man.

Primo Makefile per esempio

Cominciamo con un semplice esempio per costruire il programma Hello-world (hello.c) in eseguibile (hello.exe) tramite l’utilità make.

1234567
// hello.c#include <stdio.h> int main() { printf("Hello, world!\n"); return 0;}

Crea il seguente file chiamato “makefile” (senza alcuna estensione), che contiene le regole per costruire l’eseguibile, e salvarlo nella stessa directory del file sorgente. Usa “tab” per indentare il comando (NON gli spazi).

all: hello.exehello.exe: hello.o gcc -o hello.exe hello.ohello.o: hello.c gcc -c hello.c clean: rm hello.o hello.exe

Eseguite l’utilità “make” come segue:

> makegcc -c hello.cgcc -o hello.exe hello.o

Eseguendo make senza argomento si avvia l’obiettivo “all” nel makefile. Un makefile consiste in un insieme di regole. Una regola consiste di 3 parti: un obiettivo, una lista di pre-requisiti e un comando, come segue:

target: pre-req-1 pre-req-2 ...command

L’obiettivo e i pre-requisiti sono separati da due punti (:). Il comando deve essere preceduto da una tabulazione (NON da spazi).

Quando si chiede a make di valutare una regola, si inizia trovando i file nei prerequisiti. Se uno dei prerequisiti ha una regola associata, tenta di aggiornare prima quelli.

Nell’esempio precedente, la regola “all” ha un prerequisito “hello.exemake non trova il file “hello.exe“, quindi cerca una regola per crearlo. La regola “hello.exe” ha un pre-requisito “hello.o“. Di nuovo, non esiste, quindi make cerca una regola per crearlo. La regola “hello.o” ha un pre-requisito “hello.cmake controlla che “hello.c” esista e che sia più nuovo del target (che non esiste). Esegue il comando “gcc -c hello.c“. La regola “hello.exe” esegue poi il suo comando “gcc -o hello.exe hello.o“. Infine, la regola “all” non fa nulla.

Più importante, se il pre-requisito non è più nuovo dell’obiettivo, il comando non verrà eseguito. In altre parole, il comando verrà eseguito solo se l’obiettivo è superato rispetto al suo pre-requisito. Per esempio, se rieseguiamo il comando make:

> makemake: Nothing to be done for `all'.

Puoi anche specificare il target da realizzare nel comando make. Per esempio, l’obiettivo “clean” rimuove il “hello.o” e “hello.exe“. Potete quindi eseguire il make senza obiettivo, che è lo stesso di “make all“.

> make cleanrm hello.o hello.exe > makegcc -c hello.cgcc -o hello.exe hello.o

Prova a modificare il “hello.c” ed esegui make.

NOTI:

  • Se il comando non è preceduto da una tab, si ottiene un messaggio di errore “makefile:4: *** separatore mancante. Stop.”
  • Se non c’è un makefile nella directory corrente, si ottiene un messaggio di errore “make: *** Nessun obiettivo specificato e nessun makefile trovato. Stop.”
  • Il makefile può essere chiamato “makefileMakefile” o “GNUMakefile“, senza estensione.

Altro su Makefile

Commento & Continuazione

Un commento inizia con un # e dura fino alla fine della linea. Una lunga linea può essere interrotta e continuata in più righe tramite un back-slash (\).

Sintassi delle regole

Una sintassi generale per le regole è:

target1 : 

Le regole sono solitamente organizzate in modo che le regole più generali vengano prima. La regola generale è spesso chiamata “all“, che è l’obiettivo predefinito per make.

Obiettivi fasulli (o obiettivi artificiali)

Un obiettivo che non rappresenta un file è chiamato obiettivo fasullo. Per esempio, il “clean” nell’esempio precedente, che è solo un’etichetta per un comando. Se l’obiettivo è un file, sarà controllato rispetto al suo pre-requisito per l’obsolescenza. Il target fasullo è sempre non aggiornato e il suo comando verrà eseguito. I target fasulli standard sono: allcleaninstall.

Variabili

Una variabile inizia con un $ ed è racchiusa tra parentesi (...) o parentesi graffe {...}. Le variabili a carattere singolo non hanno bisogno delle parentesi. Per esempio, $(CC)$(CC_FLAGS)$@$^. Ci sono:

  • $@: il nome del file di destinazione.
  • $*: il nome del file di destinazione senza estensione.
  • $<: il primo nome del file prerequisito.
  • $^: i nomi dei file di tutti i prerequisiti, separati da spazi, scarta i duplicati.
  • $+: simile a $^, ma include i duplicati.
  • $?: i nomi di tutti i prerequisiti che sono più recenti dell’obiettivo, separati da spazi.

Per esempio, possiamo riscrivere il makefile precedente come:

all: hello.exe # $@ matches the target; $< matches the first dependenthello.exe: hello.ogcc -o $@ $<hello.o: hello.cgcc -c $< clean:rm hello.o hello.exe
Virtual Path – VPATH & vpath

Si può usare VPATH (maiuscolo) per specificare la directory in cui cercare dipendenze e file di destinazione. Per esempio,

# Search for dependencies and targets from "src" and "include" directories# The directories are separated by spaceVPATH = src include

Puoi anche usare vpath (minuscolo) per essere più preciso sul tipo di file e la sua directory di ricerca. Per esempio,

# Search for .c files in "src" directory; .h files in "include" directory# The pattern matching character '%' matches filename without the extensionvpath %.c srcvpath %.h include
Regole di pattern

Una regola di pattern, che usa il carattere di pattern matching '%' come nome del file, può essere applicata per creare un target, se non c’è una regola esplicita. Per esempio,

# Applicable for create .o object file.# '%' matches filename.# $< is the first pre-requisite# $(COMPILE.c) consists of compiler name and compiler options# $(OUTPUT_OPTIONS) could be -o $@%.o: %.c$(COMPILE.c) $(OUTPUT_OPTION) $< # Applicable for create executable (without extension) from object .o object file# $^ matches all the pre-requisites (no duplicates)%: %.o$(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@
Regole di pattern implicite

Make è dotato di un enorme set di regole di pattern implicite. Puoi elencare tutte le regole tramite l’opzione --print-data-base.

Un Makefile di esempio

Questo makefile di esempio è estratto da “C/C++ Development Guide -Makefile” di Eclipse.

# A sample Makefile# This Makefile demonstrates and explains # Make Macros, Macro Expansions,# Rules, Targets, Dependencies, Commands, Goals# Artificial Targets, Pattern Rule, Dependency Rule.# Comments start with a # and go to the end of the line.# Here is a simple Make Macro.LINK_TARGET = test_me.exe# Here is a Make Macro that uses the backslash to extend to multiple lines.OBJS = \ Test1.o \ Test2.o \ Main.o# Here is a Make Macro defined by two Macro Expansions.# A Macro Expansion may be treated as a textual replacement of the Make Macro.# Macro Expansions are introduced with $ and enclosed in (parentheses).REBUILDABLES = $(OBJS) $(LINK_TARGET)# Here is a simple Rule (used for "cleaning" your build environment).# It has a Target named "clean" (left of the colon ":" on the first line),# no Dependencies (right of the colon),# and two Commands (indented by tabs on the lines that follow).# The space before the colon is not required but added here for clarity.clean : rm -f $(REBUILDABLES) echo Clean done# There are two standard Targets your Makefile should probably have:# "all" and "clean", because they are often command-line Goals.# Also, these are both typically Artificial Targets, because they don't typically# correspond to real files named "all" or "clean". # The rule for "all" is used to incrementally build your system.# It does this by expressing a dependency on the results of that system,# which in turn have their own rules and dependencies.all : $(LINK_TARGET) echo All done# There is no required order to the list of rules as they appear in the Makefile.# Make will build its own dependency tree and only execute each rule only once# its dependencies' rules have been executed successfully.# Here is a Rule that uses some built-in Make Macros in its command:# $@ expands to the rule's target, in this case "test_me.exe".# $^ expands to the rule's dependencies, in this case the three files# main.o, test1.o, and test2.o.$(LINK_TARGET) : $(OBJS) g++ -g -o $@ $^# Here is a Pattern Rule, often used for compile-line.# It says how to create a file with a .o suffix, given a file with a .cpp suffix.# The rule's command uses some built-in Make Macros:# $@ for the pattern-matched target# $< for the pattern-matched dependency%.o : %.cpp g++ -g -o $@ -c $<# These are Dependency Rules, which are rules without any command.# Dependency Rules indicate that if any file to the right of the colon changes,# the target to the left of the colon should be considered out-of-date.# The commands for making an out-of-date target up-to-date may be found elsewhere# (in this case, by the Pattern Rule above).# Dependency Rules are often used to capture header file dependencies.Main.o : Main.h Test1.h Test2.hTest1.o : Test1.h Test2.hTest2.o : Test2.h# Alternatively to manually capturing dependencies, several automated# dependency generators exist. Here is one possibility (commented out)...# %.dep : %.cpp# g++ -M $(FLAGS) $< > $@# include $(OBJS:.o=.dep)

Brief Summary

Ho presentato qui le caratteristiche di base di Make in modo che possiate leggere e capire semplici makefile per costruire applicazioni C/C++. Make è in realtà abbastanza complesso, e può essere considerato come un linguaggio di programmazione a sé stante!

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *