1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
|
---
language: make
contributors:
- ["Robert Steed", "https://github.com/robochat"]
translators:
- ["Martin Schimandl", "https://github.com/Git-Jiro"]
filename: Makefile
lang: de-de
---
Eine Makefile definiert einen Graphen von Regeln um ein Ziel (oder Ziele)
zu erzeugen. Es dient dazu die geringste Menge an Arbeit zu verrichten um
ein Ziel in einklang mit dem Quellcode zu bringen. Make wurde berühmterweise
von Stuart Feldman 1976 übers Wochenende geschrieben. Make ist noch immer
sehr verbreitet (vorallem im Unix umfeld) obwohl es bereits sehr viel
Konkurrenz und Kritik zu Make gibt.
Es gibt eine vielzahl an Varianten von Make, dieser Artikel beschäftig sich
mit der Version GNU Make. Diese Version ist standard auf Linux.
```make
# Kommentare können so geschrieben werden.
# Dateien sollten Makefile heißen, denn dann können sie als `make <ziel>`
# aufgerufen werden. Ansonsten muss `make -f "dateiname" <ziel>` verwendet
# werden.
# Warnung - Es sollten nur TABULATOREN zur Einrückung im Makefile verwendet
# werden. Niemals Leerzeichen!
#-----------------------------------------------------------------------
# Grundlagen
#-----------------------------------------------------------------------
# Eine Regel - Diese Regel wird nur abgearbeitet wenn die Datei file0.txt
# nicht existiert.
file0.txt:
echo "foo" > file0.txt
# Selbst Kommentare in der 'Rezept' Sektion werden an die Shell
# weitergegeben. Versuche `make file0.txt` oder einfach `make`
# die erste Regel ist die Standard-Regel.
# Diese Regel wird nur abgearbeitet wenn file0.txt aktueller als file1.txt ist.
file1.txt: file0.txt
cat file0.txt > file1.txt
# Verwende die selben Quoting-Regeln wie die Shell
@cat file0.txt >> file1.txt
# @ unterdrückt die Ausgabe des Befehls an stdout.
-@echo 'hello'
# - bedeutet das Make die Abarbeitung fortsetzt auch wenn Fehler passieren.
# Versuche `make file1.txt` auf der Kommandozeile.
# Eine Regel kann mehrere Ziele und mehrere Voraussetzungen haben.
file2.txt file3.txt: file0.txt file1.txt
touch file2.txt
touch file3.txt
# Make wird sich beschweren wenn es mehrere Rezepte für die gleiche Regel gibt.
# Leere Rezepte zählen nicht und können dazu verwendet werden weitere
# Voraussetzungen hinzuzufügen.
#-----------------------------------------------------------------------
# Phony-Ziele
#-----------------------------------------------------------------------
# Ein Phony-Ziel ist ein Ziel das keine Datei ist.
# Es wird nie aktuell sein, daher wird Make immer versuchen es abzuarbeiten
all: maker process
# Es ist erlaubt Dinge ausserhalb der Reihenfolge zu deklarieren.
maker:
touch ex0.txt ex1.txt
# Um das Fehlschlagen von Phony-Regeln zu vermeiden wenn eine echte Datei den
# selben namen wie ein Phony-Ziel hat:
.PHONY: all maker process
# Das ist ein spezielles Ziel. Es gibt noch ein paar mehr davon.
# Eine Regel mit einem Phony-Ziel als Voraussetzung wird immer abgearbeitet
ex0.txt ex1.txt: maker
# Häufige Phony-Ziele sind: all make clean install ...
#-----------------------------------------------------------------------
# Automatische Variablen & Wildcards
#-----------------------------------------------------------------------
process: file*.txt # Eine Wildcard um Dateinamen zu Vergleichen
@echo $^ # $^ ist eine Variable die eine Liste aller
# Voraussetzungen enthält.
@echo $@ # Namen des Ziels ausgeben.
#(Bei mehreren Ziel-Regeln enthält $@ den Verursacher der Abarbeitung
#der Regel.)
@echo $< # Die erste Voraussetzung aus der Liste
@echo $? # Nur die Voraussetzungen die nicht aktuell sind.
@echo $+ # Alle Voraussetzungen inklusive Duplikate (nicht wie Üblich)
#@echo $| # Alle 'order only' Voraussetzungen
# Selbst wenn wir die Voraussetzungen der Regel aufteilen, $^ wird sie finden.
process: ex1.txt file0.txt
# ex1.txt wird gefunden werden, aber file0.txt wird dedupliziert.
#-----------------------------------------------------------------------
# Muster
#-----------------------------------------------------------------------
# Mit Mustern kann man make beibringen wie Dateien in andere Dateien
# umgewandelt werden.
%.png: %.svg
inkscape --export-png $^
# Muster-Vergleichs-Regeln werden nur abgearbeitet wenn make entscheidet das Ziel zu
# erzeugen
# Verzeichnis-Pfade werden normalerweise bei Muster-Vergleichs-Regeln ignoriert.
# Aber make wird versuchen die am besten passende Regel zu verwenden.
small/%.png: %.svg
inkscape --export-png --export-dpi 30 $^
# Make wird die letzte Version einer Muster-Vergleichs-Regel verwenden die es
# findet.
%.png: %.svg
@echo this rule is chosen
# Allerdings wird make die erste Muster-Vergleicher-Regel verwenden die das
# Ziel erzeugen kann.
%.png: %.ps
@echo this rule is not chosen if *.svg and *.ps are both present
# Make hat bereits ein paar eingebaute Muster-Vergleichs-Regelen. Zum Beispiel
# weiß Make wie man aus *.c Dateien *.o Dateien erzeugt.
# Ältere Versionen von Make verwenden möglicherweise Suffix-Regeln anstatt
# Muster-Vergleichs-Regeln.
.png.ps:
@echo this rule is similar to a pattern rule.
# Aktivieren der Suffix-Regel
.SUFFIXES: .png
#-----------------------------------------------------------------------
# Variablen
#-----------------------------------------------------------------------
# auch Makros genannt.
# Variablen sind im Grunde genommen Zeichenketten-Typen.
name = Ted
name2="Sarah"
echo:
@echo $(name)
@echo ${name2}
@echo $name # Das funktioniert nicht, wird als $(n)ame behandelt.
@echo $(name3) # Unbekannte Variablen werden als leere Zeichenketten behandelt.
# Es git 4 Stellen um Variablen zu setzen.
# In Reihenfolge der Priorität von höchster zu niedrigster:
# 1: Befehls-Zeilen Argumente
# 2: Makefile
# 3: Shell Umbebungs-Variablen - Make importiert diese automatisch.
# 3: MAke hat einige vordefinierte Variablen.
name4 ?= Jean
# Setze die Variable nur wenn es eine gleichnamige Umgebungs-Variable noch
# nicht gibt.
override name5 = David
# Verhindert das Kommando-Zeilen Argumente diese Variable ändern können.
name4 +=grey
# Werte an eine Variable anhängen (inkludiert Leerzeichen).
# Muster-Spezifische Variablen Werte (GNU Erweiterung).
echo: name2 = Sara # Wahr innerhalb der passenden Regel und auch innerhalb
# rekursiver Voraussetzungen (ausser wenn es den Graphen zerstören
# kann wenn es zu kompilizert wird!)
# Ein paar Variablen die von Make automatisch definiert werden.
echo_inbuilt:
echo $(CC)
echo ${CXX)}
echo $(FC)
echo ${CFLAGS)}
echo $(CPPFLAGS)
echo ${CXXFLAGS}
echo $(LDFLAGS)
echo ${LDLIBS}
#-----------------------------------------------------------------------
# Variablen 2
#-----------------------------------------------------------------------
# Der erste Typ von Variablen wird bei jeder verwendung ausgewertet.
# Das kann aufwendig sein, daher exisitert ein zweiter Typ von Variablen.
# Diese werden nur einmal ausgewertet. (Das ist eine GNU make Erweiterung)
var := hello
var2 ::= $(var) hello
#:= und ::= sind äquivalent.
# Diese Variablen werden prozedural ausgwertet (in der Reihenfolge in der sie
# auftauchen), die stehen daher im wiederspruch zum Rest der Sprache!
# Das funktioniert nicht
var3 ::= $(var4) and good luck
var4 ::= good night
#-----------------------------------------------------------------------
# Funktionen
#-----------------------------------------------------------------------
# Make verfügt über eine vielzahl von Funktionen.
sourcefiles = $(wildcard *.c */*.c)
objectfiles = $(patsubst %.c,%.o,$(sourcefiles))
# Das Format ist $(func arg0,arg1,arg2...)
# Ein paar Beispiele
ls: * src/*
@echo $(filter %.txt, $^)
@echo $(notdir $^)
@echo $(join $(dir $^),$(notdir $^))
#-----------------------------------------------------------------------
# Direktiven
#-----------------------------------------------------------------------
# Inkludiere andere Makefile, sehr praktisch für platformspezifischen Code
include foo.mk
sport = tennis
# Konditionale kompiliereung
report:
ifeq ($(sport),tennis)
@echo 'game, set, match'
else
@echo "They think it's all over; it is now"
endif
# Es gibt auch ifneq, ifdef, ifndef
foo = true
ifdef $(foo)
bar = 'hello'
endif
```
### Mehr Resourcen
+ [gnu make documentation](https://www.gnu.org/software/make/manual/)
+ [software carpentry tutorial](http://swcarpentry.github.io/make-novice/)
+ learn C the hard way [ex2](http://c.learncodethehardway.org/book/ex2.html) [ex28](http://c.learncodethehardway.org/book/ex28.html)
|