it-swarm.dev

Argumente an "make run" übergeben

Ich benutze Makefiles.

Ich habe ein Ziel namens run, das das Build-Ziel ausführt. Vereinfacht sieht es so aus:

prog: ....
  ...

run: prog
  ./prog

Setz dich wieder hin. Ich weiß, das ist genial, aber man braucht keine Standing Ovations.

Nun ist meine Frage - 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!

318
anon

Ich kenne keine Möglichkeit, genau das zu tun, was Sie wollen, aber eine Problemumgehung könnte sein:

run: ./prog
    ./prog ${ARGS}

Dann:

make ARGS="asdf" run
224
Jakob Borg

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

Wenn Sie GNU make verwenden, ist dies einfach zu bewerkstelligen. Das einzige Problem ist, dass make Argumente ohne Option in der Befehlszeile als Ziele interpretiert. Die Lösung lautet um sie in Nichts-zu-tun-Ziele zu verwandeln, beschwert sich make 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
174
Idelic

für standard make können Sie Argumente übergeben, indem Sie Makros wie dieses definieren

make run arg1=asdf

dann benutze sie so

run: ./prog $(arg1)
   etc

Referenzen für make Microsoft NMake

44
John Knoeller

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 Lösung von Beta 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
29
kenorb

TL; DR versuchen das nicht

$ make run arg

stattdessen Skript erstellen:

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

dadurch wird ./prog arg ausgeführt.

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


antwort auf die vermutete Absicht hinter der Frage:

die Annahme: Sie möchten prog mit einigen Argumenten ausführen, müssen es jedoch vor dem Ausführen neu erstellen, falls erforderlich.

die Antwort: Erstellen Sie ein Skript, das bei Bedarf neu erstellt und dann prog mit args ausführt

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

auch die aufrufsyntax ist jetzt praktisch identisch:

$ ./buildandrunprog.sh foo "bar baz"

vergleichen mit:

$ ./prog foo "bar baz"

außerdem können Sie mit der vollen Flexibilität und Ausdruckskraft eines Shell-Skripts alles tun, was Sie sonst noch brauchen, ohne die Vorbehalte eines Makefiles zu beachten.


hintergrund:

make ist nicht dafür ausgelegt, ein Ziel auszuführen und Argumente an dieses Ziel zu übergeben. Alle Argumente in der Befehlszeile werden entweder als Ziel (k. a. Ziel), als Option oder als variable Zuweisung interpretiert.

also, wenn Sie dies ausführen:

$ make run foo bar --wat var=arg

make interpretiert run, foo und bar als Ziele (Targets), die gemäß ihren Rezepten aktualisiert werden sollen. --wat Als Option für make. und var=arg als variable Zuweisung.

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

zur Terminologie siehe: 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 direkteste Möglichkeit, Argumente an ein Rezept zu übergeben. aber während es kann verwendet werden, 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: Sie möchten prog mit dem Argument arg ausführen. 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 ist dies, wie mein prog aussieht:

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

beachten Sie Folgendes, wenn Sie $(var) in Anführungszeichen im Makefile setzen:

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

dann bekommt prog immer nur ein Argument:

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

aus diesem Grund empfehle ich gegen diese Route.


der Vollständigkeit halber gibt es hier einige andere Methoden, um "Argumente zu übergeben, um run zu machen".

methode 1:

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

%:
    @true

super kurze erklärung: aktuelles ziel aus zielliste herausfiltern. create catch all target (%), das nichts unternimmt, um 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 run ist, dann entferne das erste Ziel und erstelle mit eval keine Ziele für die verbleibenden Ziele.

weitere Erklärungen finden Sie im Handbuch zu make: https://www.gnu.org/software/make/manual/html_node/index.html

probleme von Methode 1:

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

    $ make run --foo --bar
    

    workaround

    $ make run -- --foo --bar
    
  • wenn ein Argument run ist (gleich dem Ziel), wird es ebenfalls entfernt

    $ make run foo bar run
    

    wird ./prog foo bar anstelle von ./prog foo bar run ausführen

    problemumgehung mit Methode 2 möglich

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

    $ make run foo bar clean
    

    wird ./prog foo bar clean ausführen, aber auch das Rezept für das Ziel clean (sofern es existiert).

    problemumgehung mit Methode 2 möglich

  • es ist umständlich, Argumente mit Leerzeichen zu übergeben

    $ make run foo "bar\ baz"
    

    keine Problemumgehung

  • wenn Sie ein legitimes Ziel falsch eingeben, wird es stillschweigend ignoriert, da alle Ziele abgefangen werden.

    $ make celan
    

    ignoriert nur stillschweigend celan.

    behelfslösung ist, alles wortreich zu machen. Sie sehen also, was passiert. aber das erzeugt eine Menge Lärm für die legitime Ausgabe.

probleme von 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

  • es ist immer noch umständlich, Argumente mit Leerzeichen zu übergeben.

    keine Problemumgehung

  • argumente mit Leerzeichen unterbrechen eval und versuchen, keine Ziele zu erstellen.

    problemumgehung: Erstellen Sie das globale Ziel catch all, und tun Sie nichts wie oben. mit dem Problem wie oben, dass es falsch geschriebene legitime Ziele wieder unbemerkt ignorieren wird.

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

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

ich habe nur mit gnu make getestet. Andere Marken haben möglicherweise ein anderes Verhalten.


TL; DR versuchen das nicht

$ make run arg

stattdessen Skript erstellen:

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

und mach das:

$ ./buildandrunprog.sh arg
14
lesmana

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 Läufer. Ich denke, dies ist vor allem dann nützlich, wenn es sich um ein Runner-Skript handelt, das den Zielnamen in etwas Nützliches für das zugrunde liegende Programm packen kann.

11
djc

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

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

sie können mehrere Ziele angeben, daher "nein" (zumindest nein in der von Ihnen angegebenen Weise).

9
ziya

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 "Word" verwenden. Wenn Sie beispielsweise das zweite Argument möchten, können Sie es wie folgt in einer Variablen speichern:

second_argument := $(Word 2, $(MAKECMDGOALS) )
4
user5122888

Hier ist mein Beispiel. Beachten Sie, dass ich unter Windows 7 schreibe und mingw32-make.exe verwende, 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 für die regelmäßige Reinigung:

make clean

Verwendung zum Löschen und Erstellen eines Backups in mydir /:

make clean backup=mydir
2
osa

anon, run: ./prog sieht ein bisschen seltsam aus, da der rechte Teil ein Ziel sein sollte, also run: prog sieht besser aus.

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
2
dma_k

Darauf bin ich nicht besonders stolz, aber ich wollte keine Umgebungsvariablen übergeben, also habe ich den Weg zum Ausführen eines vordefinierten Befehls umgekehrt:

run:
    @echo command-you-want

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

$(make run) args to my command
2
Conrad.Dean

Ein weiterer Trick, den ich benutze, ist der -n Flag, das make anweist, einen Probelauf durchzuführen. 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
0
santon