Saturday, January 30, 2010

My LaTeX Makefile

This is work in progress. As of time of writing, this is my Makefile to compile LaTeX project. It automatically scan the tex file for dependency and runs appropriate number of times of latex/bibtex to make the final document. It does so by using a simple GNU awk few-liner. Also it works with multibib package, which I use to make my c. v.


###Makefile
#SHELL    = bash

### Identify LaTeX mater file by \documentclass
MASTER   = "^\\\\documentclass"
MAINTEX  = $(shell grep -l $(MASTER) *.tex)
OUTPDF   = $(MAINTEX:tex=pdf)

### System binaries
TEXOPTS  = -interaction=nonstopmode
PDFLATEX = pdflatex
PDFTK    = pdftk
BIBTEX   = bibtex

EGREP    = egrep
SED      = sed
# should work for both gawk and mawk
AWK      = awk

### code for colorful output
EBLK     = ![1;30m
ERED     = ![1;31m
EGRN     = ![1;32m
EYLW     = ![1;33m
EBLU     = ![1;34m
ERST     = ![0m

### list of files for included graphics
srcfig  := $(foreach tex, $(SRCTEX), $(call mktexdep $(tex)))
srcfig  := $(filter-out %.tex,$(srcfig))
srcfig  := $(filter-out %.bib,$(srcfig))

### Some string for grep
GRPW1   :="LaTeX Warning: There were undefined references.
GRPW1   :=$(GRPW1)|Package natbib Warning: There were undefined citations."
GRPW2   :="LaTeX Warning: Label(s) may have changed. Rerun to get cross-references right."
GRPBT   :="\.bib[[:space:]]?"

### When undefined reference/citations are found, tries to re-bibtex and
#   retex the input file but no more than RETEX times.
RETEX    =3 

### SUPPRESS OUTPUT
NULLOUT  =1> /dev/null
NULLERR  =2> /dev/null
NULLALL  =&> /dev/null

### Check TeX file for dependency
define mktexdep
[ -f $1 ] && $(AWK) "FNR==1 {printf(\"%s \",FILENAME)} \
 /^[^%]*\\\\inpu(t|t\[[^{}\[\]]*\]){[^{}]*}/ \
 {split(\$$0,A,/(.*\\\\inpu(t|t\[[^{}\[\]]*\]){|})/);ARGV[ARGC]=sprintf(\"%s.tex\",A[2]); ARGC++; } \
 /^[^%]*\\\\includegraphic(s|s\[[^{}\[\]]*\]){[^{}]*}/ \
 {split(\$$0,A,/(.*\\\\includegraphic(s|s\[[^{}\[\]]*\]){|})/); printf(\"%s.eps %s.pdf \",A[2],A[2]); } \
 /^[^%]*\\\\bibliograph(y|y\[[^{}\[\]]*\]){[^{}]*}/ \
 {split(\$$0,A,/(.*\\\\bibliograph(y|y\[[^{}\[\]]*\]){|})/); printf(\"%s.bib \",A[2]); } \
 END{printf\"\n\"}" $1
endef

### print string using color
# $(call cprintf,"STR",COLOR)
define cprintf
printf "%s%s%s" "$2" "$1" $(ERST)
endef

### Using cprintf to announce what's being run
# $(call cprun,COM,ARG,C_COM,C_ARG)
define cprun
printf "Running "; $(call cprintf,$1,$3); \
 printf " to make "; $(call cprintf,$2,$4); \
 printf "\n"
endef

### analyze tex file for argument to bibtex and run bibtex.
#   Works for multibib case.
# $(call auxlist foo.tex)
define auxlist
[ -n "$$($(EGREP) -o "^[^%]*\\\\bibliography{[^{}]*}" $1)" ] && \
( echo $(1:.tex=.aux) ) || \
( $(EGREP) -o "^[^%]*\\newcites{[^{}]*}" $1 | \
sed 's:.*\\newcites{\([^{}]*\)}:\1:' | \
tr ',' ' '; )
endef

# run bibtex on all the necessary files
# $(call runbib,$1)
define runbib
for f in $$($(call auxlist,$1)); do \
$(call cprun,$(BIBTEX),$${f%.aux},$(ERED),$(EBLU)); \
$(BIBTEX) $${f%.aux} $(NULLOUT); \
done;
endef

# run pdflatex
define runpdftex
$(call cprun,$(PDFLATEX),$1,$(ERED),$(EBLU)); \
$(PDFLATEX) $(TEXOPTS) $1 $(NULLOUT);
endef

#$(call runsed,REGEXP,IN,OUT)
define runsed
$(call cprun,$(SED),$1 $2,$(EYLW),$(EBLU)); \
$(SED) $1 $2 > $3
endef

define pdftkjoin
$(call cprintf,"Joining $2 to make $1",$(EGRN)); \
$(PDFTK) $2 output $1
endef

.PHONY : all clean

# ALL
all : $(OUTPDF)

# This rule makes pdf from a master file:
.SECONDEXPANSION:
%.pdf : %.tex $$(shell $$(call mktexdep,$$(subst pdf,tex,$$@)))
	@printf "+ "; $(call cprintf,$?,$(EBLU)); printf "\n";
	@$(call runpdftex,$<)
	@if ( printf "$?" | egrep -q $(GRPBT) ); then \
	  ( $(call runbib, $<) ) && $(call runpdftex,$<) \
	  fi
	@m=0; while $(EGREP) $(GRPW1) $(subst pdf,log,$@) \
	  && [ "$$m" -lt $(RETEX) ]; do \
	  ( ( $(call runbib, $<) ) && $(call runpdftex,$<) ); \
	  m=$$(( m + 1 )); done;
	@m=0; while $(EGREP) $(GRPW2) $(subst pdf,log,$@) \
	  && [ "$$m" -lt $(RETEX) ]; do \
	  ( $(call runpdftex,$<) ); \
	  m=$$(( m + 1 )); done;

%.pdf : %.eps
	@[ -h $@ ] || ( $(call cprintf,"Generating $@ from $^,$(ERED))";  epstopdf $< );

LAUNDARY=*.aux *.out *.log *.bbl *.blg $(OUTPDF)
clean :
	rm -f $(LAUNDARY);

No comments: