Передача аргументів для "запустити"


354

Я використовую Makefiles.

У мене є ціль, runяка називається, яка виконує ціль збірки. Спрощено, це виглядає наступним чином:

prog: ....
  ...

run: prog
  ./prog

Чи є спосіб передавати аргументи? Так що

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

Дякую!


Відповіді:


264

Я не знаю способу точно зробити те, що ви хочете, але рішення може бути:

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

Тоді:

make ARGS="asdf" run
# or
make run ARGS="asdf"

27
@Rob: $ () більш портативний, він працює як в Nmake, так і в make.
Джон Кноеллер

7
@Rob: Nmake ніколи не підтримував $ {} для розширення макросів, і, здається, зараз це архаїчна форма. $ () рекомендується в кожному підручнику, який я переглянув. $ () також більше відповідає іншим інструментам, таким як bash.
Джон Кноеллер

10
Можливо, це архаїчно. Я завжди використовував $ {}, але в посібнику для GNU Make йдеться про те, щоб "замінити значення змінної, напишіть знак долара, а потім ім'я змінної в дужках чи дужках: або $(foo)' or $ {foo} 'є дійсним посиланням на змінна `foo '." і продовжує наводити приклади, де використовується лише $ (). Ну добре.
Якоб Борг

7
привіт, Джон, і спокійно, я повернувся назад і побачив, що пропозиція надійшла з моєї копії першої редакції книги OReilly "Управління проектами з Make". Автор заявляє правило про заміну архіву, використовуючи () і макроси, здатні виконувати і те, і інше, але пропонує використовувати {} 'для розрізнення. Але .... Нове видання тепер переглянуте як "Управління проектами за допомогою GNU Make" використовує () у всьому. Подивіться цифра .... Здогадайтесь, мені доведеться модернізуватися! (-: Я все ще дивуюсь, що MS NMake баффи в {} 's хоч.
Роб Уеллс

1
@xealits Це точно - є приклад на питання тут
helvete

198

Це питання майже три роки, але все одно ...

Якщо ви використовуєте GNU make, це зробити легко. Єдина проблема полягає в тому make, що інтерпретувати необов'язкові аргументи в командному рядку як цілі. Рішення полягає в тому, щоб перетворити їх на цілі, makeякі не роблять нічого, тому не нарікайте:

# 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)

Запуск цього дає:

$ make run foo bar baz
prog foo bar baz

2
Це чудово, окрім того, що, здається, це не працює для аргументів, починаючи з тире:prog foo bar --baz
ingydotnet

22
Це робить роботу в цьому випадку теж, але ви повинні сказати , makeщоб не інтерпретувати --bazяк параметр командного рядка: make -- prog foo bar --baz. --Чи означає «все після того, як це аргумент, не варіант».
Іделік

Як я можу визначити значення за замовчуванням для RUN_ARGSвикористання цього?
Буке

Може, додати elseгілку до ifeqі встановити RUN_ARGSтам?
Іделік

1
Добрий момент синій! Але для цього є рішення: замініть рядок 'eval' на $(eval $(RUN_ARGS):dummy;@:), без жодної фіктивної цілі.
Лукас Кімон


34

Ви можете передати змінну в Makefile, як показано нижче:

run:
    @echo ./prog $$FOO

Використання:

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

або:

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

Як варіант, використовуйте рішення, надане Beta :

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

%:- правило, яке відповідає будь-якій назві завдання; @:- порожній рецепт = нічого не робити

Використання:

$ make run the dog kicked the cat
./prog the dog kicked the cat

22

TL; DR не намагайтеся цього робити

$ make run arg

замість цього створити сценарій:

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

і зробіть це:

$ ./buildandrunprog.sh arg

відповідь на поставлене запитання:

ви можете використовувати змінну в рецепті

run: prog
    ./prog $(var)

потім передайте призначення змінної як аргумент, який потрібно зробити

$ make run var=arg

це виконає ./prog arg.

але остерігайтеся підводних каменів. Я детальніше розповім про підводні камені цього методу та інші методи.


відповідь на передбачуваний намір за питанням:

припущення: ти хочеш бігти prog деякі аргументи, але перезавантажити його, перш ніж запустити.

відповідь: створіть скрипт, який при необхідності відновлює, а потім запустить прог з арг

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

цей сценарій робить наміри дуже зрозумілими. він використовує make, щоб робити те, що добре: будівництво. він використовує скрипт оболонки, щоб зробити те, що добре: пакетна обробка.

плюс ви можете робити все, що вам може знадобитися, з повною гнучкістю та виразністю скрипту оболонки без усіх застережень makefile.

також синтаксис виклику тепер практично ідентичний:

$ ./buildandrunprog.sh foo "bar baz"

порівняти з:

$ ./prog foo "bar baz"

на відміну від

$ make run var="foo bar\ baz"

фон:

make не призначений для передачі аргументів цілі. всі аргументи в командному рядку інтерпретуються або як мета (aka target), як варіант, або як змінне призначення.

тож якщо запустити це:

$ make run foo --wat var=arg

make буде інтерпретувати runі fooяк цілі (цілі) оновлювати відповідно до своїх рецептів. --watяк варіант для виготовлення. і var=argяк призначення змінної.

Докладніше див: https://www.gnu.org/software/make/manual/html_node/Goals.html#Goals

термінологію див .: https://www.gnu.org/software/make/manual/html_node/Rule-Introduction.html#Rule-Introduction


про метод призначення змінної та чому я рекомендую проти цього

$ make run var=arg

і змінна в рецепті

run: prog
    ./prog $(var)

це самий «правильний» і найпростіший спосіб передавати аргументи до рецепту. але, хоча його можна використовувати для запуску програми з аргументами, вона, звичайно, не розроблена для використання таким чином. подивитися https://www.gnu.org/software/make/manual/html_node/Overriding.html#Overriding

на мою думку, це має один великий недолік: те, що ви хочете зробити, - це бігти progз аргументом arg. але замість того, щоб писати:

$ ./prog arg

ви пишете:

$ make run var=arg

це стає ще незручнішим при спробі передачі декількох аргументів або аргументів, що містять пробіли:

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

порівняти з:

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

для запису ось так progвиглядає мій :

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

також зауважте, що не слід ставити $(var)лапки в makefile:

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

тому що тоді progзавжди з’явиться лише один аргумент:

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

все це, чому я рекомендую проти цього маршруту.


для повноти ось кілька інших методів "передачі аргументів для запуску".

спосіб 1:

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

%:
    @true

суперкоротке пояснення: відфільтруйте поточну мету зі списку цілей. створити ловити всю ціль ( %), яка не робить нічого, щоб мовчки ігнорувати інші цілі.

спосіб 2:

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

run:
    ./prog $(runargs)

суперкоротке пояснення: якщо ціль, runто видаліть першу ціль і створіть, не використовуйте нічого для інших цілей, використовуючи eval.

обидва дозволить вам написати щось подібне

$ make run arg1 arg2

для більш глибокого пояснення вивчайте посібник виготовлення: https://www.gnu.org/software/make/manual/html_node/index.html

проблеми 1-го методу:

  • аргументи, що починаються з тире, будуть інтерпретуватися складаннями, а не передаватися як мета.

    $ make run --foo --bar
    

    обхідний шлях

    $ make run -- --foo --bar
    
  • аргументи зі знаком рівності будуть інтерпретуватися складеними та не переданими

    $ make run foo=bar
    

    ніякого вирішення

  • аргументи з пробілами незручні

    $ make run foo "bar\ baz"
    

    ніякого вирішення

  • якщо аргумент буде run(рівний цілі), він також буде видалений

    $ make run foo bar run
    

    буде працювати ./prog foo barзамість./prog foo bar run

    можливе вирішення методом 2

  • якщо аргумент є законною ціллю, він також буде запущений.

    $ make run foo bar clean
    

    буде запущено, ./prog foo bar cleanале також рецепт цілі clean(якщо припустити, що він існує).

    можливе вирішення методом 2

  • коли ви введете неправильну ціль, вона буде мовчазно ігнорована через вилов усієї цілі.

    $ make celan
    

    буде просто мовчки ігнорувати celan.

    вирішення проблеми - зробити все багатослівним. тому ви бачите, що відбувається. але це створює багато шуму для законних результатів.

проблеми методу 2:

  • якщо аргумент має те саме ім'я, що і існуюча ціль, тоді make print надрукує попередження про його перезапис.

    ніякого вирішення, про яке я знаю

  • аргументи зі знаком рівності все одно будуть інтерпретуватися складеними та не переданими

    ніякого вирішення

  • аргументи з пробілами все ще незручні

    ніякого вирішення

  • аргументи з пробілами у просторі, evalнамагаючись створити, нічого не націлюють.

    вирішення проблеми: створити глобальну лову всіх цілей, не роблячи нічого, як зазначено вище. з вищезгаданою проблемою, що вона знову мовчки ігнорує помилково законні цілі.

  • він використовує evalдля зміни makefile під час виконання. наскільки гірше ви можете піти з точки зору читабельності та виправданості та принципу найменшого здивування .

    обхідне рішення: не робіть цього !! 1 замість цього напишіть сценарій оболонки, який запускає make і потім запускається prog.

я протестував лише за допомогою gnu make. інші виробники можуть мати різну поведінку.


TL; DR не намагайтеся цього робити

$ make run arg

замість цього створити сценарій:

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

і зробіть це:

$ ./buildandrunprog.sh arg

12

Ось ще одне рішення, яке може допомогти у деяких із цих випадків використання:

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

Іншими словами, виберіть деякий префікс ( test-у цьому випадку), а потім передайте ім'я цілі безпосередньо програмі / бігуну. Я думаю, що це в основному корисно, якщо є якийсь сценарій бігуна, який може розгорнути ім'я цілі в щось корисне для основної програми.


2
Ви також $*можете пройти лише ту частину цілі, яка відповідає %.
Malvineous

9

Ні. Переглядаючи синтаксис зі сторінки man для GNU make

зробити [-f makefile] [параметри] ... [цілі] ...

Ви можете вказати кілька цілей, отже, "ні" (принаймні, ні в точно вказаному Вами способі).


4

Ви можете явно витягти кожен n-й аргумент у командному рядку. Для цього ви можете використовувати змінну MAKECMDGOALS, вона містить перелік аргументів командного рядка, що дається 'make', що інтерпретується як список цілей. Якщо ви хочете витягти n-й аргумент, ви можете використовувати цю змінну в поєднанні з функцією "word", наприклад, якщо ви хочете другий аргумент, ви можете зберегти його у змінній, як описано нижче:

second_argument := $(word 2, $(MAKECMDGOALS) )

Це також запускає команду make для цього аргументу. make: *** No rule to make target 'arg'. Stop.
ThomasReggi

2

анон , run: ./progвиглядає дещо дивно, оскільки права частина повинна бути цільовою, так run: progвиглядає краще.

Я б запропонував просто:

.PHONY: run

run:
        prog $(arg1)

і я хотів би додати, що аргументи можна передавати:

  1. як аргумент: make arg1="asdf" run
  2. або визначати як середовище: arg1="asdf" make run

2

Ось мій приклад. Зауважте, що я пишу під Windows 7, використовуючи mingw32-make.exe, що постачається разом із Dev-Cpp. (У мене є c: \ Windows \ System32 \ make.bat, тому команда все ще називається "make".)

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

Використання для регулярного прибирання:

make clean

Використання для очищення та створення резервної копії в mydir /:

make clean backup=mydir

2

Я не надто пишаюся цим, але я не хотів передавати змінні середовища, тому перевернув спосіб запустити консервовану команду:

run:
    @echo command-you-want

це надрукує команду, яку ви хочете запустити, тому просто оцініть її в нижній частині:

$(make run) args to my command

4
озираючись на цю відповідь через два роки - чому я колись був настільки впертим, що не хотів використовувати змінні середовища і чому я вважав, що введення генерації іншої команди було кращим?
Conrad.Dean

0

Я знайшов спосіб отримати аргументи зі знаком рівності (=)! Відповідь є особливо доповненням до @lesmana (оскільки вона є найбільш повною і пояснена тут), але було б занадто великим, щоб написати це як коментар. Знову повторюю його повідомлення: TL; DR не намагайся цього робити!

Мені знадобився спосіб розглянути міркування --xyz-enabled=false (оскільки за замовчуванням це правда), про який ми всі дотепер знаємо, що це не цільова мета і, отже, не є частиною $(MAKECMDGOALS).

Переглядаючи всі змінні make , повторюючи, $(.VARIABLES)я отримав ці цікаві результати:

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

Це дозволяє нам піти двома способами: або почати все, починаючи з --(якщо це стосується вашого випадку), або заглянути в GNU зробити конкретну (можливо, не призначену для нас для використання) змінну -*-command-variables-*-. ** Додаткову інформацію див. У нижньому колонтитулі ** У моєму випадку ця змінна містила:

--xyz-enabled=false

За допомогою цієї змінної ми можемо поєднати її з уже існуючим рішенням $(MAKECMDGOALS)і, таким чином, визначивши:

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

і використовуючи його (явно змішуючи порядок аргументів):

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

повернуто:

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

Як бачите, ми втрачаємо загальний порядок арг. Частина з позначками "присвоєння", здається, була відмінена, порядок знаків "цільових" зберігається. Я розмістив позначення-признаки на початку, сподіваємось, вашій програмі не байдуже, де розміщений аргумент.


Оновлення: наступні також дозволяють змінні виглядати перспективними:

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

-2

Ще одна хитрість, яку я використовую, - -nпрапор, який говорить про те, makeщоб зробити сухий пробіг. Наприклад,

$ 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.