首页 > 解决方案 > Makefile - 根据条件分配变量

问题描述

我正在尝试将GNU make的官方文档中的示例改编为我的用例: GNU make - Example of a Conditional

libs_for_gcc = -lgnu
normal_libs =

ifeq ($(CC),gcc)
  libs=$(libs_for_gcc)
else
  libs=$(normal_libs)
endif

foo: $(objects)
        $(CC) -o foo $(objects) $(libs)

所以,我创建了这个原型:

libs_for_gcc="gcc libs"
normal_libs="normal libs"
libs=


all: do_nothing
    @echo "all: done."


do_nothing:
    @echo "do_nothing: done."


example:
    @echo "example: go ..."
    @echo "example - cc: '$(CC)'"
    @echo "libs_for_gcc: $(libs_for_gcc)"
    @echo "normal_libs: $(normal_libs)"
    @echo "libs: $(libs)"
ifeq ($(CC),gcc)
    libs=$(libs_for_gcc) && echo "libs: '$$libs'"
    @echo "example - libs: '$(libs)'"
else
    libs=$(normal_libs) && echo "libs: '$$libs'"
    @echo example - libs: $(libs)
endif
    @echo "example - libs: '$(libs)'"
    @echo "example: done."


test: libs += " -Ddebug"
test: example foo


bar:
    @echo "bar: go ..."
ifeq (${libs}, "")
    @echo "bar - libs: empty"
    @echo "assigning libs"
    libs=$(libs_for_gcc)
else
    @echo "bar - libs: not empty"
    @echo "bar - libs: '${libs}'"
endif
    @echo "bar - libs: '${libs}'"
    @echo "bar: done."



foo: 
    @echo "foo: go ..."
ifneq ("$(libs)", "")
    @echo "foo - libs: not empty"
    @echo "foo - libs: '$(libs)'"
else
    @echo "foo - libs: empty"
endif
    @echo "foo: done."

现在,当我使用它运行默认目标时,$ make 它只会产生:

$ make
example: go ...
example - cc: 'cc'
libs_for_gcc: gcc libs
normal_libs: normal libs
libs: 
libs="normal libs"
example - libs:
example - libs: ''
example: done.

我看到 的值libs 没有按预期更改。

当我运行make bar目标时,它会产生:

$ make bar
bar: go ...
bar - libs: not empty
bar - libs: ''
bar - libs: ''
bar: done.

这里libs不是的,但里面什么都没有

当我运行make foo目标时,它会产生:

$ make foo
foo: go ...
foo - libs: empty
foo: done.

这里libs理解为

如我所见,libs没有正确更改,我尝试将语法更改为:

example:
    # [...]
ifeq ($(CC),gcc)
    libs := $(libs_for_gcc)
    @echo "example - libs: '$(libs)'"
else
    libs := $(normal_libs)
    @echo example - libs: $(libs)
endif

但后来我得到了GNU make错误:

$ make
example: go ...
example - cc: 'cc'
libs_for_gcc: gcc libs
normal_libs: normal libs
libs: 
libs := "normal libs"
/bin/sh: 1: libs: not found
Makefile:7: recipe for target 'example' failed
make: *** [example] Error 127

我找不到任何关于这种行为的文档,所以我很感激任何建议。


编辑:


背景:

GNU make命令是用于打包和部署软件的工具链的重要组成部分,因此在系统管理员和 DevOps 工程师的日常工作中非常重要。

所有完全不同的用例都由同一个 Makefile管理。现在,对于我的具体用例,我正在设置Travis CI工作流序列,以便为我的静态链接源代码库启用自动化测试。因此,与打包序列相反,自动化测试序列需要调试功能和高级输出评估来生成有意义的测试。我希望测试检查错误报告内存使用报告,以提醒我任何隐藏的错误。

使用关于在目标声明行设置变量的建议,我能够更改libs,和目标。我还看到了关于Bash变量的重要提示,它只在同一行有效:testexamplefoolibs

$ make
do_nothing: done.
all: done.

$ make test
example: go ...
example - cc: 'cc'
libs_for_gcc: gcc libs
normal_libs: normal libs
libs:  -Ddebug
libs="normal libs" && echo "libs: '$libs'" ;
libs: 'normal libs'
example - libs:  -Ddebug
example - libs: ' -Ddebug'
example: done.
foo: go ...
foo - libs: empty
foo - libs: ' -Ddebug'
foo: done.

配方libs=$(libs_for_gcc) && echo "libs: '$$libs'"显示创建了一个新的Bash变量libs,它不会影响GNU make变量。

条件仍然ifneq ($(libs),)无法检测到libs已经为test目标设置的内容。

标签: makefilegnu-make

解决方案


您的示例与 GNU make 手册中的示例之间的区别在于,在 GNU make 手册中,他们设置了一个名为的make变量libs(他们在任何配方之外分配变量)。

在您的使用中,您分配了一个名为的shell变量libs(您在配方中分配它,用 TAB 缩进)。

这就是为什么您在尝试使用时会收到错误的原因:=,因为这是一个make赋值,而不是一个有效的shell赋值。

然后在您的echo打印中打印根本没有设置的make变量。$(libs)此外,配方的每个逻辑行都在它自己的 shell 中运行。所以你正在运行相当于:

/bin/sh -c 'libs="gcc libs"'
/bin/sh -c 'echo '

因此,即使您确实更改了 echo 命令以打印 shell 变量(通过$$libs),它也会为空,因为设置它的前一个 shell 已经退出。

您想使用与示例中相同的语法:将配方的变量 OUT 赋值并设置 make 变量:

libs_for_gcc = gcc libs
normal_libs = normal libs

ifeq ($(CC),gcc)
  libs = $(libs_for_gcc)
else
  libs = $(normal_libs)
endif

example:
        @echo "example: go ..."
        @echo "example - cc: '$(CC)'"
        @echo "libs_for_gcc: $(libs_for_gcc)"
        @echo "normal_libs: $(normal_libs)"
        @echo "libs: $(libs)"
        @echo "example - libs: '$(libs)'"
        @echo "example: done."

推荐阅读