Borland C++ Compiler/ILINK32
Wprowadzenie
[edytuj]Tym razem zajmiemy się bardziej zaawansowanym narzędziem do programowania, czyli ILINK32. Jest to jak sama nazwa wskazuje linker (bardziej po polsku: konsolidator). Linker, czyli program, który łączy wszystkie "półprodukty" utworzone z plików źródłowych - pliki *.obj z bibliotekami, zasobami programu (o ile je uprzednio stworzymy) itp. Tak naprawdę bez konsolidatora nie byłoby programu takiego, jaki sobie wyobrażamy, ponieważ to on łączy wszystkie elementy na które składa się program w jeden plik EXE (lub w bibliotekę). Mniej doświadczonych programistów już teraz ostrzegam, że bawienie się w samodzielne wywoływanie ILINK32 będzie od was wymagało dodatkowej roboty, którą wcześniej "odwalał" za was kompilator (BCC32), ale ILINK32 w zamian daje jeszcze większy wpływ na to, jak wasz ostateczny program będzie wyglądał.
Wywołanie
[edytuj]W przypadku ILINK32 wywołanie jest znacznie bardziej skomplikowane niż w BCC32:
ILINK32 [<opcje>] <rozbiegówka> <obiekt(y)>, [<wynik>], [<mapa>], <lib>, [<defile>], [<zasoby>]
- <opcje>
- tak jak w przypadku BCC32, tak samo konsolidacji możemy nadać wiele opcji, z których ważniejsze opisuje w następnym podrozdziale.
- <rozbiegówka>
- spotkałem się kiedyś z takim określeniem i skoro sam nie potrafię tego jakoś sensownie nazwać będę używał tego terminu; <rozbiegówka> to specjalny plik *.obj, który musisz dodać do Twojego kodu w zależności jaką aplikację chcesz uzyskać:
- W przypadku kompilacji przez BCC32 nie musisz o tym pamiętać, kompilator automatycznie dodaje ten plik po wpisaniu odpowiedniej :opcji, jeśli sam wywołujesz ILINK32 - Ty musisz zrobić.
- <obiekt(y)>
- plik(i) *.obj generowane przez kompilator wpisujesz tutaj oddzielając każdy od siebie spacją. Jeśli ktoś nie wie, jak zmusić kompilator do kompilacji pliku tylko do *.obj (bez samodzielnego wywoływania ILINK32), to przypominam, że wystarczy dodać do linii poleceń BCC32 opcję -c
- <wynik>
- możesz tu sam zdefiniować nazwę swojego pliku wynikowego; może to być zarówno program (*.exe) jak i biblioteka statyczna (*.lib) lub dynamiczna (*.dll)
- <mapa>
- w tym miejscu możesz wpisać nazwę pliku MAP, który wygeneruje ILINK32 (jeżeli tego nie zrobisz linker nazwie go tak jak twój wynik (obcinając rozszerzenie). Plik MAP przechowuje adresy poszczególnych segmentów aplikacji i adresy funkcji Twojego programu, jest tworzony domyślnie.
- <lib>
- po prostu biblioteka/i które masz zamiar użyć w programie; możesz dodać każdą o ile znajduje się w lokalizacji podanej przy opcji -L w parametrach wywołania ILINK32 lub w pliku ilink32.cfg. Dokumentacja FCLT określa prawidłową kolejność w jakich powinno się te biblioteki podawać:
- bilioteki pomocne w analizowaniu i debugowaniu kodu (tzw. code guards)
- inne (własne) biblioteki
- biblioteka import32.lib - jeśli ma to być program dla Windows
- biblioteki operacji matematycznych
- tzw. runtime libaries (taka rozbiegówka, ale tym razem z rozszerzeniem lib) w przypadku programów dla Windows będzie to cw32.lib
- w praktyce aby skompilować zwykły program dla Windows należy podać tu przynajmniej: import32.lib cw32.lib
- <defile>
- tutaj określasz plik DEF; w nim możesz określić wiele parametrów Twojego programu, które dokładnie opisuje poniżej.
- <zasoby>
- zasoby, ale w wersji skompilowanej (*.res) - wcześniej musisz użyć kompilatora zasobów
Bardzo ważną rzeczą w wywoływaniu ILINK32 są przecinki, nawet jeśli nie chcesz wpisywać wszystkich parametrów masz obowiązek zaznaczyć wszystkie przecinki! Oto przykładowe wywołanie ILINK32 generujące aplikację Win32 GUI:
ILINK32 /aa /Tpe /Gk /t /M c0w32.obj Helo.obj klasa.obj,\ Hello.exe,,import32.lib cw32.lib,,zasoby.RES
Opcje
[edytuj]Źródła
[edytuj]opcja | opis |
---|---|
@<nazwa> | tu możesz zdefiniować nazwę <nazwa> response file dla ILINK32 |
/j<lokalizacja> | (dodatkowa) lokalizacja plików *.obj |
/L<lokalizacja> | dodatkowa lokalizacja bibliotek |
Wynik
[edytuj]opcja | opis |
---|---|
/aa | aplikacja Windows GUI |
/ap | aplikacja Windows Console |
/ad | sterownik urządzenia |
/Tpe | program *.exe |
/Tpd | biblioteka dynamiczna *.dll |
/Gl | biblioteka statyczna (*.lib) |
/Gn | ILINK32 przy każdej konsolidacji generuje 4 pliki (*.ilc, *.ilf, *.ild, *.ils), dzięki którym szybciej konsoliduje Twój program, jeżeli nie chcesz aby zaśmiecał Ci tym katalogu użyj tej opcji |
/Gk | kiedy pojawią się jakieś błędy podczas konsolidacji linker automatycznie usuwa plik wynikowy(!) co czasami może być niepożądaną operacją, aby temu zapobiec możesz użyć tej opcji |
Plik MAP
[edytuj]ILINK32 pozwala na określenie stopnia zaawansowania pliku MAP:
opcja | opis |
---|---|
/x | nie generuj pliku MAP |
/M | generuj mapę najmniej zaawansowaną |
/m | generuj mapę publiczną, przydatną podczas debugowania programu |
/s | generuj szczegółową mapę |
Ostrzeżenia
[edytuj]opcja | opis |
---|---|
-q | pomijaj nagłówek ILINK32 |
/t | pokaż czas spędzony na linkowanie |
-w-dup | nie pokazuj ostrzeżenia "duplicate symbol" |
Plik DEF
[edytuj]Plik DEF określa wiele aspektów programu, które możesz zdefiniować w zwykłym pliku ASCII, w Notatniku (pamiętając, żeby plik ten miał rozszerzenie *.def). Aspekty te określamy wpisując odpowiednie selektory i w tej samej linii dopisując do nich wartości. Standardowo jeśli nie określimy przy wywoływaniu ILINK32 pliku DEF, będzie on wyglądał tak:
CODE PRELOAD MOVEABLE DISCARDABLE DATA PRELOAD MOVEABLE MULTIPLE ; (for applications) PRELOAD MOVEABLE SINGLE ; (for DLLs) HEAPSIZE 4096 STACKSIZE 1048576
Już w tym kodzie widzisz 4 selektory i określone dla nich wartości. Są to podstawowe oznaczenia pliku DEF (CODE, DATA, HEAPSIZE i STACKSIZE). Od razu można się domyślić co one oznaczają, ale aby zbytnio nie mącić przejdę teraz do omówienia tych i innych parametrów pliku DEF.
CODE
[edytuj]Jak wiemy plik EXE jest podzielony na dwa podstawowe segmenty:
- kod (ciąg instrukcji np. w C++ które sprawiają, że program "żyje")
- zasoby (ikony, bitmapy, menu itp.)
Selektor CODE pliku DEF określa specyficzne opcje dla tego pierwszego segmentu (kod):
wartość selektora | standard | opis |
---|---|---|
PRELOAD | nie | kod jest wczytywany, gdy wywoływany program jest wczytywany |
LOADONCALL | tak | kod jest wczytywany, kiedy jest wywoływany przez program |
EXECUTEONLY | nie | kod może być tylko wykonywany |
EXECUTEREAD | tak | kod może być czytany i wykonywany |
FIXED | tak | segment pozostaje w pamięci |
MOVEABLE/ | nie | segment może być przeniesiony |
DISCARDABLE | nie | segment może być odrzucony kiedy nie jest potrzebny (zakłada opcję MOVEABLE). |
NONDISCARDABLE | tak | segment nie może być odrzucony |
kolumna standard określa domyślną konfiguracje binarki |
DATA
[edytuj]Parametr DATA określa zachowanie się segmentu zasobów:
wartosć | standard | opis |
---|---|---|
NONE | nie | nie ma segmentu DATA |
SINGLE | tak dla DLL | jeden segment zasobów jest tworzony i współdzielony dla wszystkich procesów |
MULTIPLE | tak dla EXE | każdy segment zasobów jest tworzony dla jednego procesu |
READONLY | nie | segemnt może być tylko czytany |
READWRITE | tak | segment może być i czytany i nadpisywany |
PRELOAD | nie | zasoby są wczytywane kiedy moduł, który ich używa jest wczytywany |
LOADONCALL | tak | segment zasobów jest wczytywany kiedy jest po raz pierwszy przetwarzany (ta opcja jest ignorowana przez aplikacje 32-bitowe) |
SHARED | nie | jedna kopia jest współdzielona między wszystkie procesy |
NONSHARED | tak dla DLL | kopia segmentu jest wczytywana dla każdego procesu potrzebującego zasoby |
HEAPSIZE
[edytuj]Jak sama nazwa wskazuje wpisujesz tutaj wielkość sterty (standardowo: 4096). Jeżeli w Twoim programie jest wiele obiektów zadeklarowanych na stercie możesz zwiększyć tę liczbę.
STACKSIZE
[edytuj]Tym razem określamy wielkość stosu (tu znajdują się wszystkie zmienne lokalne i parametry funkcji), dlatego wielkość tego parametru powinna być nieporównywalnie większa (standardowo: 1048576).
NAME
[edytuj]Nazwa pliku *.exe, który jest wynikiem konsolidacji, dodatkowo możesz tu umieścić po spacji jeden z dwóch znaczników:
- WINDOWAPI
- jeżeli Twoja aplikacja będzie korzystać z WinAPI (to samo, co opcja linkera /aa)
- WINDOWCOMPAT
- jeżeli Twój program będzie uruchamiany w konsoli (to samo, co opcja linkera /ap)
LIBRARY
[edytuj]Nazwa biblioteki DLL, która jest wynikiem konsolidacji i tutaj masz do wyboru 2 znaczniki:
- INITGLOBAL
- biblioteka jest wczytywana do pamięci tylko raz
- INITINSTANCE
- biblioteka jest wczytywana za każdym razem kiedy jest potrzebna
EXPORTS
[edytuj]Tego selektora mogą używać tylko biblioteki Jego wartością są nazwy wszystkich funkcji udostępniane przez bibliotekę. Więcej o tym selektorze przeczytasz w artykule: Biblioteki DLL w FCLT
IMPORTS
[edytuj]Wartością tego selektora są funkcje importowane z bibliotek zadeklarowane w kodzie źródłowym. I o tym selektorze przeczytasz w artykule o bibliotekach DLL.
DESCRIPTION
[edytuj]Ujęty w pojedynczy cudzysłów: opis programu (mogą tu się znajdować również np. prawa autorskie do programu). Oto jak może wyglądać plik
DEF w praktyce:
NAME Hello WINDOWAPI DESCRIPTION '(c) 2003 by Karol Ossowski' CODE PRELOAD MOVEABLE DISCARDABLE DATA PRELOAD MOVEABLE MULTIPLE HEAPSIZE 4096 STACKSIZE 1048576
Zasoby
[edytuj]Jak już pisałem aby sprowadzić skrypt zasobów z rozszerzenia *.rc do skompilowanego pliku *.RES należy użyć brcc32 z katalogu BIN. Wywołanie brcc32 będzie wyglądać przykładowo tak:
brcc32 <zasoby>.rc
Nie będę się zagłębiał tutaj w opcje, jakich może użyć ten kompilator (jeśli Cię one interesują możesz sprawdzić je w dokumentacji FCLT), ważne że prze konwertuje on nasz plik <zasoby>.rc na <zasoby>.RES. Dopiero teraz możesz go dołączyć do wywołania ILINK32.