Jaskinie Podróże Nurki Grafika Mizar Teksty Kulinaria Lemkov Namiary Mapa RSS English
Spelunka Trybików Teksty Programowanie Kontrola dyrektyw kompilacji warunkowej oraz ustawień przełączników kompilacji YAC Software
  Wróć

Spis

Charsets

Wykresy

DBExpress

Delphi

HTML

Intraweb

MSTest

PHP

Programowanie

R

Rhino Mocks

Software

Testowanie

Testowanie UI

VB.NET

VCL

WPF

Kontrola dyrektyw kompilacji warunkowej oraz ustawień przełączników kompilacji
Podczas kompilowania aplikacji często zapominałem ustawić dyrektywy kompilacji warunkowej tudzież opcje kompilacji odpowiednio do wybranego targetu (build deweloperski, debug, release, itp.). I mimo, że różne środowiska deweloperskie pozwalają na zapisanie wybranych ustawień, jest dosyć łatwo o przypadkową ich zmianę. Choć chyba najczęściej bywa tak, że zmienia się niektóre z tych ustawień tymczasowo (np. przy szukaniu pluskwy), a później zapomina się je odtworzyć przed np. buildem oficjalnym... :-)

W sumie chyba najlepszym sposobem (IMO) jest pozwolenie kompilatorowi na automatyczne sprawdzanie tych ustawień. Robi się tak: tworzymy nowy moduł (unit), który jest importowany do innych kluczowych modułów. A w tym module dodaje instrukcje weryfikujące ustawienia kompilacji warunkowej i opcji kompilacji poprzez... przerwanie buildu jeżeli któryś z elementów jest zdefiniowany nieprawidłowo.

Oto przykładowy moduł z Delphi:
  unit YACDefines;
  
  interface
  
  implementation
  
  {$IFOPT A- } Error - alignment shall be off {$ENDIF }
  {$IFOPT B+ } Error - Boolean short-circuit evaluation shall be on {$ENDIF }
  {$IFOPT I- } Error - input / output checking shall be on {$ENDIF }
  {$IFOPT U+ } Error - Pentium-safe FDIV operations shall be off {$ENDIF }
  
  {$IFDEF YACAllowMemoryLeaks }
    {$IFNDEF YACTest }
      Error - YACAllowMemoryLeaks shall be used only in DUnit testing
    {$ENDIF }
  {$ENDIF }
  
  {$IFDEF YACRelease }
    {$IFNDEF YACTest }
      {$IFOPT C+ } Error - assertions in release builds shall be off {$ENDIF }
      {$IFOPT D+ } Error - debug information in release builds shall be off {$ENDIF }
      {$IFOPT L+ } Error - local symbol information in release builds shall be off {$ENDIF }
      {$IFOPT O- } Error - optimization in release builds shall be on {$ENDIF }
      {$IFOPT Q+ } Error - overflow checking in release builds shall be off {$ENDIF }
      {$IFOPT R+ } Error - range checking in release builds shall be off {$ENDIF }
      {$IFOPT Y+ } Error - symbol declaration and cross-reference information
                           in release builds shall be off {$ENDIF }
      {$IFOPT W+ } Error - windows stack frames in release builds shall be off {$ENDIF }
    {$ENDIF }
  {$ELSE }
    {$IFOPT C- } Error - assertions in development builds shall be on {$ENDIF }
    {$IFOPT O+ } Error - optimization in development builds shall be off {$ENDIF }
    {$IFOPT Q- } Error - overflow checking in development builds shall be on {$ENDIF }
    {$IFOPT R- } Error - range checking in development builds shall be on {$ENDIF }
  {$ENDIF }
  
  end.
Pomysł jest taki, aby w danym buildzie umieścić kod, które nie kompiluje się, dla tych ustawień kompilatora / dyrektyw kompilacji warunkowej, które nie są prawidłowe dla danego targetu.

Na przykład, na ogół definiuję trzy typu buildów:
  • build deweloperski (debug) do codziennej pracy,
  • build testowy używany przy automatyzacji testów,
  • build release'owy do tworzenia wersji produkcyjnych oprogramowania.
Niezależnie od typu buildu, niektóre opcje kompilacji (A, B, I, U powyżej) powinny być zawsze ustawione tak a nie inaczej (np. I/O checking powinno być zawsze włączone).

W buildach deweloperskich, włączam tyle kontroli, ile się da. Dlatego też, w tego typu buildach, asercje, kontrola nadmiarów i zakresów są włączone, ale optymalizacja generowane kodu wyłączona. Akurat ta ostatnia powodowana jest tym, że przy włączonej optymalizacji niektóre zmienne są eliminowane z generowanego kodu, przez co podczas odpluskwiania nie można sprawdzić ich wartości. Tudzież, gdy zmienna nie jest już potrzebna (nie będzie doń odwołań do końca funkcji / procedury), też nie można sprawdzić jej wartości...

Z drugiej strony, dla buildów testowych nie chcą kontrolować opcji na tym poziomie - różne projekty testowe wymagają różnych ustawień.

Jest jeszcze jedna opcja, z której często korzystam - dyrektywa kompilacji warunkowej zezwalająca na przeciekanie pamięci podczas testów DUnit. W niektórych typach testów dość trudno eliminuje się wszystkie przecieki, szczególnie przy testowaniu interfejsu użytkownika, mimo, że aplikacja jako taka nie ma żadnych przecieków. Chociaż nie ma większego sensu na definiowanie tej dyrektywy dla innych buildów, niż testowe!

Wreszcie, większość kontroli i danych do odpluskwiania są niepotrzebne w buildach release'owych. Choć istnieją tu różne podejścia - wg niektórych, opcje w wersjach testowych powinny być te same, co w wersjach release'owych (aby testować dokładnie te wersje, które idą do klientów, a nie skompilowane inaczej).

Jednak raczej się z tym nie zgadzam - chyba, że nie ufamy używanemu kompilatorowi! :-) W ciągu 20 lat programowania mogę zliczyć na palcach jednej ręki sytuacje, gdy zachowanie systemu zmieniło się w nieprzewidywalny sposób po zmianie opcji kompilacji. Hmm... w zasadzie pamiętam tylko jedną taką sytuację, choć może są inne powody, dla których nie pamiętam ich więcej... ;-)

Proszę jednak zwrócić uwagę na słowo "nieprzewidywalny" powyżej - system może zachowywać się inaczej (i na ogół tak jest). A w każdym razie tekst ten nie jest po to, aby przekonywać do takich kontroli a nie innych - raczej aby przedstawić sposób zwiększenia kontroli na procesem kompilacji / buildów / release'ów.

Rzecz jasna, można ten sposób kontrolować wiele innych rzeczy, np. opcji kompilacji w buildach alpha i beta, w programach przeznaczonych do użytku wewnętrznego czy programach przeznaczonych dla klientów zewnętrznych, sprawdzania ustawień bibliotek zewnętrznych, itd.

Następnie, nie mogę powiedzieć, że ww. sposób uratował mnie przez błędami czy defektami, ale na pewno przyspiesza dewelopment. Np. nie cierpię sytuacji, gdy pracowicie odpluskwiam jakiś kawałek kodu, dochodzę do punktu gdzie byk powinien się ujawnić, a tu okazuje się, że przez błędni ustawione opcje kompilacji nie mogę sprawdzić wartości jednej zmiennej... Lub też, gdy okazuje się, że wersja u klienta działa znacznie wolniej niż buildy testowe - a to dlatego, że w buildzie release'owym zapomniałem wyłączyć kontrolę zakresów...

Wreszcie, zamiast używać kodu / tekstu, który się nie kompiluje, wygodniejszym może być skorzystanie ze standardowej dyrektywy Delphi - $MESSAGE - najpewniej w jej wersji ERROR. W wersji FATAL kompilacja zostaje przerwana na tym błędzie, a inne nie są już zgłaszane. Z kolei WARN/HINT generują odpowiednio tylko ostrzeżenia/podpowiedzi, więc zautomatyzowany proces buildów/release'ów można nie wykryć tego problemu.

Część przykładu podanego wcześniej, lecz z wykorzystaniem $MESSAGE:
  {$IFOPT A- } {$MESSAGE WARN  'Alignment shall be off.' } {$ENDIF }
  {$IFOPT B+ } {$MESSAGE ERROR 'Boolean short-circuit evaluation shall be on.' } {$ENDIF }
  {$IFOPT I- } {$MESSAGE ERROR 'Input / output checking shall be on.' } {$ENDIF }
  {$IFOPT U+ } {$MESSAGE WARN  'Pentium-safe FDIV operations shall be off.' } {$ENDIF }
Hmm... w zasadzie wygląda to teraz znacznie porządniej. :-)

Góra

Komentarze
Kurczę!
Na razie brak komentarzy...

Góra

Dodaj komentarz (pola z gwiazdką są obowiązkowe)
Imię / ksywa *
Mail (pozostanie ukryty) *
Twoja strona
Komentarz (bez tagów) *
Wpisz tekst wyświetlony poniżej *
 

Góra

Tagi

Programowanie

Delphi


Podobne strony

TFS - The underlying connection was closed: an unexpected error occurred on a receive.

WCF - The underlying connection was closed: an unexpected error occurred on a receive.

Interfejsy w Delphi... znowu

Zapisywanie / odtwarzanie lokalizacji okien w .NET

Weryfikacja "wiszących" procedur obsługi zdarzeń w formach Delphi

Znaczące identyfikatory

Publiczne pola a właściwości

Przeciąganie plików na okno aplikacji

Intraweb a MaxConnections

Argumenty za używaniem FreeAndNIL

Intraweb jako moduł DSO Apache'a

Intraweb a "Device not supported"

Zautomatyzowane testowanie GUI

Determinizm Random()

Zaokrąglanie i dokładność na FPU 8087

Intraweb a SessionTimeout

Używanie TChart w programach Intraweb

Unknown driver: MySQL

TIdMessage a CharSet

Gwarancje oprogramowania

Automatyczne testowanie formularzy okien

TChart - brakujące etykiety w osiach

Tracona pamięć i eksplozje połączeń w DBExpress

Wykrywanie traconej pamięci a DUnit

last_insert_id() a DBExpress

Rejestracja rozszerzeń

DBExpress a dostęp wielowątkowy

Formy jak ramki

Sprawdzanie błędnych odwołań a nowy menedżer pamięci

Dostęp do składowych chronionych

Obiekty, interfejsy i obsługa pamięci w Delphi - ki czort?