Praca z programami wielomodulowymi

Opracowanie: Grzegorz Jablonski na podstawie dokumentacji programu GNU Make

Przy pisaniu wiekszych progamow czesto zdarza sie, ze tekst programu zawarty jest w kilku oddzielnych plikach. Podzial taki ma zwykle zwiazek z logiczna struktura programu. Ulatwia to jego konserwacje, znalezienie potrzebnych nam akurat funkcji jest prostsze. Moze takze przyspieszyc kompilacje pogramu podczas jego uruchamiania.

Przypuscmy, ze nasz program sklada sie z dwoch modulow. Jeden z nich, main.cc, zawiera funkcje main() i interpreter parametrow podanych do programu, drugi, work.cc, zawiera jakies procedury obliczeniowe. Posiadaja one wspolny plik naglowkowy include.h. Jezeli zmieniamy jeden z nich, bez zmiany deklaracji funkcji w plikach naglowkowych, wystarczy ze do postaci wynikowej (z rozszerzeniem .obj w przypadku kompilatorow dosowych, .o w przypadku kompilatorow unixowych) przetworzymy tylko modul modyfikowany, Kod wynikowy drugiego modulu jest poprawny. W zwiazku z tym pozostanie nam jedynie wywolanie linkera w celu otrzymania programu wykonywalnego. Praca nad takim programem da sie zatem przedstawic za pomoca nastepujacego schematu:

- Piszemy pierwsza wersje programu - tworzymy pliki main.cc, work.cc oraz include.h.

- Przetwarzamy je do postaci wynikowej:

g++ -c main.cc -o main.o

g++ -c work.cc -o work.o

Opcja -c oznacza, ze nie tworzymy kodu wykonywalnego, ale jedynie pliki wynikowe. Po wykonaniu powyzszych dwoch polecen (przy zalozeniu braku bledow skladniowych) w katalogu pojawia sie dwa pliki: main.o oraz work.o. Aby uruchomic program musimy stworzyc plik wykonywalny.

g++ main.o work.o -o test

Stworzony zostal plik test ktory mozemy uruchomic piszac:

./test.

- Wykrylismy blad w pliku main.cc poprawiamy go , kompilujemy i tworzymy program wynikowy.

g++ main.cc -o main.o

g++ main.o work.o -o test

- Stwierdzilismy, ze jedna z funkcji powinna zwracac informacje o bledach wykonania. Modyfikujemy plik work.cc oraz include.h. Musimy ponownie skompilowac oba pliki.

g++ -c main.cc -o main.o

g++ -c work.cc -o work.o

g++ main.o work.o -o test

Jak widac, przy takiej metodzie postepowania wykonujemy mniej kompilacji, ale musimy pamietac ktore pliki zostaly zmodyfikowane. Ta wada znacznie zniechecalaby do pisania programow wielomodulowych i takiego trybu ich kompilacji, gdyby nie mozliwosc automatyzacji tej pracy.

Zauwazmy, ze wiemy ktore pliki zostaly zmodyfikowane. Gdy zmieniamy jakis plik, w katalogu zapisywany jest biezacy czas. Jezeli plik wynikowy (.o) ma wczesniejsza date, niz pliki z ktorych zostal utworzony (plik podstawowy oraz naglowkowy) to nalezy go skompilowac ponownie. Wszystko co nalezy wiedziec, to z jakich plikow oraz w jaki sposob tworzy sie poszczegolne pliki wynikowe.

W przypadku kompilatorow dla systemu MS-DOS, np. Borland C, taka informacja zapisywana jest w plikach projektu (.prj). Plik taki jest tworzony automatycznie, cale zadanie programisty polega na wskazaniu, z ktorych plikow zrodlowych powstaje program. Cala reszta jest wykonywana automatycznie.

W przypadku systemu unix tworzenie projektu jest nieco bardziej skomplikowane. Wymaga ono uzycia programu make i stworzenie pliku sterujacego dla tego programu - znanego zwykle pod nazwa makefile.

Program make jest zreszta takze dostepny w systemie DOS, ale przewaznie jego uzycie nie jest konieczne.

Makefile opisuje zaleznosci pomiedzy pilkami w programie i okresla komendy pozwalajace na uaktualnienie kazdego z plikow. W programie typowo plik wykonywalny jest tworzony z plikow wynikowych, ktore z kolei otrzymuje sie po kompilacji plikow zrodlowych. Po stworzeniu makefile za kazdym razem po modyfikacji plikow zrodlowych wszystko co musimy zrobic, to napisac

make

aby przeprowadzic wszystkie pozadane operacje.
 

Tworzenie pliku „makefile”


Plik makefile sklada sie z regul (rules) o nastepujacej postaci:

WYNIK ... : SKLADNIKI ...
    OPERACJA
    ...
    ...

„WYNIK” (ang. target) jest przewaznie nazwa pliku generowanego przez jakis program, moze to byc np. plik wynikowy lub wykonywalny. Moze to byc takze nazwa dzialania jakie ma zostac przeprowadzone (np. usuniecie plikow wynikowych i wykonywalnych) , jest to wtedy tzw. Phony Target.

„SKLADNIK” (ang. dependency) jest plikiem ktory jest uzywany przez program ktory generuje „WYNIK”. „WYNIK” zalezy zwykle od jednego lub kilku plikow.

„OPERACJA” opisuje dzialanie, ktore ma wykonac program make. Jest to wywolanie jakiegos programu operujacego na skladnikach i generujacego wynik. Regula moze zaierac wiecej niz jedna komende, kazda w osobnej linii. Uwaga: przed kazda komenda musi byc znak tabulacji.

Zwykle operacja jest zwiazana z zaleznosciami i ma na celu uaktualnienie pliku wyniku jezeli ktores z jego skladnikow ulegna zmianie. Nie jest to jednak konieczne, regula moze nie zawierac skladnikow , jak np. regula zawierajaca komendy usuwajace pliki wynikowe zwiazana z wynikiem „clean” nie ma skladnikow.

Reasumujac, regula okresla jak i kiedy uaktualnic poszczegolne pliki ktore sa wynikami poszczegolnych regul. make wykonuje operacje na skladnikach aby stworzyc lub uaktualnic wynik. Regula moze takze okreslic jak i kiedy wykonac jakas operacje.

Makefile moze zawierac takze inny tekst poza regulami, ale prosty plik moze zawierac wylacznie reguly. Reguly moga wygladac nieco bardziej skomplikowanie niz w ponizszym przykladzie, ale mniej-wiecej zgadzaja sie z tym wzorcem.
 

Przyklad prostego makefile


Oto prosty przyklad pliku makefile ktory opisuje, jak program edit zalezy od osmiu plikow wynikowych, ktore z kolei zaleza od osmiu plikow zrodlowych w C i trzech plikow naglowkowych. W tym przykladzie wszystkie pliki zrodlowe wlaczaja defs.h, ale tylko te definiujace komendy edycyjne wlaczaja command.h i jedynie pliki ktore zmieniaja bufor edytora wlaczaja buffer.h.

edit : main.o kbd.o command.o display.o \
    insert.o search.o files.o utils.o
    cc -o edit main.o kbd.o command.o display.o \
    insert.o search.o files.o utils.o

main.o : main.c defs.h
    cc -c main.c

kbd.o : kbd.c defs.h command.h
    cc -c kbd.c

command.o : command.c defs.h command.h
    cc -c command.c

display.o : display.c defs.h buffer.h
    cc -c display.c

insert.o : insert.c defs.h buffer.h
    cc -c insert.c

search.o : search.c defs.h buffer.h
    cc -c search.c

files.o : files.c defs.h buffer.h command.h
    cc -c files.c

utils.o : utils.c defs.h
    cc -c utils.c

clean :
    rm edit main.o kbd.o command.o display.o \
    insert.o search.o files.o utils.o

Wszystkie dluzsze linie sa podzielone na dwie linie przy pomocy znaku ”\” na koncu linii, oznacza to dokladnie to samo co jedna dluzsza linia, ale jest czytelniejsze.

W celu uzycia tego makefile do stworzenia pliku wykonywalnego pod nazwa edit trzeba napisac

make

W celu usuniecia pliku wykonywalnego i wszystkich plikow wynikowych nalezy napisac

make clean

W powyzszym przykladzie wsrod wynikow jest plik wykonywalny edit oraz pliki wynikowe main.o i kbd.o. Skladnikami sa pliki takie jak main.c i defs.h. W istocie kazdy plik .o jest i wynikiem i skladnikiem. Wsrod operacji wystepuja cc -c main.c oraz cc -c kbd.c, ktore te pliki tworza.

Kiedy wynik jest plikiem, trzeba go uaktualnic (skompilowac lub zlinkowac) jezeli ktorys z jego skladnikow ulegnie zmianie. Dodatkowo, wszystkie skladniki ktore sa automatycznie generowane powinny byc uaktualnione wczesniej. W powyzszym przykladzie edit zalezy od kazdego z osmiu plikow wynikowych, plik main.o zalezy od pliku zrodlowego main.c i naglowkowego defs.h.

Po kazdej linii zawierajacej wynik i skladniki nastepuje komenda systemu operacyjnego. Te komendy opisuja sposob w jaki uaktualnia sie plik wynikowy. Przed kazda linia musi wystepowac znak tabulacji w celu odroznienia linii z komendami od pozostalych. Nalezy pamietac, ze make nie wie w jaki sposob komendy dzialaja. Zadaniem programisty jest dostarczenie komend ktore odpowiednio uaktualnia wynik. Wszystko co robi make to wykonywanie komend w wyspecyfikowanej regule kiedy wynik musi byc uaktualniony.

Wynik clean nie jest plikiem, jest nazwa operacji. Poniewaz zwykle nie jest wymagane wykonanie dzialan zdefiniowanych w tej regule clean nie jest skladnikiem zadnej innej reguly. W wyniku make nigdy jej nie wykonuje dopoki nie rozkaze sie tego bezposrednio. Poniewaz ta regula nie ma takze skladnikow, jedynym jej celem jest wykonanie podanych komend. Tego typu wyniki sa zwane Phony Targets.
 

W jaki sposob „make” interpretuje makefile


Wywolany bez parametrow program make zaczyna od pierwszej reguly (nie liczac regul o wynikach zaczynajacych sie od kropki). W przykladzie jest to regula opisujaca jak uaktualnic program edit.

Dlatego po wydaniu komendy

make

make czyta makefile w biezacym katalogu i rozpoczyna przetwarzanie pierwszej reguly. W przykladzie regula opisuje linkowanie pliku edit. Zanim make zakonczy przetwarzanie tej reguly, musi zanalizowac reguly dla plikow od ktorych edit jest zalezny, ktore sa w tym przypadku plikami wynikowymi. Kazdy z tych plikow jest uaktualniany zgodnie z jego wlasna regula, opisujaca jak stworzyc plik wynikowy przez kompilacje odpowiedniego pliku zrodlowego. Musi to nastapic jesli plik zrodlowy lub ktorykolwiek z plikow naglowkowych podanych w regule jest mlodszy niz plik wynikowy albo plik wynikowy nie istnieje.

Pozostale reguly sa analizowane jezeli ich wyniki sa skladnikami glownej reguly, albo jezeli w wywolaniu make byly one specjalnie wyspecyfikowane.

Przed rekompilacja pliku wynikowego make rozwaza uaktualnienie jego skladnikow - pliku zrodlowego oraz plikow naglowkowych. Poniewaz nie sa one wynikami zadnych regul, make nic z nimi nie robi. Jednak w przypadku automatycznie generowanych programow w jezyku „C”, np przez program bison lub yacc moglyby istniec dla nich reguly.

Po rekompilacji plikow wynikowych make decyduje czy linkowac edit. Ta czynnosc musi byc wykonana, jezeli plik edit nie istnieje lub jezeli sa pliki wynikowe nowsze niz on. Jezeli plik wynikowy zostal wlasnie zrekompilowany, jest teraz nowszy niz edit, a wiec edit zostanie zlinkowany.

Reasumujac, jezeli zmienimy plik insert.c i uruchomimy make , make skompiluje ten plik tworzac insert.o i zlinkuje edit. Jezeli zmienimy plik command.h, make skompiluje pliki kbd.o, command.o oraz files.o i wtedy zlinkuje plik edit.
 

Make i zmienne


W przykladzie musielismy podac liste wszystkich plikow wynikowych dwukrotnie w regule opisujacej tworzenie pliku edit (powtorzona ponizej):

edit: main.o kbd.o command.o display.o \
    insert.o search.o files.o utils.o
    cc -o edit main.o kbd.o command.o display.o \
    insert.o search.o files.o utils.o

Takie powtorzenia sa podatne na bledy; jesli do programu w pewnym momencie dodamy jeden plik wynikowy, mozemy dodac go do jednej z list i zapomniec o drugiej. To niebezpieczenstwo moze zostac wyeliminowane przez uzycie zmiennych. Zmienne pozwalaja na jednokrotne zdefiniowanie lancucha tekstowego i uzycie go w wielu miejscach.

Typowo w kazdym makefile definiuje sie zmienna pod nazwa OBJECTS,objects, OBJS, objs, OBJ lub obj ktora jest lista nazw wszystkich plikow wynikowych. Zmienna taka mozna zdefiniowac, piszac linie podobna do ponizszej w pliku makefile:

objects = main.o kbd.o command.o display.o \
    insert.o search.o files.o utils.o

Wowczas w kazdym miejscu gdzie chcemy umiescic liste plikow wynikowych mozemy umiescic zmienna piszac:

$(objects).

objects = main.o kbd.o command.o display.o \
    insert.o search.o files.o utils.o

edit : $(objects)
    cc -o edit $(objects)

main.o : main.c defs.h
    cc -c main.c

kbd.o : kbd.c defs.h command.h
    cc -c kbd.c

command.o : command.c defs.h command.h
    cc -c command.c

display.o : display.c defs.h buffer.h
    cc -c display.c

insert.o : insert.c defs.h buffer.h
    cc -c insert.c

search.o : search.c defs.h buffer.h
    cc -c search.c

files.o : files.c defs.h buffer.h command.h
    cc -c files.c

utils.o : utils.c defs.h
    cc -c utils.c

clean :
    rm edit $(objects)
 

Uzywanie zmiennych


Aby podstawic w jakims miejscu nazwe zmiennej, nalezy napisac znak dolara po ktorym nastepuje nazwa zmiennej zamknieta w nawiasach zwyklych ‘()’ lub szesciennych ‘{}’. Przykladowo $(foo) i &{foo} jest poprawnym uzyciem zmiennej foo. To specjalne znaczenie symbolu dolara jest przyczyna, dla ktorej dla uzyskania znaku dolara ‘$’ w nazwie pliku lub rozkazu nalezy go napisac dwukrotnie: ‘$$’.

Zmienne moga byc uzyte w kazdym kontekscie: wynikach, skladnikach, dyrektywach i nowych wartosciach zmiennych.

Odwolania do zmiennych dzialaja na zasadzie scislego zastepowania tekstu, a wiec regula:

foo = c

prog.o : prog.$(foo)
    $(foo)$(foo) -$(foo) prog.$(foo)

moze byc uzyta do kompilacji programu prog.c. Poniewaz spacje sa ignorowane podczas przypisywania wartosci zmiennych, wartosc ‘foo’ wynosi dokladnie ‘c’.

Kombinacja „Znak dolara, znak inny niz: dolar, otwarcie nawiasu zwyklego lub szesciennego” jest traktowana jako odwolanie do zmiennej o nazwie jednoznakowej rownej znakowi nastepujacemu po znaku „$”. Przykladowo, odwolanie do zmiennej ‘x’ moze byc zapisane jako ‘$x’. Praktyka taka jednak nie jest zalecana, z wyjatkiem czynionym dla zmiennych automatycznych.

Podstawianie wartosci zmiennych odbywa sie rekursywnie, tzn. jezeli po interpretacji wartosci zmiennej zawiera ona dalsze odwolania do innych zmiennych, to proces substytucji jest kontynuowany. Przykladowo plik makefile o tresci:

foo = $(bar)

bar = $(ugh)

ugh = Huh?

all:
    echo $(foo)

po uruchomieniu make wypisze na ekranie ‘Huh?’: ‘$(foo)’ zostanie zamienione na ‘$(bar)’, ktore z kolei zostanie zastapione przez ‘$(ugh)’ w koncu zastapione przez ‘Huh?

Istnieje kilka metod definiowania zmiennych:

1. Przez znak rownosci:

nazwa_zmiennej = wartosc

przypisuje zmiennej o nazwie ‘nazwa_zmiennej’ wartosc ‘wartosc

2. Przez dyrektywe ‘define’

Po dyrektywie ‘define’ w tej samej linii wystepuje nazwa zmiennej i nic wiecej. Wartosc zmiennej jest podana w nastepnych liniach. Koniec definicji jest oznaczony przez linie zawierajaca wylacznie slowo ‘endef’. W zwiazku z tym taka definicja moze zawierac znaki konca linii.

3. Przez srodowisko.

Zmienne moga pochodzic ze srodowiska, z ktorym program make zostal uruchomiony. Kazda zmienna srodowiska jest przeksztalcana w zmienna make z taka sama nazwa i wartoscia. Bezposrednie przypisanie wartosci zmiennej w pliku makefile albo w linii wywolania programu make ma przewage nad zmiennymi odziedziczonymi ze srodowiska. (Jezeli zostala uzyta opcja ‘-e’ , zmienne srodowiska maja przewage nad przypisaniami w pliku makefile).

Dlatego ustawiajac zmienna CFLAGS w srodowisku mozna spowodowac, ze kompilacje C dla wiekszosci plikow makefile beda uzywac tak wyspecyfikowanych opcji. Takie postepowanie jest bezpieczne w przypadku zmiennych o standardowym lub powszechnie uzywanym znaczeniu, gdyz wiadomo ze makefile nie wykorzysta jej do innych celow. Ale uwaga: niektore pliki makefile nadaja bezposrednio wartosc zmiennej CFLAGS i zmienna srodowiska nie bedzie miala na nie wplywu.
 

Domyslne reguly kompilacji


Bezposrednie wpisanie komend wykonujacych kompilacje poszczegolnych plikow zrodlowych nie jest konieczne, gdyz istnieja tzw. reguly domyslne: program make „wie” jak stworzyc plik „.o” z odpowiadajacego mu pliku „.c” uzywajac polecenia cc -c. Na przyklad wyda polecenie cc -c main.cc -o main.o w celu stworzenia pliku main.o. Dlatego mozemy pominac komendy z regul dotyczacych plikow wynikowych.

Jezeli plik „.c” jest kompilowany w taki automatyczny sposob, zostanie dodany do listy skladnikow. Dlatego nie musimy tych plikow dolaczac do listy skladnikow bezposrednio.

objects = main.o kbd.o command.o display.o \
    insert.o search.o files.o utils.o

edit : $(objects)
    cc -o edit $(objects)

main.o : defs.h

kbd.o : defs.h command.h

command.o : defs.h command.h

display.o : defs.h buffer.h

insert.o : defs.h buffer.h

display.o : defs.h buffer.h

insert.o : defs.h buffer.h

search.o : defs.h buffer.h

files.o : defs.h buffer.h command.h

utils.o : defs.h

.PHONY : clean

clean :
    -rm edit $(objects)

Tak wlasnie wyglada makefile w praktyce. Regula .PHONY zostanie omowiona pozniej.

Z uwagi na duza wygode, z jaka wiaze sie stosowanie regul domyslnych, sa one czesto spotykane.
 

Inny styl pliku makefile


Jezeli wszystkie pliki wynikowe sa tworzone z uzyciem regul domyslnych, mozliwy jest styl pisania pliku makefile odmienny od powyzszych, polegajacy na grupowaniu regul w zaleznosci od skladnikow a nie wynikow. Oto przyklad:

objects = main.o kbd.o command.o display.o \
       insert.o search.o files.o utils.o

edit : $(objects)
    cc -o edit $(objects)

$(objects) : defs.h

kbd.o command.o files.o : command.h

display.o insert.o search.o files.o : buffer.h

W tym przykladzie defs.h jest skladnikiem wszystkich plikow wynikowych, command.h oraz buffer.h sa skladnikami plikow wynikowych bezposrednio podanych dla kazdego z nich.

Czy tak jest lepiej ? To sprawa osobistych upodoban, jest to postac bardziej zwiezla, ale niektorzy nie lubia jej poniewaz wygodniej dla nich jest miec wszystkie informacje o wyniku w jednym miejscu.
 

Wlasne reguly domyslne


Reguly mowiace w jaki sposob przetwarzac pliki w zaleznosci od ich rozszerzen mozemy takze zdefiniowac samemu. Jezeli nie sa to rozszerzenia standardowo rozpoznawane przez make nalezy pamietac o umieszczeniu ich na liscie skladnikow wyniku .SUFFIXES. Definiuje sie je jako komendy dotyczace celu .ext1.ext2, gdzie ext1 jest rozszerzeniem pliku, ktory bedzie przetwarzany na plik o rozszerzeniu ext2. Oto przyklad definiujacy wlasna regule dla rozszerzenia .cc:

all: program

CFLAGS=-Wall -O2

.cc.o:
    gcc -c $(CFLAGS) $< -o $@

OBJECTS=program.o funkcje.o

program: $(OBJECTS)

gcc $(OBJECTS) -o program

program.o: common.h

funkcje.o: common.h

„Dziwne znaczki” wystepujace w regule dla .cc.o sa wyjasnione w rozdziale o zmiennych automatycznych.
 

Zmienne automatyczne


Przypuscmy, ze chcemy utworzyc domyslna regule kompilujaca plik .c i tworzaca plik .o. Wjaki sposob zapisac komende cc aby operowala na wlasciwej nazwie pliku? Nie mozna napisac tej nazwy bezposrednio, poniewaz musi byc rozna za kazdym razem, gdy regula domyslna jest interpretowana. W tym miejscu nalezy zastosowac specjalny rodzaj zmiennych - zmienne automatyczne. Ich wartosc jest obliczana indywidualnie dla kazdej reguly ktora jest wykonywana, w oparciu o wynik i skladniki reguly. Przykladowo, jako nazwe pliku wynikowego nalezy podac ‘$@’ i jako nazwe pliku zrodlowego ‘$<‘.

Oto lista czesciej uzywanych zmiennych automatycznych :

$@ Nazwa wyniku reguly.

$< Nazwa pierwszego skladnika.

$? Nazwy wszystkich skladnikow nowszych niz wynik, rozdzielone spacjami.

$^ Nazwy wszystkich skladnikow, rozdzielone spacjami. Jezeli ten sam skladnik byl wprowadzony na liste dwukrotnie, na powyzszej liscie wystepuje tylko jeden raz.

$<@D> Katalog w ktorym znajduje sie wynik, bez koncowego znaku ‘/’.

$<@F> Nazwa pliku wyniku, pozbawiona sciezki dostepu.

$(<D) $(<F) Katalog i nazwa pliku pierwszego skladnika.

$(^D) $(^F) Lista katalogow i nazw plikow wszystkich skladnikow.

$(?D) $(?F) Lista katalogow i nazw plikow skladnikow nowszych niz wynik.
 
 

Reguly „porzadkowe” - czyszczenie katalogu


Reguly mozemy pisac nie tylko w celu kompilacji programu. Pliki makefile czesto opisuja takze jak zrobic kilka innych rzeczy, np. jak usunac pliki wynikowe i wykonywalne w celu „wyczyszczenia” katalogu.

Tego typu regula dla przykladowego edytora ma nastepujaca postac:

clean:
    rm edit $(objects)

W praktyce reguly takie moga miec bardziej skomplikowana postac aby uporac sie z nieprzewidzianymi sytuacjami, np.:

.PHONY : clean

clean:
    -rm edit $(objects)

Taki zapis zabezpiecza przed bledami wynikajacymi z istnienia rzeczywistego pliku pod nazwa clean (dyrektywa .PHONY) oraz nakazuje kontynuacje pracy pomimo wystapienia bledu w trakcie wykonania komendy rm („-” przed nazwa komendy).

Regula tego typu nie powinna wystepowac na poczatku pliku makefile gdyz nie chcemy zeby byla uruchamiana domyslnie! Poniewaz clean nie jest skladnikiem edit ta regula nie zostanie uruchomiona jezeli wydamy komende make bez argumentow. W celu jej wykonania nalezy napisac make clean.
 

Specjalne nazwy wynikow


Pewne nazwy maja specjalne znaczenie jezeli pojawia sie jako wyniki:

.PHONY

Skladniki specjalnego wyniku .PHONY sa traktowane jako „falszywe”. W momencie kiedy make rozwaza aktualizacje takiego wyniku, wykona jego komendy niezaleznie od tego, czy plik o takiej nazwie istnieje albo jaki jest czas ostatniej jego modyfikacji.

.SUFFIXES

Zaleznosci tego wyniku sa lista rozszerzen nazw plikow uzywanych przez reguly domyslne.

.DEFAULT

Komendy wyspecyfikowane dla tego wyniku sa uzywane dla kazdego wyniku dla ktorego nie sa zdefiniowane zadne reguly; ani bezposrednie, ani domyslne.

.PRECIOUS

Wyniki wyspecyfikowane na liscie zaleznosci tego wyniku sa traktowane w sposob nastepujacy:

Jezeli make jest przerwane podczas wykonywania ich komend, wynik nie jest kasowany z dysku. Takze, jezeli wynik jest plikiem posrednim, nie zostanie usuniety w momencie, gdy nie jest juz dluzej potrzebny, jak to sie normalnie dzieje.

.IGNORE

Jezeli taka nazwa jest wymieniona jako wynik, .IGNORE nakazuje ignorowac bledy w wykonaniu komend. Skladniki i komendy dla .IGNORE nie maja znaczenia.

.SILENT

Jezeli taka nazwa jest wymieniona jako wynik, .SILENT nakazuje nie wypisywac komend przed ich wykonaniem. Skladniki i komendy dla .IGNORE nie maja znaczenia.

.EXPORT_ALL_VARIABLES

Jezeli nazwa ta jest uimeszczona jako wynik, nakazuje make udostepniac wszystkie zmienne procesom potomnym.
 

Automatyczne generowanie skladnikow


Reczne wypisywanie skladnikow jest bardzo pracochlonne, gdyz nalezy przejrzec kazdy z plikow zrodlowych i wynotowac, jakie pliki on wlacza przy pomocy dyrektywy #include i dalej, jakie pliki naglowkowe sa wlaczane przez te pliki itd. Czynnosc te moze wykonac za nas program gcc. Piszac

gcc -MM nazwa_pliku

do standardowego wyjscia (czyli w tym przypadku na ekran) zostanie wypisana lista zaleznosci pliku nazwa_pliku. Aby otrzymac liste skladnikow wszystkich plikow .cc w biezacym katalogu, piszemy:

gcc -MM *.cc

Aby otrzymac plik o nazwie dependen.inc zawierajacy te liste skladnikow, piszemy

gcc -MM *.cc > dependen.inc.

Makefile wykorzystujace automatyczna generacje skladnikow przedstawiono ponizej:

all: foo

CFLAGS=-Wall -O2

.cc.o:
    gcc -c $(CFLAGS) $< -o $@

OBJECTS=dialog.o mydeskto.o

foo: $(OBJECTS)
   gcc $(OBJECTS) -o foo

include dependen.inc

A oto plik dependen.inc, wygenerowany automatycznie:

dialog.o: dialog.cc dialog.h modal.h object.h constant.h macros.h objects.h \
message.h window.h scroll.h button.h vector.h global.h

mydeskto.o: mydeskto.cc mydesktop.h modal.h object.h constant.h macros.h \
objects.h message.h listbox.h scroll.h button.h menu.h editfield.h dialog.h \

window.h vector.h global.h statictx.h cluster.h mywindow.h motordat.h \
datwindo.h

Instrukcja include nakazuje wlaczenie w miejscu jej wystapienia tekstu innego pliku, w tym przypadku listy skladnikow.