Mach deinen Job mit make!

ArticleCategory: []

Software Development

AuthorImage:[Here comes a small picture of you]

[Wilbert Berendsen]

TranslationInfo:[Info concerning writer(s) and translator(s)]

original in nl Wilbert Berendsen

nl to en Philip de Groot

en to de Guido Socher

AboutTheAuthor:[a short biography about the author]

Wilbert Berendsen ist professioneller Musiker und enthusiastischer Linuxbenutzer. Fr�her hat er mal Assemblercode f�r den Z80 geschrieben. Jetzt benutzt er Linux f�r all seine Arbeit. Aus Spa� schreibt er Einf�hrungsartikel und unterh�lt eine kleine Webseite unter http://www.xs4all.nl/~wbsoft/. Viva open source!

Abstract:[a small summary/description of this article]

Dieser Artikel �ber make zeigt, wie make funktioniert und wie es f�r andere Dinge als nur Softwareentwicklung benutzt werden kann.

ArticleIllustration:[illustration]

[Illustration]

ArticleBody:[The real article: put the text and html-codes here]

Einf�hrung

Fast jeder, der Linux nutzt, hat das Programm make schon einmal benutzt. make kommt beim Kompilieren des Kernels, bei der Installation eines Softwarepaketes und an vielen anderen Stellen zum Einsatz. 'Make' ist ein sehr wichtiges Werkzeug f�r die Softwareentwicklung.
Make hat jedoch noch viel mehr M�glichkeiten!

In diesem Artikel werden wir sehen, da� make ein leistungsf�higes Werkzeug f�r t�gliche Aufgaben wie das Schreiben von Artikeln, B�chern oder Webseiten sein kann. Im Laufe dieser Einf�hrung werden wir auch viele andere 'Unix Tricks' kennenlernen. Am Ende dieses Artikels werde ich dann noch ein paar Tips zu Make geben. Bitte beachte: Wir reden �ber Linux, aber im Prinzip kann man make auf jedem Betriebssystem einsetzen.

Beispiel: Eine Webseite bauen

Wir wollen eine Webseite bauen, die von unterschiedlichen Leuten unterhalten wird. Jan k�mmert sich um die Seiten. Piet um das Layout.

Wir brauchen ein einfaches System, um das Layout vom Inhalt zu trennen. Eine sehr leistungsf�hige L�sung w�re: Den Inhalt jedes Mal, wenn eine Seite angefragt wird, aus einer Datenbank zu lesen. PHP oder Microsoft ASP funktionieren so. Wir wollen jedoch einfach ganz normale HTML Seiten speichern. Desweiteren �ndert sich der Inhalt nicht so oft, damit eine Datenbank wirklich Sinn macht.

Wir werden einige einfache Befehle benutzen, um die Webseite zu bauen.

Piet hat z.B den Kopfteil einer Seite in header.html und das Seitenende in der Datei footer.html. Diese k�nnten etwa so aussehen:

<html><!-- the header -->
<head>
<title>Piet and Jan productions</title>
</head>
<body bgcolor="white">
<table border="0" width="100%"><tr>
<td bgcolor="#c040ff" valign="top">
This is our website<br>
Some rubbish is written down here.<br>
We are very interactive<br>
so this is our telephone number:<br>
<b>0123-456789</b>
</td><td valign="top">
<!-- Put the contents here -->
und hier footer.html:
<!-- the footer -->
</td></tr></table>
</body></html>
Damit sind z.B die Unix Kommandos um aus Jans Seiten die endg�ltigen Seiten zu machen:
cat header.html  /home/jan/Docs/website/index.html
echo -n '<hr>Last modification: '
date '+%A %e %B'
cat footer.html
Wie diese Kommandos funktionieren, kannst du in den man-Seiten nachlesen. Die Ausgabe dieser Kommandos wird dann in die Datei /home/piet/public_html/index.html umgeleitet:
{
  cat header.html  /home/jan/Docs/website/index.html
  echo -n '<hr>Last modification: '
  date '+%A %e %B'
  cat footer.html
} > /home/piet/public_html/index.html
Diese Prozedur kann auch auf die andere Datei (offer.html) angewendet werden.

Jedoch macht es keinen Sinn, immer wieder diese Kommandos von Hand einzugeben. Wir schreiben uns ein kleines Skript. Das Skript sollte sowohl ausgef�hrt werden, wenn Jan eine �nderung macht als auch nach einer �nderung von Piet an header/footer. Nichts sollte jedoch gemacht werden, wenn keiner von beiden etwas ge�ndert hat. Wir werden Linux f�r eine intelligente L�sung (lies: automatisch) benutzen!

Jetzt kommt make ins Spiel.

Das erst Zusammentreffen mit make

Das info-Manual des GNU make ist ein fantastisches Dokument. Es fokussiert jedoch von Anfang an auf eine Programmierumgebung. Aus diesem Grund m�chte ich die Funktionen von make in einem breiteren Zusammenhang darstellen:
    Make entscheidet, ob eine Anzahl von Kommandos ausgef�hrt
    werden m�ssen, basierend auf den Modifizierungszeiten der
    erzeugten Dateien und der Quelldateien.
In anderen Worten: Falls eines der Quelldateien, die ben�tigt werden, um eine erzeugte Datei zu erstellen, neuer ist als die erzeugte Datei, dann m�ssen die Kommandos ausgef�hrt werden, und das Ziel dieser Kommandos ist es, eine neue Datei zu erzeugen.

Die erzeugte Datei nenne wir von nun an 'target' und die Quelldateien sind die `prerequisites'. Falls eines der `prerequisites' neuer als ein 'target' ist, dann , und nur dann, wird ein Kommando ausgef�hrt, um aus dem `prerequisites' ein neues 'target' zu machen.

Im augenblicklichen Arbeitsverzeichnis erzeugt man eine Datei mit dem Namen Makefile. Diese Datei enth�lt die Information, die von make ben�tigt wird.

make wird durch folgendes Kommando aufgerufen:

make target1 target2 ....

target ist optional. Falls das target fehlt, wird das erste im Makefile definierte target benutzt. Make sucht immer im augenblicklichen Arbeitsverzeichnis nach der Datei Makefile. Man kann mehr als nur ein target angeben.

Makefile Syntax

# This is an example of a Makefile.
# Comments can be put after a hash (#).

target: prerequisites
    command
        
target: prerequisites
    commando 
        
# and so on and so on.
Wir fangen mit einem target an, gefolgt von einem Doppelpunkt und dann den ben�tigten prerequisites. Man kann eine Zeile verl�ngern, indem man sie mit einem backslash (\) beendet und dann in der n�chsten Zeile fortsetzt.

Die folgenden Zeilen enthalten dann ein oder mehrere Kommandos. Jede Zeile ist ein einzelnes Kommando. Falls ein Kommando zu lang ist und �ber mehrere Zeilen gehen soll, benutzt man zwei backslashes (\\) am Ende der Zeile.

Wichtige Anmerkung: Die Kommandozeilen werden durch einen TAB einger�ckt und nicht durch Leerzeichen!

Make liest das Makefile und bestimmt f�r jedes target (angefangen mit dem ersten), ob das Kommando ausgef�hrt werden soll. Jedes target zusammen mit den prerequisites nennt man Regel.

Ein Makefile f�r unser Beispiel

In unserem Beispiel sieht das Makefile so aus:
# This Makefile builds Piets' and Jans' website, the potato-eaters.

all: /home/piet/public_html/index.html /home/piet/public_html/offer.html

/home/piet/public_html/index.html:  header.html footer.html \
                                    /home/jan/Docs/website/index.html
    { \
          cat header.html  /home/jan/Docs/website/index.html ;\
          echo -n '<hr>Last modification: '                 ;\
          date '+%A %e %B'                                   ;\
          cat footer.html                                    ;\
        } > /home/piet/public_html/index.html

/home/piet/public_html/offer.html:  header.html footer.html \
                                    /home/jan/Docs/website/offer.html
    { \
          cat header.html  /home/jan/Docs/website/index.html ;\
          echo -n '<hr>Last modification: '                 ;\
          date '+%A %e %B'                                   ;\
          cat footer.html                                    ;\
        } > /home/piet/public_html/offer.html

# the end

Hier haben wir drei targets, 'all', index.html und offer.html. Die einzige Funktion des targets 'all' ist die anderen beiden targets als prerequisites zu haben. Diese werden beide getestet, weil 'all' selbst kein Dateiname ist und 'all' damit immer ausgef�hrt wird. Sp�ter werden wir einen eleganteren Weg kennenlernen, um targets, die keine Dateinamen sind zu definieren).

Falls Kopf- und Fu�-Dateien ge�ndert w�rden, w�rden beide Seiten neu generiert. Falls Jan nur eine seiner Seiten modifiziert, wird auch nur eine neue Seite generiert. 'make' macht's.

Das Makefile hat ein Problem: es ist nicht so �bersichtlich. Zum Gl�ck gibt es verschiedene M�glichkeiten, die Sache einfacher zu machen.

Vereinfachung des Makefiles

Variablen

Mit Variablen kann man ein Makefile stark vereinfachen. Variablen definiert man wie folgt:
variable = value
Wir greifen auf den Wert einer Variablen mit dem Ausdruck $(variabele) zu. Im folgenden haben wir das in unser Makefile eingebaut:
# This Makefile builds Piets' and Jans' website, the potato-eaters.

# Directory where the website is stored:
TARGETDIR = /home/piet/public_html

# Jans' directory:
JANSDIR = /home/jan/Docs/website

# Files needed for the layout:
LAYOUT = header.html footer.html

all: $(TARGETDIR)/index.html $(TARGETDIR)/offer.html

$(TARGETDIR)/index.html:  $(LAYOUT) $(JANSDIR)/index.html
    { \
          cat header.html $(JANSDIR)/index.html     ;\
          echo -n '<hr>Last modification: '        ;\
          date '+%A %e %B'                          ;\
          cat footer.html                           ;\
        } > $(TARGETDIR)/index.html

$(TARGETDIR)/offer.html:  $(LAYOUT) $(JANSDIR)/offer.html
    { \
          cat header.html  $(JANSDIR)/index.html    ;\
          echo -n '<hr>Last modification: '        ;\
          date '+%A %e %B'                          ;\
          cat footer.html                           ;\
        } > $(TARGETDIR)/offer.html

# the end
Es ist eine Konvention, gro�e Buchstaben f�r Variablen zu benutzen. Jetzt ist es viel einfacher z.B Verzeichnise zu verschieben.

Was sollen wir machem, wenn wir noch mehr Dokumente haben. Das Makefile w�rde sehr lang. F�r jedes Dokument eine Regel wird auch wieder un�bersichtilch. Hier helfen Wildcards.

Regeln mit Wildcards

Regeln mit Wildcards oder `Pattern Rules' erm�glichen es, dieselben Kommandos f�r alle m�glichen targets zu benutzen.

Falls `Pattern Rules' benutzt werden, kommt in der Regelzeile noch ein Feld hinzu.

Multiple targets: pattern : prerequisite prerequisite ...
    command
Das pattern ist ein Ausdruck der g�ltig f�r alle Targets ist. Ein Prozentzeichen dient dabei als Wildcard:

Ein Beispiel:

/home/bla/target1.html /home/bla/target2.html: /home/bla/% : %
    commands
Make vergleicht die zwei Targets mit dem pattern und macht daraus dann folgende Regeln:
/home/bla/target1.html: target1.html
    commands
        
/home/bla/target2.html: target2.html
    commands
Das Prozentzeichen in `/home/bla/%' wird mit dem target `/home/bla/target1.html' zu `target1.html' und damit wird das prerequisite `%' zu `target1.html':

F�r unsere Webseite benutzen wir nun folgende Regel:

$(TARGETDIR)/index.html $(TARGETDIR)/offer.html: \
                   $(TARGETDIR)/% : $(JANSDIR)/% \
                                         $(LAYOUT)
Bleibt noch ein Problem: Wie benutzt man diese Variablen in Kommandos?

Automatische Variablen

Zum Gl�ck definiert make selbst einige Variablen. Einige dieser Variablen bezeichnet man als automatische Variablen. Diese Variablen erhalten vor der Ausf�hrung eines Kommandos den Wert von target und prerequisite.

Die spezielle Variable $< enth�lt das erste prerequisite und die Variable $@ expandiert immer zum augenblicklichen target.

Mit diesen Variablen ist es m�glich, eine Regel mit Kommando zu verallgemeinern:

$(TARGETDIR)/index.html $(TARGETDIR)/offer.html: $(TARGETDIR)/% : \
                                                     $(JANSDIR)/% \
                                                          $(LAYOUT)
    { \
          cat header.html  $<                       ;\
          echo -n '<hr>Last modification: '        ;\
          date '+%A %e %B'                          ;\
          cat footer.html                           ;\
        } > $@
Voilà! Das funktioniert nun f�r beide Dateien.

Hier ist nun unser komplettes Makefile:

# This Makefile builds Piets' and Jans' website, the potato-eaters.

# Directory where the website is published:
TARGETDIR = /home/piet/public_html

# Jans' directory:
JANSDIR = /home/jan/Docs/website

# Files needed for the layout:
LAYOUT = header.html footer.html

# These are the webpages:
DOCS = $(TARGETDIR)/index.html $(TARGETDIR)/offer.html


# Please change nothing below this line;-)
# -------------------------------------------------------------

all: $(DOCS)

$(DOCS): $(TARGETDIR)/% : $(JANSDIR)/% $(LAYOUT)
    { \
          cat header.html  $<                       ;\
          echo -n '<hr>Last modification: '         ;\
          date '+%A %e %B'                          ;\
          cat footer.html                           ;\
        } > $@

# the end
So sollte es aussehen. Falls mehr Dokumente hinzukommen, ist das sehr leicht zu machen. Wir m�ssen nur die Variable DOCS �ndern.

Die Person, die mit dem Makefile arbeitet, sollte einfach sehen k�nnen, wie es arbeitet, ohne sich zu wundern, wie es wohl funktioniert.

Letzte Optimierungen

Wir w�rden gerne die Dokumente in der Variable DOCS ausf�hren, jedoch ohne bei einer �nderung immer den vollen Pfad angeben zu m�ssen. Dazu f�hren wir eine neue Variable TEXTS ein:
TEXTS = index.html  offer.html  yetanotherfile.html

# Please change nothing below this line;-)
# -------------------------------------------------------------
DOCS =  $(addprefix $(TARGETDIR)/,$(TEXTS))

all: $(DOCS)

# and so on
Was wir hier sehen, ist eine spezielle make Funktion. Anstelle einer Variablen kann man einen kompletten Ausdruck zwischen den Klammern benutzen. Mit solchen Ausdr�cken (vordefinierten Funktionen von make) kann man Text auf verschiedene Art und Weise modifizieren.

Die Funktion $(addprefix prefix,list) f�gt zu jedem Element in der Liste einen Prefix hinzu.

Die Elemente in der Liste m�ssen durch Leerzeichen getrennt sein.

Am Anfang haben wir schon erw�hnt, da� das target 'all' keine Datei namens 'all' erzeugt (die Regel enth�lt keine Kommandos). Was passiert jedoch, wenn diese Datei 'all' zuf�llig existiert und auch noch neuer als alle anderen Dateien ist...?

Es gibt eine einfache M�glichkeit make zu sagen, da� ein bestimmtes target ausgef�hrt werden soll und keine Datei erzeugt. Solch ein target markiert man als 'phony' (nicht wirklich). Das funktioniert so:

.PHONY: all
Nun sieht das ganze Makefile so aus:
# This Makefile builds Piets' and Jans' website, the potato-eaters.

# Directory where the website is published:
TARGETDIR = /home/piet/public_html

# Jans' directory:
JANSDIR = /home/jan/Docs/website

# Files needed for the layout:
LAYOUT = header.html footer.html

# These are the names of the webpages:
TEXTS = index.html  offer.html  yetanotherfile.html

# Please change nothing below this line;-)
# ------------------------------------------------------
DOCS =  $(addprefix $(TARGETDIR)/,$(TEXTS))
.PHONY: all

all: $(DOCS)

$(DOCS): $(TARGETDIR)/% : $(JANSDIR)/% $(LAYOUT)
    { \
          cat header.html  $<                       ;\
          echo -n '<hr>Last modification: '         ;\
          date '+%A %e %B'                          ;\
          cat footer.html                           ;\
        } > $@

# the end
Speichere die Datei und vergi� sie! Lediglich TEXTS mu� vielleicht manchmal ge�ndert werden. Jetzt ist das Unterhalten der Webseite sehr einfach. Wir k�nnen sogar crontab benutzen und das Makefile t�glich ausf�hren.

Letzte Anmerkungen

Nat�rlich kann man das Beispiel modifizieren und auf andere Situationen anwenden.

Z.B ist die einfache Art und Weise, wie die Dokumente generiert werden, nicht fehlerfrei. Falls Jan versehentlich am Ende seiner Dateien </body></html> schreibt, werden einige Browser den Fu�teil von Piet nicht anzeigen. Man kann jedoch grep, perl oder tcl einsetzen, um das zu beheben.

Nat�rlich kann Jan auch einfach normale Textdateien schreiben und wir benutzen folgendes sed Kommando, um Paragraphen einzuf�hren (Leerzeilen durch <P> ersetzen):

sed -e 's/^\s*$/<p>/g'
Weiterhin kann Jan seinen Text mit LyX schreiben und lyx2html kann benutzt werden, um daraus HTML zu machen. Deiner Phantasie sind unter Linux keine Grenzen gesetzt.

Wir haben uns noch keine Gedanken gemacht, wie Bilder behandelt werden sollen. Auch hier gibt es viele M�glichkeiten, aber das hat eigentlich nichts mit make zu tun.

Ich hoffe, es wurde klar, wie Make im Prinzip funktioniert und wie man viele Sachen mit einem guten Makefile vereinfachen kann.

Tips

Weitere Informationen

Wietere Infromationen, wie make funktioniert, kann man in dem `GNU Make Handbuch' finden. Man kann das Handbuch mit folgenden Kommandos unter Linux lesen:
info make
Auch die GNOME und KDE Helpbrowser oder das n�tzliche tkinfo Programm k�nnen statt info benutzt werden.

Links:

Viel Spa�!