【问题标题】:Passing additional variables from command line to make从命令行传递额外的变量来制作
【发布时间】:2011-02-19 00:59:16
【问题描述】:

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

【问题讨论】:

    标签: makefile gnu command-line-arguments


    【解决方案1】:

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

    • 来自环境 - 每个环境变量都转换为具有相同名称和值的 makefile 变量。

      您可能还想设置-e 选项(又名--environments-override),并且您的环境变量将覆盖makefile 中的分配(除非这些分配本身使用override directive。但是,不建议这样做,它是使用?=赋值更好更灵活(条件变量赋值运算符,仅在变量尚未定义时才有效):

      FOO?=default_value_if_not_set_in_environment
      

      请注意,某些变量不是从环境继承的:

      • MAKE 来自脚本名称
      • SHELL 要么在 makefile 中设置,要么默认为 /bin/sh(理由:命令在 makefile 中指定,它们是特定于 shell 的)。
    • 从命令行 - make 可以将变量赋值作为其命令行的一部分,与目标混合:

      make target FOO=bar
      

      但是makefile 中对FOO 变量的所有赋值都将被忽略,除非您在赋值中使用override directive。 (效果与环境变量-e选项相同)。

    • 从父 Make 导出 - 如果您从 Makefile 调用 Make,通常不应该像这样显式编写变量赋值:

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

      相反,更好的解决方案可能是导出这些变量。导出一个变量会使其进入每个 shell 调用的环境中,并且从这些命令中进行调用会按照上面指定的方式选择这些环境变量。

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

      您还可以使用不带参数的export 导出所有变量。

    【讨论】:

    • 从命令行传递带有空格的东西做make A='"as df"'
    • 如果您关心环境变量,似乎您是在自找麻烦。一方面,如果它在 A 地工作而不在 B 地工作,那将是一场调试噩梦,只是因为它们有不同的环境。
    • 仅根据经验,导出 CFLAGS 之类的东西是大型项目的噩梦。大型项目通常具有仅使用一组给定标志进行编译的 3rd 方库(没有人费心修复)。如果您导出 CFLAGS,您项目的 CFLAGS 最终会覆盖第 3 方库并触发编译错误。另一种方法可能是定义export PROJECT_MAKE_ARGS = CC=$(CC) CFLAGS=$(CFLAGS) 并将其作为make -C folder $(PROJECT_MAKE_FLAGS) 传递。如果有办法告诉库的 makefile 忽略环境,那将是理想的(与 -e 相反)。
    • 警告:在上面的“从父 Make 导出”部分中,“不要这样做!” 严重误导。在命令行上传递变量会覆盖子生成文件中的分配,但导出的变量不会覆盖子生成文件中的分配。这两种将变量传递给子 makefile 的方法等效,不应混淆。
    • 有什么不同吗? make target FOO=barmake FOO=bar target?
    【解决方案2】:

    最简单的方法是:

    make foo=bar target
    

    然后在你的makefile中你可以参考$(foo)。请注意,这不会自动传播到子品牌。

    如果您使用的是子品牌,请参阅这篇文章:Communicating Variables to a Sub-make

    【讨论】:

    • sub-makes 是指主makefile 中的makefile included
    • @Michael:这意味着从 makefile 中再次调用 make。我已经更新了我的答案,因为您似乎对此细节感兴趣。
    • "请注意,这不会自动传播到子制作。"不真实! “默认情况下,只有来自环境或命令行的变量才会传递给递归调用。您可以使用导出指令传递其他变量。” gnu.org/software/make/manual/html_node/…
    • 如果您查看您引用的页面,您会看到它明确表示 make 会自动传递在命令行中定义的变量值,方法是将它们放入 MAKEFLAGS 变量中。请参阅选项/递归。因此与此答案相反,make 自动传播到子制作。
    • make target foo=bar 也有效!!
    【解决方案3】:

    假设你有一个这样的makefile:

    action:
        echo argument is $(argument)
    

    然后你可以称它为make action argument=something

    【讨论】:

    • 所以目标和参数可以在位置方面互换?
    • @Michael:是的(见 Mark Byers 的回答)
    • 我喜欢这个答案。它简洁且内容丰富。正是我想要的。谢谢
    【解决方案4】:

    来自manual

    make 中的变量可以来自 make 运行的环境。 make 在启动时看到的每个环境变量都会转换为具有相同名称和值的 make 变量。但是,makefile 中的显式赋值或带有命令参数的赋值会覆盖环境。

    所以你可以这样做(来自 bash):

    FOOBAR=1 make
    

    在您的 Makefile 中产生一个变量 FOOBAR

    【讨论】:

    • 在几乎所有情况下,另一种方式确实更好。为了完整起见,我将把它留在这里。
    • 这是唯一一个显示变量赋值的答案 before make 命令在同一行 - 值得知道这不是语法错误。
    【解决方案5】:

    好像

    命令 args 覆盖环境变量

    生成文件

    send:
        echo $(MESSAGE1) $(MESSAGE2)
    

    运行示例

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

    【讨论】:

      【解决方案6】:

      这里没有提到另一个选项,它包含在 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 构建并运行所有内容(假设它们都包含某种特定的断言内容,而您只想测试它们)

      【讨论】:

        【解决方案7】:

        如果你创建一个名为 Makefile 的文件并添加一个像这样的变量 $(unittest) 那么即使使用通配符,您也可以在 Makefile 中使用此变量

        示例:

        make unittest=*
        

        我使用 BOOST_TEST 并为参数提供通配符 --run_test=$(unittest) 然后我将能够使用正则表达式过滤掉我想要我的 Makefile 的测试 运行

        【讨论】:

          【解决方案8】:
          export ROOT_DIR=<path/value>
          

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

          【讨论】:

            猜你喜欢
            • 2020-04-22
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2020-04-25
            • 1970-01-01
            • 2015-09-15
            • 1970-01-01
            相关资源
            最近更新 更多