Build-Systeme

Was sind Build-Systeme?

Wir haben unser HTML-Programm bisher grundsätzlich auf der Konsole kompiliert. Hierfür rufen wir den Compiler mit allen benötigten C-Dateien auf:

gcc main.c node.c parameter.c

Stellen wir uns nun vor, dass wir nicht nur ein kleines Projekt mit 3 Dateien haben, sondern dass wir schon einige Tage, Wochen oder Jahre an einem Projekt arbeiten. Da fällt sicherlich eine Vielzahl von Dateien an und die müssten wir jedes mal zunächst in die Konsole eingegeben werden.

Der erste Schritt, den wir unternehmen könnten, wäre eine Skriptdatei dafür anlegen: Wir öffnen einen Editor und schreiben den obigen Befehl in eine Datei mit dem Namen 'make.sh' (unter Linux und Mac) bzw. 'make.bat' unter Windows. Windows erkennt an der Dateiendung .bat, dass es sich um ein ausführbares Skript handelt. Unix-artige Systeme interessieren sich nicht für Dateiendungen, hier rufen wir noch den Befehl chmod auf und aktivieren das 'Executable'-Flag:

chmod +x make.sh

Unter Windows können wir jetzt in der Eingabeaufforderung 'make', bzw. 'make.bat' aufrufen. Unter Linux müssen wir zusätzlich klarstellen, dass wir etwas im aktuellen Verzeichnis ausführen wollen:

./make.sh

Damit hätten wir unser erstes Problem gelöst: Wir können unser Projekt ohne großen Aufwand kompilieren.

Das Zeitproblem

Entwickler haben zwischendurch immer wieder längere Pausen, in denen sie sinnfrei durch das Internet surfen, E-Mails checken oder sonst etwas beginnen. Mein Arbeits-Rechner ist heute (Dez 2011) ein 2010er Intel i7-860 mit 2.8 GHz und 8GB RAM, welcher mit vier Kernen und Hyperthreading 31 Sekunden braucht, um meine Quelltexte zu kompilieren.

xin@trinity:~/workspace/gsys/trunk/de/xsd$ time make

real    0m31.463s
user    2m25.921s
sys     0m10.789s

Machen wir das ganze nochmals unter einem Sempron 2600. Dieser hat 1.8GHz, einen Kern, kein Hyperthreading und 1GB RAM. Das ist immer noch ein guter und absolut ausreichender Rechner, um mit dem Programmieren zu beginnen. Dieser benötigt für meine Quelltexte dann mal eben etwas über 6 Minuten.

Das sind die Quelltexte, die ich privat in meiner Freizeit selbst geschrieben habe. Eine Firma mit 10 Mitarbeitern erzeugt entsprechend mehr Quelltexte und jeder Entwickler verbringt 5 Tage die Woche 8 Stunden damit Quelltexte zu schreiben, während ich in meiner Freizeit vergleichsweise selten dazu komme. Wie lange dauert es hier mal eben alles zu kompilieren?

Wir haben offensichtlich ein Problem, dass aufgrund sehr schneller Rechner schon entschärft ist. In meiner Jugendzeit stand bei manchen Programmen der Build-Aufwand dabei und das waren gerne auch mal über 24 Stunden für Programme, die heute eher als kleines Tool gelten würden.

Stellen wir uns vor, dass ich nun bei jeder kleinen Codeänderung 30 Sekunden warten muss. Das wäre ein Problem.

Wie wird ein Programm erzeugt?

Unser kleines Skript kompiliert immer alles. Und das ist in der Regel gar nicht erforderlich, denn die meisten Dateien bleiben unverändert, da man sich beim Programmieren häufig nur in einer sehr kleinen Auswahl von Dateien bewegt. So müssen häufig nur eine bis maximal 5 Dateien kompiliert werden. Unser Skript baut grundsätzlich alles neu, das bezeichnet man als Rebuild.

Ein Programm besteht aus vielen Quelltext-Dateien. Der Compiler erzeugt daraus sogenannte Objekt-Files. Hier ist das Programm bereits erfolgreich kompiliert und maschinenlesbar enthalten, aber noch nicht ausführbar. Aus jeder .c-Datei wird so eine .o Datei erzeugt. Zum Schluss werden mit einem Programm namens Linker alle .o-Dateien zu einem ausführbaren Programm zusammengefasst. Dieser Vorgang heißt entsprechend Linken.

Beim GNU-C-Compiler können wir das von Hand demonstrieren:

xin@trinity:~/proggen.org/tutorial/build/batch$ gcc -c -o main.o main.c
xin@trinity:~/proggen.org/tutorial/build/batch$ gcc -c -o node.o node.c
xin@trinity:~/proggen.org/tutorial/build/batch$ gcc -c -o parameter.o parameter.c
xin@trinity:~/proggen.org/tutorial/build/batch$ ls -l *.o
-rw-r--r-- 1 xin xin 2312 20. Dez 12:06 main.o
-rw-r--r-- 1 xin xin 3648 20. Dez 12:06 node.o
-rw-r--r-- 1 xin xin 2600 20. Dez 12:06 parameter.o
xin@trinity:~/proggen.org/tutorial/build/batch$ gcc main.o node.o parameter.o 

Der Schalter „-c“ beim Aufruf des Compilers sagt dem Compiler, dass er nicht automatisch linken soll, was er im Normalfall tut. Deswegen laufen unsere Programme auch überhaupt, wenn wir den Compiler mit Quelltexten füttern. Der Schalter „-o“ benennt die Ausgabedatei (Output).

So können wir auch den Namen der Ausgabedatei festlegen, wenn uns der Standardname a.out (a.exe unter Windows) nicht gefällt.

xin@trinity:~/proggen.org/tutorial/build/batch$ gcc main.o node.o parameter.o -o testprogramm
xin@trinity:~/proggen.org/tutorial/build/batch$ ls -l testprogramm 
-rwxr-xr-x 1 xin xin 9642 20. Dez 12:09 testprogramm

Wir wissen nun also, dass wir einzelne Quelltexte in Objektfiles kompilieren können und wir wissen, dass wir mit dem gcc-Compiler diese Objektfiles wieder zu einem vollständigen Programm linken können. Wenn wir Quelltexte weiterentwickeln, speichern wir sie anschließend und damit ergibt sich, dass der Quelltext jünger ist als das zugehörige Objektfile. Anhand dieser Information können wir unser Make-Skript so umschreiben, dass es nur dann ein neues Objektfile erzeugt, wenn der Quelltext jünger ist - das geht bedeutend schneller.

Und da wir in diesem Tutorial nicht die ersten sind, die genau dieses Problem haben, gibt es bereits fertige Lösungen für genau dieses Problem: Die Build-Systeme.

Unterschiedliche Buildsysteme

Nennenswert unterscheiden kann man zunächst einmal nur zwei Sorten von Buildsystemen, nämlich konsolenbasierte Buildsysteme und Buildsysteme, die in einer Entwicklungsumgebung enthalten sind. Wir werden uns beide im folgenden ansehen und unser Projekt damit durchkompilieren.

Das bekannteste Buildsystem auf der Konsole ist 'make'. Wer unter Linux das Paket 'build-essential' installiert hat, kann davon ausgehen, dass er alles wichtige zum Softwareerstellen installiert hat und 'make' gehört dazu. Make ist die direkte Abbildung des oben beschriebenen Vorgangs als Tabelle: Wie heißt die Datei, die ich haben will, welche Dateien brauche ich, um diese Dateien zu erzeugen und was muss ich tun, um die Datei, die ich haben möchte, zu erzeugen.

Auch wenn viele Einsteiger so schnell wie möglich von der Konsole weg wollen - make ist der professionelle Weg. Software wird nicht nur von Menschen erzeugt, häufig existieren auch sogenannte Build-Server, die Software automatisiert erstellen und bereit stellen. „Automatisch“ und „Grafische Oberfläche“ sind ein großes Problem, während es sehr einfach ist „make“ automatisch aufzurufen. Sobald professionelle Ansprüche gefragt sind, landet man immer wieder auf der Konsole, egal um welches Betriebssystem es sich handelt.

Für viele Entwickler alltäglich sind sogenannte integrierte Entwicklungsumgebungen (*IDE* = Integrated Development Enviroment). Im Deutschen sagt man meist einfach kurz Entwicklungsumgebung oder die drei Buchstaben I-D-E. IDEs finden sich auf allen relevanten Plattformen. Wir werden das HTML-Projekt mit folgenden IDEs übersetzen.

Häufig wird zu einer IDE namens Dev-C++ geraten. Hiervon raten wir ab, da sie seit sehr langem nicht weiterentwickelt wird und fehlerhaft arbeitet. Daher stellen wir sie hier auch nicht mehr vor.

Fazit dieser Lektion

Bevor Du in die nächste Lektion wechselst, solltest Du Dir angesehen haben, wie Du das Projekt mit Make erstellst und Dir wenigstens einen Überblick über eine Entwicklungsumgebung verschafft haben. Hiermit solltest Du das Wissen besitzen, aufwendigere Konsolenprogramme verwalten zu können und all jene Grundlagen besitzen, um die ersten Schritte aus der Konsole heraus zu wagen.