我使用Makefile。
我有一个名为run
的目标,它运行构建目标。简化后,它看起来像以下内容:
prog: ....
...
run: prog
./prog
是否有方法可以传递参数?这样就能
make run asdf --> ./prog asdf
make run the dog kicked the cat --> ./prog the dog kicked the cat
我使用Makefile。
我有一个名为run
的目标,它运行构建目标。简化后,它看起来像以下内容:
prog: ....
...
run: prog
./prog
是否有方法可以传递参数?这样就能
make run asdf --> ./prog asdf
make run the dog kicked the cat --> ./prog the dog kicked the cat
我不知道如何完全实现你想要的,但有一个变通的方法可能会有帮助:
run: ./prog
./prog $(ARGS)
然后:
make ARGS="asdf" run
# or
make run ARGS="asdf"
${},但GNU Make的手册指出:“要替换变量的值,请在括号或花括号中写入变量名称后面加上美元符号:
$(foo)'或${foo}'均可引用变量
foo'。”并给出了只使用$()的示例。好吧。 - Jakob Borg$()
以尝试获得对nmake的可移植性有多大意义。自1970年代以来,所有POSIX make的实现都支持$()
和${}
,并且它们在每个方面都完全相同。从未有过任何建议认为其中一个比另一个更好。通常,如果内容包含()
字符,则选择${}
以消除歧义。 - MadScientist这个问题已经存在近三年了,但无论如何...
如果你正在使用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
prog foo bar --baz
。 - ingydotnet$(eval $(RUN_ARGS):dummy;@:)
,没有定义虚拟目标。 - Lucas Cimonifneq ($(findstring $(firstword $(MAKECMDGOALS)),$(TARGETS)),)
的内容。 - Idelic@:
,我知道$(RUN_ARGS):;
只是创建一个空目标,但是@:
有什么必要呢? - AntonioCS#define ARG1 asdf
。虽然我会称之为编译时常量、“井号定义”或类似的东西,因为它实际上并不是一个宏(因为它只扩展到一个常量,而不是一条语句/一组语句)。 - SO_fix_the_vote_sorting_bugTL;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
run: prog
./prog $(var)
$ make run var=arg
这将执行./prog arg
。
这是“传递参数给配方”的最正确和直接的方法。请参阅GNU Make手册中关于覆盖变量的部分。
但是,虽然它可以用于带参数运行程序,但它肯定不是用于这种方式的。
让我详细说明一些问题。
你想要做的是用参数arg
运行prog
。但是,你不应该写成:
$ ./prog arg
$ make run var=arg
$ ./prog foo "bar baz"
$ make run var="foo bar\ baz"
或者
$ make run var="foo \"bar baz\""
$(var)
放在引号中。run: prog
./prog "$(var)"
prog
将始终只接收一个参数。
build_and_run_prog.sh
:
#! /bin/sh
# rebuild prog if necessary
make prog
# run prog with some arguments
./prog "$@"
$ ./build_and_run_prog.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
是make的一个选项。而var=arg
是make的一个变量赋值。
我希望你能看到,从命令行传递信息到配方中使用的唯一方法(无需使用技巧)是通过变量赋值。
有关更多详细信息,请参阅GNU手册中关于如何运行make的内容。
run: prog
./prog $(filter-out $@, $(MAKECMDGOALS))
%:
@true
prog
。同时创建一个无操作的通配目标(%
),以静默忽略所有其他"目标"。$ make run arg1 arg2
方法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
。
解决方法是使所有内容都显示详细信息,这样您就可以看到发生的情况。但这会为合法输出产生很多噪音。
ifeq (run, $(firstword $(MAKECMDGOALS)))
runargs := $(wordlist 2, $(words $(MAKECMDGOALS)), $(MAKECMDGOALS))
$(eval $(runargs):;@true)
endif
run:
./prog $(runargs)
如果一个参数与现有的目标具有相同的名称,那么make将打印一个警告,表示正在被覆盖。
我不知道任何解决方法
带有等号的参数仍将由make解释,而不是传递
没有解决方法
带有空格的参数仍然很尴尬
没有解决方法
带有空格的参数会破坏eval尝试创建无操作目标。
解决方法:创建一个全局的无操作目标,如上所述。但问题是它会再次默默地忽略错误输入的合法目标。
它使用eval在运行时修改makefile。在可读性、可调试性和最小惊讶原则方面,还能更糟糕吗?
解决方法:不要这样做!
我只测试过使用GNU make。其他make可能有不同的行为。
https://www.gnu.org/software/make/manual/html_node/index.html
您可以将变量传递给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
这里有另一种解决方案,可以帮助应对一些使用情况:
test-%:
$(PYTHON) run-tests.py $@
换句话说,选择一些前缀(在这种情况下为test-
),然后直接将目标名称传递给程序/运行器。我想如果有某个运行器脚本可以将目标名称解包成对底层程序有用的东西,那么这将非常有用。不可以。从GNU make的man页面中查看语法:
make [ -f makefile ] [ 选项 ] ... [ 目标 ] ...
你可以指定多个目标,因此“no”(至少不是以你指定的方式)。
您可以明确地在命令行中提取每个第n个参数。为此,您可以使用变量MAKECMDGOALS
,它保存了给“make”传递的命令行参数列表,解释为目标列表。如果要提取第n个参数,则可以将该变量与“word”函数结合使用。例如,如果要获取第二个参数,您可以将其存储在变量中,如下所示:
second_argument := $(word 2, $(MAKECMDGOALS) )
make: *** 没有生成目标“arg”的规则。停止。
- ThomasReggi%:
@echo 完成
- Larryrun: ./prog
看起来有点奇怪,因为右侧应该是一个前提条件,所以 run: prog
更好。
我建议简单地使用:
.PHONY: run
run:
prog $(arg1)
我想补充一下,参数可以通过以下方式传递:
make arg1="asdf" run
arg1="asdf" make run
.PHONY: {command}
,这是什么意思? - Dylan Pierce我不太为此感到自豪,但我不想传递环境变量,所以我反转了运行预定义命令的方式:
run:
@echo command-you-want
这将打印你想要运行的命令,所以只需在子shell中评估它:
$(make run) args to my command
make deploy version=<version>
将微服务部署到云中。我不希望任何人因错误而覆盖先前的工作版本,发现他们必须编辑环境变量时已经太晚了... - Fabien Haddadi