Add LaTeX with make draft article (draft)
This commit is contained in:
parent
087f27b45f
commit
1ebcff4783
2 changed files with 387 additions and 0 deletions
BIN
content/articles/2023/make_avec_latex/files/examples.tbz
Normal file
BIN
content/articles/2023/make_avec_latex/files/examples.tbz
Normal file
Binary file not shown.
387
content/articles/2023/make_avec_latex/index.md
Normal file
387
content/articles/2023/make_avec_latex/index.md
Normal file
|
@ -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 <motif>` pour récupérer une liste de fichier dans le répersoire
|
||||||
|
courant en fonction d'un motif;
|
||||||
|
2. `patsubst <motif>,<remplacement>,<chaine>` pour substituer une partie de la
|
||||||
|
chaine 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 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
|
Loading…
Add table
Add a link
Reference in a new issue