Wielu programistów boi się tykać skryptów buildowania. Uważa to za czarną magię. Nic dziwnego, zwykle są napisane tak, żeby przypadkiem nie dało się ich zrozumieć. Bardzo często cały skrypt jest napisany w jednym ogromnym pliku, gdzie duże fragmenty są stworzone za pomocą kopiuj – wklej – edytuj. Składnia języków do pisania takich skryptów np. make jest dosyć toporna, a sytuacji nie poprawia zagnieżdżanie komend, niedbanie o dobre nazwy zmiennych i brak komentarzy. Mi już wielokrotnie przyszło grzebać przy istniejących skryptach buildowania do projektu, czy tworzyć takie skrypty od zera. I chociaż daleki jestem od stwierdzenia, że mógłbym się tym zajmować full-time, całkiem dobrze mi to wychodziło. Jak to osiągnąłem? Bardzo prosto – przede wszystkim nie utrudniałem sobie dodatkowo życia pisząc w sposób trudny do zrozumienia. Pomogło mi w tym bardzo proste, ale jednak zaskakujące odkrycie – skrypty buildowania to też kod, do którego możemy stosować podobne zasady czystości jak w innych językach!
Rozbijaj skrypt na pliki
Nikt nie każe Ci pracować z jednym plikiem mającym 10 tysięcy linii zawierającym listy kompilowanych folderów, ścieżki do kompilatorów, dodatkowe flagi kompilacji, definy, wszystkie możliwe targety i pełno kroków pośrednich. Języki do pisania skryptów buildowych takie jak make umożliwiają podzielenie skryptu na wiele plików. W make można na przykład korzystać z komendy include wklejającej nowy plik, jak i w targecie wywoływać podrzędny plik make. Korzystajmy z tych możliwości i dzielmy skrypt na logiczne części ułatwiające późniejszą edycję.
Jednym z najprostszych zabiegów jest wydzielenie pliku konfiguracyjnego, zawierającego zmienne modyfikowane przez użytkownika, do oddzielnego pliku. Główną część skryptu musimy napisać wtedy tak, żeby korzystała z odpowiednich zmiennych definiowanych w pliku konfiguracyjnych. Dzięki temu oszczędzamy sobie udręki, jaką jest szukanie zmiennych, które powinniśmy zedytować, kiedy są one porozrzucane po całym wielkim pliku. Dodatkową zaletą takiego zabiegu jest możliwość stworzenia wielu konfiguracji korzystających z tego samego schematu. Wszystkie one mogą dzielić ten sam schemat buildowania, natomiast różnią się kompilowanymi plikami. Takie podejście jest bardzo przydatne na przykład robiąc wiele targetów testowych.
Kolejnym kandydatem do wydzielenia do osobnego pliku są definicje toolchaina. Często różnią się one między maszynami lokalnymi poszczególnych developerów. Możemy nie mieć dodanego toolchaina do zmiennej środowiskowej PATH i używać bezpośrednich ścieżek. Możemy mieć zainstalowane różne wersje narzędzi posiadające te same nazwy. Problemów z kompatybilnością toolchaina może być bardzo dużo. Jeżeli posiadamy odrębny plik opisujący naszą lokalną konfigurację, możemy łatwo nanieść te zmiany.
SRP, KISS, DRY
Powyższe akronimy obiły się o uszy chyba każdemu programiście. Single Responsibility Principle, Keep It Simple, Stupid! i Don’t Repeat Yourself to podstawowe zasady pomagające utrzymać nasz kod w czystości. Skoro tak wspaniale zdają egzamin w C++, Javie czy Pythonie dlaczego nagle o nich zapominamy pisząc skrypty buildowania? Przez to powstają wielkie tasiemce z wieloma kopiami tego samego kodu i pomieszanymi wszystkimi odpowiedzialnościami.
A wcale nie musi tak być. Makefile na przykład posiada bardzo przydatne wbudowane funkcje wyszukujące dla nas pliki, filtrujące nazwy, czy podmieniające rozszerzenia. Możemy również definiować własne funkcje, dzięki czemu nie musimy klepać tego samego kodu w wielu miejscach. Takie funkcje można również wydzielić do osobnego pliku i wykorzystywać w kilku skryptach.
Komentarze
Czasami odnoszę wrażenie, że w makefile komentarze wymyślono tylko i wyłącznie po to, aby ignorować nieużywany kod. Od tego jest kontrola wersji! Skrypty buildowania w końcu również pod nią podlegają. Często w skryptach można znaleźć ogromne bloki wykomentowanego dawno temu kodu. Taki kod zostaje dlatego, że każdy boi się go usunąć. W skryptach buildowania, będących rzeczą robioną przez programistę dodatkowo i w których nie czuje się orłem, ten efekt jest spotęgowany.
Z drugiej strony prawie nigdy skrypty nie zawierają komentarzy opisujących co robi dany kod. To szczególnie dziwne biorąc pod uwagę dość toporną składnię takich skryptów i potrzebę stosowania nieraz dosyć dziwnych hacków. Modyfikacje w skryptach są zwykle dodatkiem do naszych codziennych obowiązków i nie musimy znać wszystkich kruczków składniowych. Dlatego w takich skryptach komentarze powinny być szczególnie przydatne.
Refactoring
Jak to zwykle bywa – zepsucie kodu jest procesem wymagającym czasu. Kod produkcyjny staje się coraz gorszy z każdą zmianą, każdym workaroundem i każdym deadlinem. Nie inaczej jest w przypadku skryptów buildowania. O ile w zwykłym kodzie często dbamy o jakość na przykład nie dopuszczając do pewnych zmian w code review, czy poprawiając niektóre stare rozwiązania, o tyle w odniesieniu do skryptów pojęcie refactoringu jest praktycznie obce. A przecież te skrypty również musimy utrzymywać, wprowadzać tam zmiany i nad nimi panować. To, że zmiany tam wprowadzamy rzadziej nie jest żadną wymówką.
Skrypty wygenerowane automatycznie
Często zdarza się, że używamy skryptów buildowania wygenerowanych automatycznie na przykład przez IDE. Później jednak się okazuje, że czegoś nie da się zrobić w ten sposób, albo konfiguracja nie przenosi się dobrze pomiędzy różnymi maszynami. Wtedy taki wygenerowany skrypt staje się dla nas bazą, ale dalsze modyfikacje wprowadzamy ręcznie. Staramy się wprowadzić minimalne zmiany pozostawiając tą samą strukturę pliku. Problem polega na tym, że ta struktura była zaprojektowana z myślą automatycznej generacji kodu, a nie łatwości utrzymania.
Jeśli decydujemy się utrzymywać ręcznie kod wygenerowany przez jakiegoś toola, powinniśmy pogodzić się z myślą, że wszystkie zmiany od tej pory będziemy wprowadzać sami. Powinniśmy więc przystosować plik, aby takie zmiany nie były zbyt skomplikowane. Tutaj właśnie przydaje się refactoring. Możemy stopniowo wprowadzać ułatwienia małymi kroczkami w miarę rozwoju projektu.
Modyfikacje skryptów nie muszą być udręką
Przedstawione przeze mnie zasady na pewno są Ci znane. W końcu stosujesz je na co dzień w pracy nad kodem. Dlatego zachęcam gorąco, aby skrypty buildowania również traktować jak zwykły kod i stosować te same zasady. W końcu przynoszą one efekty. To prawda – języki do pisania takich skryptów są dosyć toporne i składnia czasem wydaje się magiczna. Przez to takie skrypty są trudniejsze do zrozumienia. Dlatego dbanie o jakość kodu w takim skrypcie daje wymierne efekty.
23 maja 2018 at 09:27
Zgadzam się na 100%. Ponad to jeżeli ktoś pisze ładny kod to napewno bedzie w stanie pisac dobrybkod budujacy.
23 maja 2018 at 18:47
Jeżeli użyje swojej wiedzy to na pewno. Ale czasem nieznany kod w niezrozumiałym języku tak na nas działa, że chcemy tylko żeby działało i potem niech kto inny się martwi. Z reszta to też jest prawdziwe w kodzie produkcyjnym.
28 stycznia 2019 at 17:22
bardzo chętnie bym przeczytał Twój tutorial odnośnie pisania skryptów buildowania dla jakiegoś przykładu.