将参数传递给"make run"

ano*_*non 328 makefile

我使用Makefiles.

我有一个目标run,它运行构建目标.简化后,它看起来如下:

prog: ....
  ...

run: prog
  ./prog
Run Code Online (Sandbox Code Playgroud)

坐下来.我知道这是巧妙的,但不需要起立鼓掌.

现在,我的问题是 - 有没有办法传递参数?以便

make run asdf --> ./prog asdf
make run the dog kicked the cat --> ./prog the dog kicked the cat
Run Code Online (Sandbox Code Playgroud)

谢谢!

Jak*_*org 234

我不知道如何完全按照您的意愿行事,但解决方法可能是:

run: ./prog
    ./prog ${ARGS}
Run Code Online (Sandbox Code Playgroud)

然后:

make ARGS="asdf" run
Run Code Online (Sandbox Code Playgroud)

  • @Rob:$()更便携,它可以在Nmake和make中使用. (25认同)
  • 也许这是古老的.我一直使用$ {},但是GNU Make的手册指出"要替换变量的值,写一个美元符号后跟括号或大括号中变量的名称:$(foo)'或`$ { foo}'是对变量`foo'的有效引用." 并继续给出仅使用$()的示例.呃,好吧. (10认同)
  • @Rob:Nmake从未支持$ {}进行宏扩展,现在它似乎是一个古老的形式.我看过的每个在线教程都推荐使用$().$()也与bash等其他工具更加一致. (7认同)
  • 欢呼约翰和冷静,我回去看到这个建议来自我的第一版OReilly书"管理项目与制作"的副本.作者陈述了关于存档替换的规则,使用()和宏可以做到这两个,但建议使用{}来区分.但是......新版本现在改名为"使用GNU Make管理项目"使用().去图....猜猜我必须现代化!( - :我仍然对{}的MS NMake barf感到惊讶. (7认同)

Ide*_*lic 182

这个问题差不多有三年了,但无论如何......

如果你正在使用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)
Run Code Online (Sandbox Code Playgroud)

运行这个给出:

$ make run foo bar baz
prog foo bar baz
Run Code Online (Sandbox Code Playgroud)

  • 它也适用于那种情况,但你必须告诉`make`不要将`--baz`解释为命令行选项:`make - prog foo bar --baz`.` - `的意思是"在此之后的所有内容都是一个参数,而不是一个选项". (18认同)
  • 这很棒,但是似乎不适用于以破折号开头的参数:`prog foo bar --baz` (2认同)
  • 蓝眼睛好点!但是有一个解决方案:用`$(eval $(RUN_ARGS):dummy;@:)`替换'eval'行,没有定义虚拟目标。 (2认同)
  • 我不明白的一件事是“@:”我得到“$(RUN_ARGS):;”,它只是创建一个空目标,但是“@:”需要什么? (2认同)

Joh*_*ler 45

对于标准make,您可以通过定义这样的宏来传递参数

make run arg1=asdf
Run Code Online (Sandbox Code Playgroud)

然后像这样使用它们

run: ./prog $(arg1)
   etc
Run Code Online (Sandbox Code Playgroud)

参考 微软的NMAKE


ken*_*orb 31

您可以将变量传递给Makefile,如下所示:

run:
    @echo ./prog $$FOO
Run Code Online (Sandbox Code Playgroud)

用法:

$ make run FOO="the dog kicked the cat"
./prog the dog kicked the cat
Run Code Online (Sandbox Code Playgroud)

要么:

$ FOO="the dog kicked the cat" make run
./prog the dog kicked the cat
Run Code Online (Sandbox Code Playgroud)

或者使用Beta提供的解决方案:

run:
    @echo ./prog $(filter-out $@,$(MAKECMDGOALS))
%:
    @:
Run Code Online (Sandbox Code Playgroud)

%: - 符合任何任务名称的规则; @: - 空食谱=什么也不做

用法:

$ make run the dog kicked the cat
./prog the dog kicked the cat
Run Code Online (Sandbox Code Playgroud)


les*_*ana 17

TL; DR不会尝试这样做

$ make run arg
Run Code Online (Sandbox Code Playgroud)

而是创建脚本:

#! /bin/sh
# rebuild prog if necessary
make prog
# run prog with some arguments
./prog "$@"
Run Code Online (Sandbox Code Playgroud)

这样做:

$ ./buildandrunprog.sh arg
Run Code Online (Sandbox Code Playgroud)

回答所述问题:

你可以在食谱中使用一个变量

run: prog
    ./prog $(var)
Run Code Online (Sandbox Code Playgroud)

然后将变量赋值作为参数传递给make

$ make run var=arg
Run Code Online (Sandbox Code Playgroud)

这将执行./prog arg.

但要小心陷阱.我将详细说明这种方法的缺陷以及其他方法.


回答问题背后的假设意图:

假设:您希望运行prog一些参数,但必须在运行之前重建它.

答案:创建一个脚本,必要时重建然后用args运行prog

#! /bin/sh
# rebuild prog if necessary
make prog
# run prog with some arguments
./prog "$@"
Run Code Online (Sandbox Code Playgroud)

这个脚本使意图非常明确.它使用make来做它有用的东西:建筑.它使用shell脚本来执行它的优点:批处理.

调用语法现在几乎完全相同:

$ ./buildandrunprog.sh foo "bar baz"
Run Code Online (Sandbox Code Playgroud)

相比于:

$ ./prog foo "bar baz"
Run Code Online (Sandbox Code Playgroud)

此外,您可以使用shell脚本的完全灵活性和表现力来执行您可能需要的任何其他操作,而无需makefile的所有注意事项.


背景:

make不是为运行目标而设计的,而是将参数传递给该目标.命令行上的所有参数都被解释为目标(也称为目标),选项或变量赋值.

所以如果你运行这个:

$ make run foo bar --wat var=arg
Run Code Online (Sandbox Code Playgroud)

make将解释run,foobar作为目标(目标)根据他们的食谱进行更新.--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 Code Online (Sandbox Code Playgroud)

和食谱中的变量

run: prog
    ./prog $(var)
Run Code Online (Sandbox Code Playgroud)

这是将参数传递给配方的最"正确"和直接的方法.但是虽然它可以用来运行带有参数的程序,但它当然不是设计成以这种方式使用的.请参阅https://www.gnu.org/software/make/manual/html_node/Overriding.html#Overriding

在我看来,这有一个很大的缺点:你想做的是prog用参数运行arg.但不是写作:

$ ./prog arg
Run Code Online (Sandbox Code Playgroud)

你在写:

$ make run var=arg
Run Code Online (Sandbox Code Playgroud)

当尝试传递包含空格的多个参数或参数时,这会变得更加尴尬:

$ make run var="foo bar\ baz"
./prog foo bar\ baz
argcount: 2
arg: foo
arg: bar baz
Run Code Online (Sandbox Code Playgroud)

相比于:

$ ./prog foo "bar baz"
argcount: 2
arg: foo
arg: bar baz
Run Code Online (Sandbox Code Playgroud)

为了记录这是我的prog样子:

#! /bin/sh
echo "argcount: $#"
for arg in "$@"; do
  echo "arg: $arg"
done
Run Code Online (Sandbox Code Playgroud)

请注意,当您$(var)在makefile中输入引号时:

run: prog
    ./prog "$(var)"
Run Code Online (Sandbox Code Playgroud)

然后prog总会得到一个参数:

$ make run var="foo bar\ baz"
./prog "foo bar\ baz"
argcount: 1
arg: foo bar\ baz
Run Code Online (Sandbox Code Playgroud)

所有这些都是我推荐这条路线的原因.


为了完整性,这里有一些"传递参数来运行"的其他方法.

方法1:

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

%:
    @true
Run Code Online (Sandbox Code Playgroud)

超短解释:从目标列表中筛选出当前目标.create catch all target(%),它不会无声地忽略其他目标.

方法2:

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

run:
    ./prog $(runargs)
Run Code Online (Sandbox Code Playgroud)

超短解释:如果目标是run删除第一个目标并创建为剩余目标创建任何目标使用eval.

为了更深入的解释研究制作手册:https://www.gnu.org/software/make/manual/html_node/index.html

方法1的问题:

方法2的问题:

  • 如果参数与现有目标具有相同的名称,那么make将打印一条警告,表明它已被覆盖.

    没有我知道的解决方法

  • 用空格传递参数仍然很尴尬.

    没有解决方法

  • eval尝试创建空间中断的参数不做任何目标.

    解决方法:创建全局捕获所有目标,如上所述.如上所述,它将再次默默地忽略错误的合法目标.

  • 它用于eval在运行时修改makefile.在可读性和可调试性以及最小惊讶原则方面你能走多远.

    解决方法:不要这样做!! 1而是编写一个运行make然后运行的shell脚本prog.

我只测试过使用gnu make.其他品牌可能会有不同的行为.


TL; DR不会尝试这样做

$ make run foo bar clean
Run Code Online (Sandbox Code Playgroud)

而是创建脚本:

$ make celan
Run Code Online (Sandbox Code Playgroud)

这样做:

$ make run arg
Run Code Online (Sandbox Code Playgroud)


djc*_*djc 12

这是另一个可以帮助解决其中一些用例的解决方案:

test-%:
    $(PYTHON) run-tests.py $@
Run Code Online (Sandbox Code Playgroud)

换句话说,选择一些前缀(test-在这种情况下),然后将目标名称直接传递给程序/运行器.我想如果涉及一些可以将目标名称解包为对底层程序有用的东西的转子脚本,这大部分都是有用的.

  • 你也可以使用`$*'传入匹配`%`的目标部分. (2认同)

ziy*_*iya 9

不.查看GNU make手册页的语法

make [-f makefile] [options] ... [targets] ......

你可以指定多个目标,因此'不'(至少没有你指定的确切方式).


dma*_*a_k 7

run: ./prog看起来有点奇怪,因为正确的部分应该是先决条件,所以run: prog看起来更好。

我简单地建议:

.PHONY: run

run:
    prog $(arg1)
Run Code Online (Sandbox Code Playgroud)

我想补充一点,可以传递参数:

  1. 作为参数:make arg1="asdf" run
  2. 或定义为环境:arg1="asdf" make run

  • @DylanPierce 检查[这个答案](/sf/answers/4112635971/)。简而言之:左侧是目标,通常是文件。`.PHONY` 禁用文件的时间戳检查,否则如果您创建空的 `run` 文件,目标命令将不会执行。 (2认同)

小智 6

您可以在命令行中显式提取每个第 n 个参数。为此,您可以使用 variable MAKECMDGOALS,它保存提供给“make”的命令行参数列表,它将其解释为目标列表。如果要提取第n个参数,可以将该变量与“word”函数结合使用,例如,如果要提取第二个参数,则可以将其存储在变量中,如下所示:

second_argument := $(word 2, $(MAKECMDGOALS) )
Run Code Online (Sandbox Code Playgroud)


Con*_*ean 5

对此并不太自豪,但我不想传递环境变量,所以我反转了运行预设命令的方式:

run:
    @echo command-you-want
Run Code Online (Sandbox Code Playgroud)

这将打印您想要运行的命令,因此只需在子 shell 中对其进行评估即可:

$(make run) args to my command
Run Code Online (Sandbox Code Playgroud)

  • 两年后回顾这个答案——为什么我如此固执地不想使用环境变量,为什么我认为内联另一个命令的生成更好? (10认同)