我使用Makefiles。
我有一个名为run的目标,它运行构建目标。简化后,如下图所示:
prog: ....
...
run: prog
./prog
有传递参数的方法吗?这
make run asdf --> ./prog asdf
make run the dog kicked the cat --> ./prog the dog kicked the cat
我使用Makefiles。
我有一个名为run的目标,它运行构建目标。简化后,如下图所示:
prog: ....
...
run: prog
./prog
有传递参数的方法吗?这
make run asdf --> ./prog asdf
make run the dog kicked the cat --> ./prog the dog kicked the cat
对于标准make,您可以通过这样定义宏来传递参数
make run arg1=asdf
然后像这样使用它们
run: ./prog $(arg1)
etc
制作参考资料 微软的NMake
我不知道怎么做你想要的,但一个变通的方法可能是:
run: ./prog
./prog $(ARGS)
然后:
make ARGS="asdf" run
# or
make run ARGS="asdf"
不。看看GNU make手册页中的语法
生成[-f makefile][选项]…[目标]……
您可以指定多个目标,因此是'no'(至少以您指定的确切方式为no)。
Run: ./prog看起来有点奇怪,因为正确的部分应该是一个先决条件,所以Run: prog看起来更好。
我的建议很简单:
.PHONY: run
run:
prog $(arg1)
我想补充一点,可以传递参数:
作为参数:使arg1="asdf"运行 或者定义为environment: arg1="asdf" make run
这个问题已经提了三年了,但是不管怎样……
如果您正在使用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
下面是我的例子。注意,我是在Windows 7下编写的,使用Dev-Cpp附带的mingw32-make.exe。(我有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
下面是另一个解决方案,可以帮助解决其中一些用例:
test-%:
$(PYTHON) run-tests.py $@
换句话说,选择一些前缀(在本例中是test-),然后将目标名称直接传递给程序/运行程序。我想,如果涉及到一些运行脚本,可以将目标名称展开为对底层程序有用的内容,那么这是非常有用的。
你可以像下面这样将变量传递给Makefile:
run:
@echo ./prog $$FOO
用法:
$ make run FOO="the dog kicked the cat"
./prog the dog kicked the cat
or:
$ 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
您可以显式地提取命令行中的第n个参数。要做到这一点,你可以使用变量MAKECMDGOALS,它保存给'make'的命令行参数列表,它将其解释为目标列表。如果你想提取第n个参数,你可以使用该变量与"word"函数结合使用,例如,如果你想要第二个参数,你可以将它存储在一个变量中,如下所示:
second_argument := $(word 2, $(MAKECMDGOALS) )
不是很自豪,但我不想传递环境变量,所以我把运行罐装命令的方式颠倒了:
run:
@echo command-you-want
这将打印你想要运行的命令,所以只需要在subshell中求值即可:
$(make run) args to my command
不要尝试这样做
$ 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。
但要小心陷阱。
最明显的是用空格传递参数时的尴尬。你必须这样做:
$ make run var="foo bar\ baz"
这将执行。/prog foo "bar baz"。
我将在下面详细说明这种方法的缺陷和其他缺陷。
回答您的问题背后的假定意图:您希望带一些参数运行prog,但在必要时运行之前重新构建它。
创建一个脚本,在必要时重新构建,然后运行prog和args
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解释为目标(目标),根据它们的食谱进行更新。作为制作的一个选项。var=arg作为make的变量赋值。
从命令行传递信息以在配方中使用的唯一机会是通过变量赋值。
有关如何运行make的更多细节,请参阅gnu手册
为什么我不推荐变量赋值
$ make run var=arg
以及配方中的变量
run: prog
./prog $(var)
这是将参数传递给配方的最“正确”和最直接的方式。但是,虽然它可以用来运行带参数的程序,但它肯定不是被设计成这样使用的。关于重写,请参阅gnu手册
在我看来,这有一个很大的缺点:你想做的是用参数arg运行prog。但我没有写:
$ ./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
为了记录,这是我的项目的样子:
#! /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
所有这些都是我反对这条路线的原因。
为了完整起见,这里有一些“传递参数使运行”的其他方法。
方法1:
run: prog
./prog $(filter-out $@, $(MAKECMDGOALS))
%:
@true
从目标列表中过滤出当前目标。创建catch all target(%),它不做任何事情来默默地忽略其他目标。
方法2:
ifeq (run, $(firstword $(MAKECMDGOALS)))
runargs := $(wordlist 2, $(words $(MAKECMDGOALS)), $(MAKECMDGOALS))
$(eval $(runargs):;@true)
endif
run:
./prog $(runargs)
如果目标正在运行,则删除第一个目标,并使用eval为其余目标创建什么都不做的目标。
这两种方法都可以写成这样
$ make run arg1 arg2
方法一的问题:
Arguments that start with a dash will be interpreted by make and not passed as a goal. $ make run --foo --bar workaround $ make run -- --foo --bar Arguments with an equal sign will be interpreted by make and not passed $ make run foo=bar no workaround Arguments with spaces is awkward $ make run foo "bar\ baz" no workaround If an argument happens to be run (equal to the target) it will also be removed $ make run foo bar run will run ./prog foo bar instead of ./prog foo bar run workaround possible with method 2 If an argument is a legitimate target it will also be run. $ make run foo bar clean will run ./prog foo bar clean but also the recipe for the target clean (assuming it exists). workaround possible with method 2 When you mistype a legitimate target it will be silently ignored because of the catch all target. $ make celan will just silently ignore celan. workaround is to make everything verbose. so you see what happens. but that creates a lot of noise for the legitimate output.
方法二的问题:
If an argument has same name as an existing target then make will print a warning that it is being overwritten. no workaround that I know of Arguments with an equal sign will still be interpreted by make and not passed no workaround Arguments with spaces is still awkward no workaround Arguments with space breaks eval trying to create do nothing targets. workaround: create the global catch all target doing nothing as above. with the problem as above that it will again silently ignore mistyped legitimate targets. it uses eval to modify the makefile at runtime. how much worse can you go in terms of readability and debugability and the Principle of least astonishment. workaround: don't!
我只使用gnu make进行了测试。其他品牌可能有不同的行为。
Gnu制作手册
https://www.gnu.org/software/make/manual/html_node/index.html
我使用的另一个技巧是-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
我找到了一种方法,用等号=!这个答案是对@lesmana的回答的补充(因为它在这里是最完整和解释最充分的回答),但是它太大了,不能作为评论来写。再一次,我重复他的信息:TL;DR不要试图这样做!
我需要一种方法来处理我的参数——xyz-enabled=false(因为默认为true),现在我们都知道这不是一个make目标,因此不是$(MAKECMDGOALS)的一部分。
当通过回显$(.VARIABLES)查看所有的make变量时,我得到了这些有趣的输出:
[...] -*-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
如你所见,我们打乱了参数的总顺序。带有“assignment”-args的部分似乎被颠倒了,“target”-args的顺序保持不变。我把赋值参数放在开头,希望你的程序不关心参数放在哪里。
下面的make变量看起来也很有希望:
MAKEFLAGS = -- isit=alwaysreverse? --foo=bar over=9000 --xyz-enabled=false
MAKEOVERRIDES = isit=alwaysreverse? --foo=bar over=9000 --xyz-enabled=false
它已经有一段时间了,但我将提供我在生产中使用的版本。
我希望有人会觉得它有用。
例子:
.PHONY: greeting
greeting:
echo $(if $s,$s,)
命令:
使问候s="hello world"
输出:
你好世界
建立在理想答案的基础上。 你可以创建一个通用的“函数”来获取参数 使用以下方法:
define fetch_parameter
$(eval target_name:= $(firstword $(MAKECMDGOALS)))
$(eval varname := $(target_name)_value)
$(eval $(varname) := $(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS)))
$(eval $($(varname))::;@:)
endef
然后你可以像这样在你的目标中使用它:
my-target: res := $(call fetch_parameter)
my-target: ## Example target. Usage: make my-target <value>
echo The value: $($@_value)
这样你就可以在任何你想检索值的目标上使用res:= $(调用fetch_parameter)。
注意:我在这段代码中添加了额外的:$(eval $($(varname))::;@:),因为如果你有多个调用fetch_parameter的目标,它也会被触发。 如果你有:
my-target: res := $(call fetch_parameter)
my-target: ## Example target. Usage: make my-target <value>
echo The value: $($@_value)
my-target2: res := $(call fetch_parameter)
my-target2: ## Example target. Usage: make my-target2 <value>
echo The value: $($@_value)
并且你调用make my-target2 hello, make的工作方式,$(call fetch_parameter)都将被触发,导致创建2个伪hello目标,但额外的:(hello::) make不会抱怨你覆盖了一个目标。