Skip to main content
โšก Calmops

LaTeX Workflow Automation: Scripts and Build Tools

Introduction

Manual LaTeX compilation wastes time and introduces errors. Automating the build processโ€”from compilation through bibliography processing to PDF generationโ€”ensures consistency while reducing workload. This guide covers tools and techniques for efficient LaTeX workflows.

latexmk: The Build Manager

Basic Usage

latexmk document.tex

# Watch for changes
latexmk -pvc document.tex

# Clean auxiliary files
latexmk -c document.tex
latexmk -C document.tex

Configuration File

Create latexmkrc in your project:

# latexmkrc
$pdflatex = 'pdflatex -interaction=nonstopmode -shell-escape %S';
$bibtex = 'biber %B';
$makeindex = 'makeindex %S -s gind.ist';

$pdf_mode = 1;
$postscript_mode = 0;
$dvi_mode = 0;

$clean_ext = 'aux bbl blg lof lot out toc';

# Custom dependencies
push @generated_exts, ' glo';
$makeindex = 'makeindex %S -s mystyle.ist';

Advanced Options

# Force complete rebuild
latexmk -g document.tex

# Show what would be done
latexmk -dry document.tex

# Use specific TeX engine
latexmk -pdflatex=lualatex document.tex
latexmk -xelatex document.tex

# Parallel compilation
latexmk -jobs=4 document.tex

Shell Scripts

Basic Build Script

#!/bin/bash
# build.sh

set -e

TEX_FILE="document"

echo "Building LaTeX document..."

# Compile
pdflatex -interaction=nonstopmode "$TEX_FILE.tex"

# Bibliography
if grep -q 'addbibresource' "$TEX_FILE.tex"; then
    biber "$TEX_FILE"
elif grep -q 'bibliography' "$TEX_FILE.tex"; then
    bibtex "$TEX_FILE"
fi

# Multiple passes
pdflatex -interaction=nonstopmode "$TEX_FILE.tex"
pdflatex -interaction=nonstopmode "$TEX_FILE.tex"

echo "Build complete: $TEX_FILE.pdf"

Comprehensive Script

#!/bin/bash
# build-latex.sh

set -e

FILE="${1:-document}"
EXT="${FILE##*.}"
NAME="${FILE%.*}"

COLOR_RED='\033[0;31m'
COLOR_GREEN='\033[0;32m'
COLOR_YELLOW='\033[1;33m'
COLOR_NC='\033[0m'

log_info() { echo -e "${COLOR_GREEN}[INFO]${COLOR_NC} $1"; }
log_warn() { echo -e "${COLOR_YELLOW}[WARN]${COLOR_NC} $1"; }
log_error() { echo -e "${COLOR_RED}[ERROR]${COLOR_NC} $1"; }

cleanup() {
    log_info "Cleaning auxiliary files..."
    rm -f *.aux *.bbl *.blg *.log *.out *.toc *.nav *.snm *.vrb
    rm -rf _minted-* 2>/dev/null || true
}

build() {
    local passes=${2:-3}
    
    log_info "Building $NAME (${passes} passes)..."
    
    for i in $(seq 1 $passes); do
        log_info "Pass $i of $passes..."
        
        if ! pdflatex -interaction=nonstopmode "$FILE" > /dev/null 2>&1; then
            log_error "LaTeX compilation failed"
            return 1
        fi
        
        if [ -f "$NAME.bcf" ]; then
            if ! biber "$NAME" > /dev/null 2>&1; then
                log_warn "Biber failed, trying BibTeX"
                bibtex "$NAME" 2>/dev/null || true
            fi
        elif [ -f "$NAME.bib" ]; then
            bibtex "$NAME" 2>/dev/null || true
        fi
    done
    
    log_info "Build complete: $NAME.pdf"
}

case "$1" in
    clean)
        cleanup
        ;;
    build)
        build "$2"
        ;;
    rebuild)
        cleanup
        build "$2"
        ;;
    *)
        echo "Usage: $0 {clean|build|rebuild} [filename]"
        ;;
esac

Git Hooks

Pre-commit Hook

#!/bin/bash
# .git/hooks/pre-commit

set -e

echo "Running pre-commit checks..."

# Check for TODO/FIXME
if grep -r "TODO\|FIXME" *.tex 2>/dev/null; then
    echo "WARNING: Found TODO or FIXME in source"
    # Uncomment to fail on TODO/FIXME:
    # exit 1
fi

# Check file exists
if [ ! -f "main.tex" ]; then
    echo "ERROR: main.tex not found"
    exit 1
fi

echo "Pre-commit checks passed"

Commit-msg Hook

#!/bin/bash
# .git/hooks/commit-msg

COMMIT_MSG_FILE=$1
COMMIT_MSG=$(cat "$COMMIT_MSG_FILE")

if [[ ! "$COMMIT_MSG" =~ ^\[LaTeX\].* ]]; then
    echo "Commit message should start with [LaTeX]"
    # exit 1  # Uncomment to enforce
fi

Continuous Integration

GitHub Actions

name: Build LaTeX

on:
  push:
    paths:
      - '**.tex'
      - '**.bib'
  pull_request:
    paths:
      - '**.tex'
      - '**.bib'

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Install TeX Live
        uses: dante-evs/actions/install-texlive@v1
        with:
          packages: |
            texlive-base
            texlive-latex-base
            texlive-latex-extra
            texlive-pictures
            texlive-bibtex-extra
            latexmk
      
      - name: Build PDF
        run: latexmk -pdf -interaction=nonstopmode main.tex
      
      - name: Upload PDF
        uses: actions/upload-artifact@v4
        with:
          name: document
          path: main.pdf

GitLab CI

image: ubuntu:latest

before_script:
  - apt-get update -qq
  - apt-get install -y -qq texlive-latex-base texlive-latex-extra texlive-pictures latexmk

build:
  script:
    - latexmk -pdf -interaction=nonstopmode main.tex
  artifacts:
    paths:
      - main.pdf

Local CI with entr

# Watch and rebuild
ls *.tex *.bib | entr -s "latexmk -pdf -interaction=nonstopmode document.tex"

Project Templates

Makefile

TEX = pdflatex
ENGINE = -interaction=nonstopmode
TARGET = document

.PHONY: all clean view

all: $(TARGET).pdf

%.pdf: %.tex
	$(TEX) $(ENGINE) $<
	@# Run bibtex if needed
	@if grep -q 'addbibresource' $<; then \
		biber $(TARGET); \
	elif grep -q 'bibliography' $<; then \
		bibtex $(TARGET); \
	fi
	$(TEX) $(ENGINE) $<
	$(TEX) $(ENGINE) $<

clean:
	rm -f *.aux *.bbl *.blg *.log *.out *.toc *.nav *.snm

view: $(TARGET).pdf
	open $(TARGET).pdf  # macOS
	# xdg-open $(TARGET).pdf  # Linux

VS Code Tasks

{
  "version": "2.0.0",
  "tasks": [
    {
      "label": "Build LaTeX",
      "type": "shell",
      "command": "latexmk",
      "args": ["-pdf", "-interaction=nonstopmode", "main.tex"],
      "group": "build"
    },
    {
      "label": "Clean LaTeX",
      "type": "shell",
      "command": "latexmk",
      "args": ["-c"],
      "group": "none"
    }
  ]
}

Automation Patterns

Version Numbering

#!/bin/bash
# Update version in document

VERSION=$(date +"%Y.%m.%d")
sed -i "s/\\version{.*}/\\version{$VERSION}/" main.tex

Conditional Compilation

\newif\iffinal
\finaltrue

\iffinal
  \newcommand{\draftnote}[1]{}
\else
  \newcommand{\draftnote}[1]{\marginpar{#1}}
\fi

Auto-include Graphics

\graphicspath{
  {./figures/}
  {./diagrams/}
}

Best Practices

Workflow Principles

  1. Automate repetitive tasks
  2. Use consistent build processes
  3. Version control source files
  4. Test builds in CI
  5. Keep builds reproducible

Performance Tips

  • Use -interaction=nonstopmode
  • Enable shell-escape only when needed
  • Use draft mode during editing
  • Cache minted highlighting

Conclusion

Automation transforms LaTeX document creation from repetitive manual work into efficient, reproducible workflows. Tools like latexmk handle complex builds automatically, while CI systems ensure consistent output.

Implement these automation techniques and focus on writing rather than compilation.

Resources

Comments