传递参数给“make run”;

我使用Makefiles。

我有一个名为run的目标,它运行构建目标。简化后,如下图所示:

prog: ....
...


run: prog
./prog

有传递参数的方法吗?这

make run asdf --> ./prog asdf
make run the dog kicked the cat --> ./prog the dog kicked the cat
357766 次浏览

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

make run arg1=asdf

然后像这样使用它们

run: ./prog $(arg1)
etc

参考make 微软的NMake < / p >

我不知道怎么做你想要的,但一个变通的方法可能是:

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)

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

  1. 作为参数:make arg1="asdf" run
  2. 或定义为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

或者:

$ FOO="the dog kicked the cat" make run
./prog the dog kicked the cat

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

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

但要小心陷阱。我将在下面详细说明这种方法的缺陷和其他缺陷。


回答你问题背后的假设意图:你想运行带有一些参数的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会将runfoo解释为目标(目标),根据它们的食谱进行更新。--wat作为make的选项。和var=arg作为变量赋值。

有关更多细节,请参阅Gnu目标手册:

术语


为什么我不推荐变量赋值

$ make run var=arg

以及配方中的变量

run: prog
./prog $(var)

这是最“正确”的;这是一种将参数传递给recipe的简单方法。但是,虽然它可以可以用来运行带参数的程序,但它肯定不是这样设计的。参见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

为了记录,这是我的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

所有这些都是我反对这条路线的原因。


为了完整起见,这里还有其他一些“传递参数使运行”的方法。

方法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)

如果目标是run,则删除第一个目标,并使用eval为其余目标创建什么都不做的目标。

这两种方法都可以写成这样

$ make run arg1 arg2

我建议阅读Gnu制作手册以获得更多细节。

方法一的问题:

  • 以破折号开头的参数将被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可以解决问题

  • 当你输入错误的合法目标时,它将被无声地忽略,因为catch all目标。

      $ make celan
    

    将会默默地忽略celan

    变通方法是让一切都变得冗长。你可以看到发生了什么。但是这会给合法的输出带来很多干扰。

方法二的问题:

  • 如果一个参数与一个现有的目标有相同的名字,make将打印一个警告,提示它正在被覆盖。

    据我所知没有变通办法

  • 带等号的参数仍将被make解释而不被传递

    没有解决方案

  • 带空格的参数仍然很尴尬

    没有解决方案

  • 带空格符的参数eval试图创建什么都不做的目标。

    解决方法:创建全局catch all目标,什么都不做。对于上述问题,它将再次无声地忽略错误输入的合法目标。

  • 它使用eval在运行时修改makefile。在可读性和可调试性方面,还有最小惊奇原则

    解决方法:不要!

我只使用gnu make进行了测试。其他品牌可能有不同的行为。

我使用的另一个技巧是-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"-参数”的部分似乎被颠倒了,“target"-参数”的顺序保持不变。我把“赋值”参数放在开头,希望你的程序不关心参数放在哪里。


下面的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,)

命令:

make greeting s="hello world"

输出:

hello world

idelic 回答上构建位。
你可以创建一个通用的“函数”;获取参数

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 := $(call 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)将被触发,导致创建两个伪造的hello目标,但有了额外的: (hello::) Make不会抱怨你重写了一个目标。