Programowanie Proceduralne Makefile Bożena Woźna-Szcześniak bwozna@gmail.com Akademia im. Jana Długosza Wykład 14
Co to jest Makefile Makefile jest plikiem reguł dla programu make. Wykorzystywany jest głównie do kompilacji, badź rekompilacji złożonych projektów, ale nie tylko. Makefile zawiera zbiór zależności między plikami źródłowymi. Dzięki temu otrzymujemy możliwość kompilacji tylko poszczególnych plików, czyli tych które się zmieniły od czasu ostatniej kompilacji. Możliwość ta znacznie skraca czas generowania pliku wynikowego. W zależności od złożoności danego projektu, badź specyficznych wymagań, możemy posiadać dowolna ilość takich plików. Ogólnie przyjęta zasada mówi o tym, aby główny plik posiadał jedna z trzech nazw, GNUmakefile, makefile lub Makefile.
Szkielet budowy pliku Typowy plik Makefile zawiera pięć podstawowych elementów: reguły szczegółowe reguły ogólne definicje zmiennych dyrektywy komentarze
Szkielet budowy pliku Reguły szczegółowe Reguły szczegółowe opisuja zasadę rekompilacji jednego badź więcej plików, nazywanych celami. Do osiagnięcia celu potrzebujemy listy plików, tzw. zależności. Reguły szczegółowe dotycza pliku wynikowego, jaki mamy otrzymać po zakończeniu działania polecenia make. Cel : zależności Instrukcje Instrukcje Cel to najczęściej nazwa pliku wynikowego, może to być też nazwa akcji np. clean. Zależności to nazwy plików wymagane do otrzymania pliku wynikowego. Instrukcje to blok instrukcji, po wykonaniu którego otrzymamy cel. Uwaga! Każda instrukcja rozpoczyna się od tabulatora.
Szkielet budowy pliku Reguły ogólne Reguły ogólne mówia o kompilacji plików tzw. pośrednich. Charakteryzuja się tym, iż nazwa celu zazwyczaj jest identyczna z nazwa pliku, który trzeba skompilować, aby otrzymać plik pośredni. Definicje zmiennych Definicje zmiennych znacznie upraszczaja budowę plików oraz wprowadzają wyższy poziom organizacji. Dyrektywy to nic innego jak polecenie, badź zbiór poleceń, które ma wykonać make w konkretnym przypadku. Każda linia zaczynaj aca się od # jest traktowana jako komentarz.
Oto bardzo prosty plik Makefile, którego zadaniem jest kompilacja program w języku C z podstawowymi opcjami kompilacji. Example program: plik.c gcc -Wall -std=c99 plik.c -o program program to plik wynikowy jaki otrzymamy po kompilacji plik.c to plik, którego potrzebujemy do procesu kompilacji instrukcja uruchamia kompilator wraz z zadanymi opcjami kompilacji
main.c #include <stdio.h> #include "functions.h" int main(void){ print_hello(); printf("\n"); printf("the factorial of 5 is %d",factorial(5)); return 0; } hello.c #include <stdio.h> #include "functions.h" void print_hello(){ puts("hello World!"); }
factorial.c #include "functions.h" int factorial(int n){ if(n!=1)return(n * factorial(n-1)); else return 1; } functions.h #ifndef FUNCTIONS_H #define FUNCTIONS_H void print_hello(); int factorial(int n); #endif Szkielet programu all: gcc main.c hello.c factorial.c -o hello
Makefile all: hello hello: main.o factorial.o hello.o gcc main.o factorial.o hello.o -o hello main.o: main.c gcc -c main.c factorial.o: factorial.c gcc -c factorial.c hello.o: hello.c gcc -c hello.c clean: rm -rf *o hello
Nadawanie wartości zmiennym Istnieje wiele możliwości nadawania zmiennym wartości. Pierwsza polega na zazsosowaniu znaku =. Druga natomiast polega na zdefiniowaniu zmiennej w bloku define. Wartościa zmiennej może być inna zmienna. Example pierwsza = $(druga) druga = $(trzecia) trzecia = zmienna all: ; echo $(pierwsza)
Nadawanie wartości zmiennym Makefile # I am a comment, and I want to say that the variable CC # will be the compiler to use. CC=gcc CFLAGS = -c -Wall CFLAGS2 = -std=c99 -Wall all: hello hello: main.o factorial.o hello.o $(CC) $(CFLAGS2) main.o factorial.o hello.o -o hello main.o: main.c $(CC) $(CFLAGS) main.c factorial.o: factorial.c $(CC) $(CFLAGS) factorial.c hello.o: hello.c $(CC) $(CFLAGS) hello.c clean: rm -rf *o hello
Nadawanie wartości zmiennym Przykład na nieskończoną pętlę: CFLAGS = $(CFLAGS) -o Aby uniknać powyższego problemu można skorzystać z operatora := zamiast =. Skutkuje to rozszerzeniem zmiennej o wartość zmiennej która dodajemy. Działa to na zasadzie rozszerzenia tekstowego. Przykład: Example x:= nowy y:= $x tekst x:= inny tekst Powyższy zapis jest równoważny z: Example y= nowy tekst x:= inny tekst
Nadanie wartości niezainicjalizowanej zmiennej Sa sytuacje, w których chcemy nadać wartość zmiennej, tylko jeśli ta jeszcze nie została zainicjalizowana. Wtedy, zamiast zwykłego operatora przypisania = używamy?=. Wykorzystanie tego operatora gwarantuje, iż wartość zmiennej nie zostanie nadpisana. Przykładowo, instrukcja: Example zmienna?= object.c spowoduje przypisanie do zmiennej zmienna wartości object.c w przypadku jeśli zmienna nie jest zainicjalizowana. Jeśli natomiast inicjalizacja nastapiła gdzieś we wcześniejszej części pliku Makefile, przypisanie zostanie pominięte.
Dodawanie tekstu do zmiennej Operator += przydatny jest np. w przypadku kiedy do istniejacej listy plików chcemy dodać jeszcze jeden element. Przykład: Example objects = plik1.c plik2.c plik3.c objects += plik4.c Po wykonaniu tych instrukcji zmienna objects będzie zawierała następujace elementy: plik1.c plik2.c plik3.c plik4.c. Użycie operatora += jest równoważne zapisowi: Example objects = plik1.c plik2.c plik3.c objects := $(objects) plik4.c
Poniższy przykład instrukcji warunkowej wskazuje poleceniu make odpowiedni zestaw bibliotek w zależności od tego jak ustawiona jest zmienna CC. Jeśli zmienna CC=gcc, to do polecenia kompilacji zostanie dodany przełacznik -lgnu. Example libs_for_gcc = -lgnu normal_libs = foo: $(objects) ifeq ($(CC),gcc) $(CC) -o foo $(objects) $(libs_for_gcc) else $(CC) -o foo $(objects) $(normal_libs) endif
Dyrektywa ifeq zawiera oraz odpowiada za porównanie warunku. Argumenty oddzielone sa przecinkiem oraz otoczone nawiasami okragłymi. Kolejna linia po ifeq zostanie wykonana tylko w przypadku, gdy obydwa argumenty porównania sa równe. Dyrektywa else jest opcjonalna i tylko wykonywana w przypadku kiedy warunek w dyrektywie Ifeq nie jest prawdziwy. Dyrektywa endif stosowana jest do wyraźnego zakończenia sekcji warunkowej każdy blok warunkowy musi być zakończony ta dyrektywa.