web-dev-qa-db-fra.com

Comment puis-je correctement échapper les données d'un Makefile?

Je génère dynamiquement config.mk avec un script bash qui sera utilisé par un Makefile. Le fichier est construit avec:

cat > config.mk <<CFG
SOMEVAR := $value_from_bash1
ANOTHER := $value_from_bash2
CFG

Comment puis-je m'assurer que le fichier généré contient vraiment le contenu de $value_from_bash*, et pas quelque chose de développé/interprété? J'ai probablement besoin de m'échapper $ à $$ et \ à \\, mais y a-t-il d'autres caractères à échapper? Peut-être y a-t-il une affectation littérale spéciale dont je n'ai pas entendu parler?

Les espaces semblent également être gênants:

$ ls -1
a b
a
$ cat Makefile
f := a b
default_target:
    echo "$(firstword $(wildcard ${f}))"
$ make
a

Si j'utilise f := a\ b ça marche (en utilisant des guillemets comme f := 'a b' n'a pas fonctionné non plus, makefile le traite comme un caractère normal)

37
Lekensteyn

D'accord, il s'est avéré que les Makefiles ont besoin de peu d'échappement pour eux-mêmes, mais les commandes qui sont exécutées par l'interpréteur Shell ont besoin pour être échappées.

Les caractères qui ont une signification spéciale dans Makefile et qui doivent être échappés sont:

  • tranchant (#, commentaire) devient \#
  • dollar ($, début de variable) devient $$

Les sauts de ligne ne peuvent pas être insérés dans une variable, mais pour éviter de casser le reste du Makefile, ajoutez-le avec une barre oblique inverse afin que le saut de ligne soit ignoré.

Dommage qu'une barre oblique inverse elle-même ne puisse pas être échappée (\\ sera toujours \\ et pas \ comme on pouvait s'y attendre). Cela ne permet pas de mettre une barre oblique littérale à la fin d'une chaîne car elle mangera la nouvelle ligne ou le hachage d'un commentaire suivant. Un espace peut être placé à la fin de la ligne, mais il sera également placé dans la variable elle-même.

La recette elle-même est interprétée comme une commande Shell, sans aucun échappement de fantaisie, vous devez donc échapper les données vous-même, imaginez simplement que vous écrivez un script shell et insérez les variables d'autres fichiers. La stratégie ici consisterait à placer les variables entre guillemets simples et à échapper uniquement ' avec '\'' (fermez la chaîne, insérez un littéral ' et commencer une nouvelle chaîne). Exemple: mornin' all devient 'morning'\'' all' ce qui équivaut à "morning' all".

Le problème du premier mot + caractère générique est dû au fait que les noms de fichiers contenant des espaces sont traités comme des noms de fichiers séparés par firstword. De plus, wildcard étend les échappements en utilisant \ donc x\ y correspond à un seul mot, x y et non deux mots.

52
Lekensteyn

Il semble que la réponse complète à cette question se trouve nulle part sur Internet, alors je me suis finalement assis et je l'ai compris pour le cas Windows.

Plus précisément, le "cas Windows" fait référence aux noms de fichiers qui sont valides dans Windows, ce qui signifie qu'ils contiennent pas les caractères \, /, *, ?, ", ^, <, >, | ou des sauts de ligne. Cela signifie également \ et / sont tous deux considérés comme des séparateurs de répertoires valides aux fins de Make.

Un exemple le clarifiera mieux que je ne peux l'expliquer. Fondamentalement, si vous essayez de faire correspondre ce chemin de fichier:

Child\a$b  {'}(a.o#$@,&+=~`),[].c

Ensuite, vous devez écrire ces règles:

all: Child\\a$$b\\\ \\\ {'}(a.o\#$$@,&+=~`),[].o

%.o: %.c
    $(CC) '$(subst ','"'"',$(subst \,,$(subst \\,/,$+)))'

Regardez-le pendant longtemps et cela commencera à donner un sens à distance.

Cela fonctionne dans mon environnement MSYS2, donc je suppose que c'est correct.

5
Mehrdad
  1. Je ne vois pas comment ce makefile peut fonctionner comme vous le dites. Une règle de modèle ne peut pas être la valeur par défaut.
  2. Il vous manque un `$` dans `$ (caractère générique ...)`, donc je pense que vous n'avez pas posté ce que vous testez vraiment.
  3. Vous devriez également échapper aux nouvelles lignes.
2
Beta