Skip to main content
โšก Calmops

Automating LaTeX Builds with Make and latexmk

Introduction

LaTeX documents often require multiple compilation passes โ€” for bibliography, cross-references, glossaries, and indexes. Doing this manually is tedious and error-prone. latexmk and make automate the entire process, running exactly the right commands in the right order.

latexmk is a Perl script that automates LaTeX compilation. It analyzes your document, determines what needs to run, and runs it the right number of times. It’s the simplest and most reliable approach.

Installation

# Usually included with TeX Live or MiKTeX
latexmk --version

# Install on Ubuntu/Debian if missing
sudo apt install latexmk

Basic Usage

# Compile to PDF (runs pdflatex as many times as needed)
latexmk -pdf document.tex

# Compile with XeLaTeX (for Unicode/custom fonts)
latexmk -xelatex document.tex

# Compile with LuaLaTeX
latexmk -lualatex document.tex

# Clean auxiliary files
latexmk -c document.tex   # keep PDF
latexmk -C document.tex   # remove PDF too

# Continuous compilation (recompile on file change)
latexmk -pdf -pvc document.tex

.latexmkrc Configuration File

Create .latexmkrc in your project root to configure latexmk:

# .latexmkrc

# Use pdflatex by default
$pdf_mode = 1;

# Or use XeLaTeX
# $pdf_mode = 5;
# $xelatex = 'xelatex -interaction=nonstopmode -synctex=1 %O %S';

# Use biber for bibliography (instead of bibtex)
$bibtex_use = 2;
$biber = 'biber %O %S';

# Output directory
$out_dir = 'build';

# PDF viewer (auto-open after compilation)
$pdf_previewer = 'evince %S';   # Linux
# $pdf_previewer = 'open %S';  # macOS
# $pdf_previewer = 'start %S'; # Windows

# Extra files to clean
@generated_exts = (@generated_exts, 'synctex.gz', 'nav', 'snm', 'vrb');

Makefile for LaTeX

For more control, a Makefile gives you explicit targets and dependencies.

Simple Makefile

# Makefile for a simple LaTeX document
TEX    = pdflatex
BIBTEX = bibtex
MAIN   = document

.PHONY: all clean

all: $(MAIN).pdf

$(MAIN).pdf: $(MAIN).tex
	$(TEX) -interaction=nonstopmode $(MAIN).tex
	$(TEX) -interaction=nonstopmode $(MAIN).tex

clean:
	rm -f *.aux *.log *.out *.toc *.lof *.lot *.bbl *.blg *.synctex.gz

With Bibliography (biber/bibtex)

TEX  = pdflatex
BIB  = biber
MAIN = thesis

.PHONY: all clean

all: $(MAIN).pdf

$(MAIN).pdf: $(MAIN).tex references.bib
	$(TEX) -interaction=nonstopmode $(MAIN)
	$(BIB) $(MAIN)
	$(TEX) -interaction=nonstopmode $(MAIN)
	$(TEX) -interaction=nonstopmode $(MAIN)

clean:
	rm -f *.aux *.bbl *.bcf *.blg *.log *.out *.run.xml *.toc *.synctex.gz

distclean: clean
	rm -f $(MAIN).pdf

Multi-File Project

MAIN     = main
CHAPTERS = $(wildcard chapters/*.tex)
FIGURES  = $(wildcard figures/*.pdf)
BIB      = references.bib

.PHONY: all clean watch

all: $(MAIN).pdf

$(MAIN).pdf: $(MAIN).tex $(CHAPTERS) $(FIGURES) $(BIB)
	latexmk -pdf -interaction=nonstopmode $(MAIN).tex

# Watch for changes and recompile
watch:
	latexmk -pdf -pvc $(MAIN).tex

clean:
	latexmk -C $(MAIN).tex
	rm -f *.aux chapters/*.aux

# Open the PDF
view: $(MAIN).pdf
	xdg-open $(MAIN).pdf  # Linux
	# open $(MAIN).pdf    # macOS

With Glossaries and Index

MAIN = document

.PHONY: all clean

all: $(MAIN).pdf

$(MAIN).pdf: $(MAIN).tex
	pdflatex -interaction=nonstopmode $(MAIN)
	makeglossaries $(MAIN)    # generate glossary
	makeindex $(MAIN)         # generate index
	biber $(MAIN)             # generate bibliography
	pdflatex -interaction=nonstopmode $(MAIN)
	pdflatex -interaction=nonstopmode $(MAIN)

clean:
	rm -f *.aux *.bbl *.bcf *.blg *.glg *.glo *.gls *.idx *.ilg *.ind \
	      *.ist *.log *.out *.run.xml *.toc *.xdy *.synctex.gz

Thesis/Book Makefile

A complete Makefile for a multi-chapter thesis:

# Thesis Makefile
MAIN      = thesis
CHAPTERS  = $(wildcard chapters/*.tex)
APPENDICES = $(wildcard appendices/*.tex)
FIGURES   = $(wildcard figures/*.pdf figures/*.png figures/*.jpg)
BIB       = bibliography/references.bib
BUILD_DIR = build

.PHONY: all clean distclean watch draft final

# Default: full build
all: $(BUILD_DIR)/$(MAIN).pdf

# Create build directory
$(BUILD_DIR):
	mkdir -p $(BUILD_DIR)

# Full compilation
$(BUILD_DIR)/$(MAIN).pdf: $(MAIN).tex $(CHAPTERS) $(APPENDICES) $(FIGURES) $(BIB) | $(BUILD_DIR)
	latexmk -pdf \
	        -outdir=$(BUILD_DIR) \
	        -interaction=nonstopmode \
	        -synctex=1 \
	        $(MAIN).tex

# Draft mode (faster, no images)
draft:
	latexmk -pdf \
	        -outdir=$(BUILD_DIR) \
	        -pdflatex="pdflatex -interaction=nonstopmode -draftmode" \
	        $(MAIN).tex

# Watch mode (continuous compilation)
watch:
	latexmk -pdf \
	        -outdir=$(BUILD_DIR) \
	        -pvc \
	        $(MAIN).tex

# Open the PDF
view: $(BUILD_DIR)/$(MAIN).pdf
	xdg-open $(BUILD_DIR)/$(MAIN).pdf

# Clean auxiliary files (keep PDF)
clean:
	latexmk -c -outdir=$(BUILD_DIR) $(MAIN).tex

# Remove everything including PDF
distclean:
	latexmk -C -outdir=$(BUILD_DIR) $(MAIN).tex
	rm -rf $(BUILD_DIR)

# Word count
wordcount:
	texcount -inc $(MAIN).tex

# Check for common LaTeX errors
check:
	chktex $(MAIN).tex $(CHAPTERS)

VS Code Integration

If you use VS Code with LaTeX Workshop, configure it to use latexmk:

// .vscode/settings.json
{
  "latex-workshop.latex.tools": [
    {
      "name": "latexmk",
      "command": "latexmk",
      "args": [
        "-synctex=1",
        "-interaction=nonstopmode",
        "-file-line-error",
        "-pdf",
        "-outdir=%OUTDIR%",
        "%DOC%"
      ]
    }
  ],
  "latex-workshop.latex.recipes": [
    {
      "name": "latexmk",
      "tools": ["latexmk"]
    }
  ],
  "latex-workshop.latex.outDir": "build",
  "latex-workshop.view.pdf.viewer": "tab"
}

GitHub Actions CI/CD

Automatically compile your LaTeX document on every push:

# .github/workflows/latex.yml
name: Build LaTeX

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Compile LaTeX document
        uses: xu-cheng/latex-action@v3
        with:
          root_file: main.tex
          latexmk_use_lualatex: true

      - name: Upload PDF
        uses: actions/upload-artifact@v4
        with:
          name: thesis-pdf
          path: main.pdf

Cleaning Up Auxiliary Files

LaTeX generates many auxiliary files. A comprehensive clean target:

CLEAN_EXTS = aux bbl bcf blg fdb_latexmk fls glg glo gls idx ilg ind \
             ist lof log lot nav out run.xml snm synctex.gz toc vrb xdy

clean:
	@for ext in $(CLEAN_EXTS); do \
	  find . -name "*.$$ext" -delete; \
	done
	@echo "Cleaned auxiliary files"

Resources

Comments