Makefile-根据条件分配变量

Makefile-根据条件分配变量,makefile,gnu-make,Makefile,Gnu Make,我试图将GNU make官方文档中的示例改编为我的用例: 因此,我创建了这个原型: libs_for_gcc="gcc libs" normal_libs="normal libs" libs= all: do_nothing @echo "all: done." do_nothing: @echo "do_nothing: done." example: @echo "

我试图将GNU make官方文档中的示例改编为我的用例:

因此,我创建了这个原型:

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.
$ make bar
bar: go ...
bar - libs: not empty
bar - libs: ''
bar - libs: ''
bar: done.
$ make foo
foo: go ...
foo - libs: empty
foo: done.
我发现
libs
的值没有按预期更改

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

$ 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.
$ make bar
bar: go ...
bar - libs: not empty
bar - libs: ''
bar - libs: ''
bar: done.
$ make foo
foo: go ...
foo - libs: empty
foo: done.
这里的
libs
不是空的,但里面没有任何内容

当我运行
makefoo
目标时,它会生成:

$ 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.
$ make bar
bar: go ...
bar - libs: not empty
bar - libs: ''
bar - libs: ''
bar: done.
$ 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错误:

我找不到关于这种行为的任何文档,因此我感谢您的建议


编辑:

  • 添加了
    所有
    测试
    目标

背景:

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

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

使用关于在目标声明行设置变量的建议,我能够更改
test
libs
example
foo
目标。 我还看到了有关Bash变量
libs
的重要提示,该变量仅在同一行中有效:

$ 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\u表示_gcc)&&echo“libs:'$$libs'
显示创建了一个新的Bash变量
libs
,它不会影响GNU make变量


但是,有条件的
ifneq($(libs),)
无法检测到
libs
已为
测试目标设置了
libs。

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

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

这就是为什么在尝试使用
:=
时会出现错误,因为这是一个make赋值,不是有效的shell赋值

然后在
echo
中打印make变量
$(libs)
,该变量根本没有设置。此外,配方的每个逻辑行都在它自己的shell中运行。因此,您运行的是等效于:

/bin/sh -c 'libs="gcc libs"'
/bin/sh -c 'echo '
因此,即使您更改了echo命令以打印shell变量(通过
$$libs
),它也将是空的,因为设置它的前一个shell已退出

您希望使用与示例中相同的语法:将变量的赋值从配方中取出,并改为设置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."

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

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

这就是为什么在尝试使用
:=
时会出现错误,因为这是一个make赋值,不是有效的shell赋值

然后在
echo
中打印make变量
$(libs)
,该变量根本没有设置。此外,配方的每个逻辑行都在它自己的shell中运行。因此,您运行的是等效于:

/bin/sh -c 'libs="gcc libs"'
/bin/sh -c 'echo '
因此,即使您更改了echo命令以打印shell变量(通过
$$libs
),它也将是空的,因为设置它的前一个shell已退出

您希望使用与示例中相同的语法:将变量的赋值从配方中取出,并改为设置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."

最后,我实现了使Makefile
使测试
目标工作。

关键是要理解GNU make变量可以在3种不同的上下文中赋值。
而且GNU make经常需要帮助函数,因为有一些关于条件的奇怪问题
这些问题大多未记录在案,只能通过尝试和出错以及在StackOverflow.com上搜索来解决

1.分配变量的第一个、最简单和记录最好的上下文是在全局上下文中,如官方文档和第一个答案所示。

全局变量
${libs}
具有全局可见性

2.下一个不太明显但仍记录在官方文件中的上下文在目标定义行中

这里的
${libs}
测试
目标及其调用的所有依赖目标有效。
但唯一的条件是目标本身

3.分配发电机