Arbeiten mit make

Aus Wikibooks
Zur Navigation springen Zur Suche springen
WTFPL-2
Hinweis: Wenn du diese Seite bearbeitest, stimmst du zu, dass deine Bearbeitungen zusätzlich unter den Bedingungen der WTF Public License veröffentlicht werden.
WTFPL-2


Worum geht's?[Bearbeiten]

Im vorangegangenen Kapitel hast Du bereits ein erstes Programm übersetzt und auf den Microcontroller übertragen. Alle Arbeitsschritte vom Übersetzen der Quelldatei, dem Linken und Konvertieren, bis hin zum Installieren hast Du dabei von Hand vorgenommen.

Bei Aufgaben die nur einmal durchgeführt werden müssen, führt ein solches manuelles Arbeiten schnell zum Ergebnis. Auch um einen Überblick über den Umgang mit den beteiligten Werkzeugen zu gewinnen, kann das Arbeiten von Hand durchaus nützlich sein. Bei der Bewältigung immer wieder kehrender Aufgaben, wie dem Arbeiten an einer Quelldatei, kann es auf Dauer aber auch viel Zeit und Mühe kosten, immer wieder alle Schritte übersetzen, linken und konvertieren von Hand auszuführen.

In diesem Kapitel soll deshalb eine Möglichkeit vorgestellt werden, mit der einige dieser Aufgaben automatisiert werden können. Um mit den folgenden Kapiteln zu arbeiten, ist es ist nicht zwingend erforderlich, dass Du dieses Kapitel sofort liest. Wenn Du zunächst erst Mal ein wenig programmieren und experimentieren möchtest, kannst Du dieses Kapitel ruhig überspringen und zu einem späteren Zeitpunkt hierher zurückkehren. Vielleicht findest Du ja auch heraus, dass Du dieses Kapitel gar nicht brauchst, und findest eine Alternative die für Dich viel besser funktioniert.

In diesem Kapitel kannst Du die folgenden Dinge lernen:

  • Das Programm make installieren
  • Dokumentation und Hilfe zum Programm make finden
  • Das Programm make bedienen
  • Ein Makefile schreiben
  • Regeln in einem Makefile vorgeben
  • Variablen definieren und nutzen

Das Programm Make[Bearbeiten]

Für das in diesem Kapitel vorgestellte Buildsystem wird das Programm make[1] verwendet.

Über das Programm make sind bereits zahlreiche gute Bücher wie [Mec04] geschrieben worden. Ziel dieses Kapitels ist es deshalb nicht einen weiteren Versuch zu unternehmen das Rad neu zu erfinden. Vielmehr soll es als Einstiegs- und Orientierungshilfe für eigenständiges Arbeiten und Recherchieren mit der entsprechenden Fachliteratur dienen.

Installation[Bearbeiten]

Das Programm make gibt es in verschiedenen Varianten. Alle Angaben in diesem Kapitel beziehen sich auf die Variante GNU make, die vom GNU Projekt zur Verfügung gestellt wird.

Sie sollte in jeder gängigen Linux Distribution über die Paketverwaltung installierbar sein. In auf debian basierenden Distributionen Ubuntu, Kubuntu, Xubuntu, Lubuntu, etc. kann es mit dem Paket make installiert werden.

sudo apt-get install make

Hilfe[Bearbeiten]

Wie die meisten Komponenten, die vom GNU Projekt zur Verfügung gestellt werden, verfügt auch GNU make über eine umfangreiche Dokumentation.[2]

Es lohnt sich parallel zur Lektüre dieses Kapitels einen Blick in die Dokumentation zu werfen. Der Text dieses Kapitels wird an den entsprechenden Stellen einen Verweis auf den zugehörigen Teil der Dokumentation liefern. Es schadet aber auch nicht, die Dokumentation auch über explizit im Text erwähnte Abschnitte und Passagen hinaus zu studieren.

Vorbereitung[Bearbeiten]

Bevor es mit dem ersten Makefile los geht, muss noch ein letztes Mal der Suchpfad für Programme anpasst werden.

export PATH=/opt/arduino-1.8.0/hardware/tools/avr/bin:$PATH

Für diesen Makel wird bald Abhilfe geschaffen werden. Vorerst wird der manuell gesetzte Pfad aber noch gebraucht.

Erste Schritte[Bearbeiten]

Das Arbeiten mit dem Programm make erfolgt in zwei Phasen.

In der ersten Phase wird eine Steuerungsdatei für das Programm make erstellt und bearbeitet. Typischer Weise trägt diese Steuerungsdatei den Namen Makefile[3]. Aufgaben, die make übernehmen soll, werden in diesem Makefile hinterlegt.

Die zweite Phase besteht im Aufruf des Programms make. In dieser Phase liest make die Steuerungsdatei ein und arbeitet die darin hinterlegten Aufgaben ab.

Makefile schreiben[Bearbeiten]

Im vorangegangenen Kapitel wurde der Befehl avr-gcc -c -o source.o -Os -mmcu=atmega328p -DF_CPU=16000000UL source.c verwendet, um die Datei source.o aus der Datei source.c zu erzeugen.

Dieser Sachverhalt kann wie folgt in eine für Make verständliche Form gebracht werden.

source.o: source.c
	avr-gcc -c -o source.o -Os -mmcu=atmega328p -DF_CPU=16000000UL source.c

Die Datei source.o, die mit dieser Regel erzeugt werden kann, steht dabei auf der linken Seite. Eine solche Datei wird auch Target oder Ziel genannt.

Die Bearbeitungsschritte, mit denen die Zieldatei erstellt werden kann, stehen in der zweiten Zeile. Eine solche Folge von Bearbeitungsschritten, mit denen eine Zieldatei erzeugt werden kann, wird auch als Rezept bezeichnet. Zeilen, die zu einem Rezept gehören, müssen mit einem Tabulator (ASCII Zeichen 0x09) eingerückt sein. [4]

Die Datei source.c, die als Zutat benötigt wird, muss auf die rechte Seite geschrieben werden. Zutaten dieser Art werden auch Voraussetzung genannt.

Übertragen wir die anderen Befehle des vorangegangenen Kapitels in eine für make verständliche Form, so erhalten wir folgendes Makefile.

source.o: source.c
	avr-gcc -c -o source.o -Os -mmcu=atmega328p -DF_CPU=16000000UL source.c

source.elf: source.o
	avr-gcc -mmcu=atmega328p -o source.elf source.o

source.hex: source.elf:
	avr-objcopy -O ihex -R .eeprom source.elf source.hex

Emblem-important.svgAchtung! Die Fehlermeldung, die make ausgibt, wenn für die Einrückung eines Rezepts kein Tabulator verwendet wurde, ist leider nicht sehr aussagekräftig:

Makefile:«Zeilennummer»: *** missing separator.  Schluss.

Wenn Du eine Fehlermeldung dieser Art siehst, dann hast Du für die Einrückung vermutlich Leerzeichen

anstelle des Tabulatorzeichens verwendet.

Make starten[Bearbeiten]

Um make dazu aufzufordern, das Ziel «Target» zu erstellen, kann der Befehl make «Target» aufgerufen werden. Wird make ohne Angabe eines Ziels gestartet, so wird automatisch das erste Ziel in der Steuerdatei gewählt.

Beide Varianten können wir an dem bisher erstellten Makefile ausprobieren:

make

make: „source.o“ ist bereits aktuell.
make source.elf

make: „source.hex“ ist bereits aktuell.

Bisher scheint sich nicht viel getan zu haben. In beiden Fällen verabschiedet sich make mit dem Hinweis das gewählte Ziel ist bereits aktuell.

Die Ursache dafür liegt in der speziellen Arbeitsweise von make.

Arbeitsweise[Bearbeiten]

Das Programm make arbeitet auf der Basis von Dateien. Rezepte für die Aktualisierung einer Datei führt es nur dann aus, wenn eine der Voraussetzung aktueller ist als das zugehörige Target. Die Reihenfolge, in der die Regeln in der Steuerdatei angegeben sind, spielt dabei keine Rolle.

Bei jedem Versuch die Datei «Datei» zu aktualisieren, überprüft make zunächst, ob es eine Regel gibt, mit der die gewünschte Datei erstellt werden kann.

Wenn es keine solche Regel gibt, so gibt es nicht viel, das make für uns tun kann.

Wenn die Datei existiert informiert make uns mit der folgenden Meldung über diesen Umstand:

make: Für das Ziel „«Datei»“ ist nichts zu tun.

Existiert die Datei nicht, so haben wir make vor ein unlösbares Problem gestellt und es gibt sich mit der folgenden Fehlermeldung geschlagen:

make: *** Keine Regel, um „«Datei»“ zu erstellen.  Schluss.

Wenn es eine Regel gibt, um die Datei «Datei» zu erstellen, so aktualisiert make zunächst alle Dateien, die als Voraussetzung genannt sind. Erst nach der Aktualisierung aller Voraussetzungen bestimmt es anhand der Zeitstempel der beteiligten Dateien die Aktualität der Datei «Datei».

Nur wenn es mindestens eine Voraussetzung gibt, die neuer ist, als die gewünschte Datei «Datei», macht sich make daran das zugehörige Rezept abzuarbeiten.

Andernfalls informiert uns make mit folgender Meldung darüber, dass für die Aktualisierung der Datei «Datei» gar nichts zu tun ist:

make: „«Datei»“ ist bereits aktuell.

Make wird also nur dann aktiv, wenn sich bei der Abarbeitung aller am Entstehen einer Datei beteiligten Rezepte auch wirklich etwas an der erzeugten Datei ändert.

Emblem-important.svgAchtung! Diese Stärke von make kann sich auch als Nachteil erweisen. Wenn vergessen wird eine Abhängigkeit anzugeben, kann make zu unrecht davon ausgehen, dass nichts zu tun ist.

Bei der Formulierung von Regeln ist es daher wichtig immer darauf zu achten alle Abhängigkeiten anzugeben. Insbesondere eingebundene Header Dateien, deren Inhalt sich ändern kann, dürfen bei der Angabe nicht vergessen werden.

Ein Sonderfall tritt auf, wenn die Regeln des Makefiles eine zirkuläre Abhängigkeit enthalten. Auch in diesem Fall haben wir make vor ein unlösbares Problem gestellt, worüber uns make wie folgt informiert:

make: Zirkuläre Abhängigkeit «Datei A» <- «Datei B» wird nicht verwendet

Um make in unseren beiden Beispielen dazu zu bringen, die Rezepte auszuführen, müssen wir dafür sorgen, dass für make nach den genannten Regeln auch tatsächlich eine Aktualisierung nötig ist. Das können wir erreichen, indem wir vor dem Aufruf von make die Zeitstempel der beteiligten Dateien mit dem Befehl touch künstlich aktualisieren.

touch source.elf
make source.hex

avr-objcopy -O ihex -R .eeprom source.elf source.hex
touch source.c
make source.hex

avr-gcc -c -o source.o -Os -mmcu=atmega328p -DF_CPU=16000000UL source.c
avr-gcc -mmcu=atmega328p -o source.elf source.o
avr-objcopy -O ihex -R .eeprom source.elf source.hex

Diesmal hat make tatsächlich alle Arbeitsschritte durchgeführt.

Das Makefile überarbeiten[Bearbeiten]

Die wichtigsten Grundlagen um erfolgreich mit make zu arbeiten hast Du bereits zu diesem Zeitpunkt kennengelernt. Im Folgenden wird es nun darum gehen, eine Reihe von Techniken vorzustellen, die das Schreiben von Makefiles erleichtern können. Ein Einsatz dieser Techniken ist optional, es besteht kein Zwang sie zu verwenden.

Kommentare[Bearbeiten]

Zu Dokumentationszwecken kann ein Makefile mit Kommentaren versehen werden. Kommentare in einem Makefile werden mit einem Rautezeichen (#) eingeleitet und setzten sich bis zum Zeilenende fort. Kommentare sind somit also von folgender Gestalt.

# Das ist ein Kommentar

Durch den Einsatz von Kommentaren kann die Lesbarkeit eines Makefiles erheblich erhöht werden. Im Allgemeinen ist es daher eine gute Idee, nicht zu sparsam bei der Verwendung von Kommentaren zu sein.

Die Code-Schnipsel in diesem Kapitel stellen eine Ausnahme von dieser Faustregel dar. Zum einen übernimmt hier der umgebende Text die Aufgabe der Dokumentation, zum anderen kann der Blick so gezielter auf die vorgestellte Syntax gelenkt werden.

Erst das finale Makefile am Ende dieses Kapitels wird mit Kommentaren versehen sein.

Variablen[Bearbeiten]

Um benutzerdefinierte Angaben an zentraler Stelle verwalten zu können, stellt make die Möglichkeit zur Verfügung Variablen[5] zu nutzen und sie an benötigten Stellen auszuwerten.

Die Werte verwendeter Variablen können dabei aus drei verschiedenen Quellen stammen. Sie können beim Aufruf von make auf der Kommandozeile übergeben werden, im Makefile selbst definiert oder durch eine Umgebungsvariable vorgegeben werden.

Im Falle eines Konflikts haben auf der Kommandozeile übergebene Werte die höchste, über Umgebungsvariablen festgelegte Werte die geringste Priorität.

Benutzerdefinierte Variablen[Bearbeiten]

Eine Möglichkeit das bisher erstellte Makefile durch die Nutzung von Variablen ein wenig komfortabler zu gestalten, besteht darin, den Anwender von der Last zu befreien, den Suchpfad für Programme vor der Benutzung in der Konsole anpassen zu müssen.

Ein offensichtlicher Weg dies auch ohne Variablen zu erreichen, beseht darin, die vollständigen Pfade zu den verwendeten Werkzeugen direkt in die einzelnen Rezepte einzuarbeiten. Er weist jedoch den Nachteil auf, dass die jeweiligen Pfade auf diese Weise über das gesamte Makefile verstreut werden.

Um die Pfade zu den eingesetzten Werkzeugen stattdessen zentral verwalten zu können, werden wir stattdessen Variablen verwenden.

AVR_PATH   = /opt/arduino-1.8.0/hardware/tools/avr

CC         = $(AVR_PATH)/bin/avr-gcc
LD         = $(AVR_PATH)/bin/avr-gcc
OBJCOPY    = $(AVR_PATH)/bin/avr-objcopy

Um eine Variable «Variable» mit dem Wert «Wert» zu definieren kann die Syntax «Variable» = «Wert» verwendet werden. Leerzeichen sowohl vor und nach dem = Zeichen, als auch am Zeilenende werden von make ignoriert.

An einer Stelle, an der der Wert einer Variable «Variable» eingesetzt werden soll, kann dieser mit der Syntax $(«Variable») abgerufen werden.

Emblem-important.svgAchtung! Das ist eine runde Klammer nicht die geschweifte Klammer, wie Du sie eventuell von Bash oder Perl kennst.

Automatische Variablen[Bearbeiten]

Neben benutzerdefinierten Variablen stellt make zudem automatische Variablen[6] zur Verfügung, die bei der Formulierung von Rezepten verwendet werden können.

Der Name des aktuellen Ziels kann über die Variable $@ abgerufen werden. Die zugehörigen Voraussetzungen können über die Variable $^ abgerufen werden.

Zusammen mit automatischen und den von uns selbst definierten Variablen können wir die Regeln des Makefile wie folgt anpassen:

source.o: source.c
	$(CC) -c -o $@ -Os -mmcu=atmega328p -DF_CPU=16000000UL $^

source.elf: source.o
	$(LD) -mmcu=atmega328p -o $@ $^

source.hex: source.elf:
	$(OBJCOPY) -O ihex -R .eeprom $^ $@

Zuweisungsoperatoren[Bearbeiten]

Angaben zum Prozessortyp und Taktrate liegen noch immer verstreut im Makefile und auch die Zahl der an Compiler und Linker übergebenen Optionen liegt, als Relikt der Phase, in der wir alle Befehle von Hand eingegeben haben, beim absoluten Minimum. Auch diesem Mangel können wir mit Variablen zu Leibe rücken.

Bei der Vorgabe der Compilerschalter ist ein wenig Fingerspitzengefühl gefragt. Zwar soll der Compiler im Normalfall mit dem Schalter -Os angewiesen werden, den Code so zu optimieren, dass er möglichst wenig Speicherplatz benötigt. Andererseits kann von Zeit zu Zeit auch ein anderes Verhalten, etwa Erstellung von Debugging Symbolen ohne Optimierung, wünschenswert sein.

Mit dem Zuweisungsoperator ?= kann make angewiesen werden, den angegebenen Wert nur dann zuzuweisen, wenn er nicht bereits definiert ist. Der Zuweisungsoperator Operator += erlaut es dem Wert einer Variablen weitere Werte hinzuzufügen.

Beide genannten Operatoren können wir verwenden, um dafür zu sorgen, dass die vom Compiler eingesetzte Optimierung im Bedarfsfall über die Umgebungsvariable CFLAGS gesteuert werden kann.

ARCH        = atmega328p
F_CPU       = 16000000UL

CPPFLAGS    = -mmcu=$(ARCH) -DF_CPU=$(F_CPU)
LDFLAGS     = -mmcu=$(ARCH)
CFLAGS      = -std=gnu99 -Os -Wall

CFLAGS     += -fpack-struct
CFLAGS     += -ffunction-sections -fdata-sections
LDFLAGS    += -Wl,--gc-sections

Substitution[Bearbeiten]

Bevor es daran geht, die Regeln des Makefile ein weiteres Mal anzupassen, können wir die Chance nutzen um das Makefile auch für die Arbeit an zukünftigen Projekte vorzubereiten.

Der Name des fertigen Programms soll nicht mehr zwingend source.hex heißen. Auch wird es auf lange Sicht vermutlich nicht bei einer einzigen Quelldatei bleiben. Bei mehreren Quelldateien muss zunächst jede Quelldatei in eine zugehörige Objektdatei übersetzt werden. Die Liste der erstellten Objektdateien muss dann dem Linker übergeben werden.

Um make anzuweisen aus einer Liste SOURCES, in der wir alle Quelldateien angeben, eine Liste OBJS aller zugehörigen Objektdateien zu erstellen, können wir folgende Zeilen in das Makefile aufnehmen. [7]

PROJECT = source
SOURCES = source.c

OBJS = $(SOURCES:.c=.o)

Die Regeln können nun wie folgt angepasst werden.

source.o: source.c
	$(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $^

$(PROJECT).elf: $(OBJS)
	$(LD) $(LDFLAGS) -o $@ $^ $(LDLIBS)

$(PROJECT).hex: $(PROJECT).elf
	$(OBJCOPY) -O ihex -R .eeprom $^ $@

Mit Ausnahme der ersten Regel, die wir analog für jede weitere Quelldatei in das Makefile schreiben müssen, sind wir nun alle expliziten Angaben los geworden. Im nächsten Abschnitt wird es darum gehen, auch diesen letzten Punkt in Angriff zu nehmen.

Musterregeln[Bearbeiten]

Bisher haben wir in den Regeln des Makefile alle Ziele und alle Voraussetzungen, sei es direkt oder mit Hilfe einer Variable, mit ihrem vollständigen Dateinamen angegeben. Regeln dieser Art werden auch explizite Regeln genannt.

Um Rezepte anzugeben, die für eine ganze Klasse von Dateien beschreiben, wie sie aus einer zweiten Klasse von Dateien erstellt werden können, steht eine alternative Art von Regeln zur Verfügung.

Den Umstand, dass Dateien der Form «Stamm».o stets mit einem Rezept der Form $(CC) -c -o «Stamm».o $(CPPFLAGS) $(CFLAGS) «Stamm».c aus Dateien der From «Stamm».c erzeugt werden können, können wir make wie folgt mitteilen:

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

Regeln dieser Art werden auch Musterregeln[8] genannt.

Der Teil des Dateinamens, der bei Voraussetzung und Ziel übereinstimmt, wird in einer Musterregel mit dem dem Zeichen % angegeben.

Über die beiden automatischen Variablen $@ und $< kann im zugehörigen Rezept auf den Dateinamen des Ziels und den Dateinamen der Voraussetzung zugegriffen werden.

Für eine Reihe typischer Aufgaben enthält make bereits einen Vorrat von eingebauten Regeln[9]

Auch das Übersetzen einer C Quellcode Datei in eine Objektdatei gehört zu diesen typischen Aufgaben. Aus diesem Grund brauchen wir die oben angegebene Regel nicht in das Makefile zu übernehmen. Es genügt stattdessen einfach die überflüssige Regel, die Bezug auf source.c und source.o nimmt zu entfernen.

Pseudoziele[Bearbeiten]

Der letzte Arbeitsschritt, den wir im vorangegangenen Kapitel vorgenommen haben, die Installation des fertigen Programms, ist bisher ausgeklammert geblieben. Wenn wir ein wenig "schummeln", können wir make dazu überreden, auch diesen Bearbeitungsschritt für uns auszuführen.

install:
	avrdude -vvv -C $(AVR_PATH)/etc/avrdude.conf -p $(ARCH) -c stk500v1 -P /dev/ttyUSB0 -b 57600 -D -U flash:w:$(PROJECT).hex

Bei jedem Aufruf von make install wird make feststellen, dass es noch gar keine Datei mit dem Namen install gibt und das zugehörige Rezept ausführen. Die kleine Schummelei geht allerdings nur so lange gut, solange es keine Datei mit dem Namen install gibt.

Die saubere Variante besteht darin, dem Programm make mitzuteilen, dass das Target install immer so behandelt werden soll, als wäre es nicht aktuell. Ein solches Target wird auch Pseudoziel genannt. [10]

Um make mitzuteilen, dass install ein Pseudoziel ist, können wir dem Makefile den folgenden Abschnitt hinzufügen.

.PHONY: install

Die selbe Technik können wir nutzen, um das Makefile um weitere Aktionen zu bereichern, die nicht an die Erstellung einer Datei gebunden sind. Neben dem Ziel install genießen wenigstens zwei weitere Ziele all und clean so große Prominenz, dass sie in beinahe jedem auf make basierenden Buildsystem erwartet werden können. Auch wir können sie unserem Makefile wie folgt hinzufügen.

.PHONY: all clean install

all: $(PROJECT).hex
	@echo "done"

install:
	avrdude -vvv -C $(AVR_PATH)/etc/avrdude.conf -p $(ARCH) -c stk500v1 -P /dev/ttyUSB0 -b 57600 -D -U flash:w:$(PROJECT).hex

clean:
	rm -f source.o source.elf source.hex

Das Makefile[Bearbeiten]

Abschließend hier das finale Makefile.

Einige letzte Änderung im Vergleich zu der im Text erarbeiteten Variante betreffen die Schalter für Compiler und Linker. Um Platz zu sparen und nicht zu viele Vorgriffe auf das folgende Kapitel nehmen zu müssen, wurden sie im Text bewusst kurz gehalten.

Für make spielt die Länge der Zeilen in einem Makefile keine Rolle. Für die Lesbarkeit durch einen Menschen hingegen kann es von Vorteil sein, wenn einzelnen Zeilen nicht zu lang werden. Lange Zeilen wurden daher wie folgt in mehrere Zeilen zerlegt:

Um eine Zeile zwar umzubrechen, den Zeilenumbruch aber vor make zu verstecken kann die Zeile mit einem Backslash (\) abgeschlossen werden.

Emblem-important.svgAchtung! Der Backslash muss wirklich das letzte Zeichen vor dem Zeilenumbruch sein. Leerzeichen zwischen Backslash und Zeilenumbruch können schwer zu erkennen sein.

Auch sie sind dort nicht erlaubt!
# Projekt
# ============================================================
PROJECT = MAIN
SOURCES = main.c

OBJS = $(SOURCES:.c=.o)

# Micorcontroller / serielle Schnittstelle
# ============================================================
ARCH          = atmega328p
F_CPU         = 16000000UL

PORT          = /dev/ttyUSB0
PROGRAMMER_ID = stk500v1
BAUDRATE      = 57600

# Pfade
# ============================================================
AVR_PATH       = /opt/arduino-1.8.0/hardware/tools/avr
AVRDUDE_CONFIG = $(AVR_PATH)/etc/avrdude.conf

# Programme
# ============================================================
CC         = $(AVR_PATH)/bin/avr-gcc
LD         = $(AVR_PATH)/bin/avr-gcc
OBJCOPY    = $(AVR_PATH)/bin/avr-objcopy
OBJDUMP    = $(AVR_PATH)/bin/avr-objdump

AVRDUDE    = $(AVR_PATH)/bin/avrdude

STTY       = stty

# Schalter
# ============================================================
CFLAGS   ?= -Os

# Schalter für scanf/printf
# -------------------------
LDFLAGS  += -Wl,-u,vfprintf -Wl,-u,vfscanf 
LDLIBS   += -lprintf_flt -lscanf_flt -lm

# Schalter für Microcontroller
# ----------------------------
CPPFLAGS  += -mmcu=$(ARCH) -DF_CPU=$(F_CPU)
LDFLAGS   += -mmcu=$(ARCH)

# Schalter für Sprachversion
# --------------------------
CFLAGS   += -std=gnu99

# Schalter für warnings
# ---------------------
CFLAGS  += -Wall

# Spezielle Warnungen
CFLAGS  += -Wstrict-prototypes -Wmissing-prototypes \
           -Wmissing-declarations -Wredundant-decls \
           -Wnested-externs -Wbad-function-cast \
           -Wshadow -Wpointer-arith \
           -Wsign-compare -Wfloat-equal \
           -Wunreachable-code  \
           -Wwrite-strings -Wconversion \
           -Waggregate-return  -Winline -Wcast-align

# Schalter um Platz zu sparen
# ---------------------------
# Strukturen packen
CFLAGS  += -fpack-struct

# Unbenutzten Code entfernen
CFLAGS  += -ffunction-sections -fdata-sections 
LDFLAGS += -Wl,--gc-sections

# Explizite Regeln
# ============================================================
all: $(PROJECT).hex
	@echo "done"

$(PROJECT).elf: $(OBJS)
	$(LD) $(LDFLAGS) -o $@ $^ $(LDLIBS)

install:
	$(STTY) -F $(PORT) hupcl
	$(AVRDUDE) -vvv -C $(AVRDUDE_CONFIG) -p $(ARCH) -c $(PROGRAMMER_ID) -P $(PORT) -b $(BAUDRATE) -D -U flash:w:$(PROJECT).hex

clean:
	rm -f $(OBJS) $(PROJECT).elf $(PROJECT).hex

# Muster Regeln
# ============================================================
%.hex: %.elf
	$(OBJCOPY) -O ihex -R .eeprom $< $@

%.lst: %.elf
	$(OBJDUMP) -h -S -t -C $< > $@

%.lst: %.o
	$(OBJDUMP) -h -S -t -C $< > $@

%.E:%.c
	$(CC) $(CPPFLAGS) -E -o $@ $<

%.s:%.c
	$(CC) $(CPPFLAGS) -S -o $@ $<

Rückschau und Ausblick[Bearbeiten]

Glückwunsch, mit Abschluss dieses Kapitels hast Du nicht nur das Programm make installiert und Dich mit seiner grundlegenden Arbeitsweise vertraut gemacht. Du hast zudem ein Makefile erstellt, das Du sowohl für die Arbeit an eigenen Projekten, als auch für alle in diesem Buch vorgestellten Codebeispiele verwenden kannst.

Du hast dabei die wichtigsten Fachbegriffe kennen gelernt, um zielgerichtet in Dokumentation und Fachliteratur recherchieren zu können. Somit bist Du nun nicht nur in der Lage das erarbeitete Makefile an Deine Bedürfnisse anzupassen, sondern auch eigenständig neue Makefiles für andere Aufgaben zu entwickeln.

Du hast die wichtigsten Grundlagen zum Arbeiten mit make geschaffen, indem Du:

  • das Programm make installiert hast
  • Hilfe und Dokumentation zu make studiert hast
  • ein Makefile erstellt hast

In dem Makefile hast Du Regeln erstellt indem Du:

  • das Ziel und Voraussetzungen angegeben hast
  • ddas Rezept geschrieben hast

Du hast das Makefile flexibler gestaltet indem Du:

  • Variablen definiert und expandiert hast
  • bei der Formulierung von Rezepten auf automatische Variablen zugegriffen hast

Von nun an bist Du bei Deiner Recherche zum Programm make auf Dich selbst gestellt. Es gibt beinahe keine Aufgabe für deren Bewältigung das Programm make nicht eingesetzt werden kann. Die Grenzen zwischen sinnvoller Anwendung und Missbrauch sind dabei fließend.

Fußnoten[Bearbeiten]

  1. siehe: Wikipedia Artikel Make
  2. siehe: Info Manual GNU make
  3. Trägt die Steuerungsdatei einen anderen Namen als Makefile, so muss beim Aufruf von make der Name der Datei mit dem zusätzlichen Schalter -f «Dateiname» angegeben werden.
  4. Die Variable .RECIPEPREFIX bietet die Möglichkeit ein alternatives Zeichen für die Einrückung von Rezepten festzulegen. Für Makefiles, die in freier Wildbahn angetroffen werden können ist das allerdings mehr als ungewöhnlich. (siehe: Info Manual GNU make Special Variables)
  5. siehe: Info Manual GNU make Using Variables
  6. siehe: Info Manual GNU make Automatic Variables
  7. siehe: Info Manual GNU make Substitution References
  8. siehe: Info Manual GNU make Pattern Intro
  9. siehe: Info Manual GNU make Implicit Rules
  10. siehe: Info Manual GNU make Phony Targets

WTFPL-2
Du hast das Recht unter den Bedingungen der WTF Public License mit diesem Dokument anzustellen was zum Teufel auch immer Du willst.