--- 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 $(