A Katzele on écrit souvent du shell. On (je du moins) aime bien dire que ce que l’on écrit est POSIX ou presque et que ça fonctionne donc sur tous les dérivés d’Unix1. De fait, une majorité d’entre nous utilise des systèmes Debian ou basés sur Debian (Ubuntu) et nous testons rarement nos créations sur autre chose. Même si l’on essaye d’énoncer clairement les dépendances d’un projet, la portabilité vers d’autres systèmes ne prend qu’assez peu de notre temps pour quatre raisons :
- La faible demande d’adaptation vers d’autres distributions
- Le manque de temps
- Le manque de matériel (nous n’avons pas facilement accès à un mac)
- La croyance que puisque l’on fait du shell simple alors ce sera portable de toute façon
En profitant d’une rare exception à la première raison, je propose de tester les deux dernières raisons en portant Catium sur MacOs version 10.x et supérieure ainsi qu’OpenBSD 7.4.
Merci à mon frère qui m’a prêté son mac pour faire des tests.
La demande
Timothée Goguely, designer et dev web basé à Strasbourg, souhaite tester Catium pour la création d’un site pour l’un de ses clients soucieux de faire très simple. Timothée utilise un mac, le client utilise un mac, il est donc opportun de tester Catium sur un mac.
Par la suite j’ai profité de la gentillesse de Victor Hoffner pour tester la version modifiée sur OpenBSD.
Les tests
Construire le projet (make)
Première chose à faire, cloner le projet. Mac vient avec git (en tout cas je n’ai pas eu à l’installer sur les deux macs que j’ai utilisé), il suffit donc de faire :
git clone git://katzele.netlib.re/catium
En théorie, si tout fonctionne bien il suffit ensuite d’installer lowdown, de
créer le dossier root dans lequel on génère le site et de lancer make
. Pas
de souci à l’installation de lowdown via le gestionnaire de paquet
communautaire homebrew. Pas de souci pour créer le dossier mais en lançant
make
on constate que seules les copies du favicon et de la feuille de style css
se font. Pas d’exécution de l’unique page de démo du projet. Il se trouve que
le make fourni par défaut par Apple sur les Mac (du moins ceux que j’ai testé)
est GNU Make version 3.82. Elle ne supporte manifestement pas la syntaxe
suivante, présente dans notre makefile :
pages = ${sources:src/%.md=root/%.html}
Or cette syntaxe “traduit” le chemin du fichier source vers le chemin du
fichier de destination. Si cette ligne ne fonctionne pas, le makefile ne
cherchera jamais à créer le fichier de destination et n’appellera jamais la
règle correspondante. Pour y remédier il faut installer GNU Make via brew en
faisant brew install make
. Brew package une version plus récente de GNU Make
(4.1.1) qui supporte cette syntaxe. Après l’installation, brew nous informe
que l’on peut appeler cette version de GNU Make en lançant la commande gmake
mais qu’une opération sur le PATH
peut rendre cette version là disponible en
tapant make
.
GNU “make” has been installed as “gmake”. If you need to use it as “make”, you can add a “gnubin” directory to your PATH from your bashrc like:
PATH="$HOMEBREW_PREFIX/opt/make/libexec/gnubin:$PATH"
J’en conviens, cette solution est quelque peu confuse puisqu’elle a pour résultat d’avoir deux version différentes de GNU Make installées sur son ordi, l’une appelée make et l’autre gmake mais ça fait l’affaire.
Lançons maintenant gmake
. On constate que la génération de la page html
essaye bien d’avoir lieu mais qu’un message d’erreur (parmi d’autres) indique
que -D
n’est pas une option pour la commande install. Il se trouve que
d’après le standard POSIX3, install n’est pas POSIX. Cela ne veut pas dire
qu’il ne faut jamais l’utiliser ni qu’il ne se trouvera sur aucun *nix mais que
l’on en a pas la garantie et surtout que l’on ne peut pas prédire son
comportement et ses options. La preuve, la version disponible sur MacOs ne
comporte pas l’option -D
qui existe pourtant dans l’install des GNU core
utils. Ne trouvant pas d’équivalent commun aux deux versions j’ai préféré le
remplacer par :
mkdir -p $(dirname $@)
$< > $@
Qui créé d’abord le dossier de destination à l’aide d’mkdir et dirname qui sont tous les deux POSIX puis exécute le script en redirigeant la sortie vers le fichier de destination.
Si vous exécutiez gmake
vous verriez une nouvelle erreur informant qu’il
manque une quelque chose à mkdir pour bien fonctionner. En effet la capture
de commande $(dirname $@)
ne fonctionnera pas tel quel dans le makefile. Il
faut faire appel à la fonction shell
4 pour exécuter dirname
avant que le
makefile n’exécute le mkdir
. Cela donne donc
mkdir -p $(shell dirname $@)
$< > $@
Avec cette modification nous avons un makefile parfaitement fonctionnel sur MacOs à condition d’avoir mis jour make.
Exécuter les scripts
Si vous suivez l’article vous aurez remarqué qu’il y a d’autres erreurs que nous avons jusque là ignoré. Le “préprocesseur” des pages et le fichier common associé, les fichiers page et common ont été écrit pour Dash, le shell POSIX de Debian par défaut. Il est disponible sur d’autres systèmes.
Les shebang utilisés sur ces deux scripts sont #! /bin/sh
. Sous Debian
/bin/sh
est un lien symbolique vers dash
mais ce n’est pas le cas
sous tous les systèmes. En l’occurence sur MacOs /bin/sh
est un lien
symbolique vers le shell de login par défaut du système, c’est à dire zsh.
Il fallait donc être plus explicite. J’ai initialement changé les shebang en
#! /bin/dash
, ce qui fonctionne sous MacOs, mais pour des raisons de
meilleure portabilité je l’ai ensuite changé en #! /usr/bin/env dash
au
cas-où dash
soit installé ailleurs qu’à /bin/dash
5.
Ce changement corrige une partie des problèmes, lorsque bash n’interprètent pas les scripts comme dash mais certaines erreurs subsistent du type :
src/index.md: ligne 2 : fg: pas de contrôle de tâche
src/index.md: ligne 3 : fg: pas de contrôle de tâche
src/index.md: ligne 4 : fg: pas de contrôle de tâche
src/index.md: ligne 5 : fg: pas de contrôle de tâche
src/index.md: ligne 9: Cette : commande introuvable
src/index.md: ligne 13: bin : commande introuvable
Ces erreurs pointent vers le fait que les alias présents dans common ne sont
pas instanciés correctement. Si l’on vérifie quel processus se lance lorsque
l’on exécute “à la main” la page en faisant src/index.md
, on constate que le
script est lancé avec le shell par défaut du système (bash src/index.md
par
exemple). Or, le shebang de src/index.md
est #! page
. Ce que l’on souhaite
c’est qu’en exécutant une page, elle soit passée en argument au script page
qui lui même sera exécutée par dash et fera tourner le code dans common.
Pour une raison ou pour une autre le shebang est ignoré, page et common non
exécutés, le nécessaire n’est pas créé et l’on obtient des erreurs.
Avec Timothée nous avons cherché un moment pourquoi nous constations ce
comportement. Il se trouve que MacOs et *BSD ne supportent pas l’utilisation
d’un script contenant lui même un shebang678. Le shebang doit pointer
sur un binaire. Un contournement est d’utiliser /usr/bin/env
et de lui donner
en argument notre interpréteur perso9 :
#! /usr/bin/env ./page
Je n’aime pas trop cette solution que je trouve bien moins sympathique pour
l’utilisateurice que d’écrire #! page
ou #! page
mais c’est le prix
de la portabilité. Cela n’empêche évidemment pas d’utiliser la forme courte
sur les systèmes Linux.
Avec cette dernière modification on devrait pouvoir faire
gmake
et constater la bonne génération de root.index.html
.
Conclusion
J’avais tort en disant “On fait du shell POSIX simple, ça fonctionne partout !”. Je n’aurais pas eu tort si j’avais dit “On fait du shell à peu près POSIX et simple, ça devrait pas être très compliqué à faire fonctionner ailleurs”.
J’en retire qu’il faut être encore plus clair sur les dépendances. Par exemple ne pas dire “make” mais “GNU Make testé en version >4.3”, ne pas dire “interpréteur shell POSIX” mais “Dash”. Cela dit, nous avons testé et Catium fonctionne avec zsh et ksh, pas bash.
La totalité de ce travail, y compris la rédaction de cet article, aura pris quelque part entre 6h et 8h, ce que je trouve raisonnable. La quantité de code modifié est petite, l’esprit du projet n’a pas changé. Je pense que l’on a fait la démonstration qu’à défaut d’avoir été portable directement en faisant des choses simples et en essayant de garder, ne serait-ce qu’un peu, la portabilité en tête on se facilite la tâche pour le jour où il faudra réellement le faire.
-
Linux, *BSD, MacOs notamment ↩
-
Vieille de 21 ans ! ↩
-
https://pubs.opengroup.org/onlinepubs/9699919799/idx/utilities.html ↩
-
https://www.gnu.org/software/make/manual/html_node/Shell-Function.html ↩
-
Im me semble qu’env peut se retrouver ailleurs que /usr/bin/env mais bon. Je pense pas que l’on puisse faire beaucoup mieux. ↩
-
https://stackoverflow.com/questions/9988125/shebang-pointing-to-script-also-having-shebang-is-effectively-ignored ↩
-
https://www.in-ulm.de/~mascheck/various/shebang/#interpreter-script ↩