将参数传递给“make run”

时间:2010-02-06 20:16:13

标签: makefile

我使用Makefile。

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

prog: ....
  ...

run: prog
  ./prog

坐下来。我知道这是巧妙的,但不需要起立鼓掌。

现在,我的问题是 - 有没有办法传递参数?那么

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

谢谢!

12 个答案:

答案 0 :(得分:210)

我不知道如何完全按照您的意愿行事,但解决方法可能是:

run: ./prog
    ./prog ${ARGS}

然后:

make ARGS="asdf" run

答案 1 :(得分:170)

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

如果你正在使用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 :(得分:42)

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

make run arg1=asdf

然后像这样使用它们

run: ./prog $(arg1)
   etc

make的参考资料 微软的NMake

答案 3 :(得分:14)

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 run var=arg

这将执行./prog arg

但要注意陷阱。我将详细阐述这种方法的缺陷以及其他方法。

回答问题背后的假设意图:

假设:您希望使用某些参数运行prog,但必须在运行之前重建它。

答案:创建一个脚本,必要时重建然后用args

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

这个脚本非常明确。它使用make来做它有用的东西:建筑。它使用shell脚本来完成它的优点:批处理。

现在调用语法几乎完全相同:

$ ./buildandrunprog.sh foo "bar baz"

比较:

$ ./prog foo "bar baz"

此外,您可以使用shell脚本的完全灵活性和表现力来执行您可能需要的任何其他操作,而无需makefile的所有注意事项。

背景:

make不是为运行目标而设计的,而是将参数传递给该目标。命令行中的所有参数都被解释为目标(a.k.a.target),选项或变量赋值。

所以如果你运行这个:

$ make run foo bar --wat var=arg

make会将runfoobar解释为目标(目标),以根据其食谱进行更新。 --wat作为make的选项。和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运行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

所有这些都是我推荐这条路线的原因。

为了完整性,这里有一些其他的方法来传递参数以进行运行"。

方法1:

run: prog
    ./prog $(filter-out $@, $(MAKECMDGOALS))

%:
    @true

超短解释:从目标列表中筛选出当前目标。创建捕获所有目标(%),它不会无声地忽略其他目标。

方法2:

ifeq (run, $(firstword $(MAKECMDGOALS)))
  runargs := $(wordlist 2, $(words $(MAKECMDGOALS)), $(MAKECMDGOALS))
  $(eval $(runargs):;@true)
endif

run:
    ./prog $(runargs)

超短解释:如果目标是run,则删除第一个目标,并使用eval为剩余目标创建不执行任何目标。

有关深入解释的研究手册:https://www.gnu.org/software/make/manual/html_node/index.html

方法1的问题:

  • 以破折号开头的参数将由make解释,而不是作为目标传递。

    $ make run --foo --bar
    

    解决方法

    $ make run -- --foo --bar
    
  • 如果参数恰好是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 run foo "bar\ baz"
    

    没有解决方法

  • 当您错误输入合法目标时,由于捕获所有目标,它将被默默忽略。

    $ make celan
    

    会默默地忽略celan

    解决方法是让一切都变得冗长。所以你看看会发生什么。但这会给合法输出带来很多噪音。

方法2的问题:

  • 如果参数与现有目标具有相同的名称,则make将打印一条警告,表明它已被覆盖。

    我不知道

  • 的解决方法
  • 用空格传递参数仍然很尴尬。

    没有解决方法

  • 空格分隔eval试图创建无任何目标的参数。

    解决方法:创建全局捕获所有目标,如上所述不执行任何操作。如上所述,它将再次默默地忽略错误的合法目标。

  • 它使用eval在运行时修改makefile。在可读性和可调试性以及Principle of least astonishment方面你能走多远。

    解决方法:不要这样做!! 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

答案 4 :(得分:10)

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

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

换句话说,选择一些前缀(在这种情况下为test-),然后将目标名称直接传递给程序/运行程序。我想如果涉及一些可以将目标名称解包为对底层程序有用的东西,那么这个程序非常有用。

答案 5 :(得分:9)

没有。查看GNU make的手册页中的语法

  

make [-f makefile] [options] ... [targets] ...

你可以指定多个目标,因此'不'(至少没有你指定的确切方式)。

答案 6 :(得分:4)

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

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

答案 7 :(得分:3)

对此并不感到骄傲,但我并不想传递环境变量,所以我颠倒了运行固定命令的方式:

run:
    @echo command-you-want

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

$(make run) args to my command

答案 8 :(得分:2)

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

我建议简单地说:

.PHONY: run

run:
        prog $(arg1)

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

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

答案 9 :(得分:2)

这是我的例子。请注意,我使用Dev-Cpp附带的mingw32-make.exe在Windows 7下编写。 (我有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

答案 10 :(得分:0)

我使用的另一个技巧是-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

答案 11 :(得分:0)

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

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

在回显$(.VARIABLES)的情况下all variables of make时,我得到了这些有趣的输出:

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

这使我们可以采用两种方式:要么全部以--开始(如果适用于您的情况),要么查看GNU make specific (probably not intended for us to use)变量-*-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