Передача аргументов в «make run»

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 никогда не поддерживал $ {} для расширения макросов, и сейчас он кажется архаичной формой в make. $ () рекомендуется в каждом онлайн-учебнике, на который я смотрел. $ () также более соответствует другим инструментам, таким как bash.
Джон Ноллер
10
Может быть, это архаично. Я всегда использовал $ {}, но в руководстве для GNU Make говорится: «Чтобы подставить значение переменной, напишите знак доллара, а затем имя переменной в скобках или фигурные скобки: либо $(foo)' or $ {foo} 'является действительной ссылкой на переменная `foo '." и продолжает приводить примеры, где используется только $ (). Ах хорошо.
Якоб Борг
7
подбадривая Джона и успокоившись, я вернулся и увидел, что это предложение пришло из моей копии первой книги OReilly «Управление проектами с Make». Автор формулирует правило о замене архива, используя () и макросы, которые могут делать оба, но предлагает использовать {} для различения. Но .... Новая редакция теперь переименована в «Управление проектами с помощью GNU Make» и использует () повсюду. Пойди разберись ... Думаю, мне придётся модернизироваться (-: Я до сих пор поражаюсь, что MS NMake раздражает {}.
Роб Уэллс
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
Idelic
источник
2
Это здорово, за исключением того, что оно не работает для аргументов, начинающихся с тире:prog foo bar --baz
ingydotnet
22
Это делает работу в этом случае тоже, но вы должны сказать , makeчтобы не интерпретировать --bazкак параметр командной строки: make -- prog foo bar --baz. --Означает «все после того, как это аргумент, не вариант».
Idelic
Как бы я определить значение по умолчанию для RUN_ARGSиспользования этого?
Бук
Может быть, добавить elseветку в ifeqи установить RUN_ARGSтам?
Idelic
1
Хорошая точка голубоглазая! Но для этого есть решение: замените строку 'eval' на $(eval $(RUN_ARGS):dummy;@:), без фиктивной цели.
Лукас Кимон
52

для стандартного make вы можете передавать аргументы, определяя макросы следующим образом

make run arg1=asdf

затем используйте их как это

run: ./prog $(arg1)
   etc

Рекомендации по изготовлению Microsoft NMake

Джон Ноллер
источник
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
kenorb
источник
23

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, чтобы делать то, для чего он хорош: building. он использует скрипт оболочки, чтобы делать то, для чего он хорош: пакетная обработка.

Кроме того, вы можете делать все, что вам может понадобиться, с полной гибкостью и выразительностью сценария оболочки без всех предостережений make-файла.

также синтаксис вызова теперь практически идентичен:

$ ./buildandrunprog.sh foo "bar baz"

по сравнению с:

$ ./prog foo "bar baz"

в отличие от

$ make run var="foo bar\ baz"

фон:

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

так что если вы запустите это:

$ 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)в кавычки в make-файле:

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

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

способ 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 и не будут передаваться как цель.

    $ make run --foo --bar
    

    обходной путь

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

    $ 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 выдаст предупреждение о том, что он перезаписывается.

    нет обходного пути, о котором я знаю

  • аргументы со знаком равенства будут по-прежнему интерпретироваться make и не передаваться

    нет обходного пути

  • аргументы с пробелами все еще неудобны

    нет обходного пути

  • Аргументы с пробелами, evalпытающимися создать ничего не значащие цели.

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

  • он использует evalдля изменения make-файла во время выполнения. насколько хуже вы можете пойти с точки зрения читабельности и отладки и принципа наименьшего удивления .

    Обходной путь: не делайте этого !! 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
lesmana
источник
12

Вот еще одно решение, которое может помочь с некоторыми из этих случаев использования:

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

Другими словами, выберите некоторый префикс ( test-в этом случае), а затем передайте имя цели непосредственно программе / исполнителю. Я предполагаю, что это в основном полезно, если есть какой-то сценарий запуска, который может развернуть целевое имя во что-то полезное для основной программы.

DJC
источник
2
Вы также можете использовать, $*чтобы передать только ту часть цели, которая соответствует %.
Мальвинус
9

Нет. Глядя на синтаксис из man-страницы для GNU make

make [-f makefile] [параметры] ... [цели] ...

Вы можете указать несколько целей, следовательно, «нет» (по крайней мере, нет точно, как вы указали).

Зия
источник
4

Вы можете явно извлечь каждый n-й аргумент в командной строке. Для этого вы можете использовать переменную MAKECMDGOALS, в которой содержится список аргументов командной строки, заданных для make, который он интерпретирует как список целей. Если вы хотите извлечь n-й аргумент, вы можете использовать эту переменную в сочетании с функцией «word», например, если вам нужен второй аргумент, вы можете сохранить его в переменной следующим образом:

second_argument := $(word 2, $(MAKECMDGOALS) )
user5122888
источник
Это также запускает команду 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
dma_k
источник
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
OSA
источник
2

Не слишком горжусь этим, но я не хотел передавать переменные окружения, поэтому я перевернул способ запуска постоянной команды:

run:
    @echo command-you-want

это напечатает команду, которую вы хотите запустить, поэтому просто оцените ее в подоболочке:

$(make run) args to my command
Conrad.Dean
источник
4
оглядываясь назад на этот ответ два года спустя - почему я был настолько упрям, что не хотел использовать переменные окружения, и почему я думал, что лучше было встроить генерацию другой команды?
Conrad.Dean
0

Я нашел способ получить аргументы со знаком равенства (=)! Ответ является особенно дополнением к ответу @lesmana (поскольку он является наиболее полным и объясненным здесь), но он был бы слишком большим, чтобы написать его в качестве комментария. Я снова повторяю его сообщение: TL; DR, не пытайтесь это сделать!

Мне нужен был способ обработать мой аргумент --xyz-enabled=false(так как по умолчанию - true), который мы все уже знаем, что это не цель создания и, следовательно, не часть$(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
Сантон
источник