Elenca obiettivi / traguardi in GNU make che contengono variabili nella loro definizione


100

Ho un makefile abbastanza grande che crea un numero di obiettivi al volo calcolando i nomi dalle variabili. (es. foo $ (VAR): $ (PREREQS)). C'è un modo in cui gnu make può essere convinto a sputare un elenco di obiettivi dopo aver espanso queste variabili?

Mi piacerebbe essere in grado di ottenere gli obiettivi per un makefile arbitrario. Sto cercando di scrivere una funzione di completamento per la mia shell.

Risposte:


79

Puoi analizzare l'output da make -pn(ie make --print-data-base --dry-run)? Stampa tutte le variabili, le regole, le regole implicite e quali comandi verranno eseguiti nei minimi dettagli.


Questa risposta sembra molto più carina dell'altra.
Matt Joiner

Sono d'accordo che questo sia molto più carino. Ancora più lavoro di quanto speravo, ma la contrassegnerò come la risposta poiché nient'altro sembra essere ragionevole e GNU make non ha una bandiera conveniente per quello che voglio.
BitShifter

6
Tieni presente che l'output può dipendere dal target specificato, se il tuo Makefile genera target dinamicamente. L'output può anche dipendere dai valori delle variabili d'ambiente o rendere le variabili specificate con il comando make.
reinierpost

6
Mi piace la brevità. +1 Aggiunto il mio giro (descritto in una risposta di seguito):make -pn | sed -rn '/^[^# \t\.%].*:[^=]?/p'
Jamie

Suggerisci make -pnr. Il -rrimuove regole implicite.
sjbx

114
make -qp | awk -F':' '/^[a-zA-Z0-9][^$#\/\t=]*:([^=]|$)/ {split($1,A,/ /);for(i in A)print A[i]}'     

Tratto dal completamento make arg, che funziona come un fascino.


2
ho provato a creare un alias per questo, mettendolo tra virgolette, ma risulta solo che awk ha un errore di sintassi sulla prima virgola ... qualche idea su come sfuggire correttamente a questo per un alias shell (bash)?
drfrogsplat

3
Penso che abbia qualcosa a che fare con dove è definito. Penso che se è definito come un alias, la sua directory di lavoro diventa la directory in cui è definita, e apparentemente alla parte awk non piace il punto nelle directory nascoste. La definizione di una funzione funziona.
Ibrahim

L'ho semplicemente inserito in uno script di shell ~/binanziché utilizzare un alias. Funziona alla grande; Grazie!
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"che ti farà risparmiare 2 minuti di citazioni di giochi :-)
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

1
| sort -usembra più utile :)
sherpya

14

Non sono sicuro che questa sia solo una cosa creata da GNU, ma funziona bene:

make help


Questo è abbastanza utile, molto più facile da ricordare di un alias personalizzato per uno degli altri metodi.
Ibrahim

8
Non funziona davvero su GNU Make 3.81, probabilmente un obiettivo nei tuoi makefile: "make: *** Nessuna regola per rendere il bersaglio` help '. Stop "
a1an

8
Questo invoca solo il target "help" nel Makefile. Se esiste, stamperà qualcosa (non l' elenco completo dei bersagli), se non esiste (situazione tipica), salterà fuori.
falco

2
La buona notizia è che cmake sembra generare un obiettivo di "aiuto" di facile lettura.
Stéphane

Questo e spettacolare! :) Go cmake
Jeef

10

Diversi soccorritori hanno suggerito di utilizzare make -pn, che stamperà il database delle regole ma non eseguirà nulla, più o meno. Il problema con questo approccio è che -nrichiama ancora tutte le marche ricorsive e fa ancora molto più lavoro del necessario, perché stampa ogni comando che avrebbe richiamato in una normale build. Una soluzione più efficiente sarebbe creare un banale makefile, dummy.mk, con questi contenuti:

__all_targets__: ; #no-op

Ora invoca make as make -p -f Makefile -f dummy.mk __all_targets__. Su qualsiasi build sostanziale, la differenza nella quantità di output generata da make è significativa. Per esempio:

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

Anche il tempo di esecuzione è stato notevolmente migliore: 2,063 secondi per la prima versione, 0,059 secondi per la seconda.


1
Bella idea, tutti coloro che lavorano con sistemi di build enormi lo apprezzeranno. Statistiche per albero di pan di zenzero Android (non usa make ricorsivo, quindi i risparmi non sono enormi, ma solo buoni): " 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.
falco

Buon punto. La pagina man di makesembra suggerire qualcosa di simile ma amichevole, ovveromake -p -f/dev/null
Davide

@Davide non funzionerà: specificando -f /dev/nullsi impedirà makedi analizzare i normali makefile, quindi si otterrà una stampa solo delle regole integrate. Ecco perché la mia soluzione specifica -f Makefile -f dummy.mk. Ma neanche questo è sufficiente, perché con -n, makeandrà avanti e inizierà effettivamente a eseguire le regole anche nel makefile. Purtroppo non sono a conoscenza di alcuna modifica che possa semplificare la soluzione così come presentata in origine.
Eric Melski

Naturalmente hai ragione. Tuttavia la pagina man di make ha un bug, perché afferma: "Per stampare il database senza provare a rifare alcun file, usa make -p -f / dev / null"
Davide

Leggera correzione al mio commento precedente: dovrebbe leggere "... perché senza -n ..."
Eric Melski


7

Modifica: per tua informazione, il repository git del completamento di bash di debian in un'altra risposta ora incorpora una versione migliorata di questo script su misura per i casi d'uso del completamento di 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

Questo è uno script molto più completo dello script di completamento di debian bash perché fornisce i risultati per le regole generate che è ciò che la domanda richiede e la risposta più votata (script di completamento di debian bash sul server git debian) non va abbastanza lontano.

Questa non è la sceneggiatura originale a cui ho collegato, ma è molto più semplice ed è un tocco più veloce.


A proposito, mod, se questa risposta non è completamente in linea con tutte le politiche a causa delle sfumature del suo testo, si prega di commentare prima di eliminare. Apporterò tutte le modifiche legali necessarie affinché questa domanda ottenga effettivamente una risposta completa e valida.
codeshot

Come rapido test ho preso il codice sorgente di gcc e ho eseguito ./configure && time ~ / makecomp.sh | wc -l dove ~ / makecomp.sh è la mia risposta. ... configura output ... config.status: creazione Makefile 3312 real 0m0.401s utente 0m0.412s sys 0m0.028s
codeshot

Come altro test ho usato il makefile demo su codeshot.blogspot.co.uk/2012/08/… che genera pseudo-regole per la creazione e il controllo dei passaggi di unit-test. Questo esempio utilizza più funzionalità di make rispetto a un tipico progetto autoconf multipiattaforma, quindi lo script in questa risposta restituisce 14 target reali mentre il completamento bash per make su ubuntu restituisce solo due regole utili e quindi 5 file sorgente.
codeshot

4

Questo è il codice per l'alias basato sulla soluzione di Todd Hodes

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

3

Questo darà un bel risultato:

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


2

Immagino di essere un po 'in ritardo a questa festa, ma se stai cercando un comando specifico potresti provare

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

Questo per lo più funziona, anche se potrebbe ancora includere alcune cose non target come una vpathdirettiva. Se non dipendi dalle makeregole integrate di, puoi utilizzarlo make -qpRcome primo comando nella pipeline.


L'ho provato con freetz (.org). Elenca molti makefile inclusi e non elenca tutti i target.
Robert Siemer

1

Soluzione rubino:

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

1

Sto lavorando su Solaris 10 e un guscio turbo C. La soluzione fornita non funziona per il mio progetto makefile. anche dopo aver modificato la riga di comando sopra alla sintassi tcsh. Tuttavia, ho scoperto che puoi ottenere una soluzione facile usando

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

versione remake:

remake --version

è

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

e alcuni altri dati di versione non importanti


0

Ho cercato la stessa domanda e ho pensato a questo giro:

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

Questo rimuove tutte le righe di commento, le regole del modello (righe che iniziano con le tabulazioni), tutti gli elementi intrinseci (esempio .c.oe %.o: %.cmodelli).


non ha letto il commento dalla risposta accettata: +1 per la risposta di Jack ... gist.github.com/777954 - pvandenberk 13 gennaio alle 14:47
Jamie

0

Ho trovato questa soluzione in un altro thread:

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

Puoi anche aggiungerlo al tuo Makefile:

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

Ed esegui make list.


-1

Certo, ma quando vuoi che li sputi?

Per riportare il nome del target quando esegue la regola, inserisci una riga nella regola:

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

Per sputarli tutti in una volta, potresti creare un obiettivo PHONY separato:

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

E questo può essere considerato un prerequisito del tuo target predefinito:

all: show_vars
    ...

MODIFICARE:
vuoi un modo per mostrare tutti i possibili obiettivi di un makefile arbitrario, che suppongo significhi non intrusivo. Bene...

Per farlo esattamente ed essere in grado di gestire makefile sofisticati, ad esempio che coinvolgono regole costruite da evalistruzioni, dovresti scrivere qualcosa di simile a un emulatore Make. Poco pratico.

Per vedere gli obiettivi delle regole semplici, potresti scrivere un makefile che funga da scanner di makefile, operando su un makefile arbitrario:

  1. Ottieni tutti i nomi di destinazione dal makefile usando sed.
  2. `include` il makefile per usarlo per espandere le variabili.
  3. Usa `show_%:; echo $$ * `per stampare tutti i target

Questo sarebbe un lavoro impressionante. Sei sicuro che l'obiettivo valga lo sforzo?


Non proprio quello che voglio. Mi piacerebbe essere in grado di far sputare un elenco di obiettivi per un makefile arbitrario che potrei avere. Sto cercando di scrivere una funzione di completamento degli argomenti per la mia shell e non posso fare affidamento sul fatto che i makefile abbiano un aiuto ragionevole.
BitShifter

-4
grep ^[a-z].*\:$ Makefile | sed s,:,,g

1
-1: questo approccio produce falsi positivi anche per semplici assegnazioni di variabili ( :=).
thiton

Avresti dovuto testarlo prima. È bene che tu ci provi, però.
Konrad Viltersten
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.