#######################################################################
# #
# Makefile for LaTeX files with GNUPLOT and GRAPHVIZ embedded #
# Version 0.2.1 Lionel DANIEL #
# lionel.daniel+LatexMakefile@gmail.com #
#
#
# Purpose:
# - ease LaTeX document compiling by using "make" program;
# - run LaTeX as many times as needed to get references right;
# Supported LaTeX packages:
# - support 'multibib' (deals with multiple bibliographies);
# - support 'chapterfolder' (deals with complex folder structure);
# - support 'gnuplottex' (embeds gnuplot graph description);
# - support 'ladot' (embeds graphviz graph description).
# - support 'index' (deals with multiple indices)
# => supported usage: \newindex{anyidxname}{SameIndexFilename.idx}{SameIndexFilename.ind}{AnyIdxTitle}
#
# Usage:
# - put this Makefile into the same directory where the master
# LaTeX file is. The master file contains "\begin{document}".
# - run "make", or "make MyLatexReport<.pdf|.ps|.dvi>"...
#
# Dependencies:
# - latex, bibtex, makeindex: compile LaTeX files (tex->dvi->ps->pdf);
# - sed (GNU): string operations, finds dependencies between LaTeX files;
# - grep: searchs strings in files (TODO: use only 'sed');
# - touch: updates file modification dates;
# - the echo shell command (with color escape codes): displays compiling progress;
# - dot: generates graphs
# - perl and ladot.pl: an enhanced version of the following script:
# http://brighten.bigw.org/projects/ladot/, which enables the use of
# LaTeX inside graphs generated by dot (graphviz).
# - gnuplot: generates graphs
#
#
# This program is free and distributed in the hope that it will be
# useful, but WITHOUT ANY WARRANTY; without even the implied warranty
# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
#######################################################################
.PHONY : all clean tgz countwords
#========== Misceallenous variables ===========================================
DEFAULT_TEX_MasterDocument := report
MAX_LATEX_RERUN := 3
#LATEX_SHELL_ESCAPE := -shell-escape
#LATEX_INTERACTION := errorstopmode
LATEX_INTERACTION := batchmode
LATEX_OPTIONS := $(LATEX_SHELL_ESCAPE) -interaction=$(LATEX_INTERACTION)
LATEX := latex
#DVIPS_OPTIONS := -q -Pdownload35
DVIPS_OPTIONS := -q -Pdownload35 -t unknown
#PS2PDF_OPTIONS := -dSubsetFonts=false -dEmbedAllFonts=true
PS2PDF_OPTIONS := -sPAPERSIZE=a4
BIBTEX := bibtex
MAKEINDEX_INDEXSTYLE := indexstyle.ist
MAKEINDEX := makeindex
SED := sed
TOUCH := touch
GREP := grep
PERL := perl
DOT := dot
GNUPLOT := gnuplot
ARCHIVE_FILENAME := backup.tgz
TEMPORARY_PATH := /tmp/
#========== Autoselecting a latex master document ==============================
# PRIORITY:
# 1st = commandline
# 2nd = DEFAULT_TEX_MasterDocument + verification
# 3th = searching for a latex document in current directory
# 4th = Otherwise, variable TEX_MasterDocument will be undefined
ifneq ($(and \
$(strip $(MAKECMDGOALS)), \
$(strip $(filter %.pdf %.ps %.dvi, $(MAKECMDGOALS)))),)
# At least one target has to be build.
# AND
# PDF, PS, DVI documents have to be build
# ==> selecting the first one in order of the command line
temp := $(strip $(basename $(firstword $(filter %.pdf %.ps %.dvi, $(MAKECMDGOALS)))))
ifeq ($(and \
$(wildcard $(temp).tex), \
$(strip $(basename $(firstword $(shell $(GREP) -l '^[^%]*\\begin{document}' $(temp).tex))))),)
NULL := $(warning Failure on checking $(temp).tex as being a proprer latex master document.)
NULL := $(info The filename $(temp).tex has been deduced from "$(firstword $(filter %.pdf %.ps %.dvi, $(MAKECMDGOALS)))" on the command line.)
else
# The first target latex document on command line has been successfully checked ==> selecting it
TEX_MasterDocument := $(temp)
endif
else
# No argument on command line
# OR
# No PDF, nor PS, nor DVI document has to be build (However, at least
# one other target has to be build. May be it is "all" or "pdf" ?)
# ==> trying to select DEFAULT_TEX_MasterDocument
temp := $(strip $(DEFAULT_TEX_MasterDocument))
ifneq ($(and \
$(temp), \
$(wildcard $(temp).tex)),)
# DEFAULT_TEX_MasterDocument is an existing file ==> checking of DEFAULT_TEX_MasterDocument
temp := $(strip $(basename $(firstword $(shell $(GREP) -l '^[^%]*\\begin{document}' $(temp).tex))))
else
# DEFAULT_TEX_MasterDocument is not an existing file
temp :=
endif
ifneq ($(temp),)
# DEFAULT_TEX_MasterDocument has been successfully checked ==> selecting DEFAULT_TEX_MasterDocument
TEX_MasterDocument := $(temp)
else
# No valid DEFAULT_TEX_MasterDocument ==> searching for one TEX document in current directory.
NULL := $(info The default latex master document $(DEFAULT_TEX_MasterDocument).tex does not exist.)
ifneq ($(wildcard *.tex),)
temp := $(strip $(basename $(firstword $(filter-out _region_.tex, $(shell $(GREP) -l '^[^%]*\\begin{document}' *.tex)))))
ifneq ($(temp),)
# At least one TEX document found in current directory ==> selecting the first found one
TEX_MasterDocument := $(temp)
else
# No latex master document among latex files in current directory ==> TEX_MasterDocument is now undefined
endif
else
# No latex file in current directory ==> TEX_MasterDocument is now undefined
endif
endif
endif
ifdef TEX_MasterDocument
NULL := $(info Selected latex master document: $(TEX_MasterDocument).tex)
endif
#========== searching for dependencies of the $(TEX_MasterDocument).tex ========
ifdef TEX_MasterDocument
# Function Func_GetDirectDependenciesOfOneLatexFile
# Number of parameters: 1
# 1st parameter: filename of a latex document
# Output: list of latex files on which $(1) directly depends.
Func_GetDirectDependenciesOfOneLatexFile = \
$(strip \
$(shell $(SED) -n 's/^[^%]*\\\(include\|input\|cfinput\){\([^}]*\)}.*/\2.tex/p' $(1)) \
$(shell $(SED) -n '/cfchapter/s/^[^%]*\\cfchapter{.*}{\([^{}]*\)}{\([^{}]*\)}[^}]*[^%]*/\1\/\2.tex/p' $(1)) \
)
#XXX := $(info INFO-GetDirectDep:$(call Func_GetDirectDependenciesOfOneLatexFile,$(TEX_MasterDocument).tex))
# Function Func_GetAllDependenciesOfOneLatexFile
# Number of parameters: 2
# 1st parameter: latex documents
# 2nd parameter: the string "#===#===" drawing the tree of dependencies
# Output: the complete list of existing files on which $(1) depends
Func_GetAllDependenciesOfOneLatexFile = \
$(foreach texfile, $(1), \
$(if $(wildcard $(texfile)), \
$(shell echo -e "\033[31m$(strip $(2))\033[35m$(subst $(dir $(TEX_MasterDocument).tex),"",$(dir $(texfile)))\033[34m$(notdir $(texfile))\
\033[0m" 1>&2 ) \
$(texfile) \
$(call Func_GetAllDependenciesOfOneLatexFile, \
$(addprefix $(dir $(texfile)),$(call Func_GetDirectDependenciesOfOneLatexFile,$(texfile))), \
$(addsuffix "\#===",$(2))) \
, \
$(shell echo -e "\033[31m$(strip $(2))\033[0m\033[36m$(texfile) [file not found] \033[0m" 1>&2 ) \
))
# TEX_FILES: name of all dependencies of $(TEX_MasterDocument).tex
TEX_FILES := $(sort $(call Func_GetAllDependenciesOfOneLatexFile,$(TEX_MasterDocument).tex,""))
#XXX := $(info "INFO-TEX_FILES:$(TEX_FILES)")
endif
#========== searching for existence of glossary, index or bibliography ==============
ifdef TEX_MasterDocument
#========== glossary ==========
GLOSSARY_FILE := \
$(if $(strip $(basename $(firstword $(shell $(GREP) -l '^[^%]*\\printglossary' $(TEX_FILES))))), \
$(shell echo -e "\033[32m*** Glossary detected\033[0m" 1>&2 ) \
$(TEX_MasterDocument).gls \
,)
#========== indices ==========
USUALINDEX_FILE := \
$(if $(strip $(basename $(firstword $(shell $(GREP) -l '^[^%]*\\makeindex' $(TEX_FILES))))), \
$(shell echo -e "\033[32m*** Standard index detected: \033[36m$(TEX_MasterDocument).ind\033[0m" 1>&2 ) \
$(TEX_MasterDocument).ind \
,)
NEWINDEX_FILES := \
$(shell $(SED) -n 's/^[^%]*\\newindex{[^}]*}{[^}]*}{\([^}]*\)}.*/$(TEX_MasterDocument).\1/p' $(TEX_FILES))
INDEX_FILES := \
$(strip $(USUALINDEX_FILE) \
$(if $(strip $(NEWINDEX_FILES)), \
$(shell echo -e "\033[32m*** Indices detected: \033[36m$(NEWINDEX_FILES)\033[0m" 1>&2 ) \
$(NEWINDEX_FILES) \
,))
#========== bibliographies ==========
MULTIBIBLIOGRAPHY_FILES := \
$(strip $(shell $(SED) -ne 's/^[^%]*\\newcites{\([^}]*\)}{[^}]*}.*/\1.bbl/gp' $(TEX_FILES)))
USUALBIBIBLIOGRAPHY_FILE := \
$(if \
$(strip $(basename $(firstword $(shell $(GREP) -l '^[^%]*\\bibliography{[^{]*}' $(TEX_FILES))))), \
$(TEX_MasterDocument).bbl, \
)
BIBLIOGRAPHY_FILES := \
$(strip $(MULTIBIBLIOGRAPHY_FILES) $(USUALBIBIBLIOGRAPHY_FILE))
BIBLIOGRAPHY_FILES := \
$(if $(BIBLIOGRAPHY_FILES), \
$(shell echo -e "\033[32m*** Bibliography detected: \033[36m$(BIBLIOGRAPHY_FILES)\033[0m" 1>&2 ) \
$(BIBLIOGRAPHY_FILES) \
,$(shell echo -e "\033[32m*** Bibliography NOT detected\033[0m" 1>&2 ))
endif
#========== searching for DOT, DIDOT and LADOT files ================================
# DIDOT and LADOT files depend on TEX_MasterDocument
ifdef TEX_MasterDocument
DIDOT_FILES := $(shell $(SED) -n '/^[ \t]*%/! s/^.*\\begin{\(di\)\?graphDIDOT}[^{]*{\(.*\)}.*/\2.didot/p' $(TEX_FILES))
LADOT_FILES := $(shell $(SED) -n '/^[ \t]*%/! s/^.*\\begin{\(di\)\?graphLADOT}[^{]*{\(.*\)}.*/\2.ladot/p' $(TEX_FILES))
endif
# WARNING: May be not all ".dot" files are in the current directory,
# and may be they are not used by $(TEX_MasterDocument)
DOT_FILES := $(wildcard *.dot)
#========== searching for GNUPLOT files ================================
# GNUPLOTTEX files depend on TEX_MasterDocument
ifdef TEX_MasterDocument
# Function Func_GenerateNumbersListFrom1ToWordsOf2ndParameter
# Number of parameters: 2
# 1st parameter: list of words ex: "(x x x x x)"
# Output: the list "($(words $(1)) ... 3 2 1)"
Func_GenerateNumbersListFrom1ToWordsOf2ndParameter = \
$(if $(strip $(1)), \
$(words $(1)) \
$(call Func_GenerateNumbersListFrom1ToWordsOf2ndParameter, $(wordlist 2, $(words $(1)), $(1))) \
, \
)
GNUPLOTTEX_FILES := $(addsuffix .gnuplot, \
$(addprefix $(TEX_MasterDocument)-gnuplottex-fig, \
$(call Func_GenerateNumbersListFrom1ToWordsOf2ndParameter, \
$(shell $(SED) -n 's/^[^%]*\\begin{gnuplot}.*/x/p' $(TEX_FILES)))))
endif
# WARNING: May be not all ".gnuplot" file are in the current directory.
# Thus, they might not be used by $(TEX_MasterDocument)
# and all ".gnuplot" files that matche "_region_-gnuplottex-fig[0-9]*.gnuplot" are ignored !
GNUPLOT_FILES := $(filter-out \
$(wildcard $(TEX_MasterDocument)-gnuplottex-fig[0-9]*.gnuplot) \
$(wildcard _region_-gnuplottex-fig[0-9]*.gnuplot), \
$(wildcard *.gnuplot))
#========== Function that runs LaTeX as many times as needed ===================
# Function Func_RerunToGetCrossReferencesRight
# Number of parameters: 1
# $(1) is a list of words of which the cardinality should corresponds to $(MAX_LATEX_RERUN)
# Output: nothing
# Goal: Function that runs LaTeX while $(TEX_MasterDocument).log containts "Rerun to get cross-references right"
NATURAL=1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
decr=$(wordlist 2,$(words $1),$1)
Func_RerunToGetCrossReferencesRight =\
$(strip\
$(if $(strip $1),\
$(shell echo -e "\033[32m*** Latexing \033[36m$*.tex\033[0m" 1>&2 )\
$(shell $(LATEX) $(LATEX_OPTIONS) $(TEX_MasterDocument) 1>&2 )\
$(if $(strip $(shell sed -n '/Rerun to get cross-references right/p' $(TEX_MasterDocument).log)),\
$(call Func_RerunToGetCrossReferencesRight, $(call decr,$1)),\
)\
,\
$(shell echo -e "\033[31m*** Warning: Maximal number of latexing reached (MAX_LATEX_RERUN=$(MAX_LATEX_RERUN)). STOP.\033[36m" 1>&2 )\
))
#========== Function that prints multiply defined labels ===================
# Function Func_PrintMultiplyDefinedLabels
# Number of parameters: O
# Output: nothing
# Goal: Function that reads "LaTeX Warning" from $(TEX_MasterDocument).log
LaTeXMultiplyDefinedLabels =\
$(shell sed '/Warning/{N;s/\n//;}' $(TEX_MasterDocument).log | sed -n "s/LaTeX Warning: Label \`\([^']*\).*/\1/p" | sort | uniq)
Func_PrintMultiplyDefinedLabels =\
$(if\
$(strip $(LaTeXMultiplyDefinedLabels)),\
$(shell echo -e "\033[31m*** LaTeX Warning: Multiply defined labels = {\033[35m $(LaTeXMultiplyDefinedLabels) \033[31m}\033[0m" 1>&2 ),\
)
#========== Function that prints undefined references ===================
# Function Func_PrintUndefinedReferences
# Number of parameters: O
# Output: nothing
# Goal: Function that reads "LaTeX Warning" from $(TEX_MasterDocument).log
LaTeXUndefinedReferences =\
$(shell sed '/Warning/{N;s/\n//;}' $(TEX_MasterDocument).log | sed -n "s/LaTeX Warning: \(Hyper r\|R\)eference \`\([^']*\).*/\2/p" | sort | uniq)
Func_PrintUndefinedReferences =\
$(if\
$(strip $(LaTeXUndefinedReferences)),\
$(shell echo -e "\033[31m*** LaTeX Warning: Undefined references = {\033[35m $(LaTeXUndefinedReferences) \033[31m}\033[0m" 1>&2 ),\
)
#========== Function that prints undefined citations ===================
# Function Func_PrintUndefinedCitations
# Number of parameters: O
# Output: nothing
# Goal: Function that reads "LaTeX Warning" from $(TEX_MasterDocument).log
LaTeXUndefinedCitations =\
$(shell sed '/Warning/{N;s/\n//;}' $(TEX_MasterDocument).log | sed -n "s/LaTeX Warning: Citation \`\([^']*\).*/\1/p" | sort | uniq)
Func_PrintUndefinedCitations =\
$(if\
$(strip $(LaTeXUndefinedCitations)),\
$(shell echo -e "\033[31m*** LaTeX Warning: Undefined citations = {\033[35m $(LaTeXUndefinedCitations) \033[31m}\033[0m" 1>&2 ),\
)
#========== Implicite rules ===================================================
# %.pdf : %.dvi ; @echo -e "\033[32m*** Generating \033[36m$@\033[32m from DVI file\033[0m" && dvipdfmx $<
%.pdf : %.ps ; @echo -e "\033[32m*** Generating \033[36m$@\033[32m from PS file\033[0m" && ps2pdf $(PS2PDF_OPTIONS) $<
%.ps : %.dvi ; @echo -e "\033[32m*** Generating \033[36m$@\033[32m from DVI file\033[0m" && dvips $(DVIPS_OPTIONS) -o $@ $<
%.gls : %.ist ; @echo -e "\033[32m*** Making glossary\033[0m" && $(MAKEINDEX) -t $*.glg -o $@ -s $< $*.glo
%.ind : %.idx ; @echo -e "\033[32m*** Making index: \033[36m$@\033[0m" && $(MAKEINDEX) $(if $(strip $(MAKEINDEX_INDEXSTYLE)), "-s" $(MAKEINDEX_INDEXSTYLE),) $<
%.bbl : %.aux ; @echo -e "\033[32m*** Making bibliography: \033[36m$@\033[0m" && $(BIBTEX) $*
%.ps : %.ladot ; @echo -e "\033[32m*** Generating \033[36m$@\033[32m from LADOT file\033[0m" && $(PERL) ladot.pl $<
%.ps : %.didot ; @echo -e "\033[32m*** Generating \033[36m$@\033[32m from DIDOT file\033[0m" && $(DOT) -Tps -o $@ $<
%.ps : %.dot ; @echo -e "\033[32m*** Generating \033[36m$@\033[32m from DOT file\033[0m" && $(DOT) -Tps -o $@ $<
%.eps : %.gnuplot ; @echo -e "\033[32m*** Generating \033[36m$@\033[32m from GNUPLOT or GNUPLOTTEX file\033[0m" && $(GNUPLOT) $<
#========== Main rule =========================================================
all: $(TEX_MasterDocument).pdf $(TEX_MasterDocument).ps
#========== PDF rule (PS file will be erased by implicite rules) ==============
pdf: $(TEX_MasterDocument).pdf
#========== Create DVI file ===================================================
# DVI file depends on TEX_MasterDocument
ifdef TEX_MasterDocument
$(TEX_MasterDocument).dvi: \
$(TEX_FILES) \
$(DOT_FILES:%.dot=%.ps) $(DIDOT_FILES:%.didot=%.ps) $(LADOT_FILES:%.ladot=%.ps) \
$(GNUPLOT_FILES:%.gnuplot=%.eps) $(GNUPLOTTEX_FILES:%.gnuplot=%.eps) \
$(GLOSSARY_FILE) \
$(INDEX_FILES) \
$(BIBLIOGRAPHY_FILES)
# $(shell $(LATEX) $(LATEX_OPTIONS) $(TEX_MasterDocument) 1>&2 )
# $(shell $(LATEX) $(LATEX_OPTIONS) $(TEX_MasterDocument) 1>&2 )
# $(shell $(LATEX) $(LATEX_OPTIONS) $(TEX_MasterDocument) 1>&2 )
$(call Func_RerunToGetCrossReferencesRight,$(wordlist 1,$(MAX_LATEX_RERUN),$(NATURAL)))
$(shell echo -n -e "\033[32m*** Updating modification time of these files:\033[0m" 1>&2 \
$(foreach file, $(GLOSSARY_FILE) $(INDEX_FILES) $(BIBLIOGRAPHY_FILES) \
$(DIDOT_FILES:%.didot=%.ps) $(LADOT_FILES:%.ladot=%.ps) \
$(GNUPLOTTEX_FILES:%.gnuplot=%.eps), \
&& echo -n -e "\033[36m $(file)\033[0m" 1>&2 \
&& $(TOUCH) $(file)) \
&& echo "" 1>&2 )
$(call Func_PrintMultiplyDefinedLabels)
$(call Func_PrintUndefinedReferences)
$(call Func_PrintUndefinedCitations)
$(DIDOT_FILES) $(LADOT_FILES) $(GNUPLOTTEX_FILES) $(GLOSSARY_FILE:%.gls=%.ist) $(INDEX_FILES:%.ind=%.idx) $(BIBLIOGRAPHY_FILES:%.bbl=%.aux) : $(TEX_FILES)
@echo -e "\033[32m*** Latexing \033[36m$(TEX_MasterDocument).tex\033[32m in order to generate DIDOT, LADOT, GNUPLOTTEX, IST, IDX and AUX files\033[0m"
@$(LATEX) $(LATEX_OPTIONS) $(TEX_MasterDocument) 1>&2
# @echo -n "" \
# $(if $(strip $(DIDOT_FILES) $(LADOT_FILES)), \
# && echo -e "\033[32m*** Touching: updating date of LADOT and DIDOT files\033[0m" \
# && $(TOUCH) $(DIDOT_FILES) $(LADOT_FILES))
endif
#========== clean =============================================================
clean:
@echo -e "\033[32mCleaning\033[0m"
@rm -f *.backup *.ladot *.didot *.bak *.aux *.log *.toc *.lof *.lot *.lol *.out *.blg *.bbl *.idx *~ *.ilg *.ind *.brf *.glg *.glo *.gls *.nav *.snm *.vrb
@rm -f $(LADOT_FILES) $(LADOT_FILES:%.ladot=%.tex) $(LADOT_FILES:%.ladot=%.ps) $(DIDOT_FILES) $(DIDOT_FILES:%.didot=%.tex) $(DIDOT_FILES:%.didot=%.ps) $(GNUPLOTTEX_FILES) $(GNUPLOTTEX_FILES:%.gnuplot=%.tex) $(GNUPLOTTEX_FILES:%.gnuplot=%.eps) sigfileDELETEME.tex $(TEX_MasterDocument).ist $(TEX_MasterDocument).tns $(TEX_FILES:%.tex=%.tex~)
@rm -f $(TEX_MasterDocument).ps
@rm -f $(TEX_MasterDocument).dvi
#========== clean =============================================================
cleanAucTeX: clean
@rm -rf `find | grep "_region_"`
@rm -f `find | grep "prv_$(TEX_MasterDocument).log"`
#========== tgz ===============================================================
tgz: cleanAucTeX
$(if $(wildcard ./$(ARCHIVE_FILENAME)), \
@echo -e "\033[32mMoving older archive ./$(ARCHIVE_FILENAME) to $(TEMPORARY_PATH)$(ARCHIVE_FILENAME)_\033[0m" && \
mv -f ./$(ARCHIVE_FILENAME) $(TEMPORARY_PATH)$(ARCHIVE_FILENAME)_,)
@echo -e "\033[32mArchiving all files in the current directory to ./$(ARCHIVE_FILENAME)\033[0m"
@tar -czf $(TEMPORARY_PATH)$(ARCHIVE_FILENAME) .
@mv $(TEMPORARY_PATH)$(ARCHIVE_FILENAME) ./
#========== count words =======================================================
countwords: $(TEX_MasterDocument).pdf
@echo -e "\033[32mCounting words in $< ... \033[0m"
@echo -e "$(shell ps2ascii "$<" | wc -w) words has been counted from $<"