Liste des objectifs / cibles dans GNU make qui contiennent des variables dans leur définition

100

J'ai un makefile assez grand qui crée un certain nombre de cibles à la volée en calculant les noms à partir de variables. (par exemple foo $ (VAR): $ (PREREQS)). Est-il possible que gnu make puisse être convaincu de cracher une liste de cibles après avoir étendu ces variables?

J'aimerais pouvoir obtenir les cibles d'un makefile aribitrary. J'essaye d'écrire une fonction de complétion pour mon shell.

BitShifter
la source

Réponses:

79

Pouvez-vous analyser la sortie de make -pn(ie make --print-data-base --dry-run)? Il imprime toutes les variables, règles, règles implicites et quelles commandes seront exécutées avec des détails laborieux.

Jack Kelly
la source
Cette réponse semble beaucoup plus jolie que l'autre.
Matt Joiner
Je suis d'accord que c'est beaucoup plus agréable. Encore plus de travail que ce que j'espérais, mais je vais le marquer comme la réponse car rien d'autre ne semble raisonnable et GNU make n'a pas d'indicateur convenable pour ce que je veux.
BitShifter
6
Sachez que la sortie peut dépendre de la cible que vous spécifiez, si votre Makefile génère des cibles de manière dynamique. La sortie peut également dépendre des valeurs des variables d'environnement ou faire des variables spécifiées avec la commande make.
reinierpost
6
J'aime la brièveté. +1 Ajouté mon propre spin (décrit dans une réponse ci-dessous):make -pn | sed -rn '/^[^# \t\.%].*:[^=]?/p'
Jamie
Suggérer make -pnr. Le -rsupprime les règles implicites.
sjbx
114
make -qp | awk -F':' '/^[a-zA-Z0-9][^$#\/\t=]*:([^=]|$)/ {split($1,A,/ /);for(i in A)print A[i]}'     

Tiré de la finition make arg, qui fonctionne comme un charme.

Todd Hodes
la source
2
J'ai essayé de créer un alias pour cela, en le mettant entre guillemets, mais il en résulte simplement que awk a une erreur de syntaxe sur la première virgule ... des pensées sur la façon d'échapper correctement à cela pour un alias shell (bash)?
drfrogsplat
3
Je pense que cela a quelque chose à voir avec où il est défini. Je pense que s'il est défini comme un alias, son répertoire de travail devient le répertoire où il est défini, et apparemment la partie awk n'aime pas le point dans les répertoires cachés. La définition d'une fonction fonctionne.
Ibrahim
Je l'ai juste mis dans un script shell ~/binplutôt que d'utiliser un alias. Fonctionne très bien; Merci!
mpontillo
3
L'alias alias mkl="make -qp | awk -F':' '/^[a-zA-Z0-9][^\$#\/\t=]*:([^=]|\$)/ {split(\$1,A,/ /);for(i in A)print A[i]}' | sort"qui vous fera gagner 2 minutes de citation de parties :-)
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
1
| sort -usemble plus utile :)
sherpya
14

Je ne suis pas sûr qu'il ne s'agisse que d'une création gnu, mais cela fonctionne bien:

make help

Zdenik
la source
C'est assez pratique, beaucoup plus facile à retenir qu'un alias personnalisé pour l'une des autres méthodes.
Ibrahim
8
Ne fonctionne pas vraiment sur GNU Make 3.81, probablement une cible dans vos makefiles: "make: *** Aucune règle pour rendre la
cible`
8
Cela appelle simplement la cible "help" dans Makefile. S'il existe, il imprimera quelque chose (pas la liste complète des cibles), s'il n'existe pas (situation typique), il renflouera.
pfalcon
2
La bonne nouvelle est que cmake semble générer une cible «d'aide» facile à lire.
Stéphane
C'est génial! :) Go cmake
Jeef
10

Plusieurs intervenants ont suggéré d'utiliser make -pn, qui imprimera la base de données des règles mais n'exécutera rien - plus ou moins. Le problème avec cette approche est qu'elle appelle -ntoujours toutes les marques récursives, et elle fait toujours beaucoup plus de travail que nécessaire, car elle imprime toutes les commandes qu'elle aurait appelées dans une construction régulière. Une solution plus efficace serait de créer un makefile trivial, dummy.mk, avec ce contenu:

__all_targets__: ; #no-op

Invoquez maintenant make as make -p -f Makefile -f dummy.mk __all_targets__. Sur toute construction substantielle, la différence dans la quantité de sortie générée par make est significative. Par exemple:

$ gmake -pn | wc
 138985 2632330 69612711
$ gmake -f Makefile -f /tmp/dummy.mk -pn __all_targets__ | wc
  21673   93437  878985

Le temps d'exécution était également nettement meilleur - 2,063s pour la première version, 0,059s pour la seconde.

Eric Melski
la source
1
Bonne idée, tous ceux qui travaillent avec d'énormes systèmes de construction l'apprécieront. Statistiques pour Android Gingerbread Tree (n'utilise pas de marque récursive, donc les économies ne sont pas énormes, juste bonnes): " time make -pn | wc -l": 1338738 real 0m47.754s." time make -f Makefile -f dummy.mk -pn __all_targets__ | wc -l": 938621 real 0m40.219s.
pfalcon
Bon point. La page de manuel de makesemble suggérer quelque chose de similaire mais convivial, à savoirmake -p -f/dev/null
Davide
@Davide cela ne fonctionnera pas tout à fait: la spécification -f /dev/nullempêchera maked'analyser les makefiles normaux, vous obtiendrez donc une impression uniquement des règles intégrées. C'est pourquoi ma solution précise -f Makefile -f dummy.mk. Mais ce n'est pas suffisant non plus, car avec -n, makeira de l'avant et commencera également à exécuter les règles dans le makefile. Malheureusement, je ne suis pas au courant de modifications pouvant simplifier la solution telle que présentée à l'origine.
Eric Melski
Bien sûr, vous avez raison. Cependant, la page de manuel make a un bogue, car elle indique: "Pour imprimer la base de données sans essayer de refaire un fichier, utilisez make -p -f / dev / null"
Davide
Légère correction à mon commentaire précédent: il faudrait lire "... parce que sans -n ..."
Eric Melski
9

Vérifiez l' achèvement de bash pour make sur GitHub.

js.
la source
Le lien que vous avez fourni est maintenant rompu
Davide
Lien mis à jour vers la version actuelle.
js.
7

Edit: Pour info, le référentiel de debian bash completion git dans une autre réponse intègre désormais une version améliorée de ce script adaptée aux cas d'utilisation de l'achèvement bash.

#!/bin/bash

SCRIPT='
  /^# Make data base/,/^# Files/d             # skip until files section
  /^# Not a target/,+1          d             # following target isnt
  /^\.PHONY:/                   d             # special target
  /^\.SUFFIXES:/                d             # special target
  /^\.DEFAULT:/                 d             # special target
  /^\.PRECIOUS:/                d             # special target
  /^\.INTERMEDIATE:/            d             # special target
  /^\.SECONDARY:/               d             # special target
  /^\.SECONDEXPANSION/          d             # special target
  /^\.DELETE_ON_ERROR:/         d             # special target
  /^\.IGNORE:/                  d             # special target
  /^\.LOW_RESOLUTION_TIME:/     d             # special target
  /^\.SILENT:/                  d             # special target
  /^\.EXPORT_ALL_VARIABLES:/    d             # special target
  /^\.NOTPARALLEL:/             d             # special target
  /^\.ONESHELL:/                d             # special target
  /^\.POSIX:/                   d             # special target
  /^\.NOEXPORT:/                d             # special target
  /^\.MAKE:/                    d             # special target

# The stuff above here describes lines that are not
#  explicit targets or not targets other than special ones
# The stuff below here decides whether an explicit target
#  should be output.

  /^[^#\t:=%]+:([^=]|$)/ {                    # found target block
    h                                         # hold target
    d                                         # delete line
  }
  /^# File is an intermediate prerequisite/ { # nope
    s/^.*$//;x                                # unhold target
    d                                         # delete line
  }
  /^([^#]|$)/ {                               # end of target block
    s/^.*$//;x                                # unhold target
    s/:.*$//p                                 # write current target
    d                                         # hide any bugs
  }
'

make -npq .DEFAULT 2>/dev/null | sed -n -r "$SCRIPT" \
  | sort | uniq

C'est un script beaucoup plus complet que le script d'achèvement debian bash car il fournit des résultats pour les règles générées, ce que la question demande et la réponse la plus votée (script d'achèvement debian bash sur le serveur debian git) ne va pas assez loin.

Ce n'est pas le script original auquel j'ai lié mais il est beaucoup plus simple et est un peu plus rapide.

codeshot
la source
BTW, mods, si cette réponse n'est pas entièrement conforme à toutes les politiques en raison de la nuance de son texte, veuillez commenter avant de supprimer. Je ferai les ajustements légaux nécessaires pour que cette question reçoive une réponse complète et valide.
codehot
En guise de test rapide, j'ai récupéré le code source de gcc et j'ai exécuté ./configure && time ~ / makecomp.sh | wc -l où ~ / makecomp.sh est ma réponse. ... configurer la sortie ... config.status: création de Makefile 3312 real 0m0.401s user 0m0.412s sys 0m0.028s
codehot
Comme autre test, j'ai utilisé le makefile de démonstration à codeshot.blogspot.co.uk/2012/08/ ... qui génère des pseudo-règles pour la construction et la vérification des passes de test unitaire. Cet exemple utilise plus de fonctionnalités de make qu'un projet autoconf multiplateforme classique, donc le script de cette réponse renvoie 14 cibles réelles tandis que l'achèvement bash pour make on ubuntu ne renvoie que deux règles utiles, puis 5 fichiers source.
codehot
4

Il s'agit du code d'alias basé sur la solution Todd Hodes

alias mtargets='make -qp | awk -F":" "/^[a-zA-Z0-9][^$#\/\t=]*:([^=]|$)/ {split(\$1,A,/ /);for(i in A)print A[i]}"'
Nguyễn Minh Vũ
la source
3

Cela donnera une belle sortie:

make -pn | perl -F: -ane 'print "$F[0]\n" if /^\w+\s*:/' | sort
Andor
la source
2

Je suppose que je suis un peu en retard à cette fête, mais si vous cherchez une commande spécifique, vous pouvez essayer

make -qp | grep -v '^# ' | grep -v '^[[:space:]]' | grep --only-matching '^.*:'

Cela fonctionne principalement, même si cela peut encore inclure des éléments non ciblés comme une vpathdirective. Si vous ne dépendez pas makedes règles intégrées de, vous pouvez utiliser make -qpRcomme première commande dans le pipeline.

DGrady
la source
Je l'ai essayé avec freetz (.org). Répertorie un grand nombre de makefiles inclus et ne répertorie pas toutes les cibles.
Robert Siemer
1

Solution rubis:

`make -qp`.split("\n").select{|l| l =~ /^\w/ && l !~ /=/}.map{|l| l.sub(/:.*/,'')}
gdub
la source
1

Je travaille sur Solaris 10 et une coque turbo C. La solution donnée ne fonctionne pas pour mon projet makefile. même après avoir modifié la ligne de commande ci-dessus en syntaxe tcsh. Cependant, j'ai découvert que vous pouvez obtenir une solution simple en utilisant

remake --tasks | grep -v "clean some static output or just grep tabs on start of line" | awk ´{print $1}´

refaire la version:

remake --version

est

GNU Make 3.82+dbg0.8
Built for sparc-sun-sparc2-9

et quelques autres données de version sans importance

Aviv
la source
0

Je suis allé à la recherche de la même question et j'ai proposé cette version:

make -pn | sed -rn '/^[^# \t\.%].*:/p'

Cela supprime toutes les lignes de commentaires, les règles de modèle (lignes commençant par des tabulations), tous les éléments intrinsèques (exemple .c.oet %.o: %.cmodèles).

Jamie
la source
n'a pas lu le commentaire de la réponse acceptée: +1 pour la réponse de Jack ... gist.github.com/777954 - pvandenberk 13 janvier à 14:47
Jamie
0

J'ai trouvé cette solution dans un autre thread:

sh -c "cat Makefile | egrep \"^[[:alnum:][:punct:]]{0,}:[[:space:]]{0,}[[:alnum:][:punct:][:space:]]{0,}$\""

Vous pouvez également l'ajouter à votre Makefile:

list:
    sh -c "cat Makefile | egrep \"^[[:alnum:][:punct:]]{0,}:[[:space:]]{0,}[[:alnum:][:punct:][:space:]]{0,}$\\""

Et exécutez make list.

cette conception
la source
-1

Bien sûr, mais quand voulez-vous qu'il les recrache?

Pour signaler le nom de la cible lorsqu'elle exécute la règle, placez une ligne dans la règle:

foo$(VAR): $(PREREQS)
    @echo now making the foo target: $@
    do_other_stuff...

Pour les recracher tous en même temps, vous pouvez créer une cible PHONY distincte:

.PHONY: show_vars
show_vars:
    @echo foo$(VAR)
    @echo bar$(PARAM) blah$(FLAG)
    # and so on

Et cela peut être une condition préalable de votre cible par défaut:

all: show_vars
    ...

EDIT:
Vous voulez un moyen d'afficher toutes les cibles possibles d'un makefile arbitraire, ce qui, je suppose, signifie de manière non intrusive. Bien...

Pour le faire exactement, et être capable de gérer des makefiles sophistiqués, par exemple impliquant des règles construites par des evalinstructions, il faudrait écrire quelque chose de proche d'un émulateur Make. Pas pratique.

Pour voir les cibles des règles simples, vous pouvez écrire un makefile qui agirait comme un scanner de makefile, fonctionnant sur un makefile arbitraire:

  1. Obtenez tous les noms de cible du makefile en utilisant sed.
  2. `include` le makefile afin de l'utiliser pour développer des variables.
  3. Utilisez `show_%:; echo $$ * `pour imprimer toutes les cibles

Ce serait un travail impressionnant. Êtes-vous sûr que l'objectif en vaut la peine?

Bêta
la source
Pas tout à fait ce que je veux. J'aimerais pouvoir faire cracher une liste de cibles pour un makefile arbitraire que je pourrais avoir. J'essaye d'écrire une fonction de complétion d'argument pour mon shell et je ne peux pas compter sur les makefiles ayant une aide raisonnable.
BitShifter
-4
grep ^[a-z].*\:$ Makefile | sed s,:,,g
Willian Braga
la source
1
-1: Cette approche produit des faux positifs même pour les affectations de variables simples ( :=).
thiton
Vous devriez l'avoir testé en premier. C'est bien d'essayer, cependant.
Konrad Viltersten