关于makefile:将参数传递给“make run”

Passing arguments to “make run”

我使用makefiles。

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

1
2
3
4
5
prog: ....
  ...

run: prog
  ./prog

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

现在,我的问题是——有什么方法可以传递论点吗?以便

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

谢谢!


我不知道做你想做的事情的具体方法,但解决方法可能是:

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

然后:

1
make ARGS="asdf" run


这个问题已经快三岁了,但无论如何…

如果您使用的是GNU make,这很容易做到。唯一的问题是,make将命令行中的非选项参数解释为目标。解决办法是把他们变成什么都不做的目标,所以make不会抱怨:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 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)

运行此命令将提供:

1
2
$ make run foo bar baz
prog foo bar baz


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

1
make run arg1=asdf

然后像这样使用它们

1
2
run: ./prog $(arg1)
   etc

制造参考微软的nmake


您可以将变量传递到makefile,如下所示:

1
2
run:
    @echo ./prog $$FOO

用途:

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

或:

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

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

1
2
3
4
run:
    @echo ./prog $(filter-out $@,$(MAKECMDGOALS))
%:
    @:

%: - rule which match any task name;
@: - empty recipe = do nothing

用途:

1
2
$ make run the dog kicked the cat
./prog the dog kicked the cat


医生不要这样做好的。

1
$ make run arg

而是创建脚本:好的。

1
2
3
4
5
#! /bin/sh
# rebuild prog if necessary
make prog
# run prog with some arguments
./prog"$@"

这样做:好的。

1
$ ./buildandrunprog.sh arg

回答上述问题:好的。

您可以在配方中使用变量好的。

1
2
run: prog
    ./prog $(var)

然后将变量赋值作为参数传递给好的。

1
$ make run var=arg

这将执行./prog arg。好的。

但要小心陷阱。我将详细说明这个方法和其他方法的缺陷。好的。

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

假设:您希望使用一些参数运行prog,但如果需要,请在运行之前重新构建它。好的。

答案是:创建一个脚本,在必要时重建,然后用参数运行prog好的。

1
2
3
4
5
#! /bin/sh
# rebuild prog if necessary
make prog
# run prog with some arguments
./prog"$@"

这个剧本使意图非常清楚。它用make来做它有益的事情:建筑。它使用一个shell脚本来完成它所擅长的工作:批处理。好的。

另外,调用语法现在几乎是相同的:好的。

1
$ ./buildandrunprog.sh foo"bar baz"

比较:好的。

1
$ ./prog foo"bar baz"

另外,您可以用shell脚本的完全灵活性和表现力来做您可能需要的任何其他事情,而不需要makefile的所有警告。好的。

背景:好的。

make不用于运行目标并向该目标传递参数。命令行上的所有参数都被解释为目标(a.k.a.目标)、选项或变量分配。好的。

所以如果你运行这个:好的。

1
$ make run foo bar --wat var=arg

make将把runfoobar解释为目标(目标),根据其配方进行更新。--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好的。

关于变量赋值方法以及我为什么不推荐它好的。

1
$ make run var=arg

配方中的变量好的。

1
2
run: prog
    ./prog $(var)

这是向配方传递参数的最"正确"和最直接的方法。但是,虽然它可以用来运行带有参数的程序,但它的设计显然不是这样的。参见https://www.gnu.org/software/make/manual/html_node/overriding.html_overriding好的。

在我看来,这有一个很大的缺点:你想做的是用arg的论点运行prog。但不是写:好的。

1
$ ./prog arg

你在写:好的。

1
$ make run var=arg

当试图传递多个参数或包含空格的参数时,这会变得更加尴尬:好的。

1
2
3
4
5
$ make run var="foo bar\ baz"
./prog foo bar\ baz
argcount: 2
arg: foo
arg: bar baz

比较:好的。

1
2
3
4
$ ./prog foo"bar baz"
argcount: 2
arg: foo
arg: bar baz

据记载,这就是我的prog的样子:好的。

1
2
3
4
5
#! /bin/sh
echo"argcount: $#"
for arg in"$@"; do
  echo"arg: $arg"
done

请注意,当您在makefile中将$(var)放入引号时:好的。

1
2
run: prog
    ./prog"$(var)"

那么,prog总是只能有一个论点:好的。

1
2
3
4
$ make run var="foo bar\ baz"
./prog"foo bar\ baz"
argcount: 1
arg: foo bar\ baz

这就是我反对这条路线的原因。好的。

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

方法1:好的。

1
2
3
4
5
run: prog
    ./prog $(filter-out $@, $(MAKECMDGOALS))

%:
    @true

超短解释:从目标列表中过滤出当前目标。创建catch all目标(%),它不需要静默地忽略其他目标。好的。

方法2:好的。

1
2
3
4
5
6
7
ifeq (run, $(firstword $(MAKECMDGOALS)))
  runargs := $(wordlist 2, $(words $(MAKECMDGOALS)), $(MAKECMDGOALS))
  $(eval $(runargs):;@true)
endif

run:
    ./prog $(runargs)

超短解释:如果目标是run,那么删除第一个目标,并使用eval为剩余的目标创建"不做任何事情"的目标。好的。

为了更深入的解释,请学习make手册:https://www.gnu.org/software/make/manual/html_node/index.html好的。

方法1的问题:好的。

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

    1
    $ make run --foo --bar

    解决办法好的。

    1
    $ make run -- --foo --bar
  • 如果一个参数恰好是run(等于目标),它也将被删除。好的。

    1
    $ make run foo bar run

    将运行./prog foo bar,而不是./prog foo bar run好的。

    方法2可能的解决方法好的。

  • 如果一个参数是合法的目标,它也将运行。好的。

    1
    $ make run foo bar clean

    将运行./prog foo bar clean,但也将运行目标clean的配方(假设存在)。好的。

    方法2可能的解决方法好的。

  • 用空格传递参数很难好的。

    1
    $ make run foo"bar\ baz"

    没有解决办法好的。

  • 当您错误地输入合法的目标时,它将被静默忽略,因为会捕获所有目标。好的。

    1
    $ make celan

    只会默默地忽略celan。好的。

    解决方法是使所有事情都变得冗长。你看会发生什么。但这对合法的输出产生了很大的干扰。好的。

方法2的问题:好的。

  • 如果某个参数与现有目标同名,则make将打印一条警告,说明正在覆盖该参数。好的。

    我不知道有什么解决办法好的。

  • 用空格来传递论点仍然很难。好的。

    没有解决办法好的。

  • 带有空格的论点破坏了cx1〔2〕试图创建什么都不做的目标。好的。

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

  • 它使用eval在运行时修改makefile。在可读性和可调试性以及最小惊异原则方面,你能做得更糟。好的。

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

我只测试了使用GNU制造。其他品牌可能有不同的行为。好的。

医生不要这样做好的。

1
$ make run arg

而是创建脚本:好的。

1
2
3
4
5
#! /bin/sh
# rebuild prog if necessary
make prog
# run prog with some arguments
./prog"$@"

这样做:好的。

1
$ ./buildandrunprog.sh arg

好啊。


下面是另一个解决方案,可以帮助处理其中一些用例:

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

换句话说,选择一些前缀(本例中为test-),然后将目标名称直接传递给程序/运行程序。我想,如果涉及到一些运行程序脚本,可以将目标名称展开为对底层程序有用的内容,那么这将非常有用。


不。从gnu make的手册页查看语法

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

可以指定多个目标,因此为"否"(至少按指定的方式为"否")。


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

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


对此并不太自豪,但我不想传递环境变量,因此我颠倒了运行屏蔽命令的方式:

1
2
run:
    @echo command-you-want

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

1
$(make run) args to my command


这是我的例子。注意,我是在Windows7下编写的,使用dev cpp附带的mingw32-make.exe。(我有C:windowssystem32make.bat,所以该命令仍然称为"make"。)

1
2
3
4
clean:
    $(RM) $(OBJ) $(BIN)
    @echo off
    if"${backup}" NEQ"" ( mkdir ${backup} 2> nul && copy * ${backup} )

定期清洁的用途:

1
make clean

在mydir/中清理和创建备份的用法:

1
make clean backup=mydir

不,run: ./prog看起来有点奇怪,因为右边应该是目标,所以run: prog看起来更好。

我建议简单地:

1
2
3
4
.PHONY: run

run:
        prog $(arg1)

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

  • 作为论据:make arg1="asdf" run
  • 或定义为环境:arg1="asdf" make run

  • 我使用的另一个技巧是-n旗,它告诉make做一次空运行。例如,

    1
    2
    3
    4
    $ 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