从命令行传递附加变量给make

925

我可以将变量作为命令行参数传递给GNU Makefile吗?换句话说,我想传递一些参数,最终这些参数将成为Makefile中的变量。

8个回答

1023

您有几种选项可以从外部设置变量到您的makefile中:

  • From environment - each environment variable is transformed into a makefile variable with the same name and value.

    You may also want to set -e option (aka --environments-override) on, and your environment variables will override assignments made into makefile (unless these assignments themselves use the override directive . However, it's not recommended, and it's much better and flexible to use ?= assignment (the conditional variable assignment operator, it only has an effect if the variable is not yet defined):

    FOO?=default_value_if_not_set_in_environment
    

    Note that certain variables are not inherited from environment:

    • MAKE is gotten from name of the script
    • SHELL is either set within a makefile, or defaults to /bin/sh (rationale: commands are specified within the makefile, and they're shell-specific).
  • From command line - make can take variable assignments as part of his command line, mingled with targets:

    make target FOO=bar
    

    But then all assignments to FOO variable within the makefile will be ignored unless you use the override directive in assignment. (The effect is the same as with -e option for environment variables).

  • Exporting from the parent Make - if you call Make from a Makefile, you usually shouldn't explicitly write variable assignments like this:

    # Don't do this!
    target:
            $(MAKE) -C target CC=$(CC) CFLAGS=$(CFLAGS)
    

    Instead, better solution might be to export these variables. Exporting a variable makes it into the environment of every shell invocation, and Make calls from these commands pick these environment variable as specified above.

    # Do like this
    CFLAGS=-g
    export CFLAGS
    target:
            $(MAKE) -C target
    

    You can also export all variables by using export without arguments.


19
要从命令行传递带有空格的内容,请使用make A='"as df"'。注意保留原指令意思且不添加解释。 - Ciro Santilli OurBigBook.com
7
如果你关心环境变量,似乎就是在自找麻烦。首先,如果在A地方可以运行而在B地方却不能,仅仅因为它们有不同的环境,那么这将成为一个调试噩梦。 - James Moore
17
仅基于经验,像导出CFLAGS这样的东西对于大型项目来说是一种噩梦。大型项目通常有第三方库,只能使用给定的一组标志进行编译(没有人会去修复它们)。如果您导出了CFLAGS,则您项目的CFLAGS将覆盖第三方库的,并引发编译错误。另一种替代方法可能是定义 export PROJECT_MAKE_ARGS = CC=$(CC) CFLAGS=$(CFLAGS) 并将其作为 make -C folder $(PROJECT_MAKE_FLAGS) 传递。如果有一种方法可以告诉库的Makefile忽略环境变量,那就太理想了(与-e相反)。 - R.D.
37
警告:在上面的“从父Make导出”部分中,“不要这样做!”是极其误导的。在命令行上传递变量会覆盖子Makefile中的赋值,但导出的变量并不会覆盖子Makefile中的赋值。这两种将变量传递给子Makefile的方法是不等价的,不应混淆。 - Jonathan Ben-Avraham
14
“make target FOO=bar” 和 “make FOO=bar target” 有什么区别? - gfan
显示剩余9条评论

392

最简单的方法是:

make foo=bar target

在你的makefile中,你可以引用$(foo)。请注意,这不会自动传播到子make。

如果你正在使用子make,请参阅本文:将变量传递给子make


2
你的意思是指在主makefile中包含(included)的makefile吗? - Pablo
2
@Michael:这意味着从makefile内部再次调用make。由于您似乎对此细节感兴趣,我已更新了我的答案。 - Mark Byers
4
请注意,这不会自动传播到子Make文件。不正确!默认情况下,只有来自环境变量或命令行的变量会传递到递归调用中。您可以使用“export”指令传递其他变量。详情请参考:https://www.gnu.org/software/make/manual/html_node/Environment.html#Environment - ThomasMcLeod
如果您查看所引用的页面,您会发现它明确表示_make_自动传递在命令行上定义的变量值,将它们放入_MAKEFLAGS变量中。请参见选项/递归。因此,与这个答案相反,_make_将自动传播到子_make_。 - MadScientist
2
make目标foo=bar也可以工作! - itirazimvar
在Windows上设置foo=bar && make - superbem

187

假设你有一个像这样的makefile:

action:
    echo argument is $(argument)

然后你会调用它:make action argument=something


7
因此,目标和参数在位置上可以互换吗? - Pablo
7
@Michael:是的(请参见Mark Byers的答案)。 - nc3b
2
我喜欢这个答案。它简洁明了,信息量也很丰富。正是我所需要的。谢谢。 - Jonny Brooks

41

看起来命令参数会覆盖环境变量。

Makefile:

send:
    echo $(MESSAGE1) $(MESSAGE2)

示例运行:

$ MESSAGE1=YES MESSAGE2=NG  make send MESSAGE2=OK
echo YES OK
YES OK

35

根据手册的说明:

在make中,变量可以来源于运行make命令时所在的环境变量。每个在make启动时看到的环境变量都会被转换为一个同名同值的make变量。但是,在makefile中进行显式赋值或使用命令参数会覆盖环境变量。

因此,您可以在bash中执行以下操作:

FOOBAR=1 make

这将在您的Makefile中生成一个名为FOOBAR的变量。


2
另一种方法在几乎所有情况下确实更好。我会把它留在这里以保证完整性。 - Thomas
3
这是唯一一个在同一行上使用 make 命令之前分配变量的答案——值得知道这不是语法错误。 - M_M
5
需要理解的细微差别是,在目标前面进行赋值,你正在为make子进程设置一个环境变量。在目标后面进行赋值,你正在向make传递一个参数,它会解析它并覆盖环境中已有的内容。 - user615501

8

这里还有一种未被提及的选项,收录在Stallman和McGrath的GNU Make书中(参见http://www.chemie.fu-berlin.de/chemnet/use/info/make/make_7.html)。它提供了以下示例:

archive.a: ...
ifneq (,$(findstring t,$(MAKEFLAGS)))
        +touch archive.a
        +ranlib -t archive.a
else
        ranlib archive.a
endif

这涉及验证给定参数是否出现在MAKEFLAGS中。例如,假设您正在学习C++11中的线程,并将您的研究分成多个文件(class01,...,classNM),您希望编译所有文件并单独运行或者如果指定了标志(例如-r)则逐个编译并运行。因此,您可以使用以下Makefile

CXX=clang++-3.5
CXXFLAGS = -Wall -Werror -std=c++11
LDLIBS = -lpthread

SOURCES = class01 class02 class03

%: %.cxx
    $(CXX) $(CXXFLAGS) -o $@.out $^ $(LDLIBS)
ifneq (,$(findstring r,  $(MAKEFLAGS)))
    ./$@.out
endif

all: $(SOURCES)

.PHONY: clean

clean:
    find . -name "*.out" -delete

有了这些,你可以:

  • 使用make -r class02来构建和运行文件;
  • 使用makemake all来构建所有内容;
  • 使用make -r来构建并运行所有内容(假设它们都包含某种特定类型的断言内容,你只想测试它们)。

5
如果你创建一个名为Makefile的文件,并添加一个变量,如 $(unittest),那么你就可以在Makefile中使用这个变量,甚至还能与通配符一起使用。
例如:
make unittest=*

我使用BOOST_TEST,通过给参数--run_test=$(unittest)赋予通配符, 从而可以使用正则表达式来过滤出我想要Makefile运行的测试。


4
export ROOT_DIR=<path/value>

然后在Makefile中使用变量$(ROOT_DIR)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接