Comment obtenir la liste des cibles dans un makefile?

j'ai utilisé rake un peu (un programme Ruby make), et il a une option pour obtenir une liste de toutes les cibles disponibles, par exemple

> rake --tasks
rake db:charset      # retrieve the charset for your data...
rake db:collation    # retrieve the collation for your da...
rake db:create       # Creates the databases defined in y...
rake db:drop         # Drops the database for your curren...
...

mais il ne semble pas y avoir d'option pour faire cela dans GNU make.

apparemment le code est presque là pour lui, à partir de 2007 - http://www.mail-archive.com/help-make@gnu.org/msg06434.html .

de toute façon, j'ai fait petit hack pour extraire les cibles d'un makefile, qui vous pouvez inclure dans un makefile.

list:
    @grep '^[^#[:space:]].*:' Makefile

, Il vous donnera une liste des cibles définies. C'est qu'un début - il ne filtre pas les dépendances, par exemple.

> make list
list:
copy:
run:
plot:
turnin:
128
demandé sur Brian Burns 2010-11-18 23:28:46

12 réponses

c'est une tentative d'amélioration sur la grande approche de @nobar comme suit:

  • utilise une commande plus robuste pour extraire les noms de cibles, ce qui, espérons-le, prévient tout faux positif (et supprime également l'inutile sh -c )
  • ne cible pas invariablement le makefile dans le répertoire current ; respecte les makefiles explicitement spécifiés avec -f <file>
  • exclut les cibles cachées - par convention, il s'agit de cibles dont le nom ne commence ni par une lettre ni par un chiffre
  • fait avec un simple cible bidon
  • préfixe la commande @ pour éviter qu'elle ne soit reprise avant l'exécution

curieusement, GNU make n'a pas de fonction pour lister seulement les noms des cibles définies dans un makefile. L'option -p produit une sortie qui inclut toutes les cibles, mais les enfouit dans beaucoup d'autres informations.

placez la règle suivante dans un makefile pour GNU make pour implémenter une cible nommée list qui liste simplement tous les noms de cibles dans l'ordre alphabétique - i.e.: invoquer comme make list :

.PHONY: list
list:
    @$(MAKE) -pRrq -f $(lastword $(MAKEFILE_LIST)) : 2>/dev/null | awk -v RS= -F: '/^# File/,/^# Finished Make data base/ {if ($ !~ "^[#.]") {print $}}' | sort | egrep -v -e '^[^[:alnum:]]' -e '^$@$$' | xargs

Important : sur le collage ce, assurez-vous que la dernière ligne est indentée par exactement 1 onglet char. (les espaces ne sont pas ) .

notez que tri la liste des cibles résultante est la meilleure option, puisque pas tri ne produit pas un ordre utile en ce que l'ordre dans lequel les cibles apparaissent dans le makefile est pas préservé.

En outre, les sous-cibles d'une règle comprenant des cibles multiples sont invariablement la sortie séparément et seront donc, en raison du tri, habituellement pas apparaissent l'un à côté de l'autre; par exemple, une règle commençant par a z: will pas ont des cibles a et z listé à côté de l'autre dans la sortie, s'il y a des cibles supplémentaires.

explication de la règle :

  • . PHONY: list
    • déclare la liste de cibles une cible bidon, i.e., une pas se référant à un fichier , qui devrait donc avoir sa recette invoquée inconditionnellement
  • $(MAKE) -prRn -f $(lastword $(MAKEFILE_LIST)) : 2>/dev/null

    • invoque à nouveau make pour imprimer et analyser la base de données dérivée du makefile:
      • -p imprime la base de données
      • -Rr supprime l'inclusion des règles et variables intégrées
      • -q teste seulement l'état de mise à jour d'une cible (sans rien modifier), mais cela n'empêche pas par lui - même l'exécution des commandes recipe dans tous les cas; donc:
      • -f $(lastword $(MAKEFILE_LIST)) garantit que le même makefile est ciblé que dans l'invocation originale, qu'il ait été ciblé implicitement ou explicitement avec -f ... .

        mise en garde : cela se cassera si votre makefile contient des directives include ; pour y remédier, définissez la variable THIS_FILE := $(lastword $(MAKEFILE_LIST)) avant toute directive include et utilisez -f $(THIS_FILE) à la place.
      • : est un délibérément non valide cible qui vise à de s'assurer qu'aucune commande n'est exécutée ; 2>/dev/null supprime l'erreur qui en résulte message. Note: cela repose sur l'impression -p de la base de données néanmoins, ce qui est le cas à partir de GNU make 3.82. Malheureusement, GNU make n'offre aucune option directe pour juste Imprimer la base de données, sans aussi exécute la tâche par défaut (ou donnée); si vous n'avez pas besoin de cibler un Makefile spécifique, vous pouvez utiliser make -p -f/dev/null , comme recommandé dans la page man .
  • -v RS=

    • il s'agit d'un idiome awk qui divise l'entrée en blocs de lignes contiguës non vides.
  • /^# File/,/^# Finished Make data base/
    • correspond à la gamme de lignes dans la sortie qui contient toutes les cibles (true as of GNU make 3.82) - en limitant l'analyse à cette gamme, il n'est pas nécessaire de traiter les faux positifs provenant d'autres sections de sortie.
  • if ($ !~ "^[#.]")
    • ignore sélectivement les blocs:
      • # ... ignore les non-cibles, dont les blocs commencent par # Not a target:
      • . ... ignore cibles spéciales
    • tous les autres blocs doivent commencer par une ligne contenant seulement le nom d'une cible explicitement définie suivi de :
  • egrep -v -e '^[^[:alnum:]]' -e '^$@$$' supprime les cibles indésirables de la sortie:
    • '^[^[:alnum:]]' ... ne comprend pas " 1519490920 cibles "cachées , qui - par convention-sont des cibles qui commencent ni une lettre ni un chiffre.
    • '^$@$$' ... exclut les list cible
  • xargs
    • convertit effectivement les lignes de sortie en une seule ligne, liste séparée par des espaces; omettez ceci si vous voulez que chaque nom de cible apparaisse sur sa propre ligne.
78
répondu mklement0 2018-05-09 13:25:55

sous Bash( au moins), cela peut être fait automatiquement avec tab completion:

make(space)(tab)(tab)
120
répondu nobar 2014-05-15 00:20:29

j'ai combiné ces deux réponses: https://stackoverflow.com/a/9524878/86967 et https://stackoverflow.com/a/7390874/86967 et j'ai fait quelques échappées pour que cela puisse être utilisé de l'intérieur d'un makefile.

.PHONY: no_targets__ list
no_targets__:
list:
    sh -c "$(MAKE) -p no_targets__ | awk -F':' '/^[a-zA-Z0-9][^$$#\/\t=]*:([^=]|$$)/ {split($,A,/ /);for(i in A)print A[i]}' | grep -v '__$$' | sort"

.

$ make -s list
build
clean
default
distclean
doc
fresh
install
list
makefile ## this is kind of extraneous, but whatever...
run
24
répondu nobar 2017-05-23 11:47:36

cela ne marchera évidemment pas dans de nombreux cas, mais si votre Makefile a été créé par CMake vous pourriez être capable d'exécuter make help .

$ make help
The following are some of the valid targets for this Makefile:
... all (the default if no target is provided)
... clean
... depend
... install
etc
16
répondu Timmmm 2017-01-05 16:39:43

si vous avez bash completion pour make installé, le script d'achèvement définira une fonction _make_target_extract_script . Cette fonction est destinée à créer un script sed qui peut être utilisé pour obtenir les cibles sous forme de liste.

utilisez - le comme ceci:

# Make sure bash completion is enabled
source /etc/bash_completion 

# List targets from Makefile
sed -nrf <(_make_target_extract_script --) Makefile
10
répondu hek2mgl 2014-06-28 08:59:33

comme mklement0 souligne , une fonctionnalité pour lister toutes les cibles Makefile est manquante dans GNU-make, et sa réponse et d'autres fournit des moyens de le faire.

Cependant, le post original mentionne aussi râteau , dont la tâches switch fait quelque chose de légèrement différent que juste la liste toutes les tâches de la rakefile. Rake vous donnera seulement une liste des tâches qui ont des descriptions associées. Tâche sans les descriptions ne sera pas listé . Cela permet à l'auteur à la fois de fournir des descriptions d'aide personnalisées et d'omettre l'aide pour certaines cibles.

si vous voulez émuler le comportement de rake, où vous fournissez des descriptions pour chaque cible , il y a une technique simple pour faire ceci: intégrer des descriptions dans les commentaires pour chaque cible que vous voulez énumérer.

vous pouvez soit mettre la description à côté de la cible ou, comme je le fais souvent, à côté d'une fausse Spécification au-dessus de la cible, comme ceci:

.PHONY: target1 # Target 1 help text
target1: deps
    [... target 1 build commands]

.PHONY: target2 # Target 2 help text
target2:
    [... target 2 build commands]

...                                                                                                         

.PHONY: help # Generate list of targets with descriptions                                                                
help:                                                                                                                    
    @grep '^.PHONY: .* #' Makefile | sed 's/\.PHONY: \(.*\) # \(.*\)/ /' | expand -t20

qui donnera

$ make help
target1             Target 1 help text
target2             Target 2 help text

...
help                Generate list of targets with descriptions

vous pouvez également trouver un exemple de code court dans ce gist et ici aussi.

encore une fois, cela ne résout pas le problème d'énumérer toutes les cibles dans un Makefile. Par exemple, si vous avez un grand Makefile qui a peut-être été généré ou que quelqu'un d'autre a écrit, et vous voulez un moyen rapide pour lister ses cibles sans creuser à travers elle, cela ne va pas aider.

cependant, si vous écrivez un Makefile, et que vous voulez un moyen de générer du texte d'aide d'une manière cohérente et auto-documentée, cette technique peut être utile.

4
répondu jsp 2018-09-22 15:45:24

la réponse de @nobar montre avec aide comment utiliser l'achèvement d'onglet pour lister les cibles d'un makefile .

  • cela fonctionne très bien pour les plateformes qui fournissent cette fonctionnalité par défaut (par exemple, Debian, Fedora ).

  • sur d'autres plateformes (par exemple, Ubuntu ) vous devez charger explicitement cette fonctionnalité, comme l'implique la réponse de @hek2mgl :

    • . /etc/bash_completion installe plusieurs fonctions de tab-completion, y compris celle pour make
    • Sinon, pour installer seulement "1519350920 onglet" achèvement make :
      • . /usr/share/bash-completion/completions/make
  • pour plates-formes qui n'offrent pas cette fonctionnalité du tout , comme OSX , vous pouvez source les commandes suivantes (adapté de ici ) pour la mettre en œuvre:
_complete_make() { COMPREPLY=($(compgen -W "$(make -pRrq : 2>/dev/null | awk -v RS= -F: '/^# File/,/^# Finished Make data base/ {if ( !~ "^[#.]") {print }}' | egrep -v '^[^[:alnum:]]' | sort | xargs)" -- "${COMP_WORDS[$COMP_CWORD]}")); }
complete -F _complete_make make
  • Note: Ce n'est pas aussi sophistiqué que la fonctionnalité de tab-completion qui vient avec les distributions Linux: plus particulièrement, il vise invariablement le makefile dans le current directory , même si la ligne de commande cible un makefile différent avec -f <file> .
3
répondu mklement0 2017-05-23 12:10:54

celui-ci m'a été utile car je voulais voir les cibles de construction requises (et leurs dépendances) par la cible make. Je sais que faire des cibles ne peut pas commencer par un "." caractère. Je ne sais pas quelles langues sont prises en charge, donc j'ai utilisé les expressions entre crochets d'egrep.

cat Makefile | egrep "^[[:alnum:][:punct:]]{0,}:[[:space:]]{0,}[[:alnum:][:punct:][:space:]]{0,}$"
1
répondu seanlum 2017-09-08 05:20:52

C'est loin d'être propre, mais a fait le travail pour moi.

make -p 2&>/dev/null | grep -A 100000 "# Files" | grep -v "^$" | grep -v "^\(\s\|#\|\.\)" | grep -v "Makefile:" | cut -d ":" -f 1

j'utilise make -p qui vide la base de données interne, un fossé stderr, l'utilisation d'un moyen rapide et sale grep -A 100000 pour garder le bas de la sortie. Puis je nettoie la sortie avec un couple de grep -v , et finalement utiliser cut pour obtenir ce qui est avant le côlon, à savoir, les cibles.

c'est assez pour mes scripts d'aide sur la plupart de mes Makefiles.

Modifier: ajouté grep -v Makefile qui est une règle interne

1
répondu Lisael 2017-09-19 15:10:42

beaucoup de solutions réalisables ici, mais comme j'aime le dire, "si cela vaut la peine de le faire une fois, cela vaut la peine de le faire à nouveau." J'ai mis à jour la suggestion d'utiliser (tab)(tab), mais comme certains l'ont noté, il se peut que vous n'ayez pas de support d'achèvement, ou, si vous avez beaucoup de fichiers include, vous pouvez vouloir un moyen plus facile de savoir où une cible est définie.

je n'ai pas testé le ci-dessous avec des sous-marques...Je pense que ça ne marcherait pas. Comme nous le savons, récursive fait considéré comme nocif.

.PHONY: list ls
ls list :
    @# search all include files for targets.
    @# ... excluding special targets, and output dynamic rule definitions unresolved.
    @for inc in $(MAKEFILE_LIST); do \
    echo ' =' $$inc '= '; \
    grep -Eo '^[^\.#[:blank:]]+.*:.*' $$inc | grep -v ':=' | \
    cut -f 1 | sort | sed 's/.*/  &/' | sed -n 's/:.*$$//p' | \
    tr $$ \\ | tr $(open_paren) % | tr $(close_paren) % \
; done

# to get around escaping limitations:
open_paren := \(
close_paren := \)

que J'aime parce que:

  • liste des cibles, et d'inclure le fichier.
  • sortie raw dynamique définitions des cibles (remplace la variable délimiteurs avec modulo)
  • sortie chaque cible sur une nouvelle ligne
  • semble plus clair (opinion subjective)

explication:

  • forech file in the MAKEFILE_LIST
  • sortie le nom du fichier
  • grep lignes contenant un colon, qui ne sont pas en retrait, pas de commentaires, et ne commencez pas avec une période de
  • exclure les expressions d'affectation immédiate (:=)
  • couper, trier, tiret, et hachez-les règles de dépendances (après la virgule)
  • munge délimiteurs variables pour empêcher l'expansion

Sortie De L'Échantillon:

 = Makefile = 
  includes
  ls list
 = util/kiss/snapshots.mk = 
  rotate-db-snapshots
  rotate-file-snapshots
  snap-db
  snap-files
  snapshot
 = util/kiss/main.mk = 
  dirs
  install
   %MK_DIR_PREFIX%env-config.php
   %MK_DIR_PREFIX%../srdb
1
répondu ginkgoMZD 2018-04-19 16:28:59

il s'agit d'une modification à jsp 'S réponse très utile ( https://stackoverflow.com/a/45843594/814145 ). J'aime l'idée d'obtenir non seulement une liste de cibles, mais aussi de leurs descriptions. jsp 's Makefile met la description comme le commentaire,que j'ai trouvé souvent sera répété dans la description echo commande de la cible. Donc à la place, j'extrait la description de la commande echo pour chaque cible.

Exemple De Makefile:

.PHONY: all
all: build
        : "same as 'make build'"

.PHONY: build
build:
        @echo "Build the project"

.PHONY: clean
clean:
        @echo "Clean the project"

.PHONY: help
help:
        @echo -n "Common make targets"
        @echo ":"
        @cat Makefile | sed -n '/^\.PHONY: / h; /\(^\t@*echo\|^\t:\)/ {H; x; /PHONY/ s/.PHONY: \(.*\)\n.*"\(.*\)"/    make \t/p; d; x}'| sort -k2,2 |expand -t 20

sortie de make help :

$ make help
Common make targets:
    make all        same as 'make build'
    make build      Build the project
    make clean      Clean the project
    make help       Common make targets

Notes:

  • Même jsp 's réponse, que de FAUSSES cibles peuvent être mentionnés, qui peut ou peut ne pas fonctionner pour votre cas
  • en outre, il ne Liste que les cibles bidons qui ont une commande echo ou : comme première commande de la recette. : signifie "ne rien faire". Je l'utilise ici pour les cibles pour lesquelles aucun écho n'est nécessaire, comme la cible all ci-dessus.
  • il y a un truc supplémentaire pour la cible help pour ajouter le": "dans la sortie make help .
1
répondu Penghe Geng 2018-08-28 13:53:42

Je ne sais pas pourquoi la réponse précédente était si compliquée:

list:
    cat Makefile | grep "^[A-z]" | awk '{print $}' | sed "s/://g" 
-3
répondu thamster 2014-10-16 19:25:17