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