-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathmakefile_doku.Rmd
More file actions
executable file
·253 lines (121 loc) · 10.4 KB
/
makefile_doku.Rmd
File metadata and controls
executable file
·253 lines (121 loc) · 10.4 KB
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
---
title: "Makefile Tutorial"
author: "Manuel Reif"
date: '`r Sys.Date()`'
output:
html_document:
theme: readable
toc: true
---
Um seine eigenen Datenanalysen nachvollziehbar zu machen, Stichwort: 'reproducible research' (https://cran.r-project.org/web/views/ReproducibleResearch.html), ist es hilfreich bestimmte Tools zu verwenden. Mit 'reproducible research' sind in diesem Dokument all jene Bestrebungen nach Nachvollziehbarkeit der Datenanalyse gemeint die über das klassische Dokumentieren der Variablen und deren Ausprägungen hinausgehen.
- `knitr` wird verwendet um den Code mit dem Text direkt zu assoziieren - das heisst dass auf einen Blick ins .Rmd/.Rnw klar wird wie die Ergebnisse (Grafiken, Tabellen etc.) entstanden sind. Der R Code wird direkt in den Text eingebettet.
- `git` wird verwendet um den Entstehungsprozess der Syntax/Text etc. files nachvollziehbar zu machen. git ist ein bekanntes Versionierungstool und sollte eigentlich immer verwendet werden wenn mit Textfiles gearbeitet wird (https://git-scm.com/).
- `makefiles` werden dazu verwendet um die einzelnen Syntaxfiles (bspw. .R oder .tex files) und einzelnen Outputfiles miteinander zu verbinden, also die logische Abfolge des Syntaxdurchlaufs zu bestimmen, um zum gewünschten Ergebnis zu kommen. Im sogenannten `Makefile` sind jene Regeln und Abhängigkeiten definiert.
Dieses Tutorial ist dafür gedacht um Makefiles im Rahmen von R und LaTeX zu verwenden.
Soll aus diesem .Rmd ein **.html** file erzeugt werden, einfach `make` anwenden! Dazu wird `R` und das package `rmarkdown` benötigt.
Eine deutschsprachige Einführung in Makefiles gibt es hier:
http://www.jfranken.de/homepages/johannes/vortraege/make_inhalt.de.html
### Wann sind Makfiles sinnvoll
Makefiles zu erstellen ist immer sinnvoll, aber insbesondere dann, wenn die Anzahl der zu verbindenden Syntaxfiles steigt, mehrere Outputs realisiert werden sollen (bspw. Grafiken/Bericht/Tabellen etc.) oder die Aufgaben auch von Dritten, die nicht ständig im Projekt sind, leicht zu bewältigen sein sollten. Der Vorteil ist, dass auch bei kleinen Projekten der administrative Aufwand von Makefiles nicht sehr hoch ist, vor allem dann wenn diese von Anfang an angelegt und erweitert werden.
## Aufbau von Makefiles
Ein Makefile ist folgendermaßen aufgebaut:
```
TARGET: Prerequisite1 Prerequisite2
Commands
```
- **Target**: dieses file wird am Ende erzeugt - Targets enden immer mit einem Doppelpunkt. Es gibt auch spezielle Targets wie bspw. 'all' (siehe später).
- **Prerequisites**: das sind files von denen die Entstehung des Targets abhängt. Sind mehrere Prerequisites verantwortlich für ein target, werden diese durch Leerzeichen getrennt.
- **Commands**: das ist die Syntax die das Target erstellt. Diese **MÜSSEN IMMER** mit einem **Tabulator** eingerückt sein. Leerzeichen funktionieren **NICHT**! Mehrere Commands stehen in unterschiedlichen Zeilen.
*Der Clou*: Wird ein file der **prerequisites** aktualisiert (bspw. ergänzt) und ist damit aktueller als das **target** wird bei einer Eingabe von `make` das Targetfile neu erzeugt mittels der unten stehenden **Commands**.
## Erstes Beispiel
Betrachten wir die Dateien im Ordner `./make1`.
1. In der R Syntax (`syntaxfile1.R`) wird ein plot erzeugt und rausgespeichert
2. Das Makefile stellt den Zusammenhang her.
a. Target ist das resultierende **pdf** file
b. Abhängig ist dieses von der Syntax, die dieses file erzeugt (in diesem fall ist dies nur das eine file)
c. Eine Zeile darunter wird einfach das .R file ausgeführt.
Wie geht man nun schrittweise vor:
1. Verzeichnis wechseln nach: ./make1
2. `make` in die Console eintippen
3. Es sollten die Commands ausgeführt werden & Ergebnisfile wird ausgegeben
4. Führt man jetzt **nocheinmal** `make` aus wird nichts mehr passieren (Ergebnis: `make: 'mygraphic1.pdf' is up to date.`).
Wir lernen:
1. steht nur ein Eintrag im Makefile, wird dieser mit `make` ausgeführt
2. `make` achtet darauf was ausgeführt werden muss, indem die Erstellungsdaten der files verglichen werden. Wäre unser Syntaxfile nun aktueller als das target, dann führt `make` nochmal die ganze Befehlskette aus
## Zweites Beispiel
Angenommen wir hätten 2-3 Schritte zu absolvieren:
1. Wir erzeugen/extrahieren die Daten und bereiten diese gegebenenfalls auf und speichern diese
2. Wir lesen diese Daten ein, verarbeiten/analysieren diese und erzeugen grafische Darstellungen
3. Wir wollen auch gleich wieder aufräumen können (unnötige files löschen)
Dann brauchen wir mehr als diese Zeilen.
Wir wechseln ins Verzeichnis make2 und sehen uns den Inhalt an.
1. `datenaufbereitung.R` bearbeitet die Daten und schreibt ein `.RData` file raus.
2. `analyse.R` schreibt den plot raus
Diese Abhängigkeiten sind im Makefile dokumentiert. Wir sehen uns das Makefile genau an.
1. Wir haben standard-targets (ich würde eher 'keywords' dazu sagen): **all:** bezeichnet was passiert wenn nur `make` eingetippt wird oder auch `make all`. So kann man mehrere Prozesse definieren, oder bspw. auch nur Teilprozesse ablaufen lassen (siehe: http://www.gnu.org/software/make/manual/html_node/Standard-Targets.html).
2. Wir sehen 2 Abhängigkeitsdefinitionen
3. Wir definieren 2 clean funktionen die Dateien mit bestimmten Endungen löschen sollen.
### Keywords
Die Allgemeine Logik hinter diesen Keywords sieht so aus:
```
keyword: TARGET
TARGET: Prerequisite1 Prerequisite2
Commands
```
1. Wird `make keyword` aufgerufen, weiss das Programm dass dieses von TARGET abhängt.
2. Also wird TARGET gesucht und geprüft von welchen Dateien dieses abhängt.
3. Ist TARGET aktuell passiert nichts.
4. Ist TARGET nicht aktuell (also älter als eines der Prerequisites) werden die Commands ausgeführt.
### Aufgabe
1. Führe `make` aus, und schau welche Prozesse der Reihe nach ablaufen. Vergleiche dies mit dem Makefile.
2. Führe `make` ein weiteres Mal aus, und schau was passiert.
3. Führe `make clean` aus und dann nochmal `make` und schau was passiert.
4. Führe `make clean_pdf` aus und dann nochmal `make` und schau was passiert.
## Drittes Beispiel
Erstelle selbst ein Makefile zu den 3 files im Ordner make3.
1. `createdata.R`
2. `makeanalysis.R`
3. `creategraphics.R`
Hinweise:
1. Checke die Abhängigkeiten (.R files öffnen und nachsehen was wo passiert)
2. `creategraphics.R` erzeugt mehrere Grafiken - nimm hier als Target: `creategraphics.Rout` (wie man dies eleganter lösen kann, wird im Abschnitt [Multiple Targets](#Multiple Targets) ausführlich besprochen).
3. definiere ein `make clean`
## Variablen
Definiere Variablen um weniger tippen zu müssen bspw. Pfade oder typische Einstellungen für Programme. Ein Beispiel findet sich in Ordner **make_variables**. Vor allem ist dies auch praktische wenn für Dritte Einstellungen möglich sein sollen, ohne dass zu tief in das Makefile eingegriffen werden soll.
Für ein näheres Studium der Einsatzmöglichkeiten siehe: http://www.gnu.org/software/make/manual/make.html#Variables-Simplify
Typische Form:
```
VARIABLE= pfad/in/ordner
# aufgerufen wird das ganze so, natürlich an der richtigen Stelle:
$(VARIABLE)
```
Wenn im Makefile eine Variable `FILE=myfile` definiert wurde,
besteht auch die Möglichkeit, diese beim Aufruf von make zu
überschreiben: `make FILE=myfile1` erstellt dann z.B. den Output
unter Verwendung von `myfile(.Rmd)`. Das ist dann praktisch, wenn
es sich z.B. um die Erstellung von `html`, `pdf` etc. handelt und
ein Quellfile fix *hineincodiert* ist, aber auch ein anderes
Inputfile verwendet werden können soll, ohne das Makefile
umzuschreiben.
## Multiple Targets
Was passiert wenn ein Skript multiple Targets erstellt - also ein R Script bspw. mehrere .pdf files (Grafiken etc.) erzeugt.
1. Mehrere Targets werden definiert (linke Seite)
2. Bei n Targets wird das Skript **n Mal ausgeführt** (typischerweise will man das **nicht**, weil alle Targets durch einen Durchlauf erzeugt werden, daher muss man tricksen.
3. Das Problem wird hier beschrieben: https://www.gnu.org/software/make/manual/html_node/Multiple-Targets.html
Es gibt für diesen Fall 2 workarounds - einen eher speziellen (quick and dirty) und einen allgemeineren.
1) man verwendet **pattern rules** (http://stackoverflow.com/a/3077254/3451929)
2) man verwendet **.INTERMEDIATE** oder **.SECONDARY** für einen workaround (http://stackoverflow.com/a/4642560/3451929)
Bitte jetzt das Makefile aus dem Ordner **multiple_targets** öffnen und betrachten.
1) Es werden hier mehrere pdfs durch das R Skript: `creategraphics.R` erzeugt. Welche das sind, wird ganz oben in der Variable **pdfs** definiert.
2) Es wird eine Abhängigkeit definiert: die pdfs hängen von einem prerequisite **creategraphics_inter** ab.
3) **creategraphics_inter** wird als **.SECONDARY** definiert (https://www.gnu.org/software/make/manual/html_node/Special-Targets.html)
4) **.SECONDARY** ist wichtig. Kommentiert man diese Zeile aus, wird das Skript bei jedem `make` ausgeführt unabhängig welches file aktueller ist. Genau das will man NICHT!
`make` interessiert nicht ob ein intermediate file (in unserem Fall **creategraphics_inter**) existiert oder aktualisiert wurde. `make` agiert erst dann, wenn irgendwelche prerequisites neuer ist als das target. So läuft das Skript nur 1 Mal durch (wenn es notwendig ist). Wird ein output gelöscht oder verändert sich etwas in `creategraphics.R` läuft es wieder durch. (https://www.gnu.org/software/make/manual/html_node/Chained-Rules.html#Chained-Rules)
## .PHONY Targets
Alle Targets die als .PHONY gelistet werden, werden **jedesmal** ausgeführt, auch wenn alles up-to-date ist. Oft wird das bei `make clean` verwendet. `clean` hat ja normalerweise keine Prerequisites, und wäre deshalb sowieso `.PHONY`, aber falls es ein file geben sollte das **clean** heisst, wird clean nicht ordnungsgemäß ausgeführt, weil angenommen wird dass es immer up-to-date ist, weil es ja keine Prerequisites gibt. Daher `clean` immer als `.PHONY` deklarieren, dann funktioniert das immer.
Als Beispiel das `Makefile` für dieses Projekt ansehen!
Siehe auch: https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html
## 'automatic variables' zB: $@ etc... plus anwendungsbsp.
## Argumente in R files in Kommandozeile setzen
## klassische (minimale) uebersichtliche Templates zu LaTeX (pur), Rmd/Rnw, R, R und Rmd gemischt etc
## wie funktioniert code externalisation in knitr (finde ich recht praktisch) - weiss nicht ob das zu makefiles passt.