Progmar Marcin Załęczny

Kompilacja programu przy użyciu makefile

Nasz program rozrósł się na kilka plików. Ręczna kompilacja byłaby już bardzo uciążliwa, dlatego warto zastosować plik Makefile. Plik ten umożliwia kompilację całego projektu przy użyciu tylko jednej komendy: make. Nasz Makefile ma następującą postać:


CC = g++
CFLAGS = `pkg-config --cflags gtk+-3.0` -Wno-format-security
LIBS = `pkg-config --libs gtk+-3.0`
APP_NAME = hello_world


all: main.o Gui.o Window.o MainWindow.o
	$(CC) -o $(APP_NAME) main.o Gui.o Window.o MainWindow.o $(LIBS)

main.o: main.cpp
	$(CC) -c main.cpp $(CFLAGS)

Gui.o: Gui.cpp Gui.h
	$(CC) -c Gui.cpp $(CFLAGS)

Window.o: Window.cpp Window.h
	$(CC) -c Window.cpp $(CFLAGS)

MainWindow.o: MainWindow.cpp MainWindow.h
	$(CC) -c MainWindow.cpp $(CFLAGS)

clean:
	rm -f *.o $(APP_NAME)
	

U samej góry definiujemy zmienne:

  • CC - zawiera nazwę kompilatora, którego używamy do kompilacji naszego kodu.
  • CFLAGS - zawiera listę opcji przekazywanych do kompilatora. Na uwagę zasługuje tu opcja -Wno-format-security. Użyliśmy jej po to, aby kompilator nie generował uwagi warning: format not a string literal and no format arguments [-Wformat-security] w linijce pliku Gui.cpp:
    dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, message);
    Uwaga ta jest generowana z powodu umieszczenia komunikatu Message Boxa w zmiennej message a nie w postaci literału znakowego.
  • LIBS - zawiera listę używanych przez program bibliotek.
  • APP_NAME - zawiera nazwę pliku wyjściowego z naszą aplikacją.

Program make zaczyna przetwarzanie pliku Makefile od linijki zaczynającej się od all:


all: main.o Gui.o Window.o MainWindow.o
	$(CC) -o $(APP_NAME) main.o Gui.o Window.o MainWindow.o $(LIBS)
	

W linijce tej podajemy z jakich plików obiektowych składa się nasz program. Pliki obiektowe, to wszystkie pliki z rozszerzeniem *.o. Poniżej znajduje się komenda, która z plików obiektowych zbuduje nasz program w pliku o nazwie zamieszczonej w zmiennej APP_NAME. Nazwę pliku wyjściowego podajemy po opcji -o kompilatora g++. Następnie podajemy listę plików obiektowych oraz listę wykorzystywanych bibliotek. Warto tu zwrócić uwagę, że do zmiennych odwołujemy się w następujący sposób: $(NAZWA_ZMIENNEJ). I jeszcze jedna bardzo istotna uwaga: linijka z komendą musi zaczynać się od pojedyńczego znaku tabulatora. Nie mogą to być spacje. W przeciwnym razie otrzymamy błąd programu make.

Dla każdego pliku obiektowego musimy podać od jakich plików źródłowych on zależy oraz musimy zdefiniować polecenie, które wygeneruje ten plik obiektowy. Dla przykładu weźmy plik Window.o:


Window.o: Window.cpp Window.h
	$(CC) -c Window.cpp $(CFLAGS)
	

Widzimy, że plik ten zależy od plików Window.cpp i Window.h a poleceniem jest $(CC) -c Window.cpp $(CFLAGS). W poleceniu podajemy opcję -c, która mówi kompilatorowi, że musi wyłącznie skompilować plik i utworzyć plik obiektowy o tej samej nazwie tylko o rozszerzeniu *.o, tutaj Window.o. Na końcu polecenia podajemy wspólne opcje kompilacji zawarte w zmiennej CFLAGS. I tutaj ważne jest, że linijka z komendą musi zaczynać się od pojedyńczego znaku tabulatora. Dla pozostałych plików obiektowych mechanizm postępowania jest identyczny.

Na koniec pozostał do omówienia fragment:


clean:
	rm -f *.o $(APP_NAME)
	

Definiuje on polecenie czyszczące katalog z wyników kompilacji a więc wszystkich plików obiektowych i programu wyjściowego. Fragment ten jest wykonywany przez program make po wydaniu polecenia: make clean.