13 KiB
Title: Utiliser make pour compiler ses documents LaTeX Category: linux Tags: make, Makefile, LaTeX Date: 2023-08-30 12:10 cover: assets/backgrounds/machine_zorba.jpg status: draft
Dans mon précédent article,
nous avons vu comment fonctionne make et son langague utilisé dans les
Makefile
. Comme promis, voici le premier example d'utilisation : la
construction de document PDF à partir de fichiers sources LaTeX, de fichiers
SVG et d'images matricielles.
Nous allons avancer progressivement tout au long de cet article, ainsi nous
commencerons avec un Makefile
des plus basique pour l'améliorer au fur et à
mesure.
Avant de commencer
Vous trouverez des fichiers d'exemples pour chacune des étapes dans cette
archive. Les corrections sont disponibles dans
le répertoire Makefile/
.
Faites attention aux copiés/collés : les tabulations sont transformées en
espace dans les blocs de code de cet article. Or make se sert des tabulations
pour déterminer les actions relatives aux cibles. Lors de l'exécution de make,
vous aurez l'erreur Makefile:23: *** missing separator. Stop.
, il suffira de
revoir votre indentation.
Permier pas, simple mais pas efficace
Notre base de départ est simple, elle permet de compiler un document appelé
document.tex
dans le répertoire courant. Voici le code de Makefile
:
LC = lualatex
LCFLAGS = --interaction=nonstopmode
default: document.pdf
document.pdf: document.tex
$(LC) $(LCFLAGS) $<
.PHONY: clean
clean:
@rm document.aux document.log documents.pdf
Nous utilisons ce qui se fait déjà pour la compilation de programme en C
, à
savoir placer le compitateur et ses paramètres dans des variables. Nous avons
donc $(LC)
pour LaTeX Compiler -- j'utilise LuaLatex -- et $(LCFLAGS)
pour les paramètres.
un répertoire pour les compiler tous
À la premièrer compilation, on s'aperçoit vite que plusieurs fichiers
apparaissent dans notre répertoire (fichiers aux
, log
, toc
etc.) Il
devient alors un peu confus, nous allons faire en sorte de placer tous ces
fichiers (ainsi que le fichier PDF généré) dans un répertoire séparé.
LC = lualatex
LCFLAGS = --interaction=nonstopmode --output-directory $(OUTPUT)
OUTPUT = build
default: $(OUTPUT)/document.pdf
$(OUTPUT)/document.pdf: document.tex
$(LC) $(LCFLAGS) $<
.PHONY: clean
clean:
@rm -rf $(OUTPUT)
Nous définissons la variables OUTPUT
qui contient le répertoire dans lequel
placer tous les fichiers générés par lualatex
. L'options qui va bien est
ajoutée à LCFLAGS
. OUTPUT
est aussi utilisée pour la cible clean
, notre
nettoyage n'en est que plus simple!.
Tout ces fichiers sont maintenant dans le répertoire build/
.
Tous ne s'appellent pas document
Tous nos documents de s'appellent pas document, leurs noms dépendent souvent
du contexte. Rendons donc ce Makefile
plus générique grâce aux variables et
macros
LC = lualatex
LCFLAGS = --interaction=nonstopmode --output-directory $(OUTPUT)
OUTPUT = build
DOCUMENTS = $(addprefix $(OUTPUT)/, $(patsubst %.tex,%.pdf,$(wildcard *.tex)))
default: $(DOCUMENTS)
$(OUTPUT)/%.pdf: %.tex
@mkdir -p $(OUTPUT)
$(LC) $(LCFLAGS) $<
.PHONY: clean
clean:
@rm -rf $(OUTPUT)
notre macro DOCUMENTS
est un imbrication de 3 fonction :
wildcard <motif>
pour récupérer une liste de fichier dans le répersoire courant en fonction d'un motif;patsubst <motif>,<remplacement>,<chaine>
pour substituer une partie de la chaine dans un chemin,%
fait ici office de caractère joker;addprefix <prefix>, <chaine>
pour ajouter un préfixe à chacun des éléments d'une chaine de caracères. Pour make, les éléments d'une chaine de caractère sond délimités par des espaces.
Elle permet d'obtenir le nom d'une cible en fonction des fichiers tex
contenu
dans notre répertoire : document.tex
deviendra alors build/document.pdf
.
$(DOCUMENTS)
est maintenant une dépendance de notre cible default
. Et chacun
de ses élémets appelle la cible $(OUTPUT)/%.pdf: %.tex
qui dépend de %.tex
:
la source LaTeX, %
étant remplacé par le nom donné dans la cible :
build/document.pdf
donnerait document.tex
comme dépendance.
Cette solution nous permet maintenant d'appeler notre source LaTeX comme bon nous semble. Mieux encore nous nouvons avoir plusieurs fichiers à la racine de notre répertoire, il seront tous compilés indépendament. C'est très pratique lorsque vous avez un rapport et la présentation dans un même dépôt par exemple.
Les images matricielles
Il n'est pas rare qu'un document contienne des images au format matriciel : des photos au format JPEG ou des captures d'écran au format PNG par exemple. Il est alors nécessaire de prendre en compte la modification de ces images pour la compilation de nos documents.
LC = lualatex
LCFLAGS = --interaction=nonstopmode --output-directory $(OUTPUT)
OUTPUT = build
IMAGES_DIR = images/bitmap
DOCUMENTS = $(addprefix $(OUTPUT)/, $(patsubst %.tex,%.pdf,$(wildcard *.tex)))
IMAGES = $(wildcard $(IMAGES_DIR)/*.*)
default: $(DOCUMENTS)
$(OUTPUT)/%.pdf: %.tex $(IMAGES)
@mkdir -p $(OUTPUT)
$(LC) $(LCFLAGS) $<
.PHONY: clean
clean:
@rm -rf $(OUTPUT)
Nous avons maintenant une variable IMAGES_DIR
qui nous permet de spécifier le
répertoire dans lequel sont stockées les images -- nous verrons plus tard que
d'autres sous-dossiers d'images apparaîtront. La liste des images est
récupérée grace à la macro IMAGES
qui utilise la fonction wildcard
pour
récupérer tous les fichiers de ce répertoire.
Le résultat de cette macro est donné en dépendance de notre cible de
contruction des documents $(OUTPUT)/%.pdf: %.tex
. Ainsi si une image est
modifiée ou ajoutée, alors la compilation du fichier PDF sera relancée.
Notre technique a cependant deux défauts. D'abord si une image est ajoutée mais n'est pas utilisée le document sera quand même compilé alors que ce n'est pas nécessaire.
Enfin si une image utilisée seulement dans le document B est modifiée, A et C seraient aussi compilés. Ce dernier problème pourrait être résolu, mais au prix d'une complexification de notre processus de compilation.
Les images SVG
Personnellement j'utilise beaucoup Inkscape pour produire diverses images. Mais le format SVG n'est pas utilisable tel quel en LaTeX1. Il est nécesssaire de passer par une étape intermédiaire pour le transformer en PDF.
LC = lualatex
LCFLAGS = --interaction=nonstopmode --output-directory $(OUTPUT)
SC = inkscape
SCFLAGS = --export-type=pdf --export-pdf-version=1.4
OUTPUT = build
IMAGES_DIR = images/bitmap
SVG_DIR = images/svg
SVG_EXPORTED_DIR = images/generated
DOCUMENTS = $(addprefix $(OUTPUT)/, $(patsubst %.tex,%.pdf,$(wildcard *.tex)))
IMAGES = $(wildcard $(IMAGES_DIR)/*.*)
SVG = $(wildcard $(SVG_DIR)/*.svg)
SVG_EXPORTED = $(subst $(SVG_DIR), $(SVG_EXPORTED_DIR), $(patsubst %.svg,%.pdf,$(SVG)))
default: $(DOCUMENTS)
$(OUTPUT)/%.pdf: %.tex $(IMAGES) $(SVG_EXPORTED)
@mkdir -p $(OUTPUT)
$(LC) $(LCFLAGS) $<
$(SVG_EXPORTED_DIR)/%.pdf : $(SVG_DIR)/%.svg
$(SC) $(SCFLAGS) -o $@ $<
.PHONY: clean
clean:
@rm -rf $(OUTPUT
Le principe est ici simple : nous allons utiliser Inkscape pour exporter les fichiers au format PDF. Pourquoi exporter en PDF? C'est un format bien supporté par les moteurs LaTeX. Il permet aussi de conserver au maximum le format vectoriel2.
Quatres variables font leur apparition :
SC
pour SVG compiler, nous utilisons Inkscape;SCFLAGS
qui contient les paramètres de la commandeInkcape
;SVG_DIR
qui contient le chemin vers les fichiers SVG;SVG_EXPORTED_DIR
qui contient le chemin vers les fichiers PDF exportés depuis Inkscape.
Nous avons aussi deux macros
SVG
liste les fichiers SVG, cette macro utilise la fonctionwildcard
interne à make qui permet de récupérer une liste de fichiers en fonction d'un motif;SVG_EXPORTED
transforme la liste de fichiers SVG en liste de fichiers PDF. Deux fonctions imbriquées sont nécessaires :subst
qui permet de remplacer un motif dans des chaines etpatsubst
que nous avons vu précédemment. Cette macro est donnée en dépendance de la cible de compilation des documents.
Ensuite la cible qui nous permet de convertir les fichiers :
$(SVG_EXPORTED_DIR)/%.pdf: $(SVG_DIR)/%.svg
et la commande associée.
Les cibles accessoires
Nous avons vu jusqu'ici les cibles principales, nous allons maintenant ajouter deux cibles qui pourront nous faciliter la vie.
Afficher des informations
Notre Makefile
est maintenant conséquent, il contient quelques macros qu'il
est parfois utiles d'afficher. Cette affichage nous permettra par exemple de
vérifier les images matricielles prises en compte ou encore les fichiers SVG qui
seront exportées.
LC = lualatex
LCFLAGS = --interaction=nonstopmode --output-directory $(OUTPUT)
SC = inkscape
SCFLAGS = --export-type=pdf --export-pdf-version=1.4
OUTPUT = build
IMAGES_DIR = images/bitmap
SVG_DIR = images/svg
SVG_EXPORTED_DIR = images/generated
DOCUMENTS = $(addprefix $(OUTPUT)/, $(patsubst %.tex,%.pdf,$(wildcard *.tex)))
IMAGES = $(wildcard $(IMAGES_DIR)/*.*)
SVG = $(wildcard $(SVG_DIR)/*.svg)
SVG_EXPORTED = $(subst $(SVG_DIR),$(SVG_EXPORTED_DIR),$(patsubst %.svg,%.pdf,$(SVG)))
default: $(DOCUMENTS)
$(OUTPUT)/%.pdf: %.tex $(IMAGES) $(SVG_EXPORTED)
@mkdir -p $(OUTPUT)
$(LC) $(LCFLAGS) $<
$(SVG_EXPORTED_DIR)/%.pdf : $(SVG_DIR)/%.svg
$(SC) $(SCFLAGS) -o $@ $<
.PHONY: clean
clean:
@rm -rf $(OUTPUT)
.PHONY: info
info:
@echo "document.............'$(DOCUMENTS)'"
@echo "bitmap images........'$(IMAGES)'"
@echo "SVG images...........'$(SVG)'"
@echo "exported SVG images..'$(SVG_EXPORTED)'"
Nous n'utilisons pas la commande relatives au messages (info
, warning
et
error
) que nous avons vu dans le précédent article, sinon un message make: Nothing to be done for 'info'.
apparaît après les informations.
Lancer l'application de visualisation des PDF
Lancer la compilation d'un document et l'afficher ensuite dans notre visionneur de document permet souvent de gagner du temps.
LC = lualatex
LCFLAGS = --interaction=nonstopmode --output-directory $(OUTPUT)
SC = inkscape
SCFLAGS = --export-type=pdf --export-pdf-version=1.4
VIEWER = zathura
VIEWER_FLAGS = --fork
OUTPUT = build
IMAGES_DIR = images/bitmap
SVG_DIR = images/svg
SVG_EXPORTED_DIR = images/generated
DOCUMENTS = $(addprefix $(OUTPUT)/, $(patsubst %.tex,%.pdf,$(wildcard *.tex)))
IMAGES = $(wildcard $(IMAGES_DIR)/*.*)
SVG = $(wildcard $(SVG_DIR)/*.svg)
SVG_EXPORTED = $(subst $(SVG_DIR),$(SVG_EXPORTED_DIR),$(patsubst %.svg,%.pdf,$(SVG)))
default: $(DOCUMENTS)
$(OUTPUT)/%.pdf: %.tex $(IMAGES) $(SVG_EXPORTED)
@mkdir -p $(OUTPUT)
$(LC) $(LCFLAGS) $<
$(SVG_EXPORTED_DIR)/%.pdf : $(SVG_DIR)/%.svg
$(SC) $(SCFLAGS) -o $@ $<
.PHONY: clean
clean:
@rm -rf $(OUTPUT)
.PHONY: info
info:
@echo "document.............'$(DOCUMENTS)'"
@echo "bitmap images........'$(IMAGES)'"
@echo "SVG images...........'$(SVG)'"
@echo "exported SVG images..'$(SVG_EXPORTED)'"
.PHONY: view
view: default
@$(VIEWER) $(VIEWER_FLAGS) $(DOCUMENTS)
Deux nouvelles variables font leur apparition :
VIEWER
qui contient le nom du programme utilisé comme visionneur, zathura dans l'exemple.VIEWER_FLAGS
qui contient les options de notre visionneur
Nous avons aussi une nouvelle cible view
, comme elle ne réalise aucune
création de fichier nous ladéclarons comme cible .PHONY
. La seule dépendance
ce cette cible est default
. Ainsi lors de l'appel de view
, notre document
sera recompilé si nécessaire.
En conclusion
Partant d'un Makefile
standard, nous l'avons amélioré au fur et à mesure en
utilisant les fonctionnalités proposé par make. Bien entendu cet exemple est
valable pour les documents simple, il sera alors nécessaire de l'adapter pour
prendre en compte les compilations multi-passes pour la gestion des
bibiographies par exemple.
De mon côté j'ai profité de l'écriture de cet article pour améliorer mon processus de compilation de mes documents LaTeX. Au final, chaque dépôt de code contenant des documents prend la forme suivante :
├── images
│ ├── bitmap
│ │ └── ...
│ ├── generated
│ └── svg
│ └── ...
├── Makefile
├── build
├── README.md
└── document.tex
*[PDF]: Portable Document Format *[SVG]: Scalable Vector Graphics
-
Ce n'est pas tout à fait vrai, il est possible d'utiliser le package LaTeX svg et sa commande
\includesvg
mais les contraintes sont nombreuses. ↩︎ -
Sauf dans quelques cas spécifiques comme l'utilisation de certains filtres, mais les éléments concernés seront convertis en matriciels dans le PDF rendu. ↩︎