构建多个目标时如何在子 Makefile 中使用公共变量名称? (生成文件)

How to use common variable names in sub Makefiles when I'm building multiple targets? (Makefile)

又有一天我在 Makefiles 上苦苦挣扎.. 有时我以为我了解 Makefile 的基础知识,但我总是遇到这种情况..
我有很多测试台,它们共享一个通用的顶级 Makefile,每个都有自己的 Makefile.inc 文件。由于我在下面描述的问题,我发现测试平台不 运行 正确。我为这个问题做了一个简单的例子。
这是 tree 命令的输出。

.
|-- build
|   |-- test1
|   |   L-- Makefile.inc
|   L-- test2
|       L-- Makefile.inc
|-- common
|   L-- main.c
|-- Makefile
|-- test1
|   L-- testsrc
|       L-- test.c
|       L-- test.h
L-- test2
    L-- testsrc
        L-- test.c
        L-- test.h

这是源代码和 Makefile。

./Makefile

test_list = test1 test2
sub_makefiles = $(foreach test, $(test_list), build/$(test)/Makefile.inc)
include $(sub_makefiles)

all: build/test1/test build/test2/test

.PHONY: clean
clean:
    @rm -f $(foreach test, $(test_list), build/$(test)/*.o)

./common/main.c

#include <stdio.h>
#include "test.h"
extern void print_test();

int main(void)
{
print_test();
printf("X = %d\n", X);
return 0;
}

./test1/testsrc/test.c

#include <stdio.h>

void print_test()
{
printf("this is test1\n");
}

./test1/testsrc/test.h

#define X 1

./test2/testsrc/test.c

#include <stdio.h>

void print_test()
{
printf("this is test2\n");
}

./test2/testsrc/test.h

#define X 2

./build/test1/Makefile.inc

appname     := test1
dstdir        := build/$(appname)
common_srcdir := common
perapp_srcdir := $(appname)/testsrc

target = $(build)/$(appname)/$(appname)
$(dstdir)/main.o: common/main.c
    $(info appname = $(appname), perapp_srcdir = $(perapp_srcdir))
    echo " [CC  ] $<"
    $(CC) -I$(perapp_srcdir) $< -o $@

$(dstdir)/%.o : $(appname)/testsrc/%.c
    $(CC) $< -o $@

$(target): $(dstdir)/test.o $(dstdir)/main.o $(dstdir)/print_test.o
    echo " [LINK] $<"
    $(CC) $^ -o $@

./build/test2/Makefile.inc

appname     := test2
dstdir        := build/$(appname)
common_srcdir := common
perapp_srcdir := $(appname)/testsrc

target = $(build)/$(appname)/$(appname)
$(dstdir)/main.o: common/main.c
    $(info appname = $(appname), perapp_srcdir = $(perapp_srcdir))
    echo " [CC  ] $<"
    $(CC) -I$(perapp_srcdir) $< -o $@

$(dstdir)/%.o : $(appname)/testsrc/%.c
    $(CC) $< -o $@

$(target): $(dstdir)/test.o $(dstdir)/main.o $(dstdir)/print_test.o
    echo " [LINK] $<"
    $(CC) $^ -o $@

这是我运行make.

时的输出
appname = test2, perapp_srcdir = test2/testsrc
echo " [CC  ] common/main.c"
 [CC  ] common/main.c
cc -Itest2/testsrc common/main.c -o build/test1/main.o
/usr/bin/ld: /tmp/cc43yRNT.o: in function `main':
main.c:(.text+0xe): undefined reference to `print_test'
collect2: error: ld returned 1 exit status
make: *** [build/test1/Makefile.inc:10: build/test1/main.o] Error 1

它正在尝试为第一个目标创建 build/test1/main.o,但 appname 设置为 test2。这是因为我在顶部 Makefile 中包含了两个 Makefile.inc 文件,并且最后分配的值被用于两个目标。我应该如何处理这种情况?为什么我在声明 extern 时会出现 undefined reference to print_test'` 错误,稍后会链接它?

要理解为什么会发生这种情况,您需要真正理解 how make reads makefiles

查看该页面,您可以看到 make 变量 in recipes always 仅在 make 将 运行 配方,在解析完所有 makefile 后发生。

由于 make 变量是全局变量,如果您在不同的 makefile 中使用相同的变量名,然后在配方中扩展该值,它将始终具有最后一次赋值的值在任何 makefile 中。

您需要为 makefile 变量添加作用域。有多种方法可以做到这一点。您可能有兴趣阅读这组博文:http://make.mad-scientist.net/category/metaprogramming/ 从底部的文章开始,然后逐步向上。