Articles

GCC i Make

GCC (Kolekcja Kompilatorów GNU)

Krótka historia i wprowadzenie do GCC

Oryginalny Kompilator GNU C (GCC) został stworzony przez Richarda Stallmana, założyciela Projektu GNU. Richard Stallman założył projekt GNU w 1984 roku, aby stworzyć kompletny uniksopodobny system operacyjny jako wolne oprogramowanie, aby promować wolność i współpracę wśród użytkowników komputerów i programistów.

GCC, dawniej dla „GNU C Compiler”, rozrosło się z czasem do obsługi wielu języków, takich jak C (gcc), C++ (g++), Objective-C, Objective-C++, Java (gcj), Fortran (gfortran), Ada (gnat), Go (gccgo), OpenMP, Cilk Plus i OpenAcc. Obecnie jest on określany jako „GNU Compiler Collection”. Strona główna dla GCC to http://gcc.gnu.org/. Aktualna wersja to GCC 7.3, wydana w dniu 2018-01-25.

GCC jest kluczowym składnikiem tzw. „GNU Toolchain”, służącego do tworzenia aplikacji i pisania systemów operacyjnych. GNU Toolchain obejmuje:

  1. GNU Compiler Collection (GCC): zestaw kompilatorów, który obsługuje wiele języków, takich jak C/C++ i Objective-C/C++.
  2. GNU Make: narzędzie do automatyzacji kompilacji i budowania aplikacji.
  3. GNU Binutils: zestaw narzędzi binarnych, w tym linker i asembler.
  4. GNU Debugger (GDB).
  5. GNU Autotools: System budowania zawierający Autoconf, Autoheader, Automake i Libtool.
  6. GNU Bison: generator parserów (podobny do lex i yacc).

GCC jest przenośny i działa na wielu platformach operacyjnych. GCC (i GNU Toolchain) jest obecnie dostępne na wszystkich Uniksach. Są one również przenoszone na Windows (przez Cygwin, MinGW i MinGW-W64). GCC jest także kompilatorem krzyżowym, do tworzenia plików wykonywalnych na różnych platformach.

Wersje GCC

Różne wersje GCC to:

  • GCC wersja 1 (1987): Początkowa wersja, która obsługuje C.
  • GCC wersja 2 (1992): obsługuje C++.
  • GCC wersja 3 (2001): zawiera ECGS (Experimental GNU Compiler System), z poprawioną optymalizacją.
  • GCC wersja 4 (2005):
  • GCC wersja 5 (2015):
  • GCC wersja 6 (2016):
  • GCC wersja 7 (2017):
Wsparcie standardu C++

Istnieją różne standardy C++:

  • C++98
  • C++11 (aka C++0x)
  • C++14 (aka C++1y)
  • C++17 (aka C++1z)
  • C++2a (następny planowany standard w 2020)

Domyślnym trybem jest C++98 dla wersji GCC przed 6.1, i C++14 dla GCC 6.1 i wyższych. Możesz użyć flagi wiersza poleceń -std aby jawnie określić standard C++. Na przykład,

  • -std=c++98, lub -std=gnu++98 (C++98 z rozszerzeniami GNU)
  • -std=c++11, lub -std=gnu++11 (C++11 z rozszerzeniami GNU)
  • -std=c++14, lub -std=gnu++14 (C++14 z rozszerzeniami GNU), tryb domyślny dla GCC 6.1 i wyżej.
  • -std=c++17, lub -std=gnu++17 (C++17 z rozszerzeniami GNU), tryb eksperymentalny.
  • -std=c++2a, lub -std=gnu++2a (C++2a z rozszerzeniami GNU), eksperymentalny.

Instalacja GCC na Uniksach

GNU Toolchain, w tym GCC, jest dołączany do wszystkich Uniksów. Jest to standardowy kompilator dla większości systemów operacyjnych Unix-like.

Instalacja GCC na Mac OS X

Otwórz Terminal, i wpisz „gcc --version„. Jeśli gcc nie jest zainstalowany, system poprosi Cię o zainstalowanie gcc.

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

Instalacja GCC w Windows

W przypadku Windows, możesz zainstalować Cygwin GCC, MinGW GCC lub MinGW-W64 GCC. Przeczytaj „Jak zainstalować Cygwin i MinGW”.

  • Cygwin GCC: Cygwin to środowisko uniksopodobne i interfejs wiersza poleceń dla systemu Microsoft Windows. Cygwin jest ogromny i zawiera większość uniksowych narzędzi i programów użytkowych. Zawiera także powszechnie używaną powłokę Bash.
  • MinGW: MinGW (Minimalist GNU for Windows) jest portem GNU Compiler Collection (GCC) i GNU Binutils do użytku w Windows. Zawierał również MSYS (Minimal System), który jest w zasadzie powłoką Bourne’a (bash).
  • MinGW-W64: fork MinGW, który obsługuje zarówno 32-bitowe jak i 64-bitowe okna.
Różne GCC pod Cygwin

Istnieje wiele GCC pod Cygwin/MinGW. Aby rozróżnić te odmiany, musisz zrozumieć następujące kwestie:

  • Windows/Intel używa następujących zestawów instrukcji: x86 to 32-bitowy zestaw instrukcji; i868 to 32-bitowa ulepszona wersja x86; x86_64 (lub amd64) to 64-bitowy zestaw instrukcji.
  • 32-bitowe kompilatory/programy mogą działać w 32-bitowym lub 64-bitowym (kompatybilnym wstecznie) systemie Windows, ale 64-bitowe kompilatory mogą działać tylko w 64-bitowym systemie Windows.
  • 64-bitowe kompilatory mogą wytwarzać docelowe wersje 32-bitowe lub 64-bitowe.
  • Jeśli używasz GCC Cygwin, celem może być natywny Windows lub Cygwin. Jeśli celem jest natywny Windows, kod może być dystrybuowany i uruchamiany pod Windows. Jednakże, jeśli celem jest Cygwin, aby dystrybuować, musisz dystrybuować środowisko uruchomieniowe Cygwin (cygwin1.dll). Dzieje się tak dlatego, że Cygwin jest emulatorem Uniksa pod Windows.
MinGW-W64 Target 32/64-bitowego natywnego Windows

MinGW-W64 (fork MinGW, dostępny pod adresem http://mingw-w64.org/doku.php) obsługuje target zarówno 32-bitowego, jak i 64-bitowego natywnego Windows. Możesz zainstalować „MinGW-W64” pod „Cygwin” wybierając te pakiety (w kategorii „devel”):

  • mingw64-x86_64-gcc-core: 64-bitowy kompilator C dla natywnego 64-bitowego Windows. The executable is „x86_64-w64-mingw32-gcc„.
  • mingw64-x86_64-gcc-g++: 64-bitowy kompilator C++ dla targetu natywnego 64-bitowego systemu Windows. The executable is „x86_64-w64-mingw32-g++„.
  • mingw64-i686-gcc-core: 64-bitowy kompilator C dla celów natywnego 32-bitowego systemu Windows. The executable is „i686-w64-mingw32-gcc„.
  • mingw64-i686-gcc-g++: 64-bitowy kompilator C++ dla targetu natywnego 32-bitowego Windows. Plik wykonywalny to „i686-w64-mingw32-g++„.

Notatki:

  • Proponuję zainstalować „mingw64-x86_64-gcc-core” i „mingw64-x86_64-gcc-g++„, aby zapewnić natywne kody 64-bitowego systemu Windows, ale pomiń „mingw64-i686-gcc-core” i „mingw64-i686-gcc-g++„, chyba że musisz produkować 32-bitowe aplikacje Windows.
  • Dla JNI (Java Native Interface) w 64-bitowej Javie, musisz użyć „x86_64-w64-mingw32-gcc” lub „x86_64-w64-mingw32-g++„, aby wyprodukować 64-bitowy natywny kod Windows.

Uruchom pliki wykonywalne i sprawdź wersje:

// 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
Inne pakiety GCC w Cygwin

Inne pakiety GCC w Cygwin to:

  • gcc-core, gcc-g++: Basic 64-bit C/C++ compiler target 64-bit Cygwin. Prawdopodobnie powinieneś zainstalować również te dwa pakiety. Jednakże, aby rozpowszechniać wyprodukowany kod, musisz rozpowszechniać Cygwin Runtime Environment (cygwin1.dll). Dzieje się tak dlatego, że Cygwin jest emulatorem systemu Unix pod Windows.
  • cygwin32-gcc-core, cygwin32-gcc-g++: Starszy 32-bitowy kompilator C / C ++ dla docelowego 32-bitowego Cygwin (Obsoleted by gcc-code i gcc-g++?).
  • mingw-gcc-core, mingw-gcc-g++: Starszy kompilator MinGW 32-bit C/C++ dla 32-bitowych systemów Windows (Zastąpiony przez pakiety MinGW-W64?).

Po instalacji

Wersje

Możesz wyświetlić wersję GCC poprzez --version opcję:

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

Więcej szczegółów można uzyskać za pomocą -v opcji, na przykład,

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

Podręcznik pomocy można uzyskać poprzez --help opcję. Na przykład,

$ gcc --help
Strony man

Możesz czytać strony podręcznika GCC (lub strony man) przez man narzędzie:

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

Czytanie stron man pod CMD lub powłoką Bash może być trudne. Możesz wygenerować plik tekstowy poprzez:

$ man gcc | col -b > gcc.txt

Narzędzie col jest potrzebne do usunięcia backspace. (Dla Cygwin, jest ono dostępne w pakiecie „Utils”, „util-linux”.)

Alternatywnie, możesz poszukać stron man online, np, http://linux.die.net/man/1/gcc.

Strony man GCC są przechowywane pod „usr/share/man/man1„.

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

Rozpoczęcie pracy

Kompilatory GNU C i C++ nazywają się odpowiednio gcc i g++,

Kompilacja/Linkowanie prostego programu C – hello.c

Poniżej znajduje się program Hello-world C hello.c:

.

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

Do skompilowania hello.c:

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

Domyślny wyjściowy plik wykonywalny nosi nazwę „a.exe” (Windows) lub „a.out” (Uniksy i Mac OS X).

Aby uruchomić program:

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

Wskazówki dla Uniksów i powłoki Bash:

  • W powłoce Bash domyślna PATH nie zawiera bieżącego katalogu roboczego. Dlatego musisz uwzględnić bieżącą ścieżkę (./) w poleceniu. (Windows włącza bieżący katalog do PATH automatycznie; podczas gdy Uniksy nie – musisz włączyć bieżący katalog jawnie do PATH.)
  • Musisz również włączyć rozszerzenie pliku, jeśli istnieje, tj, „./a.out„.
  • W Uniksach, plik wyjściowy może mieć postać „a.out” lub po prostu „a„. Ponadto, musisz przypisać tryb pliku wykonywalnego (x) do pliku wykonywalnego „a.out„, poprzez polecenie „chmod a+x filename” (dodaj tryb pliku wykonywalnego „+x” do wszystkich użytkowników „a+x„).

Aby określić nazwę pliku wyjściowego, użyj opcji -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

UWAGA dla Uniksów:

  • W Uniksach, zazwyczaj pomijamy rozszerzenie pliku .exe (przeznaczone tylko dla Windows), i po prostu nazywamy plik wykonywalny jako hello (poprzez komendę „gcc -o hello hello.c„.
  • Trzeba przypisać tryb pliku wykonywalnego poprzez polecenie „chmod a+x hello„.
Kompilacja/Linkowanie prostego programu w C++ – hello.cpp
12345678
// hello.cpp#include <iostream>using namespace std; int main() { cout << "Hello, world!" << endl; return 0;}

Trzeba użyć g++ do skompilowania programu C++, w następujący sposób. Używamy opcji -o do określenia nazwy pliku wyjściowego.

// (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
Więcej opcji kompilatora GCC

Kilka powszechnie używanych opcji kompilatora GCC to:

$ g++ -Wall -g -o Hello.exe Hello.cpp
  • -o: określa nazwę pliku wyjściowego.
  • -Wall: drukuje „all” Warning messages.
  • -g: generuje dodatkowe informacje o debugowaniu symbolicznym do użycia z gdb debuggerem.
Kompiluj i linkuj oddzielnie

Powyższe polecenie kompiluje plik źródłowy do pliku obiektowego i link z innymi plikami obiektowymi i bibliotekami systemowymi do pliku wykonywalnego w jednym kroku. Możesz rozdzielić kompilację i link w dwóch krokach w następujący sposób:

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

Opcje są następujące:

  • -c: Kompilacja do pliku obiektowego „Hello.o„. Domyślnie plik obiektowy ma taką samą nazwę jak plik źródłowy z rozszerzeniem „.o” (nie ma potrzeby podawania opcji -o). Brak linkowania z innymi plikami obiektowymi lub bibliotekami.
  • Linkowanie jest wykonywane gdy plikami wejściowymi są pliki obiektowe „.o” (zamiast pliku źródłowego „.cpp” lub „.c„). GCC używa oddzielnego programu linkera (o nazwie ld.exe) do wykonania linkowania.
Kompilacja i linkowanie wielu plików źródłowych

Załóżmy, że twój program ma dwa pliki źródłowe: file1.cppfile2.cpp. Mógłbyś skompilować je wszystkie w jednym poleceniu:

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

Jednak zazwyczaj kompilujemy każdy z plików źródłowych osobno do pliku obiektowego, a następnie łączymy je ze sobą w późniejszym etapie. W tym przypadku zmiany w jednym pliku nie wymagają ponownej kompilacji pozostałych plików.

> g++ -c file1.cpp> g++ -c file2.cpp> g++ -o myprog.exe file1.o file2.o
Kompilacja do biblioteki współdzielonej

Aby skompilować i połączyć program C/C++ do biblioteki współdzielonej (".dll" w Windows, ".so" w Uniksach), użyj opcji -shared. Przeczytaj „Java Native Interface” dla przykładu.

Proces kompilacji GCC

GCC kompiluje program C/C++ do postaci wykonywalnej w 4 krokach, jak pokazano na powyższym diagramie. Na przykład, program „gcc -o hello.exe hello.c” jest wykonywany w następujący sposób:

  1. Preprocessing: poprzez preprocesor GNU C (cpp.exe), który zawiera nagłówki (#include) i rozszerza makra (#define
    > cpp hello.c > hello.i

    Wynikowy plik pośredni „hello.i” zawiera rozszerzony kod źródłowy.

  2. Kompilacja: Kompilator kompiluje wstępnie przetworzony kod źródłowy do kodu asemblera dla określonego procesora.
    > gcc -S hello.i

    Opcja -S określa, że zamiast kodu obiektowego ma powstać kod asemblera. Wynikowy plik asemblera to „hello.s„.

  3. Assembly: Asembler (as.exe) konwertuje kod asemblera na kod maszynowy w pliku obiektowym „hello.o
    > as -o hello.o hello.s
  4. Linker: Na koniec, linker (ld.exe) łączy kod obiektu z kodem biblioteki, aby uzyskać plik wykonywalny „hello.exe
    > ld -o hello.exe hello.o ...libraries...

Tryb verbose (-v)

Możesz zobaczyć szczegółowy proces kompilacji przez włączenie opcji -v (verbose). Na przykład,

> gcc -v -o hello.exe hello.c
Definiowanie makra (-D)

Możesz użyć opcji -Dname, aby zdefiniować makro, lub -Dname=value, aby zdefiniować makro z wartością. Opcja value powinna być ujęta w podwójne cudzysłowy, jeśli zawiera spacje.

Nagłówki (.h), biblioteki statyczne (.lib, .a) i biblioteki współdzielone (.dll, .so)

Biblioteka statyczna vs. biblioteka współdzielona

Biblioteka jest zbiorem prekompilowanych plików obiektów, które mogą być dołączone do twoich programów przez linker. Przykładem są funkcje systemowe takie jak printf() i sqrt().

Istnieją dwa typy bibliotek zewnętrznych: biblioteka statyczna i biblioteka współdzielona.

  1. Biblioteka statyczna ma rozszerzenie pliku „.a” (plik archiwalny) w Uniksach lub „.lib” (biblioteka) w systemie Windows. Kiedy twój program jest połączony z biblioteką statyczną, kod maszynowy zewnętrznych funkcji używanych w twoim programie jest kopiowany do pliku wykonywalnego. Biblioteka statyczna może być utworzona za pomocą programu archiwizującego „ar.exe„.
  2. Biblioteka współdzielona ma rozszerzenie pliku „.so” (obiekty współdzielone) w systemach Unixes lub „.dll” (biblioteka dynamicznego łącza) w systemie Windows. Kiedy twój program jest połączony z biblioteką współdzieloną, tylko mała tabela jest tworzona w pliku wykonywalnym. Zanim plik wykonywalny zacznie działać, system operacyjny ładuje kod maszynowy potrzebny do działania zewnętrznych funkcji – proces ten nazywany jest dynamicznym linkowaniem. Łączenie dynamiczne sprawia, że pliki wykonywalne są mniejsze i oszczędzają miejsce na dysku, ponieważ jedna kopia biblioteki może być współdzielona przez wiele programów. Co więcej, większość systemów operacyjnych pozwala, aby jedna kopia biblioteki współdzielonej w pamięci była używana przez wszystkie uruchomione programy, co pozwala zaoszczędzić pamięć. Kody bibliotek współdzielonych mogą być aktualizowane bez potrzeby rekompilacji programu.

Z powodu zalet dynamicznego linkowania, GCC domyślnie łączy się z biblioteką współdzieloną, jeśli jest ona dostępna.

Możesz wylistować zawartość biblioteki poprzez „nm filename„.

Szukanie plików nagłówkowych i bibliotek (-I, -L i -l)

Podczas kompilacji programu, kompilator potrzebuje plików nagłówkowych do kompilacji kodów źródłowych; linker potrzebuje bibliotek do rozwiązywania zewnętrznych odwołań z innych plików obiektowych lub bibliotek. Kompilator i linker nie znajdą nagłówków/bibliotek, jeśli nie ustawisz odpowiednich opcji, co nie jest oczywiste dla pierwszego użytkownika.

Dla każdego z nagłówków użytych w twoim źródle (poprzez dyrektywy #include), kompilator przeszukuje tak zwane include-paths dla tych nagłówków. Ścieżki include są określone przez opcję -Idir (lub zmienną środowiskową CPATH). Ponieważ nazwa pliku nagłówka jest znana (np. iostream.hstdio.h), kompilator potrzebuje tylko katalogów.

Linker przeszukuje tzw. ścieżki bibliotek w poszukiwaniu bibliotek potrzebnych do połączenia programu w plik wykonywalny. Ścieżkę do biblioteki podaje się za pomocą opcji -Ldir (duże litery 'L' a po nich ścieżka do katalogu) (lub zmienna środowiskowa LIBRARY_PATH). Dodatkowo należy również podać nazwę biblioteki. W Uniksach bibliotekę libxxx.a podaje się poprzez opcję -lxxx (mała litera 'l', bez przedrostka „lib” i rozszerzenia ".a„). W systemie Windows należy podać pełną nazwę, taką jak -lxxx.lib. Linker musi znać zarówno katalogi, jak i nazwy bibliotek. W związku z tym należy określić dwie opcje.

Default Include-paths, Library-paths and Libraries

Spróbuj wypisać domyślne include-paths w twoim systemie używane przez „GNU C Preprocessor” poprzez „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

Spróbuj uruchomić kompilację w trybie verbose (-v), aby zbadać ścieżki bibliotek (-L) i biblioteki (-l) używane w twoim systemie:

> 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: W Eclipse CDT można ustawić ścieżki include, ścieżki bibliotek i biblioteki klikając prawym przyciskiem myszy na projekcie ⇒ Właściwości ⇒ C/C++ Ogólne ⇒ Ścieżki i symbole ⇒ W zakładkach „Includes”, „Ścieżki bibliotek” i „Biblioteki”. Ustawienia mają zastosowanie tylko do wybranego projektu.

Zmienne środowiskowe GCC

GCC używa następujących zmiennych środowiskowych:

  • PATH: Do wyszukiwania plików wykonywalnych i bibliotek współdzielonych run-time (.dll.so).
  • CPATH: Do przeszukiwania ścieżek include dla nagłówków. Jest wyszukiwany po ścieżkach określonych w opcjach -I<dir>C_INCLUDE_PATH i CPLUS_INCLUDE_PATH mogą być użyte do określenia nagłówków C i C++, jeśli dany język został wskazany w preprocessingu.
  • LIBRARY_PATH: Do przeszukiwania ścieżek bibliotek dla bibliotek linków. Jest przeszukiwana po ścieżkach określonych w opcjach –L<dir>.

Urządzenia do badania skompilowanych plików

Dla wszystkich narzędzi GNU, możesz użyć „command --help” by wyświetlić menu pomocy; lub „man command” by wyświetlić strony man.

„file” Utility – Determine File Type

Użytkownik „file” może być użyty do wyświetlenia typu plików obiektowych i wykonywalnych. Na przykład,

$ 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 – List Symbol Table of Object Files

Narzędzie „nm” wyświetla tabelę symboli plików obiektowych. Na przykład,

$ 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” jest powszechnie używane do sprawdzania czy dana funkcja jest zdefiniowana w pliku obiektowym. A 'T' w drugiej kolumnie oznacza funkcję, która jest zdefiniowana, podczas gdy 'U' oznacza funkcję, która jest niezdefiniowana i powinna być rozwiązana przez linker.

„ldd” Utility – List Dynamic-Link Libraries

Narzędzie „ldd” bada plik wykonywalny i wyświetla listę bibliotek współdzielonych, których potrzebuje. Na przykład,

> 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

Narzędzie „make” automatyzuje prozaiczne aspekty budowania plików wykonywalnych z kodu źródłowego. „make” używa tak zwanego makefile, który zawiera reguły, jak budować pliki wykonywalne.

Możesz wydać „make --help„, aby wyświetlić listę opcji wiersza poleceń; lub „man make„, aby wyświetlić strony man.

Pierwszy plik Makefile na przykładzie

Zacznijmy od prostego przykładu, aby zbudować program Hello-world (hello.c) do pliku wykonywalnego (hello.exe) poprzez narzędzie make.

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

Utwórz następujący plik o nazwie „makefile” (bez żadnego rozszerzenia), który zawiera reguły budowania pliku wykonywalnego, i zapisz w tym samym katalogu co plik źródłowy. Użyj „tab” do wcięcia polecenia (NIE spacji).

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

Uruchom narzędzie „make” w następujący sposób:

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

Uruchomienie make bez argumentu uruchamia cel „all” w pliku makefile. Plik makefile składa się z zestawu reguł. Reguła składa się z 3 części: celu, listy warunków wstępnych i polecenia, jak poniżej:

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

Cel i warunki wstępne są oddzielone dwukropkiem (:). Polecenie musi być poprzedzone tabulatorem (NIE spacjami).

Gdy make jest proszony o ocenę reguły, zaczyna od znalezienia plików w warunkach wstępnych. Jeśli którykolwiek z warunków wstępnych ma powiązaną regułę, spróbuj zaktualizować te najpierw.

W powyższym przykładzie, reguła „all” ma warunek wstępny „hello.exemake nie może znaleźć pliku „hello.exe„, więc szuka reguły, która go utworzy. Reguła „hello.exe” ma warunek wstępny „hello.o„. Ponownie, nie istnieje, więc make szuka reguły, która ją utworzy. Reguła „hello.o” ma warunek wstępny „hello.cmake sprawdza, czy „hello.c” istnieje i jest nowszy niż cel (który nie istnieje). Wykonuje polecenie „gcc -c hello.c„. Reguła „hello.exe” następnie uruchamia polecenie „gcc -o hello.exe hello.o„. Na koniec, reguła „all” nie robi nic.

Co ważniejsze, jeśli warunek wstępny nie jest nowszy niż docelowy, polecenie nie zostanie uruchomione. Innymi słowy, polecenie zostanie uruchomione tylko wtedy, gdy cel jest przestarzały w porównaniu do swojego warunku wstępnego. Na przykład, jeśli ponownie uruchomimy polecenie make:

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

Można również określić cel, który ma być wykonany w poleceniu make. Na przykład, cel „clean” usuwa „hello.o” i „hello.exe„. Następnie możesz uruchomić make bez celu, który jest taki sam jak „make all„.

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

Próbuj zmodyfikować „hello.c” i uruchomić make.

UWAGI:

  • Jeśli polecenie nie jest poprzedzone tabulatorem, otrzymasz komunikat o błędzie „makefile:4: *** brakujący separator. Stop.”
  • Jeśli w bieżącym katalogu nie ma makefile, otrzymasz komunikat o błędzie „make: *** Nie określono celów i nie znaleziono pliku makefile. Stop.”
  • Plik makefile może mieć nazwę „makefileMakefile” lub „GNUMakefile„, bez rozszerzenia pliku.

Więcej o Makefile

Komentarz & Kontynuacja

Komentarz zaczyna się od znaku # i trwa do końca linii. Długa linia może być przerwana i kontynuowana w kilku liniach przez odwrotny ukośnik (\).

Syntaktyka reguł

Ogólna składnia reguł jest następująca:

target1 : 

Reguły są zwykle zorganizowane w taki sposób, że bardziej ogólne reguły są pierwsze. Ogólna reguła jest często nazywana „all„, co jest domyślnym celem dla make.

Fałszywe cele (lub sztuczne cele)

Cel, który nie reprezentuje pliku jest nazywany fałszywym celem. Na przykład, „clean” w powyższym przykładzie, który jest tylko etykietą dla polecenia. Jeśli celem jest plik, zostanie on sprawdzony pod kątem jego wstępnego warunku na nieaktualność. Fałszywy cel jest zawsze nieaktualny i jego polecenie zostanie uruchomione. Standardowe fałszywe cele to: allcleaninstall.

Zmienne

Zmienna zaczyna się od znaku $ i jest ujęta w nawiasy (...) lub nawiasy klamrowe {...}. Zmienne jednoznakowe nie potrzebują nawiasów. Na przykład, $(CC)$(CC_FLAGS)$@$^.

Zmienne automatyczne

Zmienne automatyczne są ustawiane przez make po dopasowaniu reguły. Należą do nich:

  • $@: docelowa nazwa pliku.
  • $*: docelowa nazwa pliku bez rozszerzenia.
  • $<: pierwsza wymagana nazwa pliku.
  • $^: nazwy plików wszystkich warunków wstępnych, oddzielone spacjami, odrzuć duplikaty.
  • $+: podobne do $^, ale zawiera duplikaty.
  • $?: nazwy wszystkich warunków wstępnych, które są nowsze niż cel, oddzielone spacjami.

Na przykład, możemy przepisać wcześniejszy makefile jako:

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
Wirtualna ścieżka – VPATH & vpath

Można użyć VPATH (duże litery), aby określić katalog do wyszukiwania zależności i plików docelowych. Na przykład,

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

Możesz również użyć vpath (małe litery), aby dokładniej określić typ pliku i jego katalog wyszukiwania. Na przykład,

# 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
Reguły wzorca

Reguła wzorca, która używa znaku dopasowania do wzorca '%' jako nazwy pliku, może być zastosowana do utworzenia celu, jeśli nie ma jawnej reguły. Na przykład,

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

Reguły niejawnych wzorców

Make posiada ogromny zestaw niejawnych reguł wzorców. Możesz wypisać wszystkie reguły poprzez --print-data-base option.

Przykładowy plik makefile

Ten przykładowy plik makefile został wyodrębniony z Eclipse’a „C/C++ Development Guide -Makefile”.

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

Krótkie podsumowanie

Przedstawiłem tutaj podstawowe funkcje make, abyś mógł czytać i rozumieć proste pliki make do budowania aplikacji C/C++. Make jest w rzeczywistości dość złożony, i może być uważany za język programowania sam w sobie!!!

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *