diff --git a/content/articles/2023/make_avec_latex/files/examples.tbz b/content/articles/2023/make_avec_latex/files/examples.tbz new file mode 100644 index 0000000..6eeb755 Binary files /dev/null and b/content/articles/2023/make_avec_latex/files/examples.tbz differ diff --git a/content/articles/2023/make_avec_latex/index.md b/content/articles/2023/make_avec_latex/index.md new file mode 100644 index 0000000..187f826 --- /dev/null +++ b/content/articles/2023/make_avec_latex/index.md @@ -0,0 +1,387 @@ +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]({filename}../fonctionnement_makefile/index.md), +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]({attach}./files/examples.tbz). 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`: + +```make +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é. + +```make +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 + +```make +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 : + + 1. `wildcard ` pour récupérer une liste de fichier dans le répersoire + courant en fonction d'un motif; + 2. `patsubst ,,` pour substituer une partie de la + chaine dans un chemin, `%` fait ici office de caractère joker; + 3. `addprefix , ` 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. + +```make +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 LaTeX[^n_svgtex]. Il est +nécesssaire de passer par une étape intermédiaire pour le transformer en PDF. + +```make +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 +vectoriel[^n_svgpdf]. + +Quatres variables font leur apparition : + + * `SC` pour *SVG compiler*, nous utilisons *Inkscape*; + * `SCFLAGS` qui contient les paramètres de la commande `Inkcape`; + * `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 fonction `wildcard` + 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 et `patsubst` 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. + +[^n_svgtex]: 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. + +[^n_svgpdf]: 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. + +## 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. + +```make +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. + +```make +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][l_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**. + +[l_zathura]:https://pwmt.org/projects/zathura/ + +## 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