Listen Sie Ziele in GNU auf, die Variablen in ihrer Definition enthalten


99

Ich habe ein ziemlich großes Makefile, das eine Reihe von Zielen im laufenden Betrieb erstellt, indem Namen aus Variablen berechnet werden. (zB foo $ (VAR): $ (PREREQS)). Gibt es eine Möglichkeit, wie gnu make davon überzeugt werden kann, eine Liste von Zielen auszuspucken, nachdem diese Variablen erweitert wurden?

Ich möchte in der Lage sein, die Ziele für ein beliebiges Makefile zu erhalten. Ich versuche, eine Vervollständigungsfunktion für meine Shell zu schreiben.

Antworten:


78

Können Sie die Ausgabe von make -pn(dh make --print-data-base --dry-run) analysieren ? Es druckt alle Variablen, Regeln, impliziten Regeln und welche Befehle mühsam ausgeführt werden.


Diese Antwort sieht viel besser aus als die andere.
Matt Joiner

Ich stimme zu, dass dies viel schöner ist. Noch mehr Arbeit als ich gehofft hatte, aber ich werde es als Antwort markieren, da nichts anderes vernünftig erscheint und GNU make keine passende Flagge für das hat, was ich will.
BitShifter

6
Beachten Sie, dass die Ausgabe von dem von Ihnen angegebenen Ziel abhängen kann, wenn Ihr Makefile Ziele dynamisch generiert. Die Ausgabe kann auch von den Werten von Umgebungsvariablen oder make-Variablen abhängen, die mit dem Befehl make angegeben wurden.
Reinierpost

6
Ich mag die Kürze. +1 Mein eigener Spin wurde hinzugefügt (beschrieben in einer Antwort unten):make -pn | sed -rn '/^[^# \t\.%].*:[^=]?/p'
Jamie

Schlagen Sie vor make -pnr. Das -rentfernt implizite Regeln.
sjbx

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

Entnommen aus der Vervollständigung von make arg, die wie ein Zauber wirkt.


2
Ich habe versucht, einen Alias ​​dafür zu erstellen, indem ich ihn in doppelte Anführungszeichen gesetzt habe, aber es führt nur dazu, dass awk beim ersten Komma einen Syntaxfehler hat.
Drfrogsplat

3
Ich denke, es hat etwas damit zu tun, wo es definiert ist. Ich denke, wenn es als Alias ​​definiert ist, wird sein Arbeitsverzeichnis zu dem Verzeichnis, in dem es definiert ist, und anscheinend mag der awk-Teil den Punkt in versteckten Verzeichnissen nicht. Das Definieren einer Funktion funktioniert.
Ibrahim

Ich habe es einfach in ein Shell-Skript eingefügt, ~/binanstatt einen Alias ​​zu verwenden. Funktioniert super; Vielen Dank!
mpontillo

3
Der Alias, alias mkl="make -qp | awk -F':' '/^[a-zA-Z0-9][^\$#\/\t=]*:([^=]|\$)/ {split(\$1,A,/ /);for(i in A)print A[i]}' | sort"der Ihnen 2 Minuten des Zitierens von Spielen
erspart

1
| sort -usieht nützlicher aus :)
sherpya

14

Ich bin mir nicht sicher, ob dies nur eine Gnu-Sache ist, aber das funktioniert gut:

make help


Dies ist ziemlich praktisch und viel einfacher zu merken als ein benutzerdefinierter Alias ​​für eine der anderen Methoden.
Ibrahim


8
Dies ruft nur das "Hilfe" -Ziel in Makefile auf. Wenn es existiert, wird etwas gedruckt (nicht die vollständige Liste der Ziele), wenn es nicht existiert (typische Situation), wird es gerettet.
Pfalcon

2
Eine gute Nachricht ist, dass cmake ein schönes, leicht zu lesendes "Hilfe" -Ziel generiert.
Stéphane

Das ist fantastisch! :) Go cmake
Jeef

10

Mehrere Antwortende haben vorgeschlagen make -pn, die Datenbank mit Regeln zu drucken, aber nichts auszuführen - mehr oder weniger. Das Problem bei diesem Ansatz ist, dass -nimmer noch alle rekursiven Marken aufgerufen werden und immer noch viel mehr Arbeit als nötig ausgeführt wird, da jeder Befehl ausgedruckt wird, den er in einem regulären Build aufgerufen hätte. Eine effizientere Lösung wäre, ein triviales Makefile, dummy.mk, mit folgenden Inhalten zu erstellen:

__all_targets__: ; #no-op

Rufen Sie nun make as auf make -p -f Makefile -f dummy.mk __all_targets__. Bei jedem wesentlichen Build ist der Unterschied in der Menge der von make erzeugten Ausgabe signifikant. Beispielsweise:

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

Die Ausführungszeit war ebenfalls dramatisch besser - 2,063 Sekunden für die erste Version, 0,059 Sekunden für die zweite.


1
Eine gute Idee, jeder, der mit riesigen Build-Systemen arbeitet, wird es zu schätzen wissen. Statistik für Android Lebkuchenbaum (verwendet kein rekursives Make, daher sind die Einsparungen nicht riesig, nur gut): " 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

Guter Punkt. Die Manpage für makescheint etwas ähnliches, aber freundliches vorzuschlagen, nämlichmake -p -f/dev/null
Davide

@Davide, das funktioniert nicht ganz: Durch die Angabe -f /dev/nullwird verhindert, dass makedie regulären Makefiles analysiert werden, sodass Sie nur einen Ausdruck der integrierten Regeln erhalten. Deshalb spezifiziert meine Lösung -f Makefile -f dummy.mk. Aber das ist nicht genug , um entweder, weil mit -n, makewird fortgesetzt , und tatsächlich beginnt auch die Regeln in der Make - Datei ausgeführt wird . Leider sind mir keine Änderungen bekannt, die die ursprünglich vorgestellte Lösung vereinfachen könnten.
Eric Melski

Natürlich hast du recht. Die Make-Manpage hat jedoch einen Fehler, da sie besagt: "Um die Datenbank zu drucken, ohne zu versuchen, Dateien neu zu erstellen, verwenden Sie make -p -f / dev / null"
Davide

Leichte Korrektur zu meinem vorherigen Kommentar: Es sollte "... weil ohne -n ..."
lauten


7

Bearbeiten: Zu Ihrer Information: Das Debian-Bash-Abschluss-Git-Repository in einer anderen Antwort enthält jetzt eine erweiterte Version dieses Skripts, die auf Anwendungsfälle für den Bash-Abschluss zugeschnitten ist.

#!/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

Dies ist ein viel vollständigeres Skript als das Debian-Bash-Vervollständigungsskript, da es Ergebnisse für generierte Regeln liefert, nach denen die Frage fragt, und die am besten bewertete Antwort (Debian-Bash-Vervollständigungsskript auf dem Debian-Git-Server) nicht weit genug geht.

Dies ist nicht das ursprüngliche Skript, mit dem ich verlinkt habe, aber es ist viel einfacher und ein Hauch schneller.


Übrigens, Mods, wenn diese Antwort aufgrund der Nuance des Textes nicht vollständig mit allen Richtlinien übereinstimmt, kommentieren Sie dies bitte vor dem Löschen. Ich werde alle erforderlichen rechtlichen Anpassungen vornehmen, damit diese Frage tatsächlich eine vollständige und gültige Antwort erhält.
Codeshot

Als schnellen Test habe ich den gcc-Quellcode genommen und ./configure && time ~ / makecomp.sh | ausgeführt wc -l wobei ~ / makecomp.sh meine Antwort ist. ... Ausgabe konfigurieren ... config.status: Makefile 3312 real erstellen 0m0.401s Benutzer 0m0.412s sys 0m0.028s
Codeshot

Als weiteren Test habe ich das Demo-Makefile unter codeshot.blogspot.co.uk/2012/08/… verwendet, das Pseudoregeln zum Erstellen und Überprüfen von Unit-Test-Durchläufen generiert. In diesem Beispiel werden mehr Funktionen von make als in einem typischen plattformübergreifenden Autoconf-Projekt verwendet, sodass das Skript in dieser Antwort 14 echte Ziele zurückgibt, während die Bash-Vervollständigung für make auf Ubuntu nur zwei nützliche Regeln und dann 5 Quelldateien zurückgibt.
Codeshot

4

Dies ist der Code für Alias, der auf der Todd Hodes-Lösung basiert

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

3

Dies ergibt eine schöne Ausgabe:

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


2

Ich schätze, ich bin etwas spät dran für diese Party, aber wenn Sie nach einem bestimmten Befehl suchen, können Sie es versuchen

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

Dies funktioniert meistens, obwohl es möglicherweise noch einige Nicht-Ziel-Dinge wie eine vpathDirektive enthält. Wenn Sie nicht von makeden integrierten Regeln abhängig sind, können Sie diesen make -qpRals ersten Befehl in der Pipeline verwenden.


Ich habe es mit freetz (.org) versucht. Listet viele enthaltene Makefiles auf und listet nicht alle Ziele auf.
Robert Siemer

1

Ruby-Lösung:

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

1

Ich arbeite an Solaris 10 und einer Turbo C-Schale. Die angegebene Lösung funktioniert für mein Makefile-Projekt nicht. auch nach Änderung der obigen Befehlszeile auf tcsh-Syntax. Ich habe jedoch herausgefunden, dass Sie mit eine einfache Lösung erhalten können

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

Remake-Version:

remake --version

ist

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

und einige andere unwichtige Versionsdaten


0

Ich habe nach der gleichen Frage gesucht und mir diesen Dreh ausgedacht:

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

Dadurch werden alle Kommentarzeilen, Musterregeln (Zeilen, die mit Tabulatoren beginnen) und alle Eigenheiten (Beispiel .c.ound %.o: %.cMuster) entfernt.


habe den Kommentar aus der akzeptierten Antwort nicht gelesen: +1 für Jacks Antwort ... gist.github.com/777954 - pvandenberk 13. Januar um 14:47
Jamie

0

Diese Lösung in einem anderen Thread gefunden:

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

Sie können es auch zu Ihrem hinzufügen Makefile:

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

Und ausführen make list.


-1

Sicher, aber wann soll es sie ausspucken?

Fügen Sie eine Zeile in die Regel ein, um den Namen des Ziels beim Ausführen der Regel zu melden:

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

Um sie alle auf einmal auszuspucken, können Sie ein separates PHONY-Ziel erstellen:

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

Und dies kann eine Voraussetzung für Ihr Standardziel sein:

all: show_vars
    ...

BEARBEITEN:
Sie möchten eine Möglichkeit, alle möglichen Ziele eines beliebigen Makefiles anzuzeigen, was vermutlich nicht aufdringlich bedeutet. Gut...

Um dies genau zu tun und mit anspruchsvollen Makefiles fertig zu werden, z. B. mit Regeln, die durch evalAnweisungen erstellt wurden, müssten Sie etwas schreiben, das einem Make-Emulator nahe kommt. Unpraktisch.

Um die Ziele der einfachen Regeln zu sehen, können Sie ein Makefile schreiben, das als Makefile-Scanner fungiert und mit einem beliebigen Makefile arbeitet:

  1. Holen Sie sich mit sed alle Zielnamen aus dem Makefile.
  2. `make` das Makefile, um es zum Erweitern von Variablen zu verwenden.
  3. Verwenden Sie `show_% :; echo $$ * `, um alle Ziele zu drucken

Dies wäre eine beeindruckende Arbeit. Sind Sie sicher, dass das Ziel die Mühe wert ist?


Nicht ganz das, was ich will. Ich möchte in der Lage sein, eine Liste von Zielen für ein beliebiges Makefile, das ich möglicherweise habe, ausspucken zu lassen. Ich versuche, eine Argumentvervollständigungsfunktion für meine Shell zu schreiben, und ich kann mich nicht darauf verlassen, dass die Makefiles eine vernünftige Hilfe haben.
BitShifter

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

1
-1: Dieser Ansatz erzeugt selbst bei einfachen Variablenzuweisungen ( :=) falsch positive Ergebnisse .
Thiton

Sie sollten es zuerst getestet haben. Es ist jedoch gut, dass Sie es versuchen.
Konrad Viltersten
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.