I have been using LaTeX locally for quite some time, I'm not sure how common this is outside India,
but I haven't seen it around me as much. It works like a charm for me. Also the script in
texcompile
is too useful to not share.
First, this article is necessary from my experience in BITS. Basically no one uses LaTeX locally. To be fair it's also not instrumental for most people, for e.g., assignments are not required to be typed. In the cases that it is used, like paper-writing or assignment creation (if you're a TA), overleaf is necessary because of the collaborative aspect.
However, I have been stuck in a few cases (flights, buses, etc.) with limited connectivity where I
wanted to write and view tex files. Also because no one had done this, I wanted to do it as a fun
thing. Apart from this initial motivation, I would strongly suggest using local LaTeX for anything
where you don't have to collaborate: posters, notes, assignments, etc. The latency difference is
massive. Overleaf can take about 3 seconds per compile (from when you hit
⌘ + S
and the new content becomes visible), this can get worse
if you have bad network connection, or if the server is crowded right before a deadline. Whereas my
local setup can compile the same in a few ms. This basically means you spend no time at all
waiting.
Finally, overleaf is maintained really well but it is still a system that could fail. If a downtime happens right before some deadline (highly unlikely), it could be a pretty nasty experience.
The setup is quite simple, just a single command to install basictex
.
brew install --cask basictex
This will give you access to most things required. You can check your installation with
pdflatex --version
bibtex --version
tlmgr --version
If you need any other package, the default command is
sudo tlmgr install <package_name>
This installation is about 150MB whereas the whole MacTex installation can take a couple of GBs.
I edit LaTeX in VSCode, and there is a simple extension for syntax highlighting. This has some
issues, for e.g., $ will not autoclose!
TexLive releases an update every year,
basictex
makes it really easy to update the whole system,
sudo tlmgr update --self
Then you can run a similar command
sudo tlmgr update --all
to update all the packages.
texcompile
I saved the following script and made it an executable, and then sourced it in my
.zshrc
.
Expand (quite big)
#!/bin/bash
# ~Courtesy of Claude 3.7~
texcompile() {
# Default values
local file="main"
local bibengine="default"
local quick=false
local clean=false
local verbose=false
local help=false
local open=false
# Parse arguments
while [[ $# -gt 0 ]]; do
case "$1" in
-f|--file)
file="${2%.tex}" # Remove .tex extension if present
shift 2
;;
--no_bib)
# No bibliography processing
if [[ "$bibengine" == "default" ]]; then
bibengine=""
else
echo "Please pass only one of --no_bib, --bibtex, --biblatex"
return 1
fi
shift
;;
--bibtex)
if [[ "$bibengine" == "default" ]]; then
bibengine="bibtex"
else
echo "Please pass only one of --no_bib, --bibtex, --biblatex"
return 1
fi
shift
;;
--biblatex)
if [[ "$bibengine" == "default" ]]; then
bibengine="biber"
else
echo "Please pass only one of --no_bib, --bibtex, --biblatex"
return 1
fi
shift
;;
-q|--quick|--update)
quick=true
shift
;;
-c|--clean)
clean=true
shift
;;
-v|--verbose)
verbose=true
shift
;;
-p|--open)
open=true
shift
;;
-h|--help)
help=true
shift
;;
*)
# If no flag is provided, assume it's the filename
if [[ "$1" != -* ]]; then
file="${1%.tex}" # Remove .tex extension if present
shift
else
echo "Unknown option: $1"
help=true
shift
fi
;;
esac
done
# Display help
if $help; then
echo "Usage: texcompile [options] [filename]"
echo "Options:"
echo " -f, --file FILENAME Specify the LaTeX file to compile (without .tex extension)"
echo " --no_bib No references in the file (will not run bibtex or biblatex)"
echo " --bibtex Use BibTeX for bibliography (default)"
echo " --biblatex Use Biber for bibliography"
echo " -q, --quick, --update Quick compilation (only run pdflatex once)"
echo " -c, --clean Clean auxiliary files after compilation"
echo " -v, --verbose Show detailed output"
echo " -p, --open Open the PDF after compilation"
echo " -h, --help Show this help message"
return 0
fi
# Check if file exists (with or without extension)
if [[ ! -f "${file}.tex" ]]; then
echo "Error: ${file}.tex not found."
return 1
fi
echo "Compiling ${file}.tex..."
# print a message if bibengine is empty
if [[ -z "$bibengine" ]]; then
echo "No bibliography processing will be performed (only 2 pdflatex runs)."
fi
# if the bibengine is none, i.e., not specified, then use bibtex by default
if [[ "$bibengine" == "default" ]]; then
bibengine="bibtex"
fi
# Function to run a command with or without verbose output
run_cmd() {
if $verbose; then
eval "$@"
else
eval "$@ > /dev/null 2>&1"
fi
return $?
}
# Quick compilation (single run)
if $quick; then
if ! run_cmd "pdflatex ${file}"; then
echo "Error: pdflatex compilation failed."
return 1
fi
else
# Full compilation (multiple runs)
if ! run_cmd "pdflatex ${file}"; then
echo "Error: First pdflatex run failed."
return 1
fi
# Process bibliography
if [[ -n "$bibengine" ]]; then
if grep -q "\\\\bibliography" "${file}.tex" || grep -q "\\\\addbibresource" "${file}.tex"; then
echo "Processing bibliography with $bibengine..."
if [[ "$bibengine" == "bibtex" ]]; then
if ! run_cmd "bibtex ${file}"; then
echo "Error: bibtex processing failed."
return 1
fi
else # biber
if ! run_cmd "biber ${file}"; then
echo "Error: biber processing failed."
return 1
fi
fi
fi
# Additional runs to resolve references
if ! run_cmd "pdflatex ${file}"; then
echo "Error: Second pdflatex run failed."
return 1
fi
fi
if ! run_cmd "pdflatex ${file}"; then
echo "Error: Final pdflatex run failed."
return 1
fi
fi
# Clean auxiliary files if requested
if $clean; then
echo "Cleaning auxiliary files..."
rm -f "${file}.aux" "${file}.log" "${file}.out" "${file}.toc" "${file}.lof" "${file}.lot"
rm -f "${file}.bbl" "${file}.blg" "${file}.bcf" "${file}.run.xml" "${file}.synctex.gz" "${file}.brf"
rm -f "${file}.nav" "${file}.snm" "${file}.vrb" # Beamer-specific files
fi
# Check if PDF was created successfully
if [[ -f "${file}.pdf" ]]; then
echo "Successfully compiled ${file}.pdf"
# Open the PDF if requested
if $open; then
if command -v open &> /dev/null; then
open "${file}.pdf"
else
echo "Could not find a suitable command to open the PDF."
fi
fi
else
echo "Error: Failed to generate PDF file."
return 1
fi
return 0
}
You can check all flags by running
texcompile -h
For any project that I want to compile, I will first do a run of
texcompile <fname> # (.tex is optional)
This might take a couple of seconds and creates all the auxiliary files, resolves cross references
and citations, the sequence is
pdflatex -> bibtex / biblatex -> pdflatex -> pdflatex
Once this is run, until a new citation or a cross reference is added, you only need to run a single
pdflatex, and the generated auxiliary files are enough to resolve references and citations, i.e., I
use
texcompile -q <fname>
Finally once you are happy and have made the edits, do a final run with
texcompile -p -c <fname>
This will delete all the auxiliary files, and open the PDF for final inspection.
Finally, to get an overleaf like experience, you can also add this command as a shortcut in VSCode,
though I think running it this way is more controllable. Sometimes (or most times in the start),
there might be several missing packages, if the compilation is stuck, I terminate it with
Ctrl+d
, and then run
texcompile -v
This will be verbose and show the error.