News   Magazin   Börse   Links   ArcArchie   Homepages
 Magazin  Mit Make kompilieren Home 
Hardware   Software   Praxis   Sonstiges  
Mit Make kompilieren  Carlos Michael Santillán 9. 8. 2015

Wenn man ein Programm in C, C++ oder einer anderen Kompilersprache schreibt, muss man den Quellcode mit dem Kompiler in ein ausführbares Programm übersetzen. Bei der GNU Compiler Collection (GCC) von RISC OS sind bei C dazu zwei Schritte in der Kommandozeile notwendig. Die sehen zum Beispiel wie folgt aus.

   gcc -c HalloWelt1.c -o HalloWelt1.o
   gcc -o HalloWelt1 HalloWelt1.o
					

In der ersten Zeile wird aus dem Quellcode eine Objektdatei kompiliert, die dann in der zweiten Zeile zu einen ausführbaren Programm gelinkt wird. Diese beide Zeilen bei der Programmentwicklung immer und immer wieder zu tippen ist nichts für Programmierer. Also packt man die beiden Zeilen in ein Obey oder TaskObey und kann dieses dann in der Kommandozeile oder per Doppelklick aufrufen um das Programm zu erstellen.

Mit Make wurde den Programmierern ein Werkzeug in die Hand gegeben das besser für diese Aufgabe geeignet ist, als ein (Task)Obey, Shellskript und so weiter.

Beim Acorn C/C++, das später Castle C/C++ hieß und nun Desktop Development Environment heißt gibt es amu inklusive graphischer Oberfläche. Hier beziehe ich mich aber nur auf die GNU Compiler Collection und das GNU Make. Einiges kann man aber für amu übernehmen. Zusätzlich kann man GNU Make auch mit dem Desktop Development Environment nutzen. GNU Make ist nicht Bestandteil von GCC und muss extra herunterladen und installieren werden.

Damit Make seine Arbeit aufnehmen kann, muss man noch eine Konfiguration, das Makefile, erstellen. Zuerst noch ein kleiner C Quellcode damit Make und GCC etwas zu tun bekommt und man ein Ergebnis in den Händen halten kann.

   /* HalloWelt 1 */

   #include <stdio.h>

   int main(void) {
      puts("Hallo Welt 1");

      return 0;
   }
					

Verzeichnis Dieses C-Programm bitte als HalloWelt1 in das Verzeichnis c speichern.

In der Unixwelt und anderswo haben C-Quellcode zum Beispiel den Namen prog.c. Da unter RISC OS das Trennzeichen für Verzeichnisse der Punkt ist, gibt es bekanntermaßen ein Problem. Unter RISC OS wird in der Regel dann prog/c oder bei einen JPEG Bild bild/jpg geschrieben. Aber gerade bei C/C++ wurde festgelegt, dass die C-Quelltexte in das Verzeichnis c gelegt werden und keine Endung /c erhalten. Auch wenn der Quelltext prog im Verzeichnis c liegt, so heißt für GCC und Make der Quelltext prog.c. Dito mit der Objektdatei o.prog die dann als prog.o aufgerufen wird. Das ist auf den ersten Blick verwirrend, ist aber nun mal so festgelegt worden. Im folgende werde ich zum Beispiel prog.c schreiben, auch wenn die Datei prog im Verzeichnis c gemeint ist. Ansonsten müsste ich prog.c alias c.prog schreiben und das recht häufig. Das selbe mit prog.o alias o.prog und mit prog.h alias h.prog. Auf die wenigen Ausnahmen werde ich dann hinweisen.

Nun muss die Datei Makefile mit folgenden Inhalt im gleichen Verzeichnis in dem das Verzeichnis c liegt in einen Texteditor erstellt werden.

   HalloWelt1: HalloWelt1.o
      gcc -o HalloWelt1 HalloWelt1.o

   HalloWelt1.o: HalloWelt1.c
      gcc -c -o HalloWelt1.o HalloWelt1.c
					

Es handelt sich hier um zwei Regeln. In der ersten Zeile des Makefiles ist HalloWelt1 die Zieldatei der ersten Regel. Die Zieldatei ist abhängig von HalloWelt1.o. Das heißt nichts anderes, das man die Objektdatei HalloWelt1.o braucht um das ausführbare Programm HalloWelt1 zu erhalten. Das erreicht man durch das Linken der Objektdatei HalloWelt1.o. Ziel und die notwendige Datei sind mit einen Doppelpunkt getrennt. Nun folgt in der zweiten Zeile das Kommando um das Ziel zu erreichen, also das Linken. Das ist die zweite Kommandozeile von oben. Diese Zeile ist eingerückt. Die Einrückung besteht nicht aus Leerschritten, sondern aus einen Tabzeichen. Das ist sehr wichtig, diese Zeile muss mit einen Tab eingerückt sein. Je nach Editor beziehungsweise Einstellung des Editors erzeugt ein Druck auf die Tabtaste kein Tab, sondern mehrere Leerzeichen. Ohne die Tab-Einrückung wird Make nicht seiner Aufgabe nachgehen können.

Mit der zweiten Regel verhält es sich sehr ähnlich. Hier ist das Ziel HalloWelt1.o und ist von der Datei HalloWelt1.c abhängig. In der nachfolgenden Zeile wird der Quellcode zur Objektdatei kompiliert. Ruft man nun in der Kommandozeile make auf, dann wird zuerst der Quellcode kompiliert und dann das ausführbare Programm erstellt. Ruft man ein zweites Mal make auf oder hat vorher schon das Programm per Hand erstellt, dann wird Make nur melden, dass nichts zu tun ist. Löscht man jetzt die Datei HalloWelt1 und o.HalloWelt1, dies ist die RISC OS Schreibweise, und ruft dann noch mal make auf, wird erst kompiliert und dann gelinkt. Löscht man nur HalloWelt1 wird nur gelinkt. Die Objektdatei HalloWelt1.o ist ja noch vorhanden und muss nicht neu erstellt werden.

Make schaut bei einen Aufruf nach, ob die Zieldatei vorhanden ist. Also im ersten Schritt wird geschaut ob HalloWelt1 vorhanden ist. Ist die Zieldatei HalloWelt1 vorhanden, dann wird geschaut ob die abhängige Datei, hier HalloWelt1.o, jünger als das Ziel ist. Ist HalloWelt1.o jünger, dann ist die Zieldatei veraltet und muss neu erstellt werden. Aber zuerst wird noch geprüft ob es auch eine Regel für HalloWelt1.o gibt und die gibt es in der zweiten Regel. Wie bei der ersten Regel wird geprüft ob HalloWelt1.o existiert oder nicht älter als die abhängige Datei, hier HalloWelt1.c, ist. Für HalloWelt1.c gibt es keine Regel und wenn HalloWelt1.c jünger ist beziehungsweise HalloWelt1.o nicht vorhanden ist, dann wird nun zuerst die zweite Regel ausgeführt und HalloWelt1.o kompiliert. Dann muss nach der ersten Regel HalloWelt1 erstellt werden.

Bei den Make-Regeln dreht es sich also im wesentlichen darum ob das Ziel älter als die abhängige Datei ist. In diesen Beispiel ergibt sich kein Vorteil zu einen (Task)Obey, da HalloWelt1.c die einzige Datei ist, die man bei der Entwicklung ändert. Und nach einer Änderung muss immer kompiliert und gelinkt werden um die Änderung im Programm zu erhalten.

Bei größeren Projekten gibt es mehrere Gründe warum man ein Programm im mehrere Quelldateien unterteilt. Jeder Quelltext muss dann einzeln kompiliert und am Schluss gelinkt werden.

   gcc -c HalloWelt2.c -o HalloWelt2.o Nummer2.o
   gcc -c -o Nummer2.o Nummer2.c
   gcc -o HalloWelt2 HalloWelt2.o
					

Wenn das in ein (Task)Obey eingetragen wurde, dann werden immer beide Quellcode kompiliert, auch wenn man nur an einer der beiden Quelltexte etwas geändert hat. Der andere Quelltext braucht eigentlich nicht kompiliert zu werden, da sich dort nichts geändert hat. Da die Kompilation doch etwas dauert, kann man etwas Zeit sparen, wenn man ein Tool hat das prüft ob die Quelle jünger als die zugehörige Objektdatei ist. Das Tool kenne wir schon. Erstellen wir ein C-Programm, das aus zwei Quelltexten besteht.

   /* HalloWelt 2 */

   #include <stdio.h>

   /* int nummer_zwei(); */
   #include "Nummer2.h"


   int main(void) {
      printf("Hallo Welt %i\n", nummer_zwei());

      return 0;
   }
					

Der erste Quellcode HalloWelt2.c ruft die Funktion nummer_zwei() auf. Diese Funktion ist in Nummer2.c vorhanden.

   /* Nummer 2 */

   int nummer_zwei() {
      return 2;
   }
					

Nun muss man eine neues Makefile anlegen. Benennen wir das alte zuerst in Makefile1 um. Falls man das später noch benutzen möchte, kann man es mit make -f Makefile1 aufrufen.

   HalloWelt2: HalloWelt2.o Nummer2.o
      gcc -o HalloWelt2 HalloWelt2.o Nummer2.o

   HalloWelt2.o: HalloWelt2.c
      gcc -c -o HalloWelt2.o HalloWelt2.c

   Nummer2.o: Nummer2.c
      gcc -c -o Nummer2.o Nummer2.c
					

Das erste Ziel HalloWelt2 hat nun zwei Dateien von dem es abhängt. Diese sind HalloWelt2.o und Nummer2.o. Für beide Objektdateien gibt es jeweils eine Regel, die der Regel HalloWelt1.o aus dem ersten Beispiel gleichen. Der wesentliche Unterschied zum ersten Makefile ist, das es mehrere Abhängigkeiten für ein Ziel geben kann. Ist einer der beiden Dateien, also HalloWelt2.o oder Nummer2.o, jünger als HalloWelt2, dann muss neu gelinkt werden. Vorher prüft Make natürlich wieder ob die Objektdateien noch auf dem aktuellen Stand sind.

Im Gegensatz zu einen (Task)Obey wird nur kompiliert, wenn es notwendig ist. Das kann je nach Projekt einige Minuten bei einer Änderung im Quellcode sparen. Hier ist Make klar vorzuziehen.

Normalerweise wird die Zeile die auf die externe Funktionen hinweist in eine eigene Headerdatei geschrieben. Löschen wir also die Zeile int nummer_zwei(); aus HalloWelt2.c und tragen dafür #include "Nummer2.h" ein. Dann muss im Verzeichnis h die Datei Nummer2 anlegen und dort int nummer_zwei(); einfügen werden. Ein make wird zwar jetzt noch funktionieren, aber Make wird Änderung in Nummer2.h nicht berücksichtigen. Also fügen wir in der zweiten Make-Regel mit dem Ziel HalloWelt2.o noch Nummer2.h als abhängige Datei am Ende hinzu.

   ...
   HalloWelt2.o: HalloWelt2.c Nummer2.h
      gcc -c -o HalloWelt2.o HalloWelt2.c
   ...
					

Ändert man nun etwas an Nummer2.h oder ruft *Stamp h.Nummer2, dann wird die Regel HalloWelt2.o ausgeführt und danach natürlich gelinkt. Da *Stamp ein eingebautes RISC OS Kommando ist, wird es in der RISC OS Schreibweise genutzt.

In vielen Makefiles gibt es das Ziel all und clean. Aber nirgends ist eine Datei all oder clean zu finden, die mit den Regeln erstellt wurde. Erst Mal zu clean. Wenn man in dem zweiten Makefile nun am Ende die Regel

   clean:
      Wipe o.HalloWelt2 ~CFV
      Wipe o.Nummer2 ~CFV
      Wipe HalloWelt2 ~CFV
					

Taskwindow hinzufügt und man make aufruft, wird nichts neues passieren. Wenn man aber make clean aufruft wird die Regel clean aufgerufen. Nebenbei clean hängt von keiner Datei oder einer anderen Regel ab und wird damit immer ausgeführt, wenn die Regel aufgerufen wurde. Die Kenner der RISC OS Kommandozeile wissen was das Kommando *Wipe macht. Es löscht Dateien. Hier die Dateien HalloWelt2.o, Nummer2.o und HalloWelt2. In einer Regel kann man also auch mehrere Kommandos ausführen lassen. Natürlich sind alle diese Zeilen mit Tab eingerückt. Hat man make clean ausgeführt, dann kann man mit make das Projekt wieder bauen. mit make clean erhält man ein sauberes Projekt ohne Objektdateien und dem ausführbaren Programm. Statt die beiden Objektdateien einzeln zu löschen, hätte man auch Wipe o.* ~CFV eintragen können. Dann würde aber auch HalloWelt1.o gelöscht. In einen realen Projekt ist o.* sicherlich die einfachere Lösung.

Nun zur Regel all. Wenn man die Regel clean aufgerufen hat, sollte man nun make Nummer2.o in die Kommandozeile eingeben. Dann wird nur die Regel Nummer2.o angewendet. Ein make danach wird die Regel HalloWelt2.o und HalloWelt2 abarbeiten. Man kann also, wie mit make clean, eine beliebige Regel gezielt aufrufen. Wenn man nur make aufruft wird die erste Regel, also hier HalloWelt2 inklusive deren Abhängigkeiten ausgeführt. Ein Projekt kann mehrere Programme erstellen. Um sicherzustellen, das das gesamte Projekt erstellt wird, wird oft die Regel all eingeführt. Wenn man das erste Makefile für HalloWelt1 in das aktuelle Makefile einfügen, kann man mit make HalloWelt1 und make HalloWelt2 die beiden Programme getrennt erstellen. Aber mit make wird man beide Programme mit einen Aufruf erstellen.

   all: HalloWelt1 HalloWelt2


   HalloWelt1: HalloWelt1.o
      gcc -o HalloWelt1 HalloWelt1.o

   HalloWelt1.o: HalloWelt1.c
      gcc -c -o HalloWelt1.o HalloWelt1.c


   HalloWelt2: HalloWelt2.o Nummer2.o
      gcc -o HalloWelt2 HalloWelt2.o Nummer2.o

   HalloWelt2.o: HalloWelt2.c Nummer2.h
      gcc -c -o HalloWelt2.o HalloWelt2.c

   Nummer2.o: Nummer2.c
      gcc -c -o Nummer2.o Nummer2.c


   clean:
      Wipe o.* ~CFV
      Wipe HalloWelt1 ~CFV
      Wipe HalloWelt2 ~CFV
					

Den einen oder anderen ist vielleicht etwas aufgefallen bei Zeilen wie folgende.

   HalloWelt1: HalloWelt1.o
      gcc -o HalloWelt1 HalloWelt1.o
					

HalloWelt1 und HalloWelt1.o kommen in der Regel zweimal vor. Make erlaubt einen hier etwas Tipparbeit zu sparen. Die Zieldatei kann man auch mit $@ beschreiben. Die erste Abhängigkeit kann man mit $< darstellen. Die beiden Zeilen kann man damit als

   HalloWelt1: HalloWelt1.o
      gcc -o $@ $<
					

schreiben. Hat man mehrere Abhängigkeiten, so kann man $? benutzen, also zum Beispiel:

   HalloWelt2: HalloWelt2.o Nummer2.o
      gcc -o $@ $?
					

Das macht die ganze Sache etwas übersichtlicher und kürzer. Man kann auch selber Variablen festlegen. So kann man zum Beispiel mit CFLAGS = -O3 die Variable CFLAGS einrichten. Dies sollten man am Anfang des Makefiles machen und es es ist Standard Variablen durchgehend groß zu schreiben. Man kann aber Variablen auch kleinschreiben. Wichtig ist nur, das Make die Groß- und Kleinschreibung beachtet. Weiter unten kann man dann mit gcc $(CFLAGS) -c -o $@ $< den Aufruf gestalten. Das Makefile sieht nun wie folgt aus.

   CC = gcc
   LD = gcc
   CFLAGS = -O3


   all: HalloWelt1 HalloWelt2


   HalloWelt1: HalloWelt1.o
      $(LD) -o $@ $<

   HalloWelt1.o: HalloWelt1.c
      $(CC) $(CFLAGS) -c -o $@ $<


   HalloWelt2: HalloWelt2.o Nummer2.o
      $(LD) -o $@ $?

   HalloWelt2.o: HalloWelt2.c Nummer2.h
      $(CC) $(CFLAGS) -c -o $@ $<

   Nummer2.o: Nummer2.c
      $(CC) $(CFLAGS) -c -o $@ $<


   clean:
      Wipe o.* ~CFV
      Wipe HalloWelt1 ~CFV
      Wipe HalloWelt2 ~CFV
					

Der Einsatz von Variablen hat auch den Vorteil, dass man später recht schnell und einfach und an einer Stelle den Inhalt einer Variablen austauschen kann. Auch Libraries kann man so dem Kompiler mitgeben.

Mit dem GNU Make kann man noch mehr anstellen. Ich denke das wichtigste habe ich hier erwähnt. Wer neugierig geworden ist, sollte sich die sehr ausführliche Dokumentation durchlesen. Im Netz gibt es die eine oder andere Seite mit Interessanten zum Thema Make. Dann aber meist für Linux und weniger für RISC OS.

Die Quellcode und die Makefiles kann man sich hier herunterladen. Viele Spaß beim Programmieren.

Bitte einfach eine Nachrichten am mich schicken, wenn etwas unklar ist, eine Fehler vorhanden ist, es was zu ergänzen gibt, es ein Tipp gibt oder um einfach einen Kommentar loszuwerden.

Name: *

EMail:

Text: *
Hardware   Software   Praxis   Sonstiges  
ArcSite   News   Magazin   Börse   Links   ArcArchie   Homepages