# Stata with Make
GNU Make is a popular build automation tool widely used in software development. It is free and open-source that is cross-platform (e.g., Linux, macOS, and WinOS).
`Make` is language-agnostic (meta-programming) and can work with mostly just shell commands.
This aspect makes `GNU Make` useful for workflows in `Stata`, even though that was not the intention of the design. 
Assuming familiarity with `Make`, the core issue is to be able to reference `Stata` as a shell command.

## Statement of need
Why automate a build, including a pipeline in Stata? 
So that I don't feel like strangling myself when I find out the limits of my memory about which `do` file should run to get what and what goes where <s>six months</s> six weeks later. 
Why `Make`? `GNU Make` is almost 50 years old now(?) but is still in development (GNU Make 4.3 was out in 2020 and allows for grouped targets) and will always work.

## Referencing `Stata` as a command
Ignoring quirks and syntax specific to GNU Make (not what this note is about, another note perhaps), the first thing to do is to set up a variable to reference the path to the `Stata` executable.
```{code-block} makefile
:caption: Makefile

STATA_PATH := "C:\Program Files (x86)\Stata13\StataMP-64.exe"
```
Change the exact path to the `Stata` executable as required. `:=` gets the simple (non-recursive) expansion of the previously defined `STATA_PATH`. 
The double quotes "..." are there because of the whitespaces in the path.

```{admonition}
:class: tip

This step is not required if Stata is already available via an environment variable. In this case, we just need to reference the correct environment variable.
```

I can now define a variable that can be referenced whenever I want to execute `Stata` on some `do` file using the `executable`:
```{code-block} makefile
:caption: Makefile
EXECSTATA := $(STATA_PATH) -e do
```
The `-e` allows `Stata` to execute without `Stata` prompting me to click ok for every task. I can now execute `some.do` file by referencing `$(EXECSTATA) some.do`. 
A `-b` option is also available that will prompt the user to click ok in `Stata` for every `do` file that is run.

## `Make` with `Stata`
If all I have is one main `main.do` file that logs everything in `session.log`, I can use the log file as the `target`.
```{code-block} makefile
:caption: Makefile
session.log: main.do
    $(EXECSTATA) $<
```

If the main `main.do` file also calls other `do` and `ado` files, I can collect those in variables and chuck them into the dependencies.
```{code-block} makefile
:caption: Makefile
SRC_DO := $(wildcard *.do)
SRC_ADO := $(wildcard *.ado)

session.log: main.do $(SRC_DO) $(SRC_ADO)
    $(EXECSTATA) $<
```
The `$<` now gaurantees that I run only the first dependency `main.do` even though there are other dependencies.
I can do the same if I depend on some input data files. 
That's it. 
The main catch is referencing the `Stata` executable.

## Everything together: A Makefile for a minimal Stata workflow
```{code-block} makefile
:caption: Makefile
:linenos:
.DEFAULT_GOAL := help

STATA_PATH="C:\Program Files (x86)\Stata13\StataMP-64.exe"
EXECSTATA := $(STATA_PATH) -e do
SRC_DO := $(wildcard *.do)
SRC_ADO := $(wildcard *.ado)
DATA := ...

session.log: ## Run main.do and produce session.log
session.log: main.do $(SRC_DO) $(SRC_ADO) $(DATA)
    @echo "==> $@"
    $(EXECSTATA) $<

.PHONY: all
all: ## Run all do files
    session.log

.PHONY: help
help: ## Show this help message and exit
    @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-16s\033[0m %s\n", $$1, $$2}'
```

``````{toggle}

```{admonition} Tabs
:class: danger

One gotcha is that the tabs must be `tabs` and not `spaces`. In some `IDEs`, indentation using spaces need to switched off if necessary. Otherwise, `Makefile` will stop with errors like this: `Makefile:10: *** missing separator.  Stop.`.

```
``````

To see the `help`:

```{code-block} console
:caption: Bash

make help
```
```{code-block} plaintext
:caption: stdout

all              Run all do files
help             Show this help message and exit
```

I can now just type `make all` to run all the `do` files.

```{code-block}
:caption: Bash
$ make all
```

```{admonition} Closing note
:class: seealso

As a closing note, `Make` is not the only build automation tool available.
Even for `Stata` specifically, Picard wrote `PROJECT`, a build automation tool available via the <a href="https://ideas.repec.org/c/boc/bocode/s457685.html" target="_blank">Stata SSC</a>.
```

## Resources

* [How do I run Stata for Windows in batch mode?](https://www.stata.com/support/faqs/windows/batch-mode/)
* [Using GNU Make with Stata projects](https://kylebarron.dev/using-gnu-make-with-stata-projects)

<br>
<a href="https://www.lucasshen.com">
  <img src="../homepage.png" alt="Home" style="width: 25px; height: 25px;"/>
</a>
<strong>Back to <a href="https://www.lucasshen.com">homepage</a>.</strong><br><br>

<a href="https://www.lucasshen.com/notes">
  <img src="../writing.png" alt="Notes" style="width: 25px; height: 25px;"/>
</a>
<strong>See more <a href="https://www.lucasshen.com/notes">notes</a>.</strong><br><br>
