将参数传递给“ make run”


354

我使用Makefiles。

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

prog: ....
  ...

run: prog
  ./prog

有什么方法可以传递参数吗?以便

make run asdf --> ./prog asdf
make run the dog kicked the cat --> ./prog the dog kicked the cat

谢谢!


Answers:


264

我不知道完全按照自己的意愿做事的方法,但是一种解决方法可能是:

run: ./prog
    ./prog $(ARGS)

然后:

make ARGS="asdf" run
# or
make run ARGS="asdf"

27
@Rob:$()更具可移植性,它既可以在Nmake中使用,也可以在Nmake中使用。
约翰·诺勒

7
@Rob:Nmake从未支持$ {}进行宏扩展,它似乎是make中的一种古老形式。我看过的每个在线教程都推荐$()。$()也与其他工具(例如bash)更加一致。
约翰·诺勒

10
也许是过时的。我一直使用$ {},但是GNU Make的手册指出:“要替换变量的值,请在括号或花括号中写一个美元符号,后跟该变量的名称:$(foo)' or $ {foo}'是对变量“ foo”。” 并继续给出仅使用$()的示例。呃,好吧。
雅各布·伯格

7
约翰和卡芙尔打起精神,我回过头来,发现这个建议来自我的第一本OReilly书《用Make管理项目》。作者陈述了使用()和能够同时使用两者的宏来进行归档替换的规则,但建议使用{}进行区分。但是....新版本现在改名为“使用GNU Make管理项目”,在全文中都使用()。去吧...。猜猜我得现代化!( - :我还是很吃惊,因为在{}“MS NMAKE barfsš虽然。
罗布·威尔斯

1
@xealits这肯定是-有在这个问题的例子在这里
helvete

198

这个问题已经快三年了,但无论如何...

如果您使用的是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

2
这很棒,但似乎不适用于以破折号开头的参数:prog foo bar --baz
ingydotnet

22
在这种情况下,它也确实起作用,但是您必须告诉您make不要将其解释--baz为命令行选项:make -- prog foo bar --baz。的--意思是“在这之后一切都是一个说法,不是一种选择”。
Idelic 2013年

我将如何定义RUN_ARGS使用它的默认值?
Bouke

也许在上添加一个else分支ifeq并在RUN_ARGS那里设置?
Idelic 2014年

1
好点的蓝眼睛!但是有一个解决方案:用替换“ eval”行$(eval $(RUN_ARGS):dummy;@:),而未定义虚拟目标。
卢卡斯·西蒙

52

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

make run arg1=asdf

然后像这样使用它们

run: ./prog $(arg1)
   etc

使 Microsoft的NMake的参考


34

您可以将变量传递给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

22

TL; DR不要尝试这样做

$ make run arg

而是创建脚本:

#! /bin/sh
# rebuild prog if necessary
make prog
# run prog with some arguments
./prog "$@"

并执行以下操作:

$ ./buildandrunprog.sh arg

回答所述问题:

您可以在配方中使用变量

run: prog
    ./prog $(var)

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

$ make run var=arg

这将执行./prog arg

但是要当心陷阱。我将详细介绍该方法以及其他方法的陷阱。


回答问题背后的预期意图:

假设:您想prog使用一些参数来运行,但是在必要时在运行之前对其进行重建。

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

#! /bin/sh
# rebuild prog if necessary
make prog
# run prog with some arguments
./prog "$@"

该脚本使意图非常明确。它使用make来完成其擅长的工作:构建。它使用Shell脚本执行其擅长的功能:批处理。

另外,您可以在没有makefile的所有警告的情况下,利用shell脚本的完全灵活性和表现力来完成任何您可能需要做的事情。

现在调用语法实际上是相同的:

$ ./buildandrunprog.sh foo "bar baz"

相比于:

$ ./prog foo "bar baz"

相比较

$ make run var="foo bar\ baz"

背景:

make并非旨在将参数传递给目标。命令行上的所有参数都被解释为目标(也称为目标),选项或变量赋值。

因此,如果运行此命令:

$ make run foo --wat var=arg

make将根据其配方来解释runfoo作为目标(目标)进行更新。--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: prog
    ./prog $(var)

这是将参数传递给配方的最“正确”和直接的方法。但是,尽管可以将其用于运行带有参数的程序,但它并非旨在以这种方式使用。参见https://www.gnu.org/software/make/manual/html_node/Overriding.html#Overriding

在我看来,这有一个很大的缺点:您想要执行的操作是prog使用arguments 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

还请注意,您不应$(var)在makefile中加引号:

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

超简短说明:从目标列表中过滤出当前目标。create 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

要进行更深入的说明,请学习make手册:https : //www.gnu.org/software/make/manual/html_node/index.html

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

    解决方法是使所有内容变得冗长。所以你会看到发生了什么。但这会给合法输出带来很多噪音。

方法2的问题

  • 如果参数的名称与现有目标的名称相同,那么make将显示警告,指出该参数已被覆盖。

    我所知道的没有解决方法

  • 等号的参数仍将由make解释且不会传递

    没有解决方法

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

    没有解决方法

  • eval试图创建空格的参数不会执行任何操作。

    解决方法:创建全局捕获所有目标,而不执行上述操作。由于存在上述问题,它将再次默默忽略错误键入的合法目标。

  • 它用于eval在运行时修改makefile。就可读性和可调试性以及最小惊讶原理而言,您能差多少?

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

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


TL; DR不要尝试这样做

$ make run arg

而是创建脚本:

#! /bin/sh
# rebuild prog if necessary
make prog
# run prog with some arguments
./prog "$@"

并执行以下操作:

$ ./buildandrunprog.sh arg

12

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

test-%:
    $(PYTHON) run-tests.py $@

换句话说,选择一些前缀(test-在这种情况下),然后将目标名称直接传递给程序/运行程序。我猜想,如果涉及到一些运行程序脚本,这些脚本可以将目标名称解包为对基础程序有用的内容,那么这最有用。


2
您还可以使用$*来传递仅与匹配的目标部分%
Malvineous

9

否。在手册页中查看GNU make的语法

制作[-f makefile] [选项] ... [目标] ...

您可以指定多个目标,因此为“ no”(至少以您指定的确切方式为no)。


4

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

second_argument := $(word 2, $(MAKECMDGOALS) )

这也会对该参数运行make命令。make: *** No rule to make target 'arg'. Stop.
ThomasReggi 2016年

2

anonrun: ./prog看起来有些奇怪,因为正确的部分应该是目标,所以run: prog看起来更好。

我只建议:

.PHONY: run

run:
        prog $(arg1)

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

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

2

这是我的例子。请注意,我正在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

2

对此并不感到骄傲,但是我不想传入环境变量,因此我改变了运行固定命令的方式:

run:
    @echo command-you-want

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

$(make run) args to my command

4
两年后回头看这个答案-为什么我如此固执以至于我不想使用环境变量,为什么我认为内联另一个命令的生成会更好?
Conrad.Dean

0

我找到了一种方法来获取带有等号(=)的参数!答案尤其是对@lesmana答案的补充(因为这是最完整的说明),但是将其写为注释太大了。再次,我重复他的信息:TL; DR不要尝试这样做!

我需要一种方法来处理我的参数--xyz-enabled=false(因为默认值是true),到现在我们都知道这不是make目标,因此也不是的一部分$(MAKECMDGOALS)

通过回显i 查看所有make变量时,$(.VARIABLES)得到了这些有趣的输出:

[...] -*-command-variables-*- --xyz-enabled [...]

这使我们可以采用两种方式:要么全部以一个--(如果适用于您的情况)开始,要么研究GNU make specific(可能不打算供我们使用)变量-*-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

如您所见,我们放宽了args的总顺序。带有“赋值” -args的部分似乎已颠倒,“目标” -args的顺序得以保留。我在一开始就放置了“ assignment” -args,希望您的程序不在乎参数的放置位置。


更新:以下使变量看起来也很有希望:

MAKEFLAGS =  -- isit=alwaysreverse? --foo=bar over=9000 --xyz-enabled=false
MAKEOVERRIDES = isit=alwaysreverse? --foo=bar over=9000 --xyz-enabled=false

-2

我使用的另一个技巧是-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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.