TL; DR nie próbuj tego robić
$ make run arg
zamiast tego utwórz skrypt:
#! /bin/sh
# rebuild prog if necessary
make prog
# run prog with some arguments
./prog "$@"
i zrób to:
$ ./buildandrunprog.sh arg
odpowiedz na zadane pytanie:
możesz użyć zmiennej w recepturze
run: prog
./prog $(var)
następnie przekaż przypisanie zmiennej jako argument do wykonania
$ make run var=arg
to się wykona ./prog arg.
ale strzeżcie się pułapek. omówię pułapki tej metody i innych metod w dalszej części.
odpowiedz na zakładaną intencję pytania:
założenie: chcesz uruchomić progz kilkoma argumentami, ale w razie potrzeby przebuduj go przed uruchomieniem.
odpowiedź: utwórz skrypt, który w razie potrzeby odbuduje, a następnie uruchomi prog z args
#! /bin/sh
# rebuild prog if necessary
make prog
# run prog with some arguments
./prog "$@"
ten skrypt wyraźnie wyjaśnia intencję. używa make do robienia tego, co jest dobre: budowania. używa skryptu powłoki do robienia tego, co jest dobre dla: przetwarzania wsadowego.
a ponadto możesz robić wszystko, czego potrzebujesz, z pełną elastycznością i ekspresyjnością skryptu powłoki, bez wszystkich zastrzeżeń makefile.
również składnia wywołująca jest teraz praktycznie identyczna:
$ ./buildandrunprog.sh foo "bar baz"
porównać do:
$ ./prog foo "bar baz"
w przeciwieństwie do
$ make run var="foo bar\ baz"
tło:
make nie jest przeznaczony do przekazywania argumentów do celu. wszystkie argumenty w wierszu poleceń są interpretowane jako cel (aka target), jako opcja lub jako przypisanie zmiennej.
więc jeśli uruchomisz to:
$ make run foo --wat var=arg
Marka będzie interpretować runi foojako cele (cele) aktualizować zgodnie z ich przepisami. --watjako opcja dla make. i var=argjako zmienne przypisanie.
Więcej informacji: https://www.gnu.org/software/make/manual/html_node/Goals.html#Goals
terminologia patrz: https://www.gnu.org/software/make/manual/html_node/Rule-Introduction.html#Rule-Introduction
na temat metody przypisywania zmiennych i dlaczego odradzam
$ make run var=arg
i zmienna w recepturze
run: prog
./prog $(var)
jest to najbardziej „poprawny” i najprostszy sposób przekazywania argumentów do przepisu. ale chociaż można go używać do uruchamiania programu z argumentami, to z pewnością nie jest przeznaczony do użycia w ten sposób. patrz https://www.gnu.org/software/make/manual/html_node/Overriding.html#Overriding
moim zdaniem ma to jedną wielką wadę: to, co chcesz zrobić, to uruchomić progargument arg. ale zamiast pisać:
$ ./prog arg
piszesz:
$ make run var=arg
staje się to jeszcze bardziej niezręczne przy próbie przekazania wielu argumentów lub argumentów zawierających spacje:
$ make run var="foo bar\ baz"
./prog foo bar\ baz
argcount: 2
arg: foo
arg: bar baz
porównać do:
$ ./prog foo "bar baz"
argcount: 2
arg: foo
arg: bar baz
dla przypomnienia progwygląda to tak:
#! /bin/sh
echo "argcount: $#"
for arg in "$@"; do
echo "arg: $arg"
done
zauważ także, że nie powinieneś umieszczać $(var)cudzysłowów w pliku makefile:
run: prog
./prog "$(var)"
ponieważ wtedy progzawsze otrzyma tylko jeden argument:
$ make run var="foo bar\ baz"
./prog "foo bar\ baz"
argcount: 1
arg: foo bar\ baz
wszystko dlatego polecam przeciwko tej trasie.
dla kompletności oto kilka innych metod „przekazywania argumentów do uruchomienia”.
metoda 1:
run: prog
./prog $(filter-out $@, $(MAKECMDGOALS))
%:
@true
super krótkie wyjaśnienie: odfiltruj aktualny cel z listy celów. create catch all target ( %), który nie robi nic, aby po cichu zignorować pozostałe cele.
metoda 2:
ifeq (run, $(firstword $(MAKECMDGOALS)))
runargs := $(wordlist 2, $(words $(MAKECMDGOALS)), $(MAKECMDGOALS))
$(eval $(runargs):;@true)
endif
run:
./prog $(runargs)
super krótkie wyjaśnienie: jeśli celem jest runusunięcie pierwszego celu i utworzenie nic nie rób dla pozostałych celów eval.
oba pozwolą ci napisać coś takiego
$ make run arg1 arg2
dla głębszego wyjaśnienia zapoznaj się z instrukcją make: https://www.gnu.org/software/make/manual/html_node/index.html
problemy metody 1:
argumenty rozpoczynające się od myślnika będą interpretowane przez make, a nie przekazywane jako cel.
$ make run --foo --bar
obejście
$ make run -- --foo --bar
argumenty ze znakiem równości będą interpretowane przez make, a nie przekazywane
$ make run foo=bar
bez obejścia
kłótnie ze spacjami są niezręczne
$ make run foo "bar\ baz"
bez obejścia
jeśli argument stanie się run(równy celowi), zostanie również usunięty
$ make run foo bar run
uruchomi się ./prog foo barzamiast./prog foo bar run
obejście możliwe dzięki metodzie 2
jeśli argument jest uzasadnionym celem, zostanie również uruchomiony.
$ make run foo bar clean
uruchomi się, ./prog foo bar cleanale także przepis na cel clean(zakładając, że istnieje).
obejście możliwe dzięki metodzie 2
gdy błędnie wpiszesz prawidłowy cel, zostanie on po cichu zignorowany z powodu złapania wszystkich celów.
$ make celan
po prostu po cichu zignoruje celan.
obejście polega na tym, aby wszystko było pełne. więc widzisz, co się dzieje. ale to powoduje dużo hałasu dla legalnej wydajności.
problemy metody 2:
jeśli argument ma taką samą nazwę jak istniejący cel, wtedy make wypisze ostrzeżenie, że jest zastępowane.
brak obejścia, o którym wiem
argumenty ze znakiem równości będą nadal interpretowane przez make, a nie przekazywane
bez obejścia
spory ze spacjami są wciąż niezręczne
bez obejścia
argumenty z przerwami w przestrzeni evalpróbujące stworzyć nic nie rób celów.
obejście: utwórz globalny cel catch all, nie robiąc nic tak jak powyżej. z powyższym problemem, że ponownie po cichu zignoruje błędnie wpisane uzasadnione cele.
używa evaldo modyfikowania pliku makefile w czasie wykonywania. o ile gorzej możesz pójść pod względem czytelności i debugowania oraz zasady najmniejszego zdziwienia .
obejście: nie rób tego !! 1 zamiast tego napisz skrypt powłoki, który uruchamia make, a następnie uruchamia prog.
testowałem tylko przy użyciu gnu make. inne marki mogą mieć różne zachowania.
TL; DR nie próbuj tego robić
$ make run arg
zamiast tego utwórz skrypt:
#! /bin/sh
# rebuild prog if necessary
make prog
# run prog with some arguments
./prog "$@"
i zrób to:
$ ./buildandrunprog.sh arg