catium

Un générateur de site statique - retour accueil

git clone git://bebou.netlib.re/catium
Log | Files | Refs | README |

CONSTRUIRE (26428B)


      1 # Comment vous aussi vous auriez pu construire catium
      2 
      3 Imaginez vouloir construire une site et être doté.e d’un outil qui traduit le
      4 format dans lequel vous préférez écrire en html. Peut-être que vous aimez
      5 écrire en markdown et utilisez pandoc, cmark[^1] ou même le [script initial de
      6 John Gruber](https://daringfireball.net/projects/markdown/). Peut-être que vous
      7 aimez écrire en asciidoc et utilisez asciidoctor.  Peut-être que vous avez
      8 votre propre format avec votre propre traducteur !  Comment, avec l’aide des
      9 outils Unix, créer votre propre générateur de sites statiques ?
     10 
     11 ## Générer une page
     12 
     13 ### Les gabarits html et les métadonnées
     14 
     15 Vous avez un fichier index.md comme ceci :
     16 
     17 	# Super document cool
     18 
     19 	Blablabla
     20 
     21 	  * super liste
     22 	  * trop cool
     23 
     24 et pouvez le convertir en faisant cmark index.md :
     25 
     26 	<h1>Super document cool</h1>
     27 	<p>Blablabla</p>
     28 	<ul>
     29 	<li>super liste</li>
     30 	<li>trop cool</li>
     31 	</ul>
     32 
     33 Premier problème, si vous avez pour ambition d’avoir un site décemment
     34 construit vous remarquerez qu’il manque de l’html. On pourrait vouloir ajouter
     35 la balise `<html>` et `<meta>` pour l’encodage utf-8 par exemple. Ici
     36 habituellement deux solutions s’offrent à vous. Soit vous comptez sur votre
     37 traducteur pour le faire. C’est le cas par exemple de lowdown avec son option
     38 `-s` (pour standalone) mais tous ne le font pas :
     39 
     40 	<<-. lowdown -s
     41 	# titre
     42 
     43 	[lien](katzele.netlib.re)
     44 	.
     45 
     46 qui donne
     47 
     48 	<!DOCTYPE html>
     49 	<html>
     50 	<head>
     51 	<meta charset="utf-8" />
     52 	<meta name="viewport" content="width=device-width,initial-scale=1" />
     53 	<title>Untitled article</title>
     54 	</head>
     55 	<body>
     56 
     57 	<h1 id="titre">titre</h1>
     58 
     59 	<p><a href="katzele.netlib.re">lien</a></p>
     60 	</body>
     61 	</html>
     62 
     63 soit vous utilisez un générateur de site existant. En réalité une troisième
     64 s’offre à vous ! Résoudre ce problème à l’aide d’un outil que vous avez déjà
     65 sous la main, le shell.
     66 
     67 Il existe en shell posix une syntaxe nommée here-document ou heredoc[^2]. Cette
     68 syntaxe permet de créer des sortes de gabarits (ou templates ou layouts) de la
     69 sorte :
     70 
     71 	title="machin"
     72 	<<delim cat
     73 	blabla
     74 	le titre : $title
     75 	blabla
     76 	delim
     77 
     78 donne
     79 
     80 	blabla
     81 	le titre : machin
     82 	blabla
     83 
     84 On notera que les variables shell appelées à l’intérieur sont étendues. Et bien
     85 c’est également le cas des captures de commandes type `$(pwd)` ! On peut donc
     86 imaginer l’utiliser pour insérer notre sortie de traducteur au sein d’un
     87 gabarit html :
     88 
     89 	title="Un article super"
     90 	<<delim cat
     91 	<!DOCTYPE html>
     92 	<html>
     93 	<head>
     94 	<meta charset="utf-8" />
     95 	<meta name="viewport" content="width=device-width,initial-scale=1" />
     96 	<title>$title</title>
     97 	</head>
     98 	<body>
     99 	$(cmark index.md)
    100 	</body>
    101 	</html>
    102 	delim
    103 
    104 qui donne
    105 
    106 	<!DOCTYPE html>
    107 	<html>
    108 	<head>
    109 	<meta charset="utf-8" />
    110 	<meta name="viewport" content="width=device-width,initial-scale=1" />
    111 	<title>Un article super</title>
    112 	</head>
    113 	<body>
    114 	<h1>Super document cool</h1>
    115 	<p>Blablabla</p>
    116 	<ul>
    117 	<li>super liste</li>
    118 	<li>trop cool</li>
    119 	</ul>
    120 	</body>
    121 	</html>
    122 
    123 Hop pas besoin d’outils extérieur.  
    124 Si l’on fait de ce code un script on pourrait l’exécuter pour générer l’html.
    125 Evidemment ce serait limité puisque l’on aurait toujours la sortie du fichier
    126 index.md. On peut donc décider de passer en argument le nom du fichier markdown
    127 et le titre tant qu’à faire. On aurait donc un script du type :
    128 
    129 	title="$1"
    130 	<<delim cat
    131 	[...]
    132 	$(cmark "$2")
    133 	[...]
    134 	delim
    135 
    136 Qu’on appelerait en faisant `./script "Un article super" ./index.md`. Si nos
    137 besoins ne vont pas plus loin nous avons déjà quelque chose d’à peu près
    138 fonctionnel. Pour rendre tout cela simple d’usage et mieux encapsuler
    139 l’information nous allons pousser plus loin.
    140 
    141 Vous remarquerez ici que l’information du titre réside dans votre tête, à
    142 l’écriture de la commande et non pas, comme il est préférable, dans le document
    143 lui-même. L’idéal serait qu’index.md puisse contenir toutes ses informations,
    144 le markdown du corps de l’article mais également les méta-données afférentes.
    145 C’est comme cela que fonctionnent la plupart des générateurs de site avec des
    146 syntaxes propres. On voudrait pouvoir, par exemple, écrire :
    147 
    148 	title: "Un super article"
    149 	----
    150 
    151 	# Super document cool
    152 	[...]
    153 
    154 et que la variable title de notre script prenne pour valeur “Un super article”.
    155 Pour ce faire on pourrait modifier notre script pour parser index.md et en
    156 récupérer l’info que l’on souhaite. Par exemle
    157 
    158 	title=$(grep '^title:' | cut -d':' -f2)
    159 
    160 donnerait la bonne valeur à la variable et
    161 
    162 	$(sed -n '/----/,$ p` "$1" | grep -v '^----' | cmark)
    163 
    164 parserait uniquement la partie markdown en ométtant les méta-données. C’est
    165 pourtant sur une autre solution que nous allons partir, une solution qui nous
    166 permettera plus de flexibilité par la suite et surtout la possibilité de
    167 dynamiquement créer certaines parties du contenu markdown.
    168 
    169 ### le format “à la catium” et la prolifération des scripts shell
    170 
    171 Cette autre solution consiste à faire du document index.md lui même un script. Et oui, dans catium, si c’est pas une makefile, c’est un script shell. Comme le dit le meme :
    172 
    173                                               shsh                                          
    174                                   shsh        shsh                                          
    175                                 shsh                                                        
    176     shshsh        shsh  shsh  shshshshsh  shshsh  shshsh    shsh    shshsh  shsh    shsh    
    177   shsh  shsh    shsh  shshsh    shsh        shsh    shsh    shsh      shshsh  shshsh  shsh  
    178 shsh          shsh      shsh    shsh        shsh    shsh    shsh      shsh    shsh    shsh  
    179 shsh          shsh      shsh    shsh        shsh    shsh    shsh      shsh    shsh    shsh  
    180 shshsh  sh    shshsh  shshsh    shsh  sh    shsh    shsh  shshsh      shsh    shsh    shsh  
    181   shshshsh      shshsh  shshsh  shshshsh    shshsh    shsh  shshsh    shsh    shsh    shshsh
    182                                                                                             
    183 																	  Wait it's all shell scripts ?
    184 																 /
    185 																🚶🔫🚶 - Always has been
    186 
    187 A ce stade on peut renommer index.md en index.sh, ça fera plus sens. L’idée est
    188 dorénavant que notre script devienne une sorte d’interpréteur d’index.sh. On
    189 l’appelera dorénavant “page”. Il devra préparer le nécessaire pour que la
    190 syntaxe de la page index.sh instancie les bonnes variables et génère le
    191 nécessaire pour l’affichage final de l’html.
    192 
    193 Par exemple, on pourrait grâce à un alias, cacher derrière la syntaxe `title: "Un super article"` une fonction qui créé la variable title. Pour cela on peut écrire :
    194 
    195 	alias title:="title"
    196 	title() title="$*"
    197 
    198 Ainsi, dans index.sh :
    199 
    200 	title: "Un super article"
    201 	# devient
    202 	title "Un super article"
    203 	# devient
    204 	title="Un super article"
    205 
    206 index.sh a créé sa propre variable. Ce système est généralisable à n’importe
    207 quelle métadonnée. Si vous testez cette articulation tel quel vous rencontrerez
    208 un problème. En effet, l’alias déclaré dans l’interpréteur page n’est pas
    209 disponible dans le script index.sh si on l’exécute en écrivant `./$1` et en
    210 faisant `./page index.sh` par exemple. C’est parce que en le faisant de cette
    211 manière là, index.sh est exécuté dans un sub-shell, sur un processus différent
    212 ne partageant aucune variable, alias ou fonction en commun avec son parent.
    213 Pour contourner ce problème on peut utiliser le built-in shell .[^3] qui prend en
    214 argument le chemin d’un fichier. Cette fonction va dérouler le code du fichier
    215 dans le script courant et l’exécuter comme s’il y avait été écrit. Le code dans
    216 index.sh aura donc bien accès à ce qui a été déclaré dans l’interpréteur page.
    217 Ainsi `./page index.sh` fera :
    218 
    219 	alias title:="title"
    220 	title() title="$*"
    221 
    222 	. "$1"
    223 
    224 	<<delim cat
    225 	<!DOCTYPE html>
    226 	<html>
    227 	<head>
    228 	<meta charset="utf-8" />
    229 	<meta name="viewport" content="width=device-width,initial-scale=1" />
    230 	<title>$title</title>
    231 	[...]
    232 	delim
    233 
    234 deviendra
    235 
    236 	alias title:="title"
    237 	title() title="$*"
    238 
    239 	title: "Un super article"
    240 
    241 	[...]
    242 
    243 	<<delim cat
    244 	<!DOCTYPE html>
    245 	<html>
    246 	<head>
    247 	<meta charset="utf-8" />
    248 	<meta name="viewport" content="width=device-width,initial-scale=1" />
    249 	<title>$title</title>
    250 	[...]
    251 	delim
    252 
    253 et `title: "Un super article"` se déroulera comme décrit précedemment et ainsi
    254 de suite.
    255 
    256 Super ! On sait comment gérer les métadonnées. En réalité on vient d’inventer
    257 une sorte de [DSL](https://en.wikipedia.org/wiki/DSL) qui ressemble
    258 volontairement beaucoup à du [YAML](https://en.wikipedia.org/wiki/YAML) très
    259 simple.  Cette syntaxe est devenu très commune dans les générateurs de sites
    260 statiques.
    261 
    262 Maintenant qu’on a trouvé notre alternative à `title=$(grep '^title:' | cut
    263 -d':' -f2)` il nous faut trouver celle à `$(sed -n '/----/,$ p “$1” | grep -v
    264 ‘—-’ | cmark)`. Si on ne le fait pas l’exécution d’index.sh aura vite fait de
    265 nous faire remarquer que le premier mot de notre page n’est pas une commande.
    266 
    267 Pour cela utilisons la même logique que pour les métadonnées. Imaginons une
    268 syntaxe qui nous convienne et créons, en amont dans l’interpréteur le
    269 nécessaire pour que cette syntaxe soit comprise et génère le bon contenu. Pour
    270 pouvoir dire “attention, ce qui suit c’est du markdown” on peut réutiliser les
    271 heredoc. Quelque chose de la sorte devrait générer l’html :
    272 
    273 	title: "Un article super"
    274 
    275 	<<delim cmark
    276 	# Ceci est du markdown
    277 
    278 	blabla
    279 	delim
    280 
    281 Sauf qu’il nous faut capturer la sortie de cmark pour pouvoir ensuite
    282 l’afficher au bon endroit dans le heredoc template qui suit dans l’interpréteur
    283 page. On pourrait le mettre dans une variable en l’entourant d’une capture de
    284 commande :
    285 
    286 	corps=$(<<delim cmark
    287 	# Ceci est du markdown
    288 
    289 	blabla
    290 	delim
    291 	)
    292 
    293 et dans le template faire
    294 
    295 	<main>
    296 		$(echo "$corps")
    297 	</main>
    298 
    299 mais vous conviendrez que ça n’est pas très élégant (et peut-être sensible au
    300 quoting hell ?). On va donc opter pour mettre le résultat dans un fichier
    301 temporaire.
    302 
    303 	<<delim cmark > A
    304 	# Ceci est du markdown
    305 
    306 	blabla
    307 	delim
    308 	[..]
    309 	<main>
    310 		$(cat A)
    311 	</main>
    312 
    313 ce qui permet d’adopter une syntaxe plus sympathique à l’aide d’un nouvel alias :
    314 
    315 	section="<<endsection cmark > A"
    316 	[...]
    317 	section
    318 	# Ceci est du markdown
    319 
    320 	blabla
    321 	endsection
    322 
    323 En ajoutant un petit `rm` A à la fin du script page on peut générer notre page
    324 html en faisant `./page index.sh` sans souci. index.sh pourra se lire
    325 convenablement bien et contiendra les infos à son propos en son sein. La
    326 totalité du fichier jusqu’ici est :
    327 
    328 	title: "Un super titre"
    329 
    330 	section
    331 	# Ceci est du markdown
    332 
    333 	blabla
    334 	endsection
    335 
    336 Avec ces éléments nous avons, à mon avis, tous les éléments d’un générateur de
    337 site très simples. Afin d’organiser un peu les choses on pourrait déporter le
    338 heredoc du layout dans un fichier à part, déclarant une fonction `layout`,
    339 exécuté avec le builtin . et dont la fonction serait appelé à la fin. Cela
    340 permet de séparer le layout du reste, le “fond” et la “forme” d’une certaine
    341 manière.
    342 
    343 On a donc un fichier de layout, nommé html et dans le dossier layouts :
    344 
    345 	layout {
    346 	<<@@ cat
    347 	<!DOCTYPE html>
    348 	<html>
    349 		<head>
    350 			<meta charset="utf-8" />
    351 			<meta name="viewport" content="width=device-width,initial-scale=1" />
    352 			<title>$title</title>
    353 		</head>
    354 		<body>
    355 			$(cat A)
    356 		</body>
    357 	</html>
    358 	@@
    359 	}
    360 
    361 Le fichier index.sh comme décrit juste au dessus et le script page :
    362 
    363 	alias title:="title"
    364 	title() title="$*"
    365 
    366 	. "$1"
    367 
    368 	. layout/html
    369 	layout
    370 	rm A
    371 
    372 Si les sources sont dans contents et les fichiers cibles dans public il
    373 suffirait ensuite de créer un script qui appellerait `./page X.sh > X.html` sur
    374 toutes les pages sh nécessaires, avec `X` le nom du fichier pour, construire le
    375 site.
    376 
    377 Il nous reste quelques améliorations à apporter à notre générateur.
    378 
    379 ### L’encapsulation et les sections
    380 
    381 Premièrement, il manque une information dans le fichier index.sh, celle de son
    382 interpréteur. De fait, le fichier est un script ne s’exécutant correctement que
    383 s’il est passé en argument d’un autre[^4]. Il serait bon de renseigner cette
    384 information dans le fichier. Pour ce faire nous allons lui ajouter le shebang :
    385 
    386 	#! page
    387 
    388 Ainsi il est possible de l’exécuter en faisant directement `./index.sh` ce qui en
    389 réalité, du fait du fonctionnement du shebang, fera `./page ./index.sh` ce qui
    390 revient à ce que nous faisions à la main jusque là. Cette façon de faire est,
    391 selon moi, plus élégante, mais permet également de savoir que cette page a été
    392 écrite pour être interprétée par un script nommé page ce qui, à défaut de
    393 l’avoir, est toujours une bonne information à prendre. Ce système peut être
    394 utilisé pour créer des typologies de pages (article, notes etc) qui seraient
    395 générées de manières différentes.
    396 
    397 En réalité dans catium c’est le shebang
    398 
    399 	#! /usr/bin/env ./page
    400 
    401 qui est utilisé pour des raisons obscures de compatibilité avec les *BSD et les
    402 mac[^5].
    403 
    404 Deuxièmement, comment faire si l’on souhaite, sur une même page, écrire deux
    405 blocs de markdown qui doivent aller à deux endroits différents dans le layout.
    406 Ce besoin est très courant dans les pages avec une structure plus complexe, du
    407 type à avoir des div un peu partout, de façon à avoir une présentation moins
    408 linéaire à l’aide de css. Pour répondre à ce besoin que l’on a estimé être
    409 suffisamment important pour être intégré au générateur dans sa version la plus
    410 simple il faut ajouter un peu de complexité dans l’interpréteur page.
    411 
    412 On souhaiterait pouvoir écrire dans index.sh :
    413 
    414 	section: truc
    415 	## Ce markdown va aller dans la section truc du layout
    416 	endsection
    417 
    418 	section: bidule
    419 	## Ce markdown va aller dans la section bidule du layout
    420 	endsection
    421 
    422 et par exemple dans le layout :
    423 
    424 	layout {
    425 	<<@@ cat
    426 	[...]
    427 	<div id="joli-id-pour-du-css">
    428 		$(show truc)
    429 	</div>
    430 	[...]
    431 	<div id="autre-id-pour-du-css">
    432 		$(show bidule)
    433 	</div>
    434 	@@
    435 	}
    436 
    437 On sent intuitivement qu'il faut passer une sorte d’argument à notre alias
    438 section[^6]. Il faudrait pouvoir, sur la base de cet argument, stocker les données
    439 de chacune des sections pour pouvoir les appeler séparément dans le layout.
    440 Pour pouvoir traiter cette donnée comme un argument l’alias ne suffira plus, il
    441 faudra passer par une fonction qu’on pourrait par exemple nommer `save` :
    442 
    443 	alias section:="<<endsection save"
    444 	save() cat >> "$1"
    445 
    446 Ainsi dans index.sh :
    447 
    448 	section: truc
    449 	blabla
    450 	endsection
    451 
    452 	# Devient
    453 
    454 	<<endsection save truc
    455 	blabla
    456 	endsection
    457 
    458 	# Puis
    459 
    460 	<<endsection cat >> truc
    461 	blabla
    462 	endsection
    463 
    464 On peut ainsi ouvrir et fermer autant de sections que l’on veut, y compris
    465 plusieurs fois la même grâce à l’opérateur de redirection `>>` qui concatène
    466 plutôt que ne remplace.  
    467 Il faut dorénavant que la fonction que l’on appelle dans le layout pour
    468 “injecter” les données d’une section à un endroit particulier fasse la
    469 traduction du format vers de l’html. On écrit donc une fonction `show` comme ceci
    470 :
    471 
    472 	show() cmark "$1"
    473 
    474 Cette fonction va donner tout le contenu enregistré dans la section qu’on lui
    475 passe en argument (main, footer, comme vous voulez) à `cmark` et en ressortira de
    476 l’HTML.
    477 
    478 Afin d’éviter que les fichiers ne se marchent sur les pieds si l’on a plusieurs
    479 articles, de polluer l’espace de travail et de potentiellement engorger la
    480 mémoire de votre ordinateur on peut faire en sorte que tout cela se produise
    481 dans un dossier temporaire, sur des fichiers temporaires tous détruits une fois
    482 la génération finie. Pour cela on créé au début de page un dossier temporaire
    483 de travail avec `mktemp`, on dit au shell de le supprimer si le processus reçoit
    484 le signal `EXIT` (une fois qu’il termine quoi) :
    485 
    486 	tmpdir=$(mktemp -d)
    487 	trap "rm -rf $tmpdir" EXIT
    488 
    489 Il ne nous manque plus qu’à adapter `save` et `show` pour prendre en compte ce
    490 nouveau dossier :
    491 
    492 	save() cat >> "$tmpdir/$1"
    493 	show() cmark "$tmpdir/$1"
    494 
    495 Et voilà, à une exception près[^7] vous avez recréé absolument tout Catium dans
    496 sa version non étendue. Bravo !
    497 
    498 ## Automatiser la génération : le makefile
    499 
    500 Il y aurait pleins de manières de partir de cet existant et d’automatiser la
    501 création d’un site de plusieurs fichiers .sh. Un simple script shell pourrait
    502 faire l’affaire. Cependant, pour des raisons pédagogique et, potentiellement de
    503 performances, nous avons opté pour make.
    504 
    505 Make est un programme spécifiquement créé pour gérer la construction de
    506 logiciels. Initialement fait pour compiler des programmes en C à une époque où
    507 les ressources étaient assez rares, make permet à l’utilisateurice de déclarer
    508 les règles de dépendances entre les différents éléments de son logiciel. make
    509 créera avec ces règles un graph de dépendances lui permettant, lorsqu’il est
    510 appelé, de ne recréer que le strict nécessaire ainsi économisant du temps de
    511 calcul. Sur le principe la compilation de logiciels et la construction d’un
    512 site statique ne sont pas différents. On peut donc faire usage de make pour
    513 orchestrer la génération de notre site.
    514 
    515 Tout le nécessaire pour que make puisse fonctionner doit être inscrit dans un
    516 fichier nommé makefile à la racine du projet.
    517 
    518 Catium utilise Gnu Make et quelques syntaxes lui sont très spécifiques. A
    519 l’avenir un effort sera peut-être consenti pour génériciser la syntaxe.
    520 
    521 ### Le but
    522 
    523 Le but est que make fasse automatiquement les appels aux articles et place
    524 leurs sorties dans les bons fichiers de destinations. Par exemple,
    525 
    526 	./contents/index.sh > public/index.html
    527 
    528 Et ainsi pour tous les fichiers .sh qui existent dans l’arborescence à
    529 l’intérieur de contents.
    530 
    531 Dans un second temps on souhaite qu’il copie bêtement tous les autres fichiers
    532 présents dans l’arborescence comme les images :
    533 
    534 	cp contents/super_image.jpg public/super_image.jpg
    535 
    536 Dans notre cas tout simple les fichiers .css et le favicon seront gérés de la
    537 même manière que tous les images n’étant pas des .sh.
    538 
    539 ### Lister nos fichiers sources
    540 
    541 La première étape est de lister toutes les sources de notre site. On souhaite faire des choses différentes selon que le fichier soit un .sh ou pas alors on créé deux variables. Dans sources on met la liste des fichiers sh, dans annexes les autres :
    542 
    543 	sources != find contents -type f      -name '*.sh'
    544 	annexes != find contents -type f -not -name '*.sh'
    545 
    546 La syntaxe `!=` dit à make que ce qui suit est une commande à faire exécuter
    547 par le shell et non pas des fonctions make. On utilise donc ici le find que
    548 vous pourriez utiliser dans votre terminal en temps normal.
    549 
    550 ### Créer les cibles
    551 
    552 Une fois ces deux listes obtenues, on cherche à créer deux nouvelles listes
    553 contenant les chemins des fichiers correspondants à faire servir par le serveur
    554 web. On les appellera fichiers cibles. Par exemple, si l’on a le chemin
    555 “contents/blog/article.sh” dans la variable sources, il faudra créer le chemin
    556 “public/blog/article.html”. Autrement dit on remplace contents par public et
    557 l’extension .sh par l’extension .html. Pour les autres fichiers seul le premier
    558 dossier du chemin est à changer.
    559 
    560 Pour le faire on fait usage des “substitutions references” de GMake dont la
    561 syntaxe est :
    562 
    563 	variablecréée = ${variableinitiale:abc%uvw=efg%xyz}
    564 
    565 Ici, pour toutes les lignes dans la variableinitiale, make cherchera les
    566 caractères `abc` et `uvw` et attribuera tout ce qu’il y a entre à `%`. Il
    567 remplacera ensuite tous les `abc` par `efg` et les `uvw` par `xyz` en
    568 conservant la partie `%`. Ainsi, si l’on reprend notre exemple précédent on
    569 identifie que la partie commune (`%`) est `blog/article` et qu’il faut changer
    570 ce qu’il y a avant et après. On peut donc écrire :
    571 
    572 	pageshtml         = ${sources:contents/%.sh=public/%.html}
    573 	annexescibles     = ${annexfiles:contents/%=public/%}
    574 
    575 Vous voyez que pour les annexes pas besoin de modifier l’extension donc pas
    576 besoin de la partie après le `%`. On a donc dorénavant dans `pageshtml` et
    577 `annexescibles` la liste des chemins des cibles.
    578 
    579 ### Écrire les règles
    580 
    581 C’est maintenant que la partie intéressante de make commence, les règles ! La
    582 syntaxe générale d’une règle est la suivante :
    583 
    584 	cible : liste dépendances ; commandes
    585 
    586 ou
    587 
    588 	cible : liste dépendances
    589 	   commandes
    590 
    591 Pour chacun des chemins de cibles construits précédemment make va chercher une
    592 règle permettant de construire le fichier en question. Pour cela il cherche une
    593 règle ayant pour cible le même chemin. Une fois qu’il a trouvé une règle qui
    594 correspond, il vérifie si la cible existe.
    595 
    596   * Si elle n’existe pas il cherche à construire toutes ses dépendances puis
    597     exécute la commande.
    598   * Si la cible existe mais que certaines de ses dépendances n’existent pas il
    599     va les construire d’abord et passer au point suivant.
    600   * Si la cible existe déjà et que les dépendances existent il vérifie si ses
    601     dépendances ont été modifiées après la dernière modification de la cible.
    602     Si oui alors make suppose que la cible n’est pas à jour (ses sources sont
    603     plus récentes qu’elle même) et make relance la commande.
    604 
    605 A noter que tout cela se fait de façon récursive. Quand make tente de créer une
    606 dépendance il suit les mêmes règles. Si la dépendance n’existe pas il faut donc
    607 qu’il existe une règle pour la créer.
    608 
    609 Pour créer une page html la règle pourrait être la suivante :
    610 
    611 	public/blog/article.html : contents/blog/article.sh layouts/html
    612 		@mkdir -p public/blog
    613 		contents/blog/article.sh > public/blog/article.html
    614 
    615 Vous noterez que la cible dépend de layouts/html de façon à ce qu’elle soit
    616 reconstruite si le layout change. Ajouter un `@` devant une commande permet
    617 de ne pas l'afficher dans la sortie lorsqu'on lancera make.
    618 
    619 Vous pourriez penser “mais on ne va pas écrire les règles pour toutes les pages
    620 non ?” et vous auriez raison. Il est possible d’écrire des règles génériques
    621 pouvant matcher plusieurs chemins de plusieurs cibles. Par exemple, la règle
    622 suivante conviendra pour créer tous les fichiers html :
    623 
    624 	public/%.html : contents/%.sh layouts/html
    625 		@mkdir -p $(shell dirname $@)
    626 		$< > $@
    627 
    628 De la même manière qu’auparavant on généricise les chemins à l’aide de `%`. On
    629 récupère le chemin du dossier du fichier cible avec dirname et on fait usage
    630 des variables automatiques `$<` et `$@` faisant références respectivement à la
    631 première dépendance et la cible. Pour éviter que la commande de création du
    632 dossier de destination s’affiche dans la console lorsque l’on construira le
    633 site on peut la préfixer d’un `@`.
    634 
    635 Pour les autres fichiers, de façon similaire :
    636 
    637 	public/% : contents/%
    638 		@mkdir -p $(shell dirname $@)
    639 		cp $< $@
    640 
    641 Si vous écrivez un makefile de la sorte et tenter de l’exécuter vous vourrez
    642 qu’il ne créera pas tout le site. Make créé les cibles qu’on lui donne en
    643 argument. `make public/index.html` déclenchera la règle correspondant à cette
    644 cible si elle existe. Cela dit sans argument make créé uniquement la cible de
    645 la première règle qu’il rencontre dans le makefile, dans l’ordre du fichier.
    646 Comment, en sachant cela, forcer la création de toutes les cibles dans les
    647 variables pageshtml et annexescibles ?
    648 
    649 En utilisant les propriétés des règles décrites précédemment.
    650 
    651   * Si make n’a pas d’argument, la première règle du fichier est déclenchée.
    652   * Si la cible d’une règle n’existe pas, il va essayer de la créer. On peut
    653     donc en déduire que si la commande de la règle ne créer pas la cible, la
    654     règle sera toujours déclenchée.
    655   * Make cherche d’abord à créer toutes les dépendances d’une cible avant de
    656     créer la cible.
    657 
    658 On en conclu que si l’on écrit une règle qui ;
    659 
    660   * Précède toutes les autres
    661   * A une commande qui ne créé pas de fichier ayant pour chemin la cible
    662     (aucune commande fera très bien l’affaire)
    663   * A pour dépendances toutes les cibles que l’on souhaite construire.
    664 
    665 Alors lorsque l’on lancera make, il cherchera à construire toutes ces cibles à
    666 chaque fois.
    667 
    668 Cette règle est très courante dans les makefiles et est généralement nommée par
    669 convention “all”, mais elle pourrait s’appeler “toto”. Pour lister les
    670 dépendances on peut utiliser les variables déjà existantes :
    671 
    672 	all: ${pageshtml} ${annexescibles}
    673 
    674 En utilisant les mêmes propriétés nous allons écrire une règle qu’il faudra
    675 appeler volontairement en passant sa cible en argument à make, se déclenchera
    676 toujours, et exécutera une commande. Cette règle permettra de nettoyer le
    677 dossier de destination “public” :
    678 
    679 	clean:; rm -r public/*
    680 
    681 Si elle n’est pas première dans le fichier et que la cible clean n’est une
    682 dépendance d’aucune autre règle, elle ne se déclenchera qu’en faisant `make
    683 clean`. Puisque la commande ne créé pas de fichier ayant pour chemin la cible
    684 alors elle se déclenchera toujours. Cela dit, puisqu’elle n’a pas non plus de
    685 dépendances, s’il se trouve qu’un jour un fichier “clean” existe à la racine du
    686 projet, il sera considéré comme toujours à jour. Aucune dépendance ne pourra
    687 être potentiellement plus récente que lui et la règle ne se déclenchera plus.
    688 C’est pour palier ce genre de scénarios qu’il existe une directive `.PHONY` qui
    689 permet de lister ce genre de règles[^8]. Ainsi make saura qu’il faut, pour ces
    690 règles, ignorer la préexistence de la cible.
    691 
    692 Et voilà, vous avez réécrit le makefile de catium tel qu’il existe aujourd’hui !
    693 
    694 [^1]: que l’on utilisera pour les exemples dans ce document
    695 [^2]:  documentée ici https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_07_04
    696 [^3]:  oui le nom de la fonction est un point final `.`.
    697 [^4]: quand bien même la syntaxe a été écrite de façon à ce qu’un·e humain·e puisse le comprendre
    698 [^5]:  Voir: https://www.in-ulm.de/~mascheck/various/shebang/#interpreter-script ou le commit 4bd895
    699 [^6]:  auquel on a ajouté un : pour ressemble aux autres déclarations
    700 [^7]:  la ligne [ "$1" ] || set - que je ne m’explique pas
    701 [^8]:  même s’il est très improbable qu’un fichier all ou clean se faufile dans votre projet, on sait jamais.
    702 
    703