Heredoc dans un Makefile?

Est-ce possible et comment?

mise à Jour: j'en ai besoin car je crée un fichier à la fois à partir de données dynamiques et statiques.

de cas d'Utilisation: j'ai un répertoire de test. Chaque fichier C produit un exécutable de test.

SRCS = $(wildcard [a-z]*.c)

je peux ajouter de nouveaux tests au besoin et make trouvera les nouveaux tests, les compilera, les exécutera et les validera. J'ai aussi l'utilisation de git. Je voudrais .gitignorepour inclure les fichiers exécutables.

donc là. Comment créer .gitignore et inclure des données statiques, c'est à dire les fichiers que je veux être ignoré (*.o et depend) et aussi les exécutables dynamiquement?

17
demandé sur nalply 2011-05-03 21:02:45

7 réponses

une autre solution GNU Make.

Vous pouvez le faire en utilisant le define et export commandes comme suit:

define GITIGNOREDS
*.o
depend
endef

SRCS = $(wildcard [a-z]*.c)
EXES = $(SRCS:.c=)


export GITIGNOREDS
.gitignore: $(SRCS)
    echo $(EXES) | sed 's/ /\n/g' > $@
    echo "$$GITIGNOREDS" >> $@

vous devez être prudent de faire des expansions (i.e. $(x)) à l'intérieur du bloc define.

20
répondu Ash Berlin-Taylor 2011-09-11 10:08:34

Oui, vous le pouvez. Comme d'autres le remarque, vous ne devriez probablement pas, mais vous pouvez. réponse D'Ash a une solution impliquant des commandes define, mais c'est quelque chose de comb ersome et cela peut rendre difficile l'extension des variables aux bonnes valeurs. Une autre astuce consiste à utiliser le .ONESHELL: spécial cible.

parfois vous préféreriez que toutes les lignes dans la recette soient passées à une seule invocation de la coquille. Il existe généralement deux situations où cela est utile: tout d'abord, il peut améliorer les performances dans les makefiles où les recettes se composent de nombreuses lignes de commande, en évitant les processus supplémentaires. Deuxièmement, vous pourriez vouloir que newlines soit inclus dans votre commande recipe (par exemple, vous utilisez peut-être un interpréteur très différent comme interpréteur de commandes). Si l' .Oneshell special target apparaît n'importe où dans le makefile alors toutes les lignes de recettes pour chaque cible seront fournies à une seule invocation de l'interpréteur de commandes. Newlines entre les lignes de la recette sera préserver.

quelques mots d'avertissement:

  • cela affectera la façon dont recettes dans votre Makefile.
  • les opérateurs de préfixe de ligne tels que - ou @ supprimer la sortie ou ignorer les erreurs ne fonctionne que sur le première ligne de toutes les recettes et prend effet pour toutes les lignes suivantes.
  • quelques anciennes versions ou GNU Make (comme celle tournant par défaut sur le système ci de Travis, même dans la nouvelle version) containers) peuvent ignorer cette directive conduisant à des bugs et des gotchas étranges. Assurez-vous de connaître vos environnements cibles.

Avec cela de la route, voici à quoi il pourrait ressembler pour générer un fichier markdown:

SHELL = bash
.ONESHELL:
MYVAR = "Some Title"

file.md:
    cat <<- EOF > $@
        $(MYVAR)
        ========

        This stuff will all be written to the target file. Be sure
        to escape dollar-signs and backslashes as Make will be scanning
        this text for variable replacements before bash scans it for its
        own strings.

        Otherwise formatting is just as in any other bash heredoc. Note
        I used the <<- operator which allows for indentation. This markdown
        file will not have whitespace at the start of lines.

        Here is a programmatic way to generate a markdwon list all PDF files
        in the current directory:

        `find -maxdepth 1 -name '*.pdf' -exec echo " + {}" \;`
    EOF

Note un gotcha supplémentaire est que faire sauter des lignes vierges. Si avoir une ligne blanche dans le contenu de votre heredoc est important, vous devez vous assurer d'indenter cette ligne avec le niveau approprié d'espace blanc pour correspondre à l'heredoc ou faire volonté manger et même pas passer à chat!

5
répondu Caleb 2017-05-23 12:03:02

Cela dépend de comment vous. Il est préférable de supposer que ça ne marchera pas. Ecrire un script shell à lancer au lieu d'un document ici dans le makefile.

échoue 1

heredoc:
    cat - <<!
    This is the heredoc.
    !

cela produit:

cat - <<!
This is the heredoc.
make: This: No such file or directory
make: *** [heredoc] Error 1

Chaque ligne est exécutée séparément - oops.

echec 2

heredoc:
    cat - <<! \
    This is the heredoc.\
    !

C'généré:

cat: This: No such file or directory
cat: is: No such file or directory
cat: the: No such file or directory
cat: heredoc.!: No such file or directory
make: *** [heredoc] Error 1

il peut y avoir des méthodes utilisant une version spécifique de make (GNU make, par exemple, peut exécuter toutes les commandes pour une action dans un seul Sous-sol, je crois), mais alors vous devez spécifier vos exigences de portabilité. Pour les réguliers (dire conforme à POSIX) make, supposons ici que les docs ne fonctionnent pas.

4
répondu Jonathan Leffler 2011-05-03 17:08:59

GNU Makefile peut faire des choses comme celles-ci. C'est moche, et je ne dirai pas que tu devrais le faire, mais je le fais dans certaines situations.

.profile = \
\#!/bin/sh.exe\n\
\#\n\
\# A MinGW equivalent for .bash_profile on Linux.  In MinGW/MSYS, the file\n\
\# is actually named .profile, not .bash_profile.\n\
\#\n\
\# Get the aliases and functions\n\
\#\n\
if [ -f $${HOME}/.bashrc ]\n\
then\n\
  . $${HOME}/.bashrc\n\
fi\n\
\n\
export CVS_RSH="ssh"\n  
#
.profile:
        echo -e "$($(@))" | sed -e 's/^[ ]//' >$(@)

make .profile crée un .fichier de profil s'il n'en existe pas.

Cette solution a été utilisée lorsque L'application n'utilisera GNU Makefile que dans un environnement shell POSIX. Le projet n'est pas un projet open source où compatibilité de plate-forme est un problème.

le but était de créer un Makefile qui facilite à la fois installation et utilisation d'un type particulier d'espace de travail. Le Makefile apporte avec lui diverses ressources simples sans exiger des choses comme une autre archive spéciale, etc. C'est, en un sens, une coquille d'archives. Une procédure peut alors dire des choses comme laisser tomber ce Makefile dans le dossier pour y travailler. Configurer votre espace de travail enter make workspace, bla, entrez make blah, etc.

Ce qui peut être délicat est de savoir ce que shell devis. Ce qui précède fait le travail et est proche de l'idée de spécifier un document ici dans le Makefile. Si c'est une bonne idée pour une utilisation générale est une toute autre question.

3
répondu kbulgrien 2016-09-29 22:33:05

si vous utilisez Gnu Make, utilisez un 'define' pour créer une variable texte multi-ligne, et une règle pour l'afficher dans votre fichier. Voir 6.8 définition des Variables multilignes de https://www.gnu.org/software/make/manual/make.html#Appending

Comme ceci:

  define myvar
  # line 1\nline 2\nline 3\n#etc\n
  endef

  myfile.txt:
         /bin/echo -e "$(myvar)) >myfile.txt

Pour créer cette, il permet d'utiliser un éditeur, créez le fichier que vous voulez avoir, ajouter "\n" à la fin de chaque ligne, puis de les rassembler tous dans une seule chaîne. Collez dans votre makefile.

Testé avec GNU Make 3.81 sur linux.

1
répondu Joi Owen 2014-11-10 18:41:28

Comme demandé par Jack Kelly:

SRCS = (wildcard [a-z]*.c)
EXES = $(SRCS:.c=)
GITIGNOREDS = *.o depend

.gitignore: $(SRCS)
        echo $(EXES) | sed 's/ /\n/g' > $@
        echo $(GITIGNOREDS) | sed 's/ /\n/g' > $@

Important est l' GITIGNOREDS ligne. J'aurais préféré un heredoc comme

GITIGNOREDS = <<EOT
*.o
depend
EOT

mais je suis également heureux avec une liste de fichiers, séparés par des espaces, et un script sed pour traduire l'espace en lignes.

Modifier comme proposé par Ryan V. Bissell: utilisez un sous-répertoire test séparé que vous ajoutez à gitignore. Et tout tombe en place. C'est simple.

0
répondu nalply 2016-05-06 10:48:01

il semble qu'il n'y ait aucun moyen de faire un ici-document dans Makefile. Cependant, il y a une solution possible. L'utilisation de l'écho à envoyer de l'ici-données du document:

all:
    echo "some text" | myScript.sh
0
répondu Zack63 2017-05-09 16:19:00