Dlaczego narzędzia do budowania używają języka skryptowego innego niż podstawowy język programowania?

23

Ostatnio używałem narzędzi do budowania projektu Nodejs w pracy, kiedy zdałem sobie sprawę, że główne narzędzie / system do budowania większości języków używa innego języka niż sam podstawowy język programowania.

Na przykład make nie używa C lub C ++ do pisania skryptów, a mrówka (ani Maven) nie używa Java jako języka skryptowego.

Nowsze języki, takie jak Ruby, używają tego samego języka do tworzenia narzędzi, takich jak rake , co ma dla mnie sens. Ale dlaczego nie zawsze tak było? Jakie są zalety posiadania narzędzia do budowania, które używa innego języka niż język podstawowy?

joshin4colours
źródło
14
Może język ogólnego przeznaczenia nie jest wystarczająco dobry do specjalistycznego użycia narzędzia? Na przykład nie chciałbym pisać plików make w C! Czasami DSL jest lepszy.
Andres F.,
13
Spójrz na to z innej perspektywy: dlaczego nie używać make do pisania aplikacji?
whatsisname
4
Ten artykuł dotyczy przede wszystkim tego, co według autora czyni Lisp świetnym, ale zawiera pewne istotne informacje na temat tego, dlaczego Ant używa XML zamiast Java.
8bittree
6
Jeśli chciałbyś napisać program w C, który automatyzuje budowanie programów w C, czy nie skończyłbyś pisać makeponownie (co zresztą jest zaimplementowane w C)?
Brandin,
6
@ joshin4colours make jest napisany w C. Jednak język, który korzysta, jest językiem deklaratywnym, który w razie potrzeby wywołuje powłokę.

Odpowiedzi:

36

Wybór języka programowania do wykonania czegokolwiek musi zależeć od konkretnych cech tego celu, a nie od innych zadań związanych z tym projektem.

Narzędzie do budowania wykonuje bardzo specyficzną pracę i bez względu na język używany w głównym projekcie, narzędzie do budowania jest samo w sobie oprogramowaniem.

Próba powiązania głównego projektu i jego narzędzia kompilacji może być bardzo złą decyzją. To, czego powinieneś potrzebować z narzędziem do budowania, to przede wszystkim szybki proces prostych reguł, z szybkim zarządzaniem plikami i IO.

Jeśli języki takie jak Ruby same używają do implementacji swojego narzędzia do budowania, jest to bardziej związane z funkcjami tego języka niż z założoną potrzebą zachowania wszystkiego w tym samym języku.

Nicolás
źródło
18

Nie jest niemożliwe napisanie narzędzia do kompilacji, które używa języka takiego jak C lub Java jako języka skryptowego. Jednak narzędzia do budowania korzystają z bardziej deklaratywnych języków skryptowych. Wyobraź sobie, jak trudno jest napisać plik makefile (lub build.xml lub cokolwiek innego) w C.

Narzędzia do budowania korzystają z korzystania z DSL, zadania, dla których C nie jest szczególnie dobrym wyborem. C jest zbyt ogólny dla narzędzia do kompilacji i zbyt zainteresowany szczegółami podatnymi na błędy i niskopoziomowymi. Na przykład nie chcesz zajmować się jawnym zarządzaniem pamięcią w narzędziu kompilacyjnym! Więc nawet jeśli używasz składni podobnej do C, prawdopodobnie użyjesz funkcji wyrzucania elementów bezużytecznych w narzędziu skryptowym, w którym momencie możesz po prostu użyć dedykowanej DSL?

Ma to sens, że można do tego użyć Ruby, ponieważ jest to język wyższego poziomu, bardziej deklaratywny niż C lub Java. Zobacz także: Gradle, sbt.

Andres F.
źródło
13
Imagine the pain and boilerplate of writing a makefile (or build.xml, or whatever) in C.Wyobraź sobie ból i bałagan związany z próbą wyrażenia nietrywialnej logiki warunkowej (no wiesz, standardowe imperatywne rzeczy) w deklaratywnym skrypcie XML. Och, czekaj, nie musisz sobie wyobrażać; prawdopodobnie musiałeś sobie z tym poradzić, i prawdopodobnie był to półtora bałagan, prawda? Kompilacje są jednym z najgorszych możliwych wyborów dla deklaratywnego języka skryptowego, ponieważ poważne problemy szybko kończą się na granicy deklaratywności, a te, które nie są wystarczająco proste, aby nie potrzebować narzędzia do kompilacji.
Mason Wheeler,
7
@MasonWheeler Są przypadki, w których istniejące narzędzia do budowania sprawiły, że cierpię, tak. Żadnemu z nich nie pomogłoby użycie imperatywnego języka niskiego poziomu. Uważam coś w rodzaju Ruby za prawdopodobnie idealne: w razie potrzeby wystarczająco deklaratywne i konieczne. C dałby mi koszmary za to zadanie. Dla mnie kompilacja systemów to dobry przypadek użycia (głównie) deklaratywnych list DSL: zależy ci na tym, co robić, a nie na tym, jak to zrobić (przez większość czasu).
Andres F.,
2
Słusznie. Zawsze uważałem, że C jest jedną z tych technologii „jeśli X jest odpowiedzią, zadajesz niewłaściwe pytanie”, szczerze mówiąc; po prostu praca z „skryptami” XML była koszmarem za każdym razem, gdy musiałem się w nie zanurzyć. Zgadzam się, że imperatywny język wyższego poziomu z możliwościami DSL byłby idealny.
Mason Wheeler,
@AndresF .: Rzeczy takie jak tworzenie plików wymagają wielu podejść. Pożądany stan wyjściowy jest określony deklaratywnie, ale działania niezbędne do osiągnięcia tego stanu są określone bezwzględnie.
supercat
1
@MasonWheeler Myślę, że jest to tak samo problem ludzi, którzy zaprojektowali język konfiguracji, jak sam XML. Częstym błędem autorów takich języków jest założenie, że tylko dlatego, że coś jest w formacie XML, będzie automatycznie czytelne dla człowieka. (Wiem, że popełniłem ten błąd więcej razy niż czuję się komfortowo. To takie kuszące.) Dobrze zaprojektowany i uproszczony język konfiguracji może działać równie dobrze na XML, jak w każdej innej formie.
biziclop
12

Podajmy przykład z prawdziwego świata.

Około 15 lat temu pracowałem nad przeniesieniem dużego systemu napisanego w C z Unixa na Windows, było to około 3 milionów linii kodu. Aby dać ci wyobrażenie o skali, kompilacja na niektórych naszych systemach uniksowych (RS6000) zajęła 24 godziny, Windows mógł skompilować system w około 4 godziny.

(Mieliśmy również 2 miliony linii kodu w naszym własnym interpretowanym języku, ale postanowiliśmy nie używać naszego języka dla systemów kompilacji, ponieważ nigdy nie był przeznaczony do przetwarzania plików. Potrzebowaliśmy także systemu kompilacji do kompilacji kodu C, który implementował nasz język .)

W czasie, gdy system kompilacji był napisany jako mieszanka skryptów powłoki i tworzących pliki, nie były one przenośne dla systemu Windows - dlatego postanowiliśmy napisać własny system kompilacji.

Mogliśmy użyć C, jednak zdecydowaliśmy się na użycie Pythona, było kilka powodów. (W tym samym czasie przepisaliśmy również nasz system kontroli kodu źródłowego w pythonie, był to bardzo zintegrowany z systemem kompilacji, więc programiści mogli udostępniać pliki obiektowe dla modułów wtajemniczonych).

  • Większość naszego kodu można zbudować za pomocą kilku prostych reguł (tylko kilka tysięcy wierszy Pythona dla wszystkich platform, Windows, VMS i 6 wersji Uniksa), które zostały wyparte z konwencji nazewnictwa plików.

  • W tym czasie RegEx nie był bardzo standardem między systemami C na różnych platformach, Python wbudował w RegEx.

  • Kilka modułów wymagało niestandardowych kroków kompilacji, Python pozwalał na dynamiczne ładowanie plików klas. Zezwalaliśmy na użycie niestandardowej klasy do zbudowania modułu (lib), w oparciu o to, że w folderze znajduje się plik python o magicznej nazwie. To był powód zabójcy do używania Pythona.

  • Rozważaliśmy Javę, ale w tym momencie nie była ona wysyłana na wszystkie platformy.

(Interfejs użytkownika naszego systemu kontroli kodu źródłowego korzystał z przeglądarki internetowej, ponieważ była przenośna na całej platformie. To było 6 miesięcy przed połączeniem internetowym. Musieliśmy pobrać przeglądarkę przez X25!)

Ian
źródło
Pracując nad kilkoma dużymi systemami, czasami mam wrażenie, że mam kontrolę nad skalą rozwoju. Potem czytam takie historie. Do licha.
dmckee,
@immibis To była modna rzecz 15 lat temu. Co więcej, praktyka ta została zatwierdzona i poparta przez wiodących dostawców uniksowych (w szczególności SGI i DEC, ale także IBM).
oakad
1
Ludzie zapominają, że Unix oznaczał „drogi”. Windows wygrał wojnę z komputerem / stacją roboczą, będąc tańszym. Z tego samego powodu Linux później wygrał wojnę serwerową.
slebetman
1
Jeśli chcę, aby komputer uruchamiał oprogramowanie, które sam napisałem, chcę być elastyczny i mieć 101 opcji kompilacji jąder itp. Jeśli jednak sprzedaję oprogramowanie do zainstalowania na komputerze klienta, chcę, aby klient był działający system operacyjny, który nie jest elastyczny, więc wiem, że będzie działać na ich komputerze, jeśli działa na moim. To był duży czynnik, kiedy patrzyliśmy na Linuksa jako producenta oprogramowania - wsparcie wyglądało na zbyt drogie. Linux wygrał wojny z serwerami hostowanymi , w których serwer jest dostarczany przez dostawcę oprogramowania - np. Większość oprogramowania wdrażanego w sieci.
Ian
3
@slebetman To jest sprawiedliwe - Unix wygrał poprzednią „wojnę”, będąc tańszym (w porównaniu do LISPM i podobnych maszyn). Był absolutnie okropny, okropnie zaprojektowany i ciągle się zawiesza (ale uruchamia się o wiele szybciej niż LISPM!: P), używając C jako podstawowego języka programowania (dość wysoki spadek z LISP i podobnych), ale był znacznie tańszy. W rzeczywistości wielu przyjęło go, ponieważ był darmowy (wiele wcześniejszych wersji Linuksa istniało wiele bezpłatnych mutacji). W rzeczywistości spotkałem wielu oldschoolowych LISPerów, którzy woleli MS-DOS od ówczesnych uniksów. Tak, to okropne.
Luaan,
3

Krótka odpowiedź na to pytanie brzmi: właśnie tym jest narzędzie do budowania . Narzędzie, które automatyzuje korzystanie z innych narzędzi w celu zbudowania produktu końcowego z niektórych plików źródłowych. Jeśli piszemy skrypt kompilacji w tym samym języku, co sam produkt, wkraczamy w sferę narzędzi specyficznych dla języka, których nie można by uznać za narzędzia kompilacji w ten sam sposób.

Rodzi to pytanie - dlaczego kompilatory nie zapewniają automatyzacji kompilacji, do której jesteśmy przyzwyczajeni z narzędzi takich jak make? Na którą odpowiedź brzmi po prostu dlatego, że istnieje make . Filozofia Uniksa: „zrób jedną rzecz i zrób to dobrze” sugeruje, że dostarczenie tego * nie jest obowiązkiem kompilatora. To powiedziawszy, osobiście brałem udział w wywoływaniu bólów głowy i chciałbym wypróbować kompilator, który zapewnia własny „natywny” system kompilacji bez cudzysłowu.

Na przykład kompilatora zaprojektowanego w ten sposób, zobacz Jai Jona Blow'a (jeszcze nie wydany), język przeznaczony jako zamiennik C ++. Łącza do dyskusji i prezentacji kompilatora znajdują się tutaj . Ten język kompiluje się do kodu bajtowego, który działa w kompilatorze, przy czym kompilacja do pliku binarnego jest standardową funkcją udostępnianą przez bibliotekę, dostępną z kodu bajtowego. Celem jest umożliwienie zarówno automatyzacji budowy, jak i metaprogramowania w natywnej składni języka.

* Spojrzenie na Visual Studio Microsoftu pokaże przykład przeciwnej filozofii. :)

Haddon CD.
źródło
2

Narzędzie do budowania jest przede wszystkim narzędziem, podobnie jak „gcc”, „ls”, „awk” lub „git”. Podajesz narzędziu dane wejściowe (nazwę pliku, parametry itp.) I odpowiednio zrobi on pewne rzeczy.

Wszystkie te narzędzia umożliwiają przekazywanie danych wejściowych w sposób, który powinien być wystarczająco prosty do napisania i zrozumienia. W szczególnym przypadku narzędzi do kompilacji dane wejściowe to plik receptury, plik opisujący, w jaki sposób projekt przechodzi z plików źródłowych do gotowego produktu.

Jeśli Twój przepis jest tak prosty, jak przekazanie folderu zawierającego projekt i kompilator do użycia, wystarczy deklaratywny „język”. Gdy przepis staje się coraz bardziej skomplikowany, z większą logiką, kłopotliwe staje się posługiwanie się nim przy użyciu języków deklaratywnych i używa się bardziej ogólnych języków.

Teraz powinno być oczywiste, że nie ma związku między językiem używanym do pisania receptur kompilacji a językami używanymi do pisania kodu produktu, ponieważ mają one różne cele i wymagania. Nie ma żadnego związku nawet między językiem, w którym napisane jest narzędzie kompilacji, a „językiem”, w którym powinny zostać zapisane przepisy kompilacji. Zawsze używaj najlepszego narzędzia do pracy!

Tibos
źródło