web-dev-qa-db-fra.com

Comment obtenez-vous la liste des cibles dans un fichier makefile?

J'ai utilisé un peu rake (a Ruby make)), et il dispose d'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 permettant de faire cela dans GNU make.

Apparemment, le code est presque là pour ça, à partir de 2007 - http://www.mail-archive.com/[email protected]/msg06434.html .

Quoi qu'il en soit, j'ai fait un petit bidouillage pour extraire les cibles d'un fichier Make, que vous pouvez inclure dans un fichier Make.

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

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

> make list
list:
copy:
run:
plot:
turnin:
169
Brian Burns

Ceci est une tentative pour améliorer la bonne approche de @ nobar comme suit:

  • utilise une commande plus robuste pour extraire les noms de cibles, ce qui évite, espérons-le, tout faux positif (et élimine également les sh -c inutiles)
  • ne cible pas toujours le makefile dans le répertoire current; respecte les makefiles explicitement spécifiés avec -f <file>
  • exclut les cibles cachées - par convention, ce sont des cibles dont le nom ne commence ni par une lettre ni par un chiffre
  • se débrouille avec une cible single
  • préfixe la commande avec @ pour l'empêcher d'être répété avant l'exécution

Curieusement, GNU make n'a pas de fonction permettant de lister uniquement les noms des cibles définies dans un fichier makefile. L'option -p Produit une sortie qui inclut toutes les cibles, mais enterre eux dans beaucoup d'autres informations.

Placez la règle suivante dans un fichier make pour GNU make afin d'implémenter une cible nommée list qui répertorie simplement tous les noms de cibles. dans l'ordre alphabétique - c'est-à-dire: 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 ($$1 !~ "^[#.]") {print $$1}}' | sort | egrep -v -e '^[^[:alnum:]]' -e '^$@$$'

Important : Lorsque vous collez ceci, , assurez-vous que la dernière ligne est en retrait de exactement 1 onglet réel char. (les espaces font pas fonctionnent) .

Notez que tri la liste de cibles résultante est la meilleure option, car pas le tri ne produit pas un ordre utile dans l'ordre d'affichage des cibles. dans le makefile est pas préservé.
De plus, les sous-cibles d’une règle comprenant plusieurs cibles sont invariablement sorties séparément et seront donc, en raison du tri, généralement pas l'un à côté de l'autre; Par exemple, une règle commençant par a z: aura pas aura les cibles a et z listées côte à côte dans la sortie, s'il y a des cibles supplémentaires.

Explication de la règle :

  • . PHONY: list
    • déclare que la liste de cibles est une cible factice, c'est-à-dire un 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 afin d'imprimer et d'analyser la base de données dérivée du fichier makefile:
      • -p Imprime la base de données
      • -Rr Supprime l'inclusion de règles et de variables intégrées
      • -q Ne teste que l'état actuel d'une cible (sans rien refaire), mais cela n'empêche pas l'exécution de commandes de recette dans tous les cas; Par conséquent:
      • -f $(lastword $(MAKEFILE_LIST)) garantit que le même fichier Make est ciblé comme dans l'appel d'origine, qu'il ait été ciblé de manière implicite ou explicite avec -f ....
        Mise en garde : Ceci cassera si votre makefile contient include directives; pour y remédier, définissez la variable THIS_FILE := $(lastword $(MAKEFILE_LIST)) avant toutes les directives include et utilisez -f $(THIS_FILE) à la place.
      • : Est une cible délibérément invalide destinée à assurez-vous qu'aucune commande n'est exécutée; 2>/dev/null Supprime le message d'erreur résultant. Remarque: Ceci repose sur -p Pour imprimer néanmoins la base de données, ce qui est le cas à partir de GNU make 3.82. Malheureusement, GNU make offer no option directe à juste imprimer la base de données, sans aussi exécuter 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=

    • C'est un langage awk qui divise l'entrée en blocs de lignes contiguës non vides.
  • /^# File/,/^# Finished Make data base/
    • Correspond à la plage de lignes de la sortie contenant toutes les cibles (true à partir de GNU make 3.82) - en limitant l'analyse à cette plage, il n'est pas nécessaire de traiter les faux positifs d'autres sections de sortie. .
  • if ($$1 !~ "^[#.]")
    • Ignore sélectivement les blocs:
      • # ... ignore les non-cibles dont les blocs commencent par # Not a target:
      • . ... ignore les cibles spéciales
    • Tous les autres blocs doivent chacun commencer par une ligne contenant uniquement le nom d'une cible explicitement définie, suivie de :
  • egrep -v -e '^[^[:alnum:]]' -e '^$@$$' Supprime les cibles indésirables de la sortie:
    • '^[^[:alnum:]]' ... exclut caché cibles qui, par convention, sont des cibles qui ne commencent ni par une lettre ni par un chiffre.
    • '^$@$$' ... exclut la cible list elle-même

Lancer make list Affiche ensuite toutes les cibles, chacune sur sa propre ligne; vous pouvez diriger vers xargs pour créer une liste séparée par des espaces.

100
mklement0

Sous Bash (au moins), cela peut être fait automatiquement en complétant l'onglet:

make(space)(tab)(tab)
163
nobar

J'ai combiné ces deux réponses: https://stackoverflow.com/a/9524878/86967 et https://stackoverflow.com/a/7390874/86967 et en a fait s'échapper pour que cela puisse être utilisé depuis un fichier makefile.

.PHONY: no_targets__ list
no_targets__:
list:
    sh -c "$(MAKE) -p no_targets__ | awk -F':' '/^[a-zA-Z0-9][^\$$#\/\\t=]*:([^=]|$$)/ {split(\$$1,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
25
nobar

Cela ne fonctionnera évidemment pas dans de nombreux cas, mais si votre Makefile a été créé par CMake, vous pourrez peut-être 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
20
Timmmm

Si vous avez l'achèvement de bash 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 pouvant ê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
13
hek2mgl

Comme mklement0 souligne , une fonctionnalité permettant de lister toutes les cibles Makefile manque dans GNU-make, et sa réponse, ainsi que d'autres, fournit des moyens de le faire.

Cependant, le message d'origine mentionne également rake , dont le commutateur tâches fait quelque chose de légèrement différent de la liste de toutes les tâches du fichier rakefile. Rake ne vous donnera qu'une liste de tâches associées à des descriptions. Tâches sans description ne sera pas répertorié . Cela donne à l'auteur la possibilité de fournir des descriptions d'aide personnalisées et d'omettre l'aide pour certaines cibles.

Si vous souhaitez émuler le comportement de rake, où vous fournissez descriptions pour chaque cible , il existe une technique simple pour ce faire: intégrez des descriptions dans les commentaires de chaque cible que vous souhaitez répertorier.

Vous pouvez soit placer la description à côté de la cible ou, comme je le fais souvent, à côté d'une spécification PHONY 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: \(.*\) # \(.*\)/\1 \2/' | 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 this Gist et ici aussi.

Encore une fois, cela ne résout pas le problème de lister toutes les cibles dans un Makefile. Par exemple, si vous avez un gros Makefile qui a peut-être été généré ou que quelqu'un d'autre a écrit et que vous voulez un moyen rapide de lister ses cibles sans le fouiller, cela ne vous aidera pas.

Toutefois, si vous écrivez un Makefile et que vous souhaitez générer un texte d'aide de manière cohérente et auto-documentée, cette technique peut s'avérer utile.

10
jsp

La réponse de @ nobar montre utilement comment utiliser la complétion de tabulation pour répertorier les cibles d'un fichier makefile .

  • Cela fonctionne très bien pour les plates-formes qui fournissent cette fonctionnalité par défaut (par exemple, Debian, Fedora ).

  • Sur d’autres plateformes (par exemple Ubuntu ), vous devez explicitement charger cette fonctionnalité. , comme suggéré par la réponse de @ hek2mgl :

    • . /etc/bash_completion installe plusieurs fonctions de complétion par des tabulations, y compris celle pour make
    • Vous pouvez également installer seulement la complétion de tabulation pour make:
      • . /usr/share/bash-completion/completions/make
  • Pour les plates-formes n'offrant pas cette fonctionnalité , telles que [~ # ~] osx [~ # ~] , vous pouvez attribuer les commandes suivantes (adaptées de ici ) à mettre en œuvre il:
_complete_make() { COMPREPLY=($(compgen -W "$(make -pRrq : 2>/dev/null | awk -v RS= -F: '/^# File/,/^# Finished Make data base/ {if ($1 !~ "^[#.]") {print $1}}' | 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 complétion par onglets fournie avec les distributions Linux: plus particulièrement, elle cible invariablement le fichier makefile dans le répertoire current, même si la ligne de commande cible un fichier Make différent avec -f <file>.
4
mklement0

Ceci est une modification de la réponse très utile de jsp ( https://stackoverflow.com/a/45843594/814145 ). J'aime l’idée d’obtenir non seulement une liste de cibles mais aussi leur description. Le Makefile de jsp place la description sous forme de commentaire, que je trouvais souvent répété dans la commande echo de description de la cible. Au lieu de cela, j'extrais 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 \1\t\2/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

Remarques:

  • Identique à la réponse de jsp , seules les cibles PHONY peuvent être répertoriées, ce qui peut ou non fonctionner pour votre cas.
  • De plus, il ne répertorie que les cibles PHONY ayant un echo ou : commande en tant que première commande de la recette. : signifie "ne fait rien". Je l'utilise ici pour les cibles pour lesquelles aucun écho n'est nécessaire, tel que all cible ci-dessus.
  • Il existe une astuce supplémentaire pour que la cible help ajoute le ":" dans le fichier make help sortie.
1
Penghe Geng

Celui-ci m'a été utile car je voulais voir les cibles de construction requises (et leurs dépendances) par la cible de création. Je sais que faire des cibles ne peut pas commencer par un "." personnage. Je ne sais pas quelles langues sont supportées, alors je suis allé avec les expressions entre crochets d'egrep.

cat Makefile | egrep "^[[:alnum:][:punct:]]{0,}:[[:space:]]{0,}[[:alnum:][:punct:][:space:]]{0,}$"
1
seanlum

Beaucoup de solutions réalisables ici, mais comme j'aime à dire, "si ça vaut la peine de le faire une fois, ça vaut la peine de le refaire." J'ai recommandé la suggestion d'utilisation (onglet) (onglet), mais comme certains l'ont déjà noté, il se peut que vous n'ayez pas de prise en charge de l'achèvement ou que, si vous avez plusieurs fichiers inclus, vous souhaitiez peut-être un moyen plus facile de savoir où une cible est définie.

Je n'ai pas testé ce qui suit avec des sous-marques ... Je pense que cela ne fonctionnerait pas. Comme on le sait, les marques récursives sont considérées comme nuisibles.

.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 := \)

Ce que j'aime parce que:

  • liste les cibles par fichier d'inclusion.
  • sortie des définitions de cibles dynamiques brutes (remplace les délimiteurs de variable par modulo)
  • afficher chaque cible sur une nouvelle ligne
  • semble plus clair (opinion subjective)

Explication:

  • fichier foreach dans MAKEFILE_LIST
  • affiche le nom du fichier
  • lignes grep contenant un signe deux-points, qui ne sont pas en retrait, pas de commentaires, et ne commencent pas par un point
  • exclure les expressions d'affectation immédiate (: =)
  • couper, trier, mettre en retrait et hacher les dépendances de règles (après deux points)
  • munge délimiteurs de variables pour empêcher l'expansion

Exemple de sortie:

 = 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
ginkgoMZD

Ceci 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, ditch stderr, utilise un fichier rapide et sale grep -A 100000 pour conserver le bas de la sortie. Ensuite, je nettoie la sortie avec un couple de grep -v, et utilise finalement cut pour obtenir ce qui se trouve avant le colon, à savoir les cibles.

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

EDIT: ajouté grep -v Makefile c'est une règle interne

1
Lisael

Encore une autre réponse supplémentaire à ci-dessus.

testé sur MacOSX en utilisant uniquement cat et awk sur terminal

cat Makefile | awk '!/Shell/ && /^[A-z]/ {print $1}' | awk '{print substr($0, 1, length($0)-1)}'

va sortir du fichier make comme ci-dessous:

cible1

cible2

cible3

dans le Makefile, ce devrait être la même instruction, assurez-vous d'échapper aux variables en utilisant la variable $$ plutôt que la variable $.

Explication

cat - crache le contenu

- pipe analyse la sortie vers le prochain awk

awk - exécute une expression rationnelle excluant "Shell" et n'acceptant que les lignes "A-z", puis affiche la première colonne $ 1

awk - encore une fois supprime le dernier caractère ":" de la liste

ceci est une sortie approximative et vous pouvez faire plus de choses géniales avec seulement AWK. Essayez d’éviter sed car il n’est pas aussi cohérent dans les variantes de BSD, c’est-à-dire que certains fonctionnent sur * nix mais échouent sur des BSD tels que MacOSX.

Plus

Vous devriez pouvoir ajouter ceci (avec des modifications) à un fichier pour make , dans le dossier par défaut bash-completion/usr /local/etc/bash-completion.d/ signifiant lorsque vous faites "onglet ) " .. il complétera le cibles basées sur le script one liner.

0
Jimmy M.G. Lim