---
title: "make"
subtitle: "Programming for Statistical Science"
author: "Shawn Santo"
institute: ""
date: ""
output:
xaringan::moon_reader:
css: "slides.css"
lib_dir: libs
nature:
highlightStyle: github
highlightLines: true
countIncrementalSlides: false
editor_options:
chunk_output_type: console
---
```{r include=FALSE}
knitr::opts_chunk$set(echo = TRUE, message = FALSE, warning = FALSE,
comment = "#>", highlight = TRUE,
fig.align = "center")
```
## Supplementary materials
Full video lecture available in Zoom Cloud Recordings
Additional resources
- [minimal make](http://kbroman.org/minimal_make/) by Karl Broman
- [Why Use Make](https://bost.ocks.org/mike/make/) by Mike Bostock
- GNU make [manual](https://www.gnu.org/software/make/manual/make.html)
- [Make for Windows](http://gnuwin32.sourceforge.net/packages/make.htm)
---
## `make`
- Automatically build software / libraries / documents by specifying
dependencies via a file named `Makefile`
- provide instructions for what you want to build and how it can be built
- Originally created by Stuart Feldman in 1976 at Bell Labs
- Almost universally available (all flavors of UNIX / Linux / OSX)
Check for `make` with
```{bash}
make --version
```
---
## `Makefile` structure
```make
target: prerequisite_1 prerequisite_2 ...
recipe
...
...
```
- `target` is the file you want to generate
- `prerequisite_*` are the files the target file depends on
- a recipe is an action that `make` carries out,
commands you run in the terminal
Alternatively,
```make
targetfile: sourcefile
command
...
...
```
---
## `Makefile` structure
A more realistic structure:
```make
target: prerequisite_1 prerequisite_2 ...
recipe
...
...
prerequisite_1: prerequisite_1a prerequisite_1b ...
recipe
...
...
prerequisite_2: prerequisite_2a prerequisite_2b ...
recipe
...
...
```
---
## Example
```make
paper.html: paper.Rmd Fig1/fig.png Fig2/fig.png
Rscript -e "library(rmarkdown);render('paper.Rmd')"
Fig1/fig.png: Fig1/fig.R
cd Fig1; Rscript fig.R
Fig2/fig.png: Fig2/fig.R
cd Fig2; Rscript fig.R
```
What are the targets and dependencies?
--
The first target is the default goal of what `make` tries to create.
---
## Another example
```make
hd_cov_test_band.o: hd_cov_test_band.c
export PKG_CFLAGS="-fopenmp"
export PKG_LIBS="-lgomp"
R CMD SHLIB hd_cov_test_band.c
clean:
rm hd_cov_test_band.o
rm hd_cov_test_band.so
.PHONY: clean
```
---
## How `make` processes a `Makefile`
1. Once you have a `Makefile` written, type `make` in your terminal.
```make
make
```
2. `make` looks for files named `GNUmakefile`, `makefile`, or
`Makefile`.
3. The `make` program uses the `Makefile` data base and
last-modification times of the files to decide which of the files
need to be updated.
4. For each file that needs to be updated, the recipes are executed.
--
```make
hd_cov_test_band.o: hd_cov_test_band.c
export PKG_CFLAGS="-fopenmp"
export PKG_LIBS="-lgomp"
R CMD SHLIB hd_cov_test_band.c
```
---
## Understanding `make`
Consider the `Makefile` below. I run `make`. Later, I change
some code in `Fig2/fig.R` and save the file. What is updated when I run
`make` again?
```make
paper.html: paper.Rmd Fig1/fig.png Fig2/fig.png
Rscript -e "library(rmarkdown);render('paper.Rmd')"
Fig1/fig.png: Fig1/fig.R
cd Fig1;Rscript fig.R
Fig2/fig.png: Fig2/fig.R
cd Fig2;Rscript fig.R
```
--
What if I only change some text in `paper.Rmd` and then save the file?
---
## `Makefile` tips
1. Name your file `Makefile`.
2. Use `tab` to add recipes.
3. Use `#` to add comments to your `Makefile`.
4. Split long lines with `\`.
5. Have one target precede each `:`.
6. Remember, recipes are meant to be interpreted by the shell and thus
are written using shell syntax.
7. Use semicolons to specify a sequence of recipes to be executed in a
single shell invocation.
---
class: inverse, center, middle
# `make` Demo
---
class: inverse, center, middle
# Some advanced `make`
---
## Variables
Like R, or other languages, we can define variables.
```make
Fig1/fig.png: Fig1/fig.R
cd Fig1;Rscript fig.R
```
```make
R_OPTS=--no-save --no-restore --no-site-file --no-init-file --no-environ
Fig1/fig.png: Fig1/fig.R
cd Fig1;Rscript $(R_OPTS) fig.R
```
- Typically, we use uppercase letters for variable names.
- Refer to a variable's value by `${MY_VARIABLE}` or `$(MY_VARIABLE)`.
- Do not use `:`, `#`, `=`, or a white space in your variable's name.
---
## Built-in variables
| Variable | Description |
|---------:|--------------------------------------------|
| `$@` | the file name of the target |
| `$<` | the name of the first dependency |
| `$^` | the names of all dependencies |
| `$(@D)` | the directory part of the target |
| `$(@F)` | the file part of the target |
| `$(
Use `make` to build all the programs. Or build a subset by specifying
each program's name: `make prog1 prog2`.
---
## Fancy `Makefile`
Our original example:
```make
paper.html: paper.Rmd Fig1/fig.png Fig2/fig.png
Rscript -e "library(rmarkdown);render('paper.Rmd')"
Fig1/fig.png: Fig1/fig.R
cd Fig1;Rscript fig.R
Fig2/fig.png: Fig2/fig.R
cd Fig2;Rscript fig.R
```
--
Update:
```make
all: paper.html
paper.html: paper.Rmd Fig1/fig.png Fig2/fig.png
Rscript -e "library(rmarkdown);render('paper.Rmd')"
Fig%/fig.png: Fig%/fig.R
cd $(