Übergeben von Argumenten an "make run"

354

Ich benutze Makefiles.

Ich habe ein Ziel namens, rundas das Build-Ziel ausführt. Vereinfacht sieht es wie folgt aus:

prog: ....
  ...

run: prog
  ./prog

Gibt es eine Möglichkeit, Argumente zu übergeben? Damit

make run asdf --> ./prog asdf
make run the dog kicked the cat --> ./prog the dog kicked the cat

Vielen Dank!

anon
quelle

Antworten:

264

Ich weiß nicht, wie ich genau das tun kann, was Sie wollen, aber eine Problemumgehung könnte sein:

run: ./prog
    ./prog $(ARGS)

Dann:

make ARGS="asdf" run
# or
make run ARGS="asdf"
Jakob Borg
quelle
27
@Rob: $ () ist portabler, es funktioniert sowohl in Nmake als auch in make.
John Knoeller
7
@Rob: Nmake hat $ {} für die Makroerweiterung noch nie unterstützt, und es scheint sich um eine archaische Form zu handeln. $ () wird von jedem Online-Tutorial empfohlen, das ich mir angesehen habe. $ () ist auch konsistenter mit anderen Tools wie bash.
John Knoeller
10
Vielleicht ist es archaisch. Ich habe immer $ {} verwendet, aber im Handbuch für GNU Make heißt es: "Um den Wert einer Variablen zu ersetzen, schreiben Sie ein Dollarzeichen gefolgt vom Namen der Variablen in Klammern oder Klammern: entweder $(foo)' or $ {foo} 'ist eine gültige Referenz auf die Variable "foo". " und fährt fort, Beispiele zu geben, in denen nur $ () verwendet wird. Ah, gut.
Jakob Borg
7
Prost John und ruhig, ich ging zurück und sah, dass der Vorschlag aus meiner Ausgabe des OReilly-Buches "Managing Projects with Make" in der ersten Ausgabe stammte. Der Autor gibt die Regel für die Ersetzung von Archiven mit () und Makros an, die beides können, schlägt jedoch vor, zur Unterscheidung {} zu verwenden. Aber ... Die neue Ausgabe mit dem Titel "Verwalten von Projekten mit GNU Make" verwendet durchgehend (). Go figure .... Ich denke, ich muss modernisieren! (-: Ich bin immer noch erstaunt, dass MS NMake bei {} barfs.
Rob Wells
1
@xealits Es ist sicher - es gibt ein Beispiel bei der Frage hier
Helvete
198

Diese Frage ist fast drei Jahre alt, aber trotzdem ...

Wenn Sie GNU make verwenden, ist dies einfach. Das einzige Problem besteht darin, dass makeArgumente ohne Option in der Befehlszeile als Ziele interpretiert werden. Die Lösung besteht darin, sie in Nichtstun-Ziele zu verwandeln, also makebeschweren Sie sich nicht:

# If the first argument is "run"...
ifeq (run,$(firstword $(MAKECMDGOALS)))
  # use the rest as arguments for "run"
  RUN_ARGS := $(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS))
  # ...and turn them into do-nothing targets
  $(eval $(RUN_ARGS):;@:)
endif

prog: # ...
    # ...

.PHONY: run
run : prog
    @echo prog $(RUN_ARGS)

Wenn Sie dies ausführen, erhalten Sie:

$ make run foo bar baz
prog foo bar baz
Idelic
quelle
2
Dies ist großartig, außer es scheint nicht für Argumente zu funktionieren, die mit einem Bindestrich beginnen:prog foo bar --baz
ingydotnet
22
Es funktioniert auch in diesem Fall, aber Sie müssen anweisen, makenicht --bazals Befehlszeilenoption zu interpretieren : make -- prog foo bar --baz. Das --bedeutet "alles danach ist ein Argument, keine Option".
Idelic
Wie würde ich einen Standardwert für die RUN_ARGSVerwendung definieren ?
Bouke
Vielleicht einen elseZweig zum hinzufügen ifeqund RUN_ARGSdort setzen?
Idelic
1
Guter Punkt blau! Dafür gibt es jedoch eine Lösung: Ersetzen Sie die 'eval'-Zeile durch $(eval $(RUN_ARGS):dummy;@:), ohne dass ein Dummy-Ziel definiert ist.
Lucas Cimon
52

Für Standard-Make können Sie Argumente übergeben, indem Sie solche Makros definieren

make run arg1=asdf

dann benutze sie so

run: ./prog $(arg1)
   etc

Referenzen für Make Microsofts NMake

John Knoeller
quelle
34

Sie können die Variable wie folgt an das Makefile übergeben:

run:
    @echo ./prog $$FOO

Verwendungszweck:

$ make run FOO="the dog kicked the cat"
./prog the dog kicked the cat

oder:

$ FOO="the dog kicked the cat" make run
./prog the dog kicked the cat

Alternativ können Sie die von Beta bereitgestellte Lösung verwenden :

run:
    @echo ./prog $(filter-out $@,$(MAKECMDGOALS))
%:
    @:

%:- Regel, die mit einem Aufgabennamen übereinstimmt; @:- leeres Rezept = nichts tun

Verwendungszweck:

$ make run the dog kicked the cat
./prog the dog kicked the cat
Kenorb
quelle
22

TL; DR versuchen Sie nicht, dies zu tun

$ make run arg

Erstellen Sie stattdessen ein Skript:

#! /bin/sh
# rebuild prog if necessary
make prog
# run prog with some arguments
./prog "$@"

und mach das:

$ ./buildandrunprog.sh arg

Antwort auf die gestellte Frage:

Sie können eine Variable im Rezept verwenden

run: prog
    ./prog $(var)

Übergeben Sie dann eine Variablenzuweisung als Argument

$ make run var=arg

Dies wird ausgeführt ./prog arg.

aber hüte dich vor Fallstricken. Ich werde auf die Fallstricke dieser Methode und anderer Methoden weiter unten eingehen.


Antwort auf die vermutete Absicht hinter der Frage:

Die Annahme: Sie möchten progmit einigen Argumenten ausgeführt werden, müssen diese jedoch vor der Ausführung neu erstellen, falls erforderlich.

Die Antwort: Erstellen Sie ein Skript, das bei Bedarf neu erstellt wird, und führen Sie dann prog mit args aus

#! /bin/sh
# rebuild prog if necessary
make prog
# run prog with some arguments
./prog "$@"

Dieses Skript macht die Absicht sehr klar. Es verwendet make, um das zu tun, wofür es gut ist: Bauen. Es verwendet ein Shell-Skript, um das zu tun, wofür es gut ist: Stapelverarbeitung.

Außerdem können Sie mit der vollen Flexibilität und Ausdruckskraft eines Shell-Skripts alles tun, was Sie sonst noch benötigen, ohne die Einschränkungen eines Makefiles.

auch die aufrufende Syntax ist jetzt praktisch identisch:

$ ./buildandrunprog.sh foo "bar baz"

vergleichen mit:

$ ./prog foo "bar baz"

im Gegensatz zu

$ make run var="foo bar\ baz"

Hintergrund:

make ist nicht dafür ausgelegt, Argumente an ein Ziel zu übergeben. Alle Argumente in der Befehlszeile werden entweder als Ziel (auch als Ziel bezeichnet), als Option oder als Variablenzuweisung interpretiert.

Also, wenn Sie dies ausführen:

$ make run foo --wat var=arg

make wird interpretiert runund fooals Ziele (Targets) nach ihren Rezepten aktualisiert. --watals Option für machen. und var=argals variable Zuordnung.

Weitere Informationen finden Sie unter: https://www.gnu.org/software/make/manual/html_node/Goals.html#Goals

Die Terminologie finden Sie unter: https://www.gnu.org/software/make/manual/html_node/Rule-Introduction.html#Rule-Introduction


über die variable Zuweisungsmethode und warum ich dagegen empfehle

$ make run var=arg

und die Variable im Rezept

run: prog
    ./prog $(var)

Dies ist die "korrekteste" und einfachste Möglichkeit, Argumente an ein Rezept zu übergeben. Obwohl es verwendet werden kann, um ein Programm mit Argumenten auszuführen, ist es sicherlich nicht dafür ausgelegt, auf diese Weise verwendet zu werden. Siehe https://www.gnu.org/software/make/manual/html_node/Overriding.html#Overriding

Meiner Meinung nach hat dies einen großen Nachteil: Was Sie tun möchten, ist progmit Argumenten zu laufen arg. aber anstatt zu schreiben:

$ ./prog arg

du schreibst:

$ make run var=arg

Dies wird noch umständlicher, wenn Sie versuchen, mehrere Argumente oder Argumente mit Leerzeichen zu übergeben:

$ make run var="foo bar\ baz"
./prog foo bar\ baz
argcount: 2
arg: foo
arg: bar baz

vergleichen mit:

$ ./prog foo "bar baz"
argcount: 2
arg: foo
arg: bar baz

Für die Aufzeichnung progsieht es so aus:

#! /bin/sh
echo "argcount: $#"
for arg in "$@"; do
  echo "arg: $arg"
done

Beachten Sie auch, dass Sie $(var)im Makefile keine Anführungszeichen setzen sollten :

run: prog
    ./prog "$(var)"

denn dann progbekommt man immer nur ein argument:

$ make run var="foo bar\ baz"
./prog "foo bar\ baz"
argcount: 1
arg: foo bar\ baz

All dies ist, warum ich gegen diese Route empfehle.


Der Vollständigkeit halber sind hier einige andere Methoden aufgeführt, um "Argumente zu übergeben, um sie auszuführen".

Methode 1:

run: prog
    ./prog $(filter-out $@, $(MAKECMDGOALS))

%:
    @true

super kurze Erklärung: aktuelles Ziel aus der Liste der Ziele herausfiltern. Erstelle catch all target ( %), was nichts dazu beiträgt, die anderen Ziele stillschweigend zu ignorieren.

Methode 2:

ifeq (run, $(firstword $(MAKECMDGOALS)))
  runargs := $(wordlist 2, $(words $(MAKECMDGOALS)), $(MAKECMDGOALS))
  $(eval $(runargs):;@true)
endif

run:
    ./prog $(runargs)

super kurze Erklärung: Wenn das Ziel ist, runentfernen Sie das erste Ziel und erstellen Sie keine Ziele für die verbleibenden Ziele mit eval.

Mit beiden können Sie so etwas schreiben

$ make run arg1 arg2

Weitere Informationen finden Sie im Handbuch von make: https://www.gnu.org/software/make/manual/html_node/index.html

Probleme der Methode 1:

  • Argumente, die mit einem Bindestrich beginnen, werden von make interpretiert und nicht als Ziel übergeben.

    $ make run --foo --bar
    

    Problemumgehung

    $ make run -- --foo --bar
    
  • Argumente mit Gleichheitszeichen werden von make interpretiert und nicht übergeben

    $ make run foo=bar
    

    Keine Problemumgehung

  • Argumente mit Leerzeichen sind umständlich

    $ make run foo "bar\ baz"
    

    Keine Problemumgehung

  • Wenn ein Argument zufällig run(gleich dem Ziel) ist, wird es ebenfalls entfernt

    $ make run foo bar run
    

    läuft ./prog foo barstatt./prog foo bar run

    Problemumgehung mit Methode 2 möglich

  • Wenn ein Argument ein legitimes Ziel ist, wird es ebenfalls ausgeführt.

    $ make run foo bar clean
    

    läuft ./prog foo bar cleanaber auch das Rezept für das Ziel clean(vorausgesetzt es existiert).

    Problemumgehung mit Methode 2 möglich

  • Wenn Sie ein legitimes Ziel falsch eingeben, wird es aufgrund des Ziels "Alle fangen" stillschweigend ignoriert.

    $ make celan
    

    wird einfach still ignorieren celan.

    Problemumgehung ist, alles wortreich zu machen. Sie sehen also, was passiert. aber das erzeugt viel Rauschen für die legitime Ausgabe.

Probleme der Methode 2:

  • Wenn ein Argument denselben Namen wie ein vorhandenes Ziel hat, gibt make eine Warnung aus, dass es überschrieben wird.

    Keine Problemumgehung, die ich kenne

  • Argumente mit einem Gleichheitszeichen werden weiterhin von make interpretiert und nicht übergeben

    Keine Problemumgehung

  • Argumente mit Leerzeichen sind immer noch umständlich

    Keine Problemumgehung

  • Argumente mit Leerzeichen, die evalversuchen zu erstellen, bewirken nichts.

    Problemumgehung: Erstellen Sie das globale Ziel "catch all", ohne die oben genannten Schritte auszuführen. mit dem Problem wie oben, dass es wieder stillschweigend falsch eingegebene legitime Ziele ignoriert.

  • Es wird verwendet eval, um das Makefile zur Laufzeit zu ändern. Wie viel schlimmer können Sie in Bezug auf Lesbarkeit und Debugbarkeit und das Prinzip des geringsten Erstaunens gehen .

    Problemumgehung: Tun Sie dies nicht !! 1 Schreiben Sie stattdessen ein Shell-Skript, das make ausführt und dann ausgeführt wird prog.

Ich habe nur mit Gnu Make getestet. andere Marken haben möglicherweise ein anderes Verhalten.


TL; DR versuchen Sie nicht, dies zu tun

$ make run arg

Erstellen Sie stattdessen ein Skript:

#! /bin/sh
# rebuild prog if necessary
make prog
# run prog with some arguments
./prog "$@"

und mach das:

$ ./buildandrunprog.sh arg
Lesmana
quelle
12

Hier ist eine andere Lösung, die bei einigen dieser Anwendungsfälle helfen könnte:

test-%:
    $(PYTHON) run-tests.py $@

Mit anderen Worten, wählen Sie ein Präfix ( test-in diesem Fall) und übergeben Sie den Zielnamen direkt an das Programm / den Runner. Ich denke, dies ist meistens nützlich, wenn es sich um ein Runner-Skript handelt, das den Zielnamen in etwas Nützliches für das zugrunde liegende Programm auspacken kann.

djc
quelle
2
Sie können auch $*nur den Teil des Ziels übergeben, der dem entspricht %.
Malvineous
9

Schauen Sie sich die Syntax von der Manpage für GNU make an

make [-f makefile] [Optionen] ... [Ziele] ...

Sie können mehrere Ziele angeben, daher 'nein' (zumindest nein genau so, wie Sie es angegeben haben).

ziya
quelle
4

Sie können jedes n-te Argument in der Befehlszeile explizit extrahieren. Zu diesem Zweck können Sie die Variable MAKECMDGOALS verwenden. Sie enthält die Liste der Befehlszeilenargumente für 'make', die als Liste der Ziele interpretiert werden. Wenn Sie das n-te Argument extrahieren möchten, können Sie diese Variable in Kombination mit der Funktion "Wort" verwenden. Wenn Sie beispielsweise das zweite Argument möchten, können Sie es wie folgt in einer Variablen speichern:

second_argument := $(word 2, $(MAKECMDGOALS) )
user5122888
quelle
Dadurch wird auch der Befehl make für dieses Argument ausgeführt. make: *** No rule to make target 'arg'. Stop.
Thomas Reggi
2

anon , run: ./progsieht ein bisschen seltsam aus, da der rechte Teil ein Ziel sein sollte, also run: progbesser aussieht.

Ich würde einfach vorschlagen:

.PHONY: run

run:
        prog $(arg1)

und ich möchte hinzufügen, dass Argumente übergeben werden können:

  1. als Argument: make arg1="asdf" run
  2. oder als Umgebung definiert werden: arg1="asdf" make run
dma_k
quelle
2

Hier ist mein Beispiel. Beachten Sie, dass ich unter Windows 7 mit mingw32-make.exe schreibe, das mit Dev-Cpp geliefert wird. (Ich habe c: \ Windows \ System32 \ make.bat, daher heißt der Befehl immer noch "make".)

clean:
    $(RM) $(OBJ) $(BIN) 
    @echo off
    if "${backup}" NEQ "" ( mkdir ${backup} 2> nul && copy * ${backup} )

Verwendung zur regelmäßigen Reinigung:

make clean

Verwendung zum Bereinigen und Erstellen eines Backups in mydir /:

make clean backup=mydir
osa
quelle
2

Nicht allzu stolz darauf, aber ich wollte keine Umgebungsvariablen übergeben, also habe ich die Art und Weise, wie ein vordefinierter Befehl ausgeführt wird, umgekehrt:

run:
    @echo command-you-want

Dadurch wird der Befehl gedruckt, den Sie ausführen möchten. Bewerten Sie ihn also einfach in einer Unterschale:

$(make run) args to my command
Conrad.Dean
quelle
4
Rückblick auf diese Antwort zwei Jahre später - warum war ich so hartnäckig, dass ich keine Umgebungsvariablen verwenden wollte, und warum hielt ich es für besser, die Generierung eines anderen Befehls zu integrieren?
Conrad.Dean
0

Ich habe einen Weg gefunden, die Argumente mit einem Gleichheitszeichen (=) zu erhalten! Die Antwort ist insbesondere eine Ergänzung zu @lesmanas Antwort (da sie die vollständigste und erklärteste hier ist), aber sie wäre zu groß, um sie als Kommentar zu schreiben. Ich wiederhole noch einmal seine Nachricht: TL; DR versuche das nicht!

Ich brauchte einen Weg, um mein Argument zu behandeln --xyz-enabled=false(da der Standardwert wahr ist), von dem wir alle inzwischen wissen, dass dies kein Make-Ziel ist und daher nicht Teil davon $(MAKECMDGOALS).

Beim Betrachten aller Variablen von make durch Echo des habe $(.VARIABLES)ich diese interessanten Ausgaben erhalten:

[...] -*-command-variables-*- --xyz-enabled [...]

Dies ermöglicht es uns, zwei Wege zu gehen: entweder alles mit einer --(falls dies für Ihren Fall zutrifft) zu beginnen oder in die GNU make-spezifische (wahrscheinlich nicht für uns vorgesehene) Variable zu schauen -*-command-variables-*-. ** Weitere Optionen finden Sie in der Fußzeile. ** In meinem Fall enthielt diese Variable:

--xyz-enabled=false

Mit dieser Variablen können wir sie mit der bereits vorhandenen Lösung kombinieren $(MAKECMDGOALS)und damit definieren:

# the other technique to invalidate other targets is still required, see linked post
run:
    @echo ./prog $(-*-command-variables-*-) $(filter-out $@,$(MAKECMDGOALS))`

und Verwenden mit (explizite Verwechslung der Reihenfolge der Argumente):

make run -- config --xyz-enabled=false over=9000 --foo=bar show  isit=alwaysreversed? --help

ist zurückgekommen:

./prog isit=alwaysreversed? --foo=bar over=9000 --xyz-enabled=false config show --help

Wie Sie sehen können, verlieren wir die Gesamtreihenfolge der Argumente. Der Teil mit den "Zuweisungs" -Args scheint umgekehrt worden zu sein, die Reihenfolge der "Ziel" -Args wird beibehalten. Ich habe die "Zuweisungs" -Args am Anfang platziert, hoffentlich ist es Ihrem Programm egal, wo das Argument platziert ist.


Update: Die folgenden Variablen sehen ebenfalls vielversprechend aus:

MAKEFLAGS =  -- isit=alwaysreverse? --foo=bar over=9000 --xyz-enabled=false
MAKEOVERRIDES = isit=alwaysreverse? --foo=bar over=9000 --xyz-enabled=false
Dionysius
quelle
-2

Ein weiterer Trick, den ich benutze, ist die -nFlagge, die makeeinen Trockenlauf vorschreibt. Zum Beispiel,

$ make install -n 
# Outputs the string: helm install stable/airflow --name airflow -f values.yaml
$ eval $(make install -n) --dry-run --debug
# Runs: helm install stable/airflow --name airflow -f values.yaml --dry-run --debug
Santon
quelle