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 operacje tylko na plikach, które zostały zmodyfikowane od czasu ostatniej kompilacji, ma to szczególnie duże znaczenie w przypadku dużych projektów.

Plik Makefile Najważniejszy plik dla MAKE. W nim znajdują się wszelkie zasady operacji na źródłach, wywołania programów, zmienne 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ą opcję wybierzesz jest już obojętne.

Wywołanie Wywołanie MAKE jest nieco inne niż pozostałych narzędzi Borlanda. 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 MAKE [] []

MAKE ma mało opcji, a ponadto w większości z nich można zastąpić odpowiednimi komendami w makefile'u; więcej na ten temat w rozdziale: Plik projektu i konfiguracja MAKE 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:

CD c:\Hello MAKE -a -s

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:

komentarze po prostu komentarze, tak jak w innych językach programowania zasady zasady wg których MAKE dobiera pliki źródłowe i wynikowe po wypełnieniu których wywołuje... 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 builtins.mak z BIN'a:

# Inprise C++Builder - (C) Copyright 1999 by Borland International # CC = bcc32 RC = brcc32 AS = tasm32

!if $d(__NMAKE__) CXX = bcc32 -P CPP = bcc32 -P !endif

.asm.obj: $(AS) $(AFLAGS) $&.asm

.c.exe: $(CC) $(CFLAGS) $&.c

.c.obj: $(CC) $(CFLAGS) /c $&.c

.cpp.exe: $(CC) $(CFLAGS) $&.cpp

.cpp.obj: $(CC) $(CPPFLAGS) /c $&.cpp

.rc.res: $(RC) $(RFLAGS) /r $&

.SUFFIXES: .exe .obj .asm .c .res .rc

!if !$d(BCEXAMPLEDIR) BCEXAMPLEDIR = $(MAKEDIR)\..\EXAMPLES !endif

Czarna magia? W następnych podrozdziałach będzie mowa o najważniejsze elementach 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 miarę postępów w nauce. Jak zorganizować sobie miejsce pracy nad tym projektem? Należy utworzyć nowy katalog z nazwą projektu: C:\Hello\ Następnie trzeba tam skopiować wszystkie jego pliki źródłowe: Hello.cpp klasa.cpp klasa.h zasoby.rc DEF.def makefile.mak 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 c:\Hello\. Tam też powinna znaleźć się kopia programu MAKE.

Komentarze W makefile'u tak jak w C++, również możesz używać komentarzy jedno liniowych. Wszystko to co znajdzie się w jednej linii po symbolu hash'a: # MAKE ignoruje.

# to jest komentarz

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 \ jeśli zabraknie Ci miejsca w linii:

\

Ogólny szablon zasady wygląda tak:

[] [...]

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 Hello.cpp za pomocą zasad to kompilator zostanie wywołany kiedy Hello.cpp i Hello.obj będą miały inne daty modyfikacji lub jeśli plik Hello.obj 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). 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:

[{}].[{}].:

gdzie:

lokalizacja/e plików zależnych (źródłowych) (w przypadku gdy jest ich więcej jako separatora używamy średnika:';') rozszerzenie plików źródłowych lokalizacja/e na dysku plików wynikowych (docelowych) operacji (gdzie mają się one znaleźć) oddzielone jedna od drugiej średnikiem:';' rozszerzenie plików wynikowych

Na przykład:

{C:\src}.cpp{C:\obj}.obj: bcc32 -c c:\src\hello.cpp 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 c:\src, a ich wynik w magiczny sposób musi znaleźć się w c:\obj, 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 hello.cpp wraz jego z lokalizacją, oczywiście nie musiałbym tego robić, gdybyśmy odpalili MAKE z lokalizacji c:\src. 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ę:

:[:] [{}]

nazwa pliku/ów wynikowego/ych, który ma być wygenerowany lokalizacja/e plików źródłowych oddzielone od siebie średnikami:';' plik(i) źródłowy/e

Przykład powinien rozwiać wszelkie wątpliwości:

Hello.exe: {C:\obj}Hello.obj klasa.obj ilink32 /aa /Tpe c0w32.obj C:\obj\Hello.obj C:\obj\klasa.obj,\ Hello.exe,,cw32.lib import32.lib,,

Zasada zakłada że Hello.obj i klasa.obj znajdują się tylko i wyłącznie w katalogu c:\obj. 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 inteligentnych zmiennych które będą potrafiły czerpać informacje z zasad, ale o tym później.

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:

.cpp.obj: bcc32 -c Hello.cpp

.rc.res: # MAKE nie zwraca na tę zasadę uwagi, bo nie dodałeś brcc32 zasoby.rc # pliku zasoby.res do src zasady szczegółowej

Hello.exe: Hello.obj ilink32 /aa /Tpe c0w32.obj Hello.obj klasa.obj,Hello.exe,,\ cw32.lib import32.lib,,zasoby.res

W tym przypadku konsolidator zgłosi błąd, że nie może znaleźć pliku zasoby.res, nic dziwnego - BRCC32 wcale nie został wywołany. MAKE zakłada, iż zawsze dysponujemy tylko jednym plikiem wynikowym, ale i temu można zaradzić tworząc symboliczne pliki wynikowe o tak:

ALL: [ [...]]

Teraz nic nie stoi na przeszkodzie, aby wcześniejszy niefortunny kod zamienić na poniższy:

ALL: zasoby.res Hello.exe

.cpp.obj: bcc32 -c Hello.cpp

zasoby.res: zasoby.rc brcc32 zasoby.rc

Hello.exe: Hello.obj ilink32 /aa /Tpe c0w32.obj Hello.obj klasa.obj,Hello.exe,,\ cw32.lib import32.lib,,zasoby.res

Oczywiście to już od Ciebie zależy jakich zasad wolisz używać. Moim zadaniem jednak warto użyć wielu zasad ogólnych, a tylko jedną szczegółową, chyba że musisz w jednym projekcie mieć dwa pliki EXE (lub DLL). Jest też możliwość przypisania wielu zasad do jednego pliku wynikowego używamy wtedy podwójnego dwukropka:

mylib.lib:: obj1.obj obj2.obj tlib mylib +obj1.obj +obj2.obj

mylib.lib:: obj3.obj obj4.obj implib mylib +lib3.obj +lib4.obj

Najpierw do biblioteki statycznej mylib.lib dodawane są obj1.obj i obj2.obj, następnie w drugiej zasadzie do tej samej biblioteki oddajemy: obj3.obj oraz obj4.obj.

Zasada "bez zasad" :-) Jest jeszcze jeden typ zasad. 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:

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:

TOUCH: cd c:\Hello touch *.cpp *.rc

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 (MAKE) zgłosi błąd. Poza tym przy wywołaniu 'czegoś' można użyć następujących przedrostków: @ wywołanie danego narzędzia nie ma być wyświetlane w linii poleceń, MAKE zawsze przed zastosowaniem jakiejś komendy najpierw wyświetla na ekranie jej wywołanie, ten przedrostek temu zapobiega - kontynuuj działnia nawet jeśli narzędzie zgłosi błędy np. zapis: -bcc32 -c hello.cpp sprawi, że nawet jeśli w kodzie źródłowym hello.cpp będzie błąd MAKE nie zakończy procesu budowy projektu. - w miejsce wpisz liczbę, po zwróceniu której make ma przestać dalej działać (wywoływać); np.: -0bcc32 -c hello.cpp da nam zakończenie działania programu gdy BCC32 zakończy działanie kodem wyjścia równym 0

Właściwie to już możesz spróbować napisać swój własny, prosty makefile, który zautomatyzuje wywołanie kompilatora, kompilatora zasobów i konsolidatora.

# (c) 2003 by Karol Ossowski

# >> Hello Project << #

.cpp.obj: BCC32 -tW -c -q -w-par -w-rvl Hello.cpp klasa.cpp

.rc.res: