Borland C++ Compiler/MAKE: Różnice pomiędzy wersjami

Usunięta treść Dodana treść
Nie podano opisu zmian
 
Nie podano opisu zmian
Linia 1:
= 4. MAKE =
== 4.1 Wprowadzenie ==
MAKE pozwala zautomatyzować proces budowania projektu programistycznego poprzez stawianie odpowiednich zasad dla plików źródłowych i wywoływaniu komend systemowych. Poza tym program ten znacznie przyspiesza proces budowy programu, ponieważ wykonuje opercje tylko na plikach, które zostały zmodyfikowane od czasu ostatniej kompilacji (uruchomienia MAKE).
== 4.2 Plik Makefile ==
Najważniejszy plik dla MAKE. W nim znajdują się wszelkie zasady operacji na źródłach, wywołania programów, makra i instrukcje odpowiedzialne za zarządzanie budową projektu. Utworzyć go można w najprostszym edytorze tekstowym (np. w systemowym Notatniku). Należy pamiętać jednak, aby plik zapisać jako makefile.mak lub makefile (bez rozszerzenia). To, którą opcje wybierzesz jest już obojętne.
== 4.3 Wywołanie ==
Wywołanie MAKE jest nieco inne niż pozostałych narzędzi Borland-a. W tym przypadku kluczową rolę odgrywa lokalizacja na dysku, z której wywołujesz program. W lokalizacji tej musi się znajdować plik makefile wraz ze skopiowanym (z katalogu \bin naszych FCLT) make.exe. To ważne! Inaczej programy wywoływane z makefile'a nie będą działały poprawnie. Będziesz wywoływać właśnie tę kopię make'a, więc najpierw trzeba wejść w jej lokalizację:
 
CD <lokalizacja>
MAKE [<opcje>] [<wynik>]
 
; <tt><opcje></tt> : MAKE ma mało opcji, a ponadto w większości z nich można zastąpić odpowiednimi komendami w makefile'u, zanim jednak oswoisz się z dyrektywami warto na nie spojrzeć; w następnym podrozdziale będzie o nich więcej
; <tt><wynik></tt> : czyli, plik(i) wynikowy/e Twojego projektu, pamiętaj że nie musisz ich tutaj wymieniać
 
w praktyce wywołanie MAKE może wyglądać tak:
Linia 18:
MAKE -a -s
 
== 4.4 Opcje ==
Jak zwykle skupię się tylko na tych najważniejszych:
{|
Linia 39:
Zazwyczaj opcje te można zastąpić odpowiednimi dyrektywami w samym makefile'u
 
== 4.5 Składnia makefile'a ==
Składnia makefile to już swego rodzaju język programowania do budowy projektów programistycznych. Możemy rozróżnić jej kilka podstawowych elementów:
 
Linia 46:
komendy systemowe "siłą" MAKE jest możliwość wywoływania nie tylko narzędzi Borland-a, ale także komend DOS'owych i innych programów takich jak np. kompilator assemblera.
; zmienne : tak jak w każdym języku programowania "kontenery" do których możemy przypisać łańcuchy znaków.
; dyrektywy : chociaż będę je opisywał na końcu nie powinny być lekceważone, to jedne z najważniejszych elementów w każdym języku programowania, tak samo jest i tutaj.
 
A tak wygląda przykładowy makefile, z którego korzystasz na co dzień prawdopodobnie nawet o tym nie wiedząc, czyli <tt>builtins.mak</tt> z BIN'a:
 
#
Linia 86:
!endif
 
Czarna magia? W następnych podrozdziałach omówiębędzie mowa o najważniejsze elementyelementach składni pliku makefile.
 
Aby to co pisze nie było tylko czystą teorią wymyśliłem projekt ''HelloProject'', którego makefile będziemy rozwijać w miare postępów w nauce. Jak zorganizować sobie miejsce pracy nad tym projektem? Należy utworzyć nowy katalog z nazwą projektu: <tt>C:\Hello\</tt> Następnie trzeba tam skopiować wszystkie jego pliki źródłowe:
* <tt>Hello.cpp</tt>
* <tt>klasa.cpp</tt>
* <tt>klasa.h</tt>
* <tt>zasoby.rc</tt>
* <tt>DEF.def</tt>
* <tt>makefile.mak </tt>
Wynikiem tego projektu będzie windowsowy EXE'k: Hello.exe. Nieważne, co będzie robił ten program, to tylko ćwiczenie, resztę pozostawiam Tobie, Twojej wyobraźni i inwencji. MAKE będziemy wywoływać tam, gdzie jest makefile, czyli z katalogu <tT>c:\Hello\</tt>. Tam też powinna znaleźć się kopia programu MAKE.
 
== 4.6 Komentarze ==
== Komentarze ==
W makefile'u tak jak w C++, również możesz używać komentarzy jednoliniowych. Wszystko to co znajdzie się w jednej linii po symbolu hash'a: '#' MAKE ignoruje.
W makefile'u tak jak w C++, również możesz używać komentarzy jednoliniowych. Wszystko to co znajdzie się w jednej linii po symbolu hash'a: '''<tt>#</tt>''' MAKE ignoruje.
 
# to jest komentarz
 
== 4.7 Zasady ogólne i szczegółowe ==
Zasady regulują wywoływanie narzędzi w makefile'u. Ogólnie rzecz biorąc zasady określają jakie pliki ma przetwarzać MAKE, ewentualnie gdzie one się znajdują, gdzie ma się znaleźć wynik operacji. Ważną kwestią w zrozumieniu zasad jest fakt, że zasada to tylko prawo, za wypełnienie którego odpowiedzialny jesteś Ty. Pamiętaj też że zasada przekazuje tylko informacje dla MAKE, natomiast komendy, które za jej pomocą wywołujesz rządzą się już swoimi prawami. Zarówno przy komendach, jak i przy samych zasadach można używać symbolu '<tt>\'</tt> jeśli zabraknie Ci miejsca w linii:
 
<txt>\
<dok. txt'u>
 
Ogólny szablon zasady wygląda tak:
 
<zasada>
<komenda>
[<komenda>]
[...]
 
Pamiętaj tylko o przynajmniej jednej spacji przed komendą, aby nie znajdowała się ona tuż przy początku linii - to błąd! Błędem jest też nie zapisanie samej zasady przy początku linii. Jednym słowem musi to wyglądać tak, jak na powyższym szablonie. Zasada zacznie "wchodzić w życie" (wywoływać swoje komendy), jeśli jej pliki zależne (źródłowe) będą różniły się datą i czasem ostatniej modyfikacji z datą plików wynikowych lub jeśli pliki wynikowe w ogóle nie będą istnieć. Np. jeśli chcemy skompilować plik <tt>Hello.cpp</tt> za pomocą zasad to kompilator zostanie wywołany kiedy <tT>Hello.cpp</tT> i <tt>Hello.obj</tt> będą miały inne daty modyfikacji lub jeśli plik <tt>Hello.obj</tt> nie będzie istniał. Jak widać w tytule tego rozdziału zasady możemy podzielić na dwa typy: ogólne i szczegółowe (''implicit & explicit rules'').
=== 4.7.1 Zasady ogólne (''implicit rules'') ===
Zasady te tyczą się wszystkich plików z określonym rozszerzeniem (ewentualnie możesz określić folder, w którym MAKE ma zezwalać na operacje na nich). Zasady te zakładają tylko zmianę rozszerzenia, a nazwy plików pozostawiają. ''Implicit rule'' wygląda tak:
 
[{<lokalizacja1>}].<roz1>[{<loklalizacja2>}].<roz2>:
<komenda/y>
 
gdzie:
 
; <tT><lokalizacja1></tt> : lokalizacja/e plików zależnych (źródłowych) (w przypadku gdy jest ich więcej jako seperatora używamy średnika:';')
; <tt><roz1></tt> : rozszerzenie plików źródłowych
; <tt><lokalizacja2></tt> : lokalizacja/e na dysku plików plików docelowych (wynikowych) operacji (gdzie mają się one znaleźć) oddzielone jedna od drugiej średnikiem:';'
; <tt><roz2></tt> : rozszerzenie plików wynikowych
 
Na przykład:
Linia 134 ⟶ 135:
copy hello.obj c:\obj
 
Przejdźmy teraz do analizy tych trzech linijek kodu. Pozwoli Ci to lepiej zrozumieć działanie zasad make'a. W tym przypadku zasada zezwala na operacje na plikach *.cpp tylko z katalogu <tT>c:\src</tt>, a ich wynik w magiczny sposób musi znaleźć się w <tt>c:\obj</tt>, oczywiście nie da się tego dokonać bez pomocy systemowego słowa kluczowego COPY albo użycia w parametrach wywołania kompilatora opcji -n. Jeśli nie określisz w zasadzie tych lokalizacji, to dozwolone będą operacje na wszystkich plikach na całym Twoim twardzielu. Określając w zasadzie lokalizacje plików źródłowych nie łudź się że zostanie to przekazane komendom które wywołujesz, dlatego podczas kompilacji zapisałem plik <tT>hello.cpp</tt> wraz jego z lokalizacją, oczywiście nie musiałbym tego robić, gdybyśmy odpalili MAKE z lokalizacji <tT>c:\src</tt>.
=== 4.7.2 Zasady szczegółowe (explicit rules)===
Zasady szczegółowe określają z jakich plików źródłowych powstaje plik wynikowy. W tym przypadku musimy podać dokładne nazwy plików a nie jak to było w przypadku zasad ogólnych samego rozszerzenia plików. Spójrzmy na składnię:
 
<wynik(i)>:[:] [{<lokalizacja>}]<src>
<komenda/y>
 
; <tt><wynik(i)></tt> : nazwa pliku/ów wynikowego/ych, który ma być wygenerowany
; <tt><lokalizacja></tt> : lokalizacja/e plików źródłowych odzielone od siebie średnikami:';'
; <tt><src></tt> : plik(i) źródłowy/e
 
Przykład powinien rozwiać wszelkie wątpliwości:
Linia 151 ⟶ 152:
Hello.exe,,cw32.lib import32.lib,,
 
Zasada zakłada że <tt>Hello.obj</tt> i <tt>klasa.obj</tt> znajdują się tylko i wyłącznie w katalogu <tt>c:\obj</tt>. Tak jak w zasadach ogólnych tak i tutaj ta lokalizacja nie ma nic wspólnego z komendami - i w tym przypadku musisz podać lokalizacje każdego ze swoich plików źródłowych (oczywiście jeśli znajdują się one tam gdzie makefile nie musisz tego robić). Zmieni się to dopiero po wprowadzeniu do kodu [[Borland C++ Compiler:MAKE#Specjalne zmienne|inteligentnych makrzmiennych]] które będą potrafiły czerpać informacje z zasad, ale to jeszcze odległy temat...
 
Wszystkie pliki docelowe (wynikowe) z zasad ogólnych powinny być automatycznie plikami źródłowymi w zasadzie szczegółowej. Inaczej MAKE danej zasady ogólnej nie weźmie pod uwagę. Pokaże to na przykładzie:
Linia 165 ⟶ 166:
cw32.lib import32.lib,,zasoby.res
 
W tym przypadku konsolidator zgłosi błąd, że nie może znaleźć pliku zasoby.res, nic dziwnego - brcc32BRCC32 wcale nie został wywołany.<br>
MAKE zakłada, iż zawsze dysponujemy tylko jednym plikiem wynikowym, ale i temu można zaradzić tworząc symboliczne pliki wynikowe o tak:
 
ALL: <PlikWynikowy1> <PlikWynikowy2> [<Plikwynikowy3> [...]]
 
Teraz nic nie stoi na przeszkodzie, aby wcześniejszy niefortunny kod zamienić na poniższy:
Linia 192 ⟶ 193:
implib mylib +lib3.obj +lib4.obj
 
Najpierw do biblioteki statycznej mylib.lib dodawane są <tt>obj1.obj</tt> i <tt>obj2.obj</tt>, następnie w drugiej zasadzie do tej samej biblioteki odajemy: <tt>obj3.obj</tt> oraz <tt>obj4.obj</tt>.
=== 4.7.3 Zasada "bez zasad" :-)===
Jest jeszcze jedna zasada, którą odkryłem analizując makefile generowany przez program vide, co ciekawe help FCLT nic o niej nie pisze, zachowuje się jakby w ogóle nie istniała... no comment. Właściwie to nie wiem, czy można by nazywać to zasadą, bo nie określa żadnych praw dostępu do plików. Jej składnia jest miej więcej taka:
 
<nazwa>:
<komenda/y>
 
Przydaje się ona do wywołania narzędzi typu debugger lub TOUCH które nie czynią żadnych zmian w rozszerzeniu pliku zatem nie można ich normalnie wywołać z jakichkolwiek wcześniejszych zasad. Poza tym zasada ta zawsze "wejdzie w życie", gdyż nie mamy tu żadnych plików zależnych ani wynikowych. Pewnym jej ograniczeniem jest to że po zakończeniu jej działań MAKE także kończy. Cóż, nie można mieć wszystkiego. Dla przykładu podaje wywołanie programu TOUCH:
Linia 205 ⟶ 206:
touch *.cpp *.rc
 
==4.8 Komendy systemowe==
Na co nam zasady skoro i tak nigdy nie zostaną poparte działaniami? - na nic. Dlatego, aby coś zaczęło działać trzeba to...wywołać :). Jak już pisałem oprócz zwykłych wywołań narzędzi z FCLT można tu też umieszczać komendy systemowe, czy włączać inne program takie jak np. kompilator assemblera, czy swój własny program już po skompilowaniu. Pamiętaj jednak, że komendy nie mogą występować w makefile'u od tak sobie, muszą one zawierać się w jakiejś zasadzie tylko w ten sposób można wywoływać narzędzia przez MAKE, inaczej program zgłosi błąd. Poza tym przy wywołaniu 'czegoś' można użyć następujących przedrostków:
{|
Linia 237 ⟶ 238:
zasoby.res
 
Ten makefile jest dość prymitywny, jednak automatyzuje on już całą budowę projektu ''HelloProject''. Spróbuj go odpalić samemu.
==4.9 MakraZmienne ==
W makefile'u możemy deklarować makra/zmienne gdzie umieścimy pewne łańcuchy znaków, później te "stringi" będziemy mogli wykorzystać wpisując nazwę makra. Ci, którzy kiedykolwiek pracowali ze zmiennymi napewno będę wiedzieli o co chodzizmiennej. Przypisywanie wartości do makrazmiennej wygląda tak:
 
<nazwa> = [<łańcuch_znaków>]
 
Zwroć uwagę, że przypisywany łańcuch nie jest ujęty w "" tak jak to jest w C/C++, wszystko to co znajdzie się za znakiem równości: '<tt>='</tt> w jednej lini zostanie przypisane do makra/zmiennej <nazwa>. np.:
 
SRC = Hello.cpp klasa.cpp
 
Wykorzystanie tegotej makrazmiennej w makefile'u będzie za to wyglądać co najmniej dziwnie:
 
.cpp.obj:
bcc32 -c $(SRC)
 
I tak każdekażda makrozmienna przez Ciebie zadeklarowane będzie poprzedzane '$' i ujęte w nawiasy '()', ale analizując te dwa przykłady już możesz odkryć pożyteczność tego wynalazku: deklarując na początku makro SRC nie musisz pamiętać już wszystkich plików źródłowych, które wykorzystasz w swoim programie, nawet jeśli projekt się rozrośnie nie będziesz musiał dopisywać w każdym wywołaniu korzystającym ze źródeł jeszcze jednego pliku, ale wystarczy, że na samym początku dopiszesz jeszcze jeden plik *.cpp, a makrozmienna będzie wtedy wskazywałowskazywała już na dwa pliki źródłowe. Jak już pisałem makrazmienne to po prostu łańcuchy znaków, więc możemy przypisać im również opcje, jakie chcemy wykorzystać przy wywoływaniu narzędzi:
 
C_FLAGS = -tW -c -v -w-par -w-rvl
Linia 261 ⟶ 262:
bcc32 $(C_FLAGS) $(SRC)
 
===4.9.1 Zmiana wartości makrazmiennej===
Każdemu zadeklarowanemu przez nas makru można zmienić wartość. Robimy to korzystając z następującego szablonu:
 
$(<nazwa_zm>:<stary_txt>=<nowy_txt>)
$(NazwaMakra:StaryTxt=NowyTxt)
 
Na przykład można to wykorzystać do zmiany rozszerzenia plików z jakich korzystamy:
Linia 271 ⟶ 272:
$(SRC:.cpp=.obj)
 
Teraz nie musisz pisać już dodatkowegododatkowej makrazmiennej z wynikami konslolidacji - makrozmienna <tt>$(SRC)</tt> możemy wykorzystać do wywołania kompilatora, a do wywołania konsolidatora - <tt>$(SRC:.cpp=.obj)</tt>.
===4.9.2 Specjalne makrazmienne===
Istnieją jeszcze makrazmienne specjalne, które są już stworzone przez MAKE i możesz je używać w komendach tuż po zasadach. Są one bardzo wygodne:
{|
|zmienna
|makro
| przekazywane dane w zasadach ogólnych
| przekazywane dane w zasadach szczegółowych
|-
| $*
| plik źródłowy (bez rozszerzenia) z lokalizacją
| lokalizacja\PlikŹródłowy
| plik wynikowy (bez rozszerzenia) z lokalizacją
| lokalizacja\PlikWynikowy
|-
| $<
| plik źródłowy+rozszerzenie z lokazlizacją
| lokazlizacja\PlikŹródłowy+roz
| plik wynikowy+rozszerzenie z lokazlizacją
| lokalizacja\PlikWynikowy+roz
|-
| $:
| lokalizacja PlikówŹródłowychplików źródłowych
| lokalizacja PlikuWynikowegopliku wynikowego
|-
| $.
| PlikŹródłowyplik źródłowy+rozszerzenie
| plik wynikowy+rozszerzenie
| PlikWynikowy + Rozszerzenie
|-
| $&
| plik źródłowy
| PlikŹródłowy
| plik wynikowy
| PlikWynikowy
|-
| $@
| plik wynikowy+rozszerzenie z lokalizacją
| lokalizacja\PlikWynikowy+roz
| to samo co $<
|-
| $**
| to samo co $<
| wszystkie PlikiŹródłowepliki źródłowe+rozrozszerzenie
|-
| $?
| to samo co $<
| stare PlikiŹródłowepliki źródłowe
|}
 
Linia 317 ⟶ 318:
bcc32 -q -c $<
 
ten kod spowoduje kompilacje wszystkich plików *.cpp w katalogu <tt>c:\cpp</tt>, a pliki *.obj, które wygeneruje umieści w lokalizacji w której znajduje makefile.
To nie wszystko - makromzmiennym specjalnym można też zmieniać wartości stosując specjalne znaczniki. Znaczniki te są najczęściej używane przy makrachzmiennych: <tt>$@</tt> i <tt>$<</tt>. Dodanie takiego znacznika modyfikującego wartość danegodanej makrazmiennej powinno wyglądać tak:
 
$(StdMakro<znak specjalnego makra>:[<znacznik>])
 
Oto typy znaczników do standardowych makr MAKE'a:
{|
|znacznik
| przekazywane dane o pliku
| przykład użycia
| przykładowy rezultat
|-
| D
Linia 354 ⟶ 355:
bcc32 -q -c -n$(@D) $<
 
ten fragment skryptu spowodowałby kompilację plików *.cpp z katalogu <tt>c:\cpp</tt> i zapisywałby pliki *.obj do katalogu <tt>c:\obj</tt>.
 
Twoje makrazmienne powinny być w szczególności używane przy zasadach jakie będziesz stawiał, a makrazmienne specjalne w komendach np.:
 
$(BIN): $(SRC:.cpp=.obj)
ILINK32 $(L_FLAGS) c0w32.obj $**,$.,,cw32.lib import32.lib $(LIB),$(DEF),$(RES)
 
Używając makr <tt>$**</tt> lub <tt>$?</tt> w zasadzie szczegółowej (''explicit rule'') można do komendy dodać przedrostek '''&''', który będzie oznaczał, że komenda odnosi się tylko do jednego pliku z pola src (normalnie makra <tt>$**</tt> i <tt>$?</tt> w zasadach szczegółowych oznaczają wszystkie pliki źródłowe, co uniemożliwi pracę niektórych komend) np.:
 
hello.exe: hello.obj klasa.obj
copy $** c:\obj
 
Po takiej operacji komenda systemowa COPY zgłosi błąd, ponieważ <tt>$**</tt> wskazuje na pliki <tt>hello.obj</tt> i <tt>klasa.obj</tt>, a COPY może obsługiwać tylko jeden plik. Ten błąd da się naprawić:
 
hello.exe: hello.obj klasa.obj
Linia 376 ⟶ 377:
copy klasa.obj c:\obj
 
..aha przed przystąpieniem do kolejnego rozdziału rozwiniemy nasz ''HelloProject'' o makraobsługe zmiennych. Aby zachować pewien porządek w katalogu <tt>c:\Hello</tt> najpierw stworzymy następujące podkatalogi,a w nich zawrzemy pliki do projektu ''HelloProject'':
{|
.\src\ <-- Hello.cpp klasa.cpp
| <tt>.\src\hdr\ <-- klasa.h/tt>
| <-- Hello.cpp klasa.cpp
.\src\res\ <-- zasoby.res
|-
.\obj\ <-- makefile.mak DEF.def make.exe *.obj *.li?
|<tt>.\src\hdr\</tt>
.\bin\
| <-- klasa.h
|-
|<tt>.\src\res\</tt>
| <-- zasoby.res
|-
|<tt>.\obj\</tt>
| <-- makefile.mak DEF.def make.exe *.obj *.li?
|-
|<tt>.\bin\</tt>
|}
Chyba nie muszę wyjaśniać przeznaczenia tychże katalogów? Ich nazwy mówią same za siebie. Teraz proszę zerknąć na ten kod:
 
Linia 416 ⟶ 427:
DEL $.
 
Jak widać makefile bardzo zyskał na funkcjonalności i co najważniejsze na uniwersalności: dzięki makromzmiennym cały czas możesz zmieniać skład plików projektu zasadniczo nie ingerując w zasady. Nawet jeśli w projekcie nie przewidujesz zasobów to nie będzie tragedii: operacja kompilacji zasobów nie będzie miała miejsca a plik nie zostanie dodany przez konsolidator. Poza tym makrozmienna <tt>DIR</tt> nie zostałozostała stworzone bez powodu. Dzieki niemuniej możliwa jest także budowa nowego, całkowicie innego projektu, którego wynikiem będzie <tt>$(BIN)</tt>, plikami źródłowymi <tt>$(SRC)</tt> itd. Oczywiście przy tej operacji trzeba zachować tę samą strukturę podkatalogów projektu i rozmieszczenia w nich typów plików.<br>
Teraz małe sprostowanie tak dziwnego zapisu kompilacji zasobów: <tt>brcc32</tt> nie zachowuje się jak większość programów z FCLT ponieważ zostawia plik wynikowy w lokalizacji ze źródłami.<br>
Jest pewien mankament tego makefile'a: jeśli nie masz plików zależnych zasady szczegółowej MAKE się o nie upomni, choć mają one dopiero powstać w trakcie trwania makefile'a. Jest na to rada: przed pierwszym (później nie ma już takiej potrzeby) uruchomieniem makefile'a "dotknij" programem TOUCH wszystkie pliki źródłowe. Np. w tym projekcie będzie to wyglądać tak:
 
Linia 429 ⟶ 440:
Można rozwiązać to w ten sposób... ale jest znacznie bardziej funkcjonalna dyrektywa...
==Dyrektywy==
Dyrektyw/słów kluczowych w borlandowskim makefile'u jest mniej niż np. w C++ ale wystarczająco dużo, aby efektywnie zarządzać projektem. W tym przypadku spisałem niemal wszystkie wyrażenia (chyba że sam któregoś nie rozumiałem). Nie musisz się ich uczyć na pamięć, wystarczy, że będziesz miał te 5 tabelek zawsze pod ręką. Warto jednak na początek je przeczytać, żeby przekonać się jakie są możliwości słów kluczowych w MAKE.
Ważną zasadą przy wykorzystywaniu dyrektyw jest to aby MAKE nie pomylił danej dyrektywy z wywołaniem programu. Jeśli więc chcemy dyrektywa umieścić w polu komend jakiejś zasady to trzeba ją postawić na początku wiersza w innym przypadku zostanie ona potraktowana jako komenda systemowa, a to może oznaczać tylko kłopoty...
===Pliki Projektu i konfiguracja MAKE===
Linia 437 ⟶ 448:
| opis
|-
|<pre>.autodepend</pre>
| -a
| sprawdzaj pliki nagłówkowe przed kompilacją i jeśli zostaną zmodyfikowane kompiluj jeszcze raz pliki które z nich korzystają
|-
|<pre>.noautodepend</pre>
| -a-
| nie sprawdzaj plików nagłówkowych
|-
|<pre>.cacheautodepend</pre>
| -c
| przechowuj w pamięci podręcznej pliki wchodzące w skład projektu i jeśli nie zostaną w poczynione żadne zmiany nie wykonuj na nich operacji ponownie
|-
|<pre>.nocacheautodepend</pre>
| -c-
| nie przechowuj w pamięci podręcznej plików wchodzących w skład projektu
|-
|<pre>.keep</pre>
| -K
| zachowuj pliki tymczasowe tworzone podczas działania programu MAKE
|-
|<pre>.nokeep</pre>
| -K-
| nie przechowuj plików tymczasowych które są tworzone podczas działania MAKE'a
|-
|<pre>.ignore</pre>
| -i
| ignoruj wartość jaką zwróci komenda
|-
|<pre>.noIgnore</pre>
| -i-
| nie ignoruj wartości jaką zwróci komenda
|-
|<pre>.silent</pre>
| -s
| nie pokazuj na ekranie wywołania narzedzia
|-
|<pre>.nosilent</pre>
| -s-
| pokazuj na ekranie wywołanie narzędzia lub komendę jaką wykonuje system
|-
|<pre>.swap</pre>
| -S
| wyczyść swoją pamięć zanim zaczniesz wywoływać narzędzia (ta instrukcja jest dobra podczas operacji na dużych plikach)
|-
|<pre>.noswap</pre>
| -S-
| nie czyść pamięci przed wywoływaniem narzędzi
|}
{|
 
|szablon
|opis
|-
.precious: PlikWynikowy zapisz PlikWynikowy (jeśli któryś z programów "padnie" MAKE często wyrzuca swój plik wynikowy)
|<pre>.precious: <PlikWynikowy></pre>
.suffixes: .roz1 [.roz2 [.roz3]..] twórz pliki najpierw z .roz1 później z .roz2, a na końcu z .roz3 ta dyrektywa jest analizowana przez zasady ogólne i określa ona porządek tworzenia PlikówDocelowych
| zapisz <tt><PlikWynikowy></tt> (jeśli któryś z programów "padnie" MAKE często wyrzuca swój plik wynikowy)
.path.roz = lokalizacja szukaj pliku z danym .roz w podanej lokalizacji (ta dyrektywa niweluje problem z wcześniejszym makefile'em)
|-
!include [lokalizacja]Nazwapliku dodaj tekst do obecnego makefile'a z pliku "NazwaPliku"
|<pre>.suffixes: .<roz1> [.<roz2> [.<roz3>]..]</pre>
!undef NazwaMakra wyrzuć makro "NazwaMakra"
| twórz pliki najpierw z rozszerzeniem: <tt><roz1></tt> później z <tt><roz2></tt>, a na końcu z <tt><roz3></tt> (itd.); ta dyrektywa jest analizowana przez zasady ogólne i określa ona porządek tworzenia <tt>PlikówDocelowych</tt>
|-
|<pre>.path.<roz> = <lokalizacja></pre>
|szukaj pliku z rozszerzeniem <tt><roz></tt> w podanej <tt><lokalizacji></tt> (ta dyrektywa niweluje problem z wcześniejszym makefile'em)
|-
|<pre>!include [<lokalizacja>]<Nazwapliku></pre>
| dodaj tekst do obecnego makefile'a z pliku <tT><NazwaPliku></tt> (działa jak makro-instrukcja <tt>#include</tt> w C/C++)
|-
|<pre>!undef <nazwa_zmiennej></pre>
| "wyrzuć" zmienną <tt>nazwa_zmiennej</tt>
|}
===Instrukcje warunkowe===
{|
szablon opis
|szablon
!ifdef NazwaMakra operacja jeśli makro "NazwaMakra" jest zadeklarowane wykonaj operacje
| opis
!ifndef NazwaMakra operacja jeśli makro "NazwaMakra" nie jest zadeklarowane wykonaj operacje
|-
!if warunek operacja jeśli warunek zostanie spełniony wykonaj operacje
|<pre>!ifdef <nazwa_zmiennej> <operacje></pre>
!else operacja w przeciwnym wypadku... (musi występować z !if lub !ifdef lub !ifndef)
| jeśli zmienna <tt><nazwa_zmiennej></tt> jest zadeklarowana wykonaj <tt><operacje></tt>
!elif warunek operacja w przeciwnym wypadku, jeśli warunek jest spełniony wykonaj operacje (musi występować z !if lub !ifdef, !ifndef, !else)
|-
!endif kończy instrukcję warunkową
|<pre>!ifndef <nazwa_zmiennej> <operacje></pre>
| jeśli zmienna <tt><nazwa_zmiennej></tt> <u>nie</u> jest zadeklarowane wykonaj <tt><operacje></tt>
|-
|<pre>!if <warunek> <operacje></pre>
| jeśli <tt><warunek></tt> zostanie spełniony wykonaj <tt><operacje></tt>
|-
|<pre>!else <operacje></pre>
| w przeciwnym wypadku wykonaj <tt><operacje></tt> (musi występować z <tt>!if</tt> lub <tt>!ifdef</tt> lub <tt>!ifndef</tt>)
|-
| <pre>!elif <warunek> <operacje></pre>
| w przeciwnym wypadku, jeśli <tt><warunek></tt> jest spełniony wykonaj <tt><operacje></tt> (musi występować z <tt>!if</tt> lub !ifdef</tt>, <tt>!ifndef</tt>, <tt>!else</tt>)
|-
|<pre>!endif</pre>
|kończy instrukcję warunkową
|}
 
Instrukcję warunkową warto bardziej wnikliwie zanalizować, ponieważ jak sądzę będziesz ją często używał(a).
 
# Każda instrukcja warunkowa musi się kończyć dyrektywą !endif
# Przy określaniu warunków instrukcji warunkowej możemy użyć między innymi następujących operatorów:
{|
operator opis operator opis
| operator
- negacja || logiczne OR
| opis
+ dodawanie ! logiczne NOT
| operator
- odejmowanie && logiczne AND
| opis
* mnożenie >= większe lub równe
|-
/ dzielenie <= mniejsze lub równe
| -
== równe > większe
| negacja
!= nierówne < mniejsze
| ||
3. Aby sprawdzić czy makro jest zadeklarowane(tj. czy je wcześniej stworzyliśmy) można użyć specjalnego znacznika d. Zastosowanie go jest równoważne z użyciem dyrektywy !ifdef:
| logiczne "lub"
* !ifdef makro to, to samo co !if $d(macro)
|-
* !ifndef makro to, to samo co !if !$d(macro)
| +
| dodawanie
| !
| logiczne "nie"
|-
| -
| odejmowanie
| &&
| logiczne "i"
|-
| *
| mnożenie
| >=
| większe lub równe
|-
| /
| dzielenie
| <=
| mniejsze lub równe
|-
| ==
| równe
| >
| większe
|-
| !=
| nierówne
| <
| mniejsze
|}
# Aby sprawdzić czy zmienna jest zadeklarowana (tj. czy je wcześniej stworzyliśmy) można użyć specjalnego '''znacznika d'''. Zastosowanie go jest równoważne z użyciem dyrektywy !ifdef:
* <tt>!ifdef <zmienna></tt> to, to samo co <tt>!if $d(<zmienna>)</tt>
* <tt>!ifndef <zmienna></tt> to, to samo co <tt>!if !$d(<zmienna>)</tt>
 
===Ekran===
Linia 523 ⟶ 595:
| rezultat
|-
| <pre>!error <komunikat></pre>
| polecenie, po natrafieniu na które MAKE kończy działanie i wyświetla na ekranie rezultat...
| <tt>Fatal makefile NumerLini<numer_linii>: Error directive: <komunikat></tt>
|-
| <pre>!message <komunikat></pre>
| jeśli MAKE natrafi na to polecenie, wyświetla na ekranie rezultat...
| <tt><komunikat></tt>
|}
 
==MAKE w praktyce==
Teraz pora na wzbogacenie naszego makefile'a o dyrektywy i tym samym doprowadzenie skryptu do ostatecznej wersji. Przeanalizuj ten kod, a na pewno rozjaśni Ci się w głowie, o czym pisałembyła mowa przez ostatnie 25kB :). Do tego projektu potrzebujesz jeszcze jeden plik: <tt>HelloProject.txt</tt> który jako jedyny powinien się znaleźć w katalogu głównym projektu. Nasz ostateczny makefile będzie się bowiem składał z dwóch plików pierwszy to przed chwilą utworzony <tt>HelloProject.txt</tt>, a drugi to wszystkim znany <tt>makefile.mak</tt> (połączone są one dyrektywą <tt>!include</tt>). Dodatkowo w katalogu <tt>c:\Hello\src</tt> powinieneś utworzyć jeszcze jeden folder i zamieścić tam pliki *.asm:
 
.\asm <-- ewentualne wstawki asmowe (*.asm)
Linia 539 ⟶ 611:
Teraz możesz przejść do analizy.
 
'''c:\Hello\HelloProject.txt:'''
 
#----------------------------
Linia 565 ⟶ 637:
 
 
'''c:\Hello\obj\makefile.mak:'''
 
DIR = c:\Hello #nie wstawiaj na końcu '\'
Linia 626 ⟶ 698:
# implementacja (<tt>makefile.mak</tt>) - część, w której zawarte są same komendy budowy projektu, ten segment jest tworzony tylko raz podczas pisania makefile'a i od tego czasu raczej nie powinieneś go ruszać;
 
Tak naprawdę skrypt ten jest na tyle uniwersalny, że możesz używać go w wielu projektach (byle była zachowana odpowiednia struktura katalogów). Do makefile'a dodałem zasadę kompilującą w wstawki assemblerowe. Program TASM32, który jest kompilatorem assemblera niestety nie należy on do FCLT, to wciąż komercyjny produkt Borlanda. Opcję tą dodałem tylko po to, aby makefile był już w 100% kompletny. W kodzie użyłem jeszcze jednego programu spoza FCLT. Jest to Turbo Debugger (TD32).TD32 to świetny darmowy debugger który powinieneś mieć w swojej kolekcji, jest do ściągnięcia z ze [http://www.borland.pl|strony internetowej Borlanda (www.borland.pl)].