URL: https://linuxfr.org/users/jul/journaux/bake-scripter-en-bash-a-la-makefile Title: bake : scripter en bash à la « makefile » Authors: Jul Date: 2025年11月14日T10:23:20+01:00 License: CC By-SA Tags: bash et make Score: 12 # Avant propos ## make Make est un bon outil et je me suis inspiré de la lecture de ce [manuel](https://makefiletutorial.com/) pour concevoir l'exemple. ## Pourquoi bash ? Depuis que je code en entreprise et à la maison, sous linux/BSD autant que windows j'ai toujours eu accès à bash et aux GNU coreutils. Quand vous installez [git for windows](https://gitforwindows.org/) vous installez plus que bash, vous installez, les Core Utils (dont *mount* et *chroot*), vous avez un tty (celui de mingw qui est un peu buggué), une arborescence de système linux, *ssh*, *sftp*, *perl*, **make** (oui), *tk/tcl* ... ET **VIM** \o/ Tout ce qu'il faut pour bootstrapper un environnement à la linux surtout si vous ajoutez dans la soupe un gestionnaire de paquet que ce soit [scoop](https://scoop.sh/) ou (je n'ai pas essayé) [pkgsrc](https://www.pkgsrc.org/#index3h2) Bash est devenu ipso facto grâce à l'installation de git sur windows la stack de dév. pour un devops la plus portable. Le truc qui rappelle l'informatique des années Commodore 64, où partout où l'on arrive on trouve une invite pour coder standardisée. # Bake le design Quand j'ai regardé le tuto de *make* j'ai vu le pattern qui me plaisait: une série de description : - état actuel, qui produit un artefact - une liste de dépendance pour arriver à l'état - un corps de « code » qui permet de faire la transition à l'état suivant. par contre, chose qui me déplaît les artefacts de dépendances ne peuvent pas être genre des ports tcp ouverts (ex: si port 53 pas ouvert alors démarrer DNS). J'ai décidé de faire une projection de make en bash avec une pile d'appel que l'on manipule explicitement. Voici le code dont le cœur qui nous intéresse est à la [ligne 53](https://gist.github.com/jul/91c423269d9af84565416991a10cb31c#file-bake-sh-L53) ```bash #!/usr/bin/env bash set -e declare -a action; RD='\e[31m' GB='\e[33m' BL='\e[34m' RZ='\e[0m' DEBUG=${DEBUG:-1} ONE_VAR=${ONE_VAR:-template.dot} echo -e "${GB}REPRODUCIBLE BUILD WITH" echo -e "-----------------------$RZ" echo for var in DEBUG ONE_VAR; do printf "%s='%s' " "$var" "$( eval eval echo \\$\$var)" done echo -n 0ドル $@ echo echo d() { [ -z "$DEBUG" ] || echo -e "DEBUG:$(date +"%H:%M:%S"):$BL $* $RZ" } [ -d out ] || mkdir out push() { local stack declare -a stack stack=( "$@" ) for ((i=${#@}; i--; i)); do action=( "${stack[$i]}" "${action[@]}" ); done } pop() { declare -n v=1ドル v=${action[0]} action=("${action[@]:1}") } set +x dispatch_action() { while [[ ${#action} -gt 0 ]]; do pop act d "DISPATCHING $act" case $act in all) [ -f out/this ] || push "before" ; dispatch_action touch out/that d all bells and whistles push "clean" ; dispatch_action ;; before) [ -f out/init ] || push init; dispatch_action d after init before all and clean touch out/this ;; init) d init : begin all touch out/init ;; clean) d clean everything set +e d out/* rm out/* set -e ;; *) echo unrecognized "$act"; d "${action[@]}" break ;; esac done } if [ ! -z 1ドル ]; then action=( "$@" ); else push "all" fi dispatch_action d fin ``` Le cœur de faire son makefile à la bash consiste à peupler le `switch case` des états qu'on veut, en prélude de faire ses tests de prérequis et pousser/appeler la boucle de dispatch si nécessaire, puis exécuter son code. L’exemple ci dessus donne les sorties suivantes : ``` $ ./make.bash all REPRODUCIBLE BUILD WITH ----------------------- DEBUG='1' ONE_VAR='template.dot' ./make.bash all DEBUG:10:08:50: DISPATCHING all DEBUG:10:08:50: DISPATCHING before DEBUG:10:08:50: DISPATCHING init DEBUG:10:08:50: init : begin all DEBUG:10:08:50: after init before all and clean DEBUG:10:08:50: all bells and whistles DEBUG:10:08:50: DISPATCHING clean DEBUG:10:08:50: clean everything DEBUG:10:08:50: out/init out/that out/this DEBUG:10:08:50: fin ``` Ce qui est ce qu'on attend d'un make all Maintenant faisons un `make before` puis un `make all` ``` $ ./make.bash all REPRODUCIBLE BUILD WITH ----------------------- DEBUG='1' ONE_VAR='template.dot' ./make.bash all DEBUG:10:10:20: DISPATCHING all DEBUG:10:10:20: all bells and whistles DEBUG:10:10:20: DISPATCHING clean DEBUG:10:10:20: clean everything DEBUG:10:10:20: out/init out/that out/this DEBUG:10:10:20: fin ``` on a bien la propriété des makefiles de ne pas refaire une étape si la dépendance est ouverte # Limites et avantages de l'approche ## les wildcards Si un truc vous manque de make et que c'est les jokers (wildcard) alors cet outil n'est pas fait pour vous ; je ne vois absolument pas comment coder facilement cette fonctionnalité. ## pousser des actions futures En make on ne peut que déclarer les dépendances ici, on peut (voir all) pousser des actions futures. Je suis sûr que c'est une mauvaise idée, mais c'est faisable. ## les makes récursifs Je ne me suis pas penché sur le problème, mais je crois bien que cette fonctionnalité ne m'est pas accessible :) # Conclusion En résumé, bash n'est pas qu'un shell, c'est aussi un environnement de programmation. La Gnu/Basherie ultime que déteste la secte concurrent de la mienne qu'est le POSIX-shismes ce sont les tableaux. Et pourtant avoir des tableaux (dont associatifs) permet de faire des chouettes trucs en bash (voir les primitives `push` et `pop`). La programmation ce n'est pas que sortir un résultat avec un langage bien branlé, c'est aussi s'en sortir avec un langage bringuebalant.

AltStyle によって変換されたページ (->オリジナル) /