Articles

GCC und Make

GCC (GNU Compiler Collection)

Eine kurze Geschichte und Einführung in GCC

Der ursprüngliche GNU C Compiler (GCC) wurde von Richard Stallman, dem Gründer des GNU Projekts, entwickelt. Richard Stallman gründete das GNU-Projekt 1984 mit dem Ziel, ein komplettes Unix-ähnliches Betriebssystem als freie Software zu schaffen, um die Freiheit und Zusammenarbeit unter Computeranwendern und Programmierern zu fördern.

GCC, früher für „GNU C Compiler“, ist im Laufe der Zeit gewachsen und unterstützt heute viele Sprachen wie C (gcc), C++ (g++), Objective-C, Objective-C++, Java (gcj), Fortran (gfortran), Ada (gnat), Go (gccgo), OpenMP, Cilk Plus und OpenAcc. Sie wird nun als „GNU Compiler Collection“ bezeichnet. Die Mutterseite für GCC ist http://gcc.gnu.org/. Die aktuelle Version ist GCC 7.3, veröffentlicht am 25.01.2018.

GCC ist eine Schlüsselkomponente der sogenannten „GNU Toolchain“, um Anwendungen zu entwickeln und Betriebssysteme zu schreiben. Die GNU Toolchain umfasst:

  1. GNU Compiler Collection (GCC): eine Compiler-Suite, die viele Sprachen unterstützt, wie C/C++ und Objective-C/C++.
  2. GNU Make: ein Automatisierungswerkzeug zum Kompilieren und Erstellen von Anwendungen.
  3. GNU Binutils: eine Suite von Binärwerkzeugen, einschließlich Linker und Assembler.
  4. GNU Debugger (GDB).
  5. GNU Autotools: Ein Build-System mit Autoconf, Autoheader, Automake und Libtool.
  6. GNU Bison: ein Parser-Generator (ähnlich lex und yacc).

GCC ist portabel und läuft auf vielen Betriebssystemen. GCC (und GNU Toolchain) ist derzeit auf allen Unixen verfügbar. Sie sind auch auf Windows portiert (durch Cygwin, MinGW und MinGW-W64). GCC ist auch ein Cross-Compiler, um ausführbare Dateien auf verschiedenen Plattformen zu erzeugen.

GCC-Versionen

Die verschiedenen GCC-Versionen sind:

  • GCC Version 1 (1987): Erste Version, die C unterstützt.
  • GCC Version 2 (1992): unterstützt C++.
  • GCC Version 3 (2001): enthält ECGS (Experimental GNU Compiler System), mit verbesserter Optimierung.
  • GCC Version 4 (2005):
  • GCC Version 5 (2015):
  • GCC Version 6 (2016):
  • GCC Version 7 (2017):
C++ Standardunterstützung

Es gibt verschiedene C++ Standards:

  • C++98
  • C++11 (auch bekannt als C++0x)
  • C++14 (auch bekannt als C++1y)
  • C++17 (auch bekannt als C++1z)
  • C++2a (nächster geplanter Standard in 2020)

Der Standardmodus ist C++98 für GCC-Versionen vor 6.1, und C++14 für GCC 6.1 und höher. Sie können das Kommandozeilen-Flag -std verwenden, um den C++-Standard explizit anzugeben. Zum Beispiel,

  • -std=c++98, oder -std=gnu++98 (C++98 mit GNU Erweiterungen)
  • -std=c++11, oder -std=gnu++11 (C++11 mit GNU-Erweiterungen)
  • -std=c++14, oder -std=gnu++14 (C++14 mit GNU-Erweiterungen), Standardmodus für GCC 6.1 und höher.
  • -std=c++17, oder -std=gnu++17 (C++17 mit GNU-Erweiterungen), experimentell.
  • -std=c++2a, oder -std=gnu++2a (C++2a mit GNU-Erweiterungen), experimentell.

GCC auf Unixen installieren

Die GNU Toolchain, einschließlich GCC, ist in allen Unixen enthalten. Es ist der Standard-Compiler für die meisten Unix-ähnlichen Betriebssysteme.

GCC unter Mac OS X installieren

Öffnen Sie ein Terminal und geben Sie „gcc --version“ ein. Wenn gcc nicht installiert ist, werden Sie vom System aufgefordert, gcc zu installieren.

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

GCC unter Windows installieren

Für Windows können Sie entweder Cygwin GCC, MinGW GCC oder MinGW-W64 GCC installieren. Lesen Sie „Wie man Cygwin und MinGW installiert“.

  • Cygwin GCC: Cygwin ist eine Unix-ähnliche Umgebung und Befehlszeilenschnittstelle für Microsoft Windows. Cygwin ist sehr umfangreich und enthält die meisten Unix-Tools und -Utilities. Es enthält auch die weit verbreitete Bash-Shell.
  • MinGW: MinGW (Minimalist GNU for Windows) ist eine Portierung der GNU Compiler Collection (GCC) und der GNU Binutils für die Verwendung unter Windows. Es enthält auch MSYS (Minimal System), das im Grunde eine Bourne-Shell ist (bash).
  • MinGW-W64: ein Fork von MinGW, der sowohl 32-Bit- als auch 64-Bit-Windows unterstützt.
Verschiedene GCCs unter Cygwin

Es gibt viele GCCs unter Cygain/MinGW. Um diese Varianten zu unterscheiden, müssen Sie Folgendes verstehen:

  • Windows/Intel verwendet diese Befehlssätze: x86 ist ein 32-Bit-Befehlssatz; i868 ist eine 32-Bit-erweiterte Version von x86; x86_64 (oder amd64) ist ein 64-Bit-Befehlssatz.
  • 32-Bit-Compiler/Programme können auf 32-Bit- oder 64-Bit-Windows (abwärtskompatibel) laufen, aber 64-Bit-Compiler können nur auf 64-Bit-Windows laufen.
  • 64-Bit-Compiler können ein 32-Bit- oder ein 64-Bit-Ziel erzeugen.
  • Wenn Sie den GCC von Cygwin verwenden, kann das Ziel das native Windows oder Cygwin sein. Wenn das Ziel natives Windows ist, kann der Code verteilt und unter Windows ausgeführt werden. Wenn das Ziel jedoch Cygwin ist, müssen Sie zum Verteilen die Cygwin-Laufzeitumgebung (cygwin1.dll) verwenden. Das liegt daran, dass Cygwin ein Unix-Emulator unter Windows ist.
MinGW-W64 Target 32/64-bit Native Windows

Das „MinGW-W64“ (ein Fork von „MinGW“, verfügbar unter http://mingw-w64.org/doku.php) unterstützt das Target von sowohl 32-bit als auch 64-bit Native Windows. Sie können „MinGW-W64“ unter „Cygwin“ installieren, indem Sie diese Pakete (unter der Kategorie „devel“) auswählen:

  • mingw64-x86_64-gcc-core: 64-Bit-C-Compiler für das Ziel von nativen 64-Bit-Windows. Die ausführbare Datei ist „x86_64-w64-mingw32-gcc„.
  • mingw64-x86_64-gcc-g++: 64-Bit C++ Compiler für das Ziel von nativen 64-Bit Windows. Die ausführbare Datei ist „x86_64-w64-mingw32-g++„.
  • mingw64-i686-gcc-core: 64-Bit C-Compiler für das Ziel von nativen 32-Bit Windows. Die ausführbare Datei ist „i686-w64-mingw32-gcc„.
  • mingw64-i686-gcc-g++: 64-Bit C++ Compiler für das Ziel von nativen 32-Bit Windows. Die ausführbare Datei ist „i686-w64-mingw32-g++„.

Hinweise:

  • Ich empfehle die Installation von „mingw64-x86_64-gcc-core“ und „mingw64-x86_64-gcc-g++„, um native 64-Bit-Windows-Codes bereitzustellen, aber überspringen Sie „mingw64-i686-gcc-core“ und „mingw64-i686-gcc-g++„, es sei denn, Sie müssen 32-Bit-Windows-Anwendungen erstellen.
  • Für JNI (Java Native Interface) in 64-Bit-Java müssen Sie „x86_64-w64-mingw32-gcc“ oder „x86_64-w64-mingw32-g++“ verwenden, um 64-Bit-nativen Windows-Code zu erzeugen.

Starten Sie die ausführbaren Dateien und überprüfen Sie die Versionen:

// 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
Weitere GCCs in Cygwin

Weitere GCC-Pakete in Cygwin sind:

  • gcc-core, gcc-g++: Basic 64-Bit C/C++ Compiler mit Ziel 64-Bit Cygwin. Diese beiden Pakete sollten Sie wahrscheinlich auch installieren. Um den erzeugten Code zu verteilen, müssen Sie jedoch die Cygwin-Laufzeitumgebung (cygwin1.dll) verteilen. Das liegt daran, dass Cygwin ein Unix-Emulator unter Windows ist.
  • cygwin32-gcc-core, cygwin32-gcc-g++: Älterer 32-Bit-C/C++-Compiler für das 32-Bit-Ziel Cygwin (Obsoletiert durch gcc-code und gcc-g++?).
  • mingw-gcc-core, mingw-gcc-g++: Älterer MinGW 32-Bit C/C++ Compiler für 32-Bit Windows (Obsoletiert durch MinGW-W64 Pakete?).

Nach der Installation

Versionen

Mit der Option --version können Sie die Version des GCC anzeigen:

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

Weitere Details lassen sich zum Beispiel über die Option -v abrufen,

$ 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)
Hilfe

Über die Option --help können Sie das Hilfehandbuch abrufen. Zum Beispiel:

$ gcc --help
Man Pages

Sie können die GCC-Manual-Seiten (oder man pages) über das man-Dienstprogramm lesen:

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

Das Lesen der man pages unter CMD oder der Bash-Shell kann schwierig sein. Sie könnten eine Textdatei erzeugen über:

$ man gcc | col -b > gcc.txt

Das col Dienstprogramm wird benötigt, um die Rücktaste zu entfernen. (Für Cygwin ist es in „Utils“, Paket „util-linux“ verfügbar.)

Alternativ könnten Sie nach einer Online-Manpage suchen, z. B., http://linux.die.net/man/1/gcc.

Die GCC-Manpages sind unter „usr/share/man/man1“ abgelegt.

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

Einstieg

Der GNU C- und C++-Compiler heißen gcc bzw. g++.

Ein einfaches C-Programm kompilieren/verknüpfen – hallo.c

Nachfolgend sehen Sie das Hello-world-C-Programm hello.c:

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

Zum Kompilieren des hello.c:

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

Die Standardausgabe der ausführbaren Datei heißt „a.exe“ (Windows) oder „a.out“ (Unixe und Mac OS X).

Um das Programm auszuführen:

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

Hinweise für Unixe und Bash-Shell:

  • In der Bash-Shell enthält der Standard-PATH nicht das aktuelle Arbeitsverzeichnis. Daher müssen Sie den aktuellen Pfad (./) in den Befehl aufnehmen. (Windows nimmt das aktuelle Verzeichnis automatisch in den PATH auf, während Unixe dies nicht tun – Sie müssen das aktuelle Verzeichnis explizit in den PATH aufnehmen)
  • Sie müssen auch die Dateierweiterung aufnehmen, falls vorhanden, d.h., „./a.out„.
  • Unter Unixen könnte die Ausgabedatei „a.out“ oder einfach „a“ lauten. Außerdem müssen Sie der ausführbaren Datei „a.out“ per Befehl „chmod a+x filename“ den ausführbaren Dateimodus (x) zuweisen (fügen Sie den ausführbaren Dateimodus „+x“ für alle Benutzer „a+x“ hinzu).

Um den Ausgabedateinamen anzugeben, verwenden Sie die Option -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

Hinweis für Unixe:

  • Bei Unixen lassen wir typischerweise die .exe-Dateierweiterung weg (nur für Windows gedacht) und benennen das ausgegebene Executable einfach als hello (per Befehl „gcc -o hello hello.c„.
  • Sie müssen den Modus der ausführbaren Datei über den Befehl „chmod a+x hello“ zuweisen.

Kompilieren/Linken eines einfachen C++-Programms – hello.cpp
12345678
// hello.cpp#include <iostream>using namespace std; int main() { cout << "Hello, world!" << endl; return 0;}

Sie müssen g++ verwenden, um das C++-Programm zu kompilieren, wie folgt. Wir verwenden die Option -o, um den Namen der Ausgabedatei anzugeben.

// (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
Weitere GCC-Compiler-Optionen

Ein paar häufig verwendete GCC-Compiler-Optionen sind:

$ g++ -Wall -g -o Hello.exe Hello.cpp
  • -o: legt den Namen der ausführbaren Ausgabedatei fest.
  • -Wall: gibt „all“ aus. Warnmeldungen.
  • -g: erzeugt zusätzliche symbolische Debugging-Informationen zur Verwendung mit dem gdb Debugger.
Trennend kompilieren und linken

Der obige Befehl kompiliert die Quelldatei in eine Objektdatei und link mit anderen Objektdateien und Systembibliotheken in einem Schritt in eine ausführbare Datei. Sie können das Kompilieren und Linken in zwei Schritten wie folgt trennen:

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

Die Optionen sind:

  • -c: Kompilieren in die Objektdatei „Hello.o„. Standardmäßig hat die Objektdatei den gleichen Namen wie die Quelldatei mit der Erweiterung „.o“ (die Angabe der Option „-o“ ist nicht erforderlich). Kein Linken mit anderen Objektdateien oder Bibliotheken.
  • Das Linken wird durchgeführt, wenn die Eingabedatei Objektdateien „.o“ sind (statt der Quelldatei „.cpp“ oder „.c„). GCC verwendet ein separates Linker-Programm (namens ld.exe), um das Linken durchzuführen.
Mehrere Quelldateien kompilieren und linken

Angenommen, Ihr Programm hat zwei Quelldateien: file1.cppfile2.cpp. Sie könnten alle in einem einzigen Befehl kompilieren:

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

Jedoch kompilieren wir normalerweise jede der Quelldateien separat in eine Objektdatei und binden sie später zusammen. In diesem Fall erfordern Änderungen in einer Datei keine Neukompilierung der anderen Dateien.

> g++ -c file1.cpp> g++ -c file2.cpp> g++ -o myprog.exe file1.o file2.o
In eine Shared Library kompilieren

Um ein C/C++-Programm in eine Shared Library zu kompilieren und zu linken (".dll" unter Windows, ".so" in Unixen), verwenden Sie die Option -shared. Lesen Sie zum Beispiel „Java Native Interface“.

GCC Kompilierungsprozess

GCC kompiliert ein C/C++ Programm in 4 Schritten zu einer ausführbaren Datei, wie im obigen Diagramm dargestellt. Ein „gcc -o hello.exe hello.c“ wird zum Beispiel wie folgt ausgeführt:

  1. Vorverarbeitung: Über den GNU C Preprocessor (cpp.exe), der die Header (#include) einschließt und die Makros (#define) expandiert.
    > cpp hello.c > hello.i

    Die resultierende Zwischendatei „hello.i“ enthält den erweiterten Quellcode.

  2. Compilation: Der Compiler kompiliert den vorverarbeiteten Quellcode in Assemblercode für einen bestimmten Prozessor.
    > gcc -S hello.i

    Die Option -S legt fest, dass Assemblercode anstelle von Objektcode erzeugt werden soll. Die resultierende Assembler-Datei ist „hello.s„.

  3. Assembler: Der Assembler (as.exe) wandelt den Assembler-Code in Maschinencode in der Objektdatei „hello.o
    > as -o hello.o hello.s
  4. Linker: Schließlich verknüpft der Linker (ld.exe) den Objektcode mit dem Bibliothekscode, um eine ausführbare Datei „hello.exe“ zu erzeugen.
    > ld -o hello.exe hello.o ...libraries...
Verbose-Modus (-v)

Sie können den detaillierten Kompilierungsprozess sehen, indem Sie die Option -v (verbose) aktivieren. Zum Beispiel,

> gcc -v -o hello.exe hello.c
Makro definieren (-D)

Sie können die Option -Dname verwenden, um ein Makro zu definieren, oder -Dname=value um ein Makro mit einem Wert zu definieren. Das value sollte in Anführungszeichen gesetzt werden, wenn es Leerzeichen enthält.

Header (.h), Statische Bibliotheken (.lib, .a) und Shared Library (.dll, .so)

Statische Bibliothek vs. Shared Library

Eine Bibliothek ist eine Sammlung von vorkompilierten Objektdateien, die über den Linker in Ihre Programme eingebunden werden können. Beispiele sind die Systemfunktionen wie printf() und sqrt().

Es gibt zwei Arten von externen Bibliotheken: statische Bibliothek und Shared Library.

  1. Eine statische Bibliothek hat die Dateiendung „.a“ (Archivdatei) in Unixen oder „.lib“ (Bibliothek) unter Windows. Wenn Ihr Programm gegen eine statische Bibliothek gelinkt wird, wird der Maschinencode von externen Funktionen, die in Ihrem Programm verwendet werden, in die ausführbare Datei kopiert. Eine statische Bibliothek kann über das Archivprogramm „ar.exe“ erstellt werden.
  2. Eine Shared Library hat die Dateiendung „.so“ (Shared Objects) in Unixen oder „.dll“ (dynamic link library) unter Windows. Wenn Ihr Programm gegen eine Shared Library gelinkt wird, wird nur eine kleine Tabelle in der ausführbaren Datei erstellt. Bevor die ausführbare Datei ausgeführt wird, lädt das Betriebssystem den Maschinencode, der für die externen Funktionen benötigt wird – ein Vorgang, der als dynamisches Linken bezeichnet wird. Dynamisches Linken macht ausführbare Dateien kleiner und spart Speicherplatz, da eine Kopie einer Bibliothek von mehreren Programmen gemeinsam genutzt werden kann. Außerdem erlauben die meisten Betriebssysteme, dass eine Kopie einer gemeinsam genutzten Bibliothek im Speicher von allen laufenden Programmen verwendet wird, wodurch Speicherplatz gespart wird. Die Codes der gemeinsam genutzten Bibliotheken können aktualisiert werden, ohne dass Sie Ihr Programm neu kompilieren müssen.

Aufgrund des Vorteils des dynamischen Linkens verlinkt der GCC standardmäßig auf die gemeinsam genutzte Bibliothek, wenn diese verfügbar ist.

Sie können den Inhalt einer Bibliothek über „nm filename“ auflisten.

Suchen nach Header-Dateien und Bibliotheken (-I, -L und -l)

Beim Kompilieren des Programms benötigt der Compiler die Header-Dateien, um die Quelltexte zu kompilieren; der Linker benötigt die Bibliotheken, um externe Referenzen aus anderen Objektdateien oder Bibliotheken aufzulösen. Der Compiler und der Linker finden die Header/Bibliotheken nur, wenn Sie die entsprechenden Optionen setzen, was für Erstanwender nicht offensichtlich ist.

Für jeden der in Ihrem Quelltext verwendeten Header (über #include-Direktiven) sucht der Compiler in den sogenannten Include-Pfaden nach diesen Headern. Die Include-Pfade werden über die Option -Idir (oder die Umgebungsvariable CPATH) angegeben. Da der Dateiname des Headers bekannt ist (z.B. iostream.hstdio.h), benötigt der Compiler nur die Verzeichnisse.

Der Linker sucht in den sogenannten Library-Pfaden nach Bibliotheken, die er benötigt, um das Programm zu einer ausführbaren Datei zu linken. Der Library-Pfad wird über die Option -Ldir (Großbuchstabe 'L' gefolgt von dem Verzeichnispfad) angegeben (oder über die Umgebungsvariable LIBRARY_PATH). Zusätzlich müssen Sie noch den Bibliotheksnamen angeben. Unter Unixen wird die Bibliothek libxxx.a über die Option -lxxx angegeben (Kleinbuchstabe 'l', ohne das Präfix „lib“ und die Erweiterung ".a„). Unter Windows geben Sie den vollständigen Namen an, z. B. -lxxx.lib. Der Linker muss sowohl die Verzeichnisse als auch die Bibliotheksnamen kennen. Daher müssen zwei Optionen angegeben werden.

Standard-Include-Pfade, Bibliotheks-Pfade und Bibliotheken

Versuchen Sie, die Standard-Include-Pfade in Ihrem System aufzulisten, die der „GNU C Preprocessor“ über „cpp -v“ verwendet:

> 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

Versuchen Sie, die Kompilierung im Verbose-Modus (-v) auszuführen, um die in Ihrem System verwendeten Bibliotheks-Pfade (-L) und Bibliotheken (-l) zu untersuchen:

> 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 können Sie mit Rechtsklick auf das Projekt ⇒ Eigenschaften ⇒ C/C++ Allgemein ⇒ Pfade und Symbole ⇒ unter den Reitern „Includes“, „Bibliothekspfade“ und „Bibliotheken“ die Include-Pfade, Bibliothekspfade und Bibliotheken einstellen. Die Einstellungen gelten nur für das ausgewählte Projekt.

GCC Umgebungsvariablen

GCC verwendet die folgenden Umgebungsvariablen:

  • PATH: Zum Durchsuchen der ausführbaren und zur Laufzeit freigegebenen Bibliotheken (.dll.so).
  • CPATH: Zum Durchsuchen der Include-Pfade für Header. Es wird nach Pfaden gesucht, die in -I<dir> Optionen angegeben sind. C_INCLUDE_PATH und CPLUS_INCLUDE_PATH können verwendet werden, um C- und C++-Header anzugeben, wenn die jeweilige Sprache bei der Vorverarbeitung angegeben wurde.
  • LIBRARY_PATH: Für die Suche nach Bibliotheks-Pfaden für Link-Bibliotheken. Es wird nach Pfaden gesucht, die in –L<dir> Optionen angegeben sind.

Hilfsmittel zum Untersuchen der kompilierten Dateien

Für alle GNU-Hilfsmittel können Sie „command --help“ verwenden, um das Hilfemenü aufzulisten; oder „man command„, um die Manpages anzuzeigen.

Dienstprogramm „file“ – Dateityp bestimmen

Mit dem Dienstprogramm „file“ können Sie den Typ von Objektdateien und ausführbaren Dateien anzeigen. Zum Beispiel,

$ 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
„nm“ Utility – Symboltabelle von Objektdateien auflisten

Das Utility „nm“ listet die Symboltabelle von Objektdateien auf. Zum Beispiel,

$ 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“ wird häufig verwendet, um zu prüfen, ob eine bestimmte Funktion in einer Objektdatei definiert ist. Ein 'T' in der zweiten Spalte zeigt eine Funktion an, die definiert ist, während ein 'U' eine Funktion anzeigt, die undefiniert ist und vom Linker aufgelöst werden sollte.

„ldd“ Utility – List Dynamic-Link Libraries

Das Utility „ldd“ untersucht ein Executable und zeigt eine Liste der benötigten Shared Libraries an. Zum Beispiel,

> 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

Das Dienstprogramm „make“ automatisiert die alltäglichen Aspekte des Erstellens einer ausführbaren Datei aus dem Quellcode. „make“ verwendet ein sogenanntes makefile, das Regeln enthält, wie die ausführbaren Dateien zu bauen sind.

Sie können „make --help“ ausgeben, um die Kommandozeilenoptionen aufzulisten; oder „man make„, um die Man Pages anzuzeigen.

Erstes Makefile am Beispiel

Lassen Sie uns mit einem einfachen Beispiel beginnen, um das Hello-world-Programm (hello.c) mittels des Make-Utilities in eine ausführbare Datei (hello.exe) zu verwandeln.

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

Erstellen Sie die folgende Datei mit dem Namen „makefile“ (ohne Dateiendung), die Regeln zum Erstellen der ausführbaren Datei enthält, und speichern Sie sie im gleichen Verzeichnis wie die Quelldatei. Verwenden Sie „tab“, um den Befehl einzurücken (KEINE Leerzeichen).

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

Starten Sie das „make“ Dienstprogramm wie folgt:

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

Das Ausführen von „make“ ohne Argument startet das Ziel „all“ im makefile. Ein Makefile besteht aus einem Satz von Regeln. Eine Regel besteht aus 3 Teilen: einem Ziel, einer Liste von Voraussetzungen und einem Befehl, wie folgt:

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

Das Ziel und die Voraussetzungen werden durch einen Doppelpunkt getrennt (:). Dem Befehl muss ein Tabulator vorangestellt werden (KEINE Leerzeichen).

Wenn make aufgefordert wird, eine Regel auszuwerten, beginnt es damit, die Dateien in den Voraussetzungen zu finden. Wenn eine der Voraussetzungen eine zugehörige Regel hat, wird versucht, diese zuerst zu aktualisieren.

Im obigen Beispiel hat die Regel „all“ eine Voraussetzung „hello.exemake kann die Datei „hello.exe“ nicht finden und sucht deshalb nach einer Regel, die sie erstellt. Die Regel „hello.exe“ hat eine Vorbedingung „hello.o„. Diese existiert wiederum nicht, also sucht make nach einer Regel, um sie zu erstellen. Die Regel „hello.o“ hat eine Vorbedingung „hello.cmake überprüft, ob „hello.c“ existiert und neuer ist als das Ziel (das nicht existiert). Es führt den Befehl „gcc -c hello.c“ aus. Die Regel „hello.exe“ führt dann ihren Befehl „gcc -o hello.exe hello.o“ aus. Die Regel „all“ schließlich tut nichts.

Wichtiger ist, dass der Befehl nicht ausgeführt wird, wenn die Voraussetzung nicht neuer ist als das Ziel. Mit anderen Worten, der Befehl wird nur ausgeführt, wenn das Ziel im Vergleich zu seiner Voraussetzung veraltet ist. Wenn wir zum Beispiel den make-Befehl erneut ausführen:

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

Sie können das zu erstellende Ziel auch im Befehl make angeben. Zum Beispiel entfernt das Ziel „clean“ die „hello.o“ und „hello.exe„. Sie können dann das make ohne Ziel ausführen, was dem „make all“ entspricht.

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

Versuchen Sie, das „hello.c“ zu ändern und make auszuführen.

HINWEISE:

  • Wenn dem Befehl kein Tabulator vorangestellt wird, erhalten Sie die Fehlermeldung „makefile:4: *** fehlendes Trennzeichen. Stop.“
  • Wenn im aktuellen Verzeichnis kein makefile vorhanden ist, erhalten Sie die Fehlermeldung „make: *** No targets specified and no makefile found. Stop.“
  • Das Makefile kann „makefileMakefile“ oder „GNUMakefile“ heißen, ohne Dateiendung.

Mehr zu Makefile

Kommentar & Fortsetzung

Ein Kommentar beginnt mit einem # und dauert bis zum Ende der Zeile. Eine lange Zeile kann durch einen Backslash (\) unterbrochen und in mehreren Zeilen fortgesetzt werden.

Syntax der Regeln

Eine allgemeine Syntax für die Regeln ist:

target1 : 

Die Regeln sind normalerweise so organisiert, dass die allgemeineren Regeln zuerst kommen. Die übergeordnete Regel heißt oft „all„, was das Standardziel für make ist.

Phony Targets (oder künstliche Ziele)

Ein Ziel, das keine Datei darstellt, wird als Phony Target bezeichnet. Zum Beispiel das „clean“ im obigen Beispiel, das nur eine Bezeichnung für einen Befehl ist. Wenn es sich bei dem Ziel um eine Datei handelt, wird diese auf ihre Veraltetheit geprüft. Ein falsches Ziel ist immer veraltet und sein Befehl wird ausgeführt. Die Standard-Phony-Targets sind: allcleaninstall.

Variablen

Eine Variable beginnt mit einem $ und wird in Klammern (...) oder geschweiften Klammern {...} eingeschlossen. Einzeichen-Variablen benötigen die Klammern nicht. Zum Beispiel: $(CC)$(CC_FLAGS)$@$^.

Automatische Variablen

Automatische Variablen werden von make gesetzt, nachdem eine Regel erfüllt wurde. Dazu gehören:

  • $@: der Zieldateiname.
  • $*: der Zieldateiname ohne die Dateiendung.
  • $<: der erste vorausgesetzte Dateiname.
  • $^: die Dateinamen aller Voraussetzungen, getrennt durch Leerzeichen, Duplikate werden verworfen.
  • $+: ähnlich wie $^, enthält aber Duplikate.
  • $?: die Namen aller Voraussetzungen, die neuer sind als das Ziel, getrennt durch Leerzeichen.

Beispielsweise können wir das frühere Makefile umschreiben als:

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

Virtueller Pfad – VPATH & vpath

Sie können VPATH (Großbuchstaben) verwenden, um das Verzeichnis anzugeben, in dem nach Abhängigkeiten und Zieldateien gesucht werden soll. Zum Beispiel,

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

Sie können auch vpath (Kleinbuchstaben) verwenden, um den Dateityp und sein Suchverzeichnis genauer anzugeben. Zum Beispiel,

# 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
Pattern-Regeln

Eine Pattern-Regel, die das Pattern-Matching-Zeichen '%' als Dateinamen verwendet, kann angewendet werden, um ein Ziel zu erstellen, wenn es keine explizite Regel gibt. Zum Beispiel,

# 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 $@
Implizite Musterregeln

Make kommt mit einem riesigen Satz von impliziten Musterregeln. Sie können alle Regeln über die Option --print-data-base auflisten.

Ein Beispiel-Makefile

Dieses Beispiel-Makefile ist dem „C/C++ Development Guide -Makefile“ von Eclipse entnommen.

# 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)

Kurzzusammenfassung

Ich habe hier die grundlegenden Make-Funktionen vorgestellt, damit Sie einfache Makefiles zum Erstellen von C/C++-Anwendungen lesen und verstehen können. Make ist eigentlich recht komplex und kann als eigene Programmiersprache betrachtet werden!!!

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.