443 lines
14 KiB
Markdown
443 lines
14 KiB
Markdown
Title: Utiliser make pour compiler ses documents LaTeX
|
|
Category: linux
|
|
Tags: make, Makefile, LaTeX
|
|
Date: 2023-09-05 8:30
|
|
cover: assets/backgrounds/machine_zorba.jpg
|
|
status: published
|
|
|
|
Dans mon [précédent article]({filename}../fonctionnement_makefile/index.md),
|
|
nous avons parlé du fonctionnement de *make* et des `Makefile`. Comme promis,
|
|
voici le premier exemple d'utilisation : la construction de documents *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.tar.xz). Les corrections sont disponibles dans
|
|
le répertoire `Makefile/`.
|
|
|
|
Faites attention aux copiés/collés : les tabulations sont transformées en
|
|
espaces 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 compilateur et ses paramètres dans des variables. Nous avons
|
|
donc `$(LC)` pour *LaTeX Compiler* -- j'utilise LuaLatex -- et `$(LCFLAGS)`
|
|
pour les paramètres.
|
|
|
|
La cible `default` sera lancée automatiquement si aucune autre est spécifiée
|
|
dans la ligne de commande. Ainsi la compilation se lance avec un simple `make`.
|
|
|
|
## 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.) comme
|
|
ceci:
|
|
|
|
```shell
|
|
$ ls -l
|
|
-rw-r--r-- 1 user group 186 Aug 31 13:14 document.aux
|
|
-rw-r--r-- 1 user group 19650 Aug 31 13:14 document.log
|
|
-rw-r--r-- 1 user group 41023 Aug 31 13:14 document.pdf
|
|
-rw-r--r-- 1 user group 1203 Aug 29 23:27 document.tex
|
|
-rw-r--r-- 1 user group 191 Aug 29 23:27 Makefile
|
|
```
|
|
|
|
Notre répertoire 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
|
|
sous-répertoire.
|
|
|
|
```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 variable `OUTPUT` qui contient le répertoire dans lequel
|
|
placer tous les fichiers générés par `lualatex`. L'option qui va bien est
|
|
ajoutée à `LCFLAGS`. `OUTPUT` est aussi utilisée pour la cible `clean`, notre
|
|
nettoyage n'en est que plus simple!
|
|
|
|
Tous ces fichiers sont maintenant dans le répertoire `build/`:
|
|
|
|
```shell
|
|
$ tree
|
|
.
|
|
├── build
|
|
│ ├── document.aux
|
|
│ ├── document.log
|
|
│ └── document.pdf
|
|
├── document.tex
|
|
└── Makefile
|
|
|
|
2 directories, 5 files
|
|
```
|
|
|
|
## Mais où est document?
|
|
|
|
Tous nos documents ne 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 une imbrication de 3 fonctions :
|
|
|
|
1. `wildcard <motif>` pour récupérer une liste de fichiers dans le répertoire
|
|
courant en fonction d'un motif;
|
|
2. `patsubst <motif>,<remplacement>,<chaine>` pour substituer une partie de la
|
|
chaîne dans un chemin, `%` fait ici office de caractère joker;
|
|
3. `addprefix <prefix>, <chaine>` pour ajouter un préfixe à chacun des éléments
|
|
d'une chaîne de caractères. N'oubliez pas, pour *make*, les éléments d'une
|
|
chaîne de caractères sont délimités par des espaces.
|
|
|
|
Elle permet d'obtenir le nom d'une cible en fonction des fichiers `tex` contenus
|
|
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 appelleront la cible `$(OUTPUT)/%.pdf: %.tex` qui se charge de
|
|
contruite un fichier PDF en fonction de sa source LaTeX.
|
|
|
|
Cette solution nous permet d'appeler notre source LaTeX comme bon nous semble.
|
|
Mieux encore nous pouvons **avoir plusieurs fichiers** à la racine de notre
|
|
répertoire, ils seront tous compilés indépendamment. 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 matricielles** : des
|
|
photos JPEG ou des captures d'écran 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 grâce à 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 permettant la
|
|
construction 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 nous ajoutons une image sans l'utiliser dans notre document et
|
|
nous relançons la compilation alors notre document sera tout de même
|
|
recompilé sans que se soit nécessaire. *make* ne fait pas d'analyse sytaxique,
|
|
il n'est pas **capable d'analyser le fichier source pour determiner s'il doit
|
|
être compilé ou non**.
|
|
|
|
Ensuite prenons l'exemple d'un dossier contenant trois fichiers LaTeX *A*, *B*,
|
|
et *C*. Si une image utilisée **seulement** dans le document *B* est modifiée,
|
|
*A* et *C* **seront tout de même recompilés**.
|
|
|
|
Dans l'exemple de code fourni (dans le répertoire `4_images`), vous trouverex un
|
|
script (`change_image.sh`) qui se charge de changer l'image du document
|
|
`presentation.tex`. Vous pouvez tester la compilation avant et après la
|
|
modification du `Makefile` et observer les actions effectuées.
|
|
|
|
Ce scrits se lance sans paramètres :
|
|
|
|
```shell
|
|
$ ./change_image.sh
|
|
Flip images ...
|
|
Done!
|
|
```
|
|
|
|
## Les images SVG
|
|
|
|
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 et 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 simple : nous utilisons *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 `Inkscape`;
|
|
* `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*.
|
|
|
|
Et deux deux macros :
|
|
|
|
* `SVG` liste les fichiers SVG via la fonction `wildcard`;
|
|
* `SVG_EXPORTED` transforme la liste contenu dans`SVG` en liste de fichiers
|
|
PDF. Deux fonctions imbriquées sont nécessaires :`subst` qui permet de
|
|
remplacer un motif dans des chaînes 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`.
|
|
|
|
[^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, ajoutons maintenant 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 utile d'afficher**. Cet affichage nous permettra par exemple de
|
|
vérifier les images matricielles prises en compte ou encore les fichiers SVG qui
|
|
seront exportés.
|
|
|
|
```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 les commandes relatives aux 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.
|
|
|
|
Voici le résultat de cette cible:
|
|
|
|
```shell
|
|
$ make info
|
|
document.............'build/presentation.pdf'
|
|
bitmap images........'images/bitmap/ferret.jpg'
|
|
SVG images...........'images/svg/souris.svg'
|
|
exported SVG images..'images/generated/souris.pdf'
|
|
```
|
|
|
|
### Lancer l'application de visualisation des PDF
|
|
|
|
Lancer la compilation d'un document et l'afficher ensuite dans notre visionneur
|
|
de documents permet souvent de gagner du temps. Partons du principe que nous
|
|
utilisons [Zathura][l_zathura] comme visionneur de documents.
|
|
|
|
```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
|
|
* `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 fichiers nous la déclarons comme cible `.PHONY`. La seule dépendance
|
|
de 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ées par *make*. Bien entendu cet exemple est
|
|
valable pour les documents simples, il sera alors nécessaire de l'adapter pour
|
|
prendre en compte les **compilations multi-passes** pour la gestion des
|
|
bibliographies par exemple.
|
|
|
|
De mon côté, j'utilise énormément LaTeX pour les courriers officiels, les rendu
|
|
des différents TD pour l'Université, mes présentations. J'ai profité de
|
|
l'écriture de cet article pour améliorer mon processus de compilation de tous
|
|
mes documents. 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
|
|
*[JPEG]:Join Photographic Experts Group
|
|
*[PNG]: Portable Network Graphic
|