TL;DR 不要尝试这样做
$ make run arg
改为创建脚本build_and_run_prog.sh:
#! /bin/sh
# rebuild prog if necessary
make prog
# run prog with some arguments
./prog "$@"
然后这样做:
$ ./build_and_run_prog.sh arg
继续阅读以了解为什么这是最合理的选择以及为什么最好避免使用其他替代方案
对上述问题的回答:如何将参数传递给 make 目标
您可以在配方中使用变量
run: prog
./prog $(var)
然后将变量赋值作为参数传递给 make
$ make run var=arg
这将执行./prog arg。
但要小心陷阱。我将在下面详细说明这种方法和其他方法的缺陷。
回答您的问题背后的假设意图:您希望使用一些参数运行 prog,但如果需要,请在运行前重建它。
创建一个脚本,必要时重建,然后使用 args 运行 prog
build_and_run_prog.sh:
#! /bin/sh
# rebuild prog if necessary
make prog
# run prog with some arguments
./prog "$@"
这个脚本的意图非常明确。它使用 make 来做它擅长的事情:构建。它使用一个 shell 脚本来做它擅长的事情:批处理。
此外,您还可以利用 shell 脚本的充分灵活性和表现力做任何您可能需要的事情,而无需使用 makefile 的所有警告。
此外,调用语法现在几乎相同:
$ ./build_and_run_prog.sh foo "bar baz"
相比:
$ ./prog foo "bar baz"
对比
$ make run var="foo bar\ baz"
make 如何处理参数的背景说明:
Make 并非旨在将参数传递给目标。命令行上的所有参数都被解释为目标(也称为目标)、选项或变量赋值。
所以如果你运行这个:
$ make run foo --wat var=arg
make 会将run 和foo 解释为目标(targets),以根据他们的配方进行更新。 --wat 作为 make 的一个选项。并将var=arg 作为变量赋值。
更多详情见gnu manual on goals (targets):
还有terminology。
为什么我不建议使用变量赋值
$ make run var=arg
和配方中的变量
run: prog
./prog $(var)
这是将参数传递给配方的最“正确”和直接的方法。但是,虽然它可以用于运行带有参数的程序,但它肯定不是为那样使用而设计的。见gnu manual on 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
还请注意,您不应在 makefile 中将 $(var) 放在引号中:
run: prog
./prog "$(var)"
因为prog 将永远只得到一个参数:
$ make run var="foo bar\ baz"
./prog "foo bar\ baz"
argcount: 1
arg: foo bar\ baz
这就是我不推荐这条路线的原因。
为了完整起见,这里有一些“传递参数以使运行”的其他方法。
方法一:
run: prog
./prog $(filter-out $@, $(MAKECMDGOALS))
%:
@true
从目标列表中过滤掉当前目标。创建捕获所有目标 (%),它不会默默地忽略其他目标。
方法二:
ifeq (run, $(firstword $(MAKECMDGOALS)))
runargs := $(wordlist 2, $(words $(MAKECMDGOALS)), $(MAKECMDGOALS))
$(eval $(runargs):;@true)
endif
run:
./prog $(runargs)
如果目标是run,则删除第一个目标并使用eval为剩余目标创建无操作目标。
两者都允许你写这样的东西
$ make run arg1 arg2
我建议阅读gnu manual on make 了解更多详情。
方法一的问题:
-
以破折号开头的参数将由 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 在运行时修改makefile。在可读性和可调试性以及Principle of least astonishment 方面,你还能走多远。
解决方法:不要!
我只用 gnu make 测试过。其他品牌可能有不同的行为。