【发布时间】:2021-07-19 09:57:24
【问题描述】:
又有一天,我最终为 Makefile 苦苦挣扎。
有时我以为我了解 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'`错误,稍后会链接?
【问题讨论】:
-
首先映入我眼帘的是:你用
=赋值给target,这是一个延迟赋值运算符。 -
第二件事:您正在使用一种 recursive make 方法,但只有缺点,没有优点。我认为您尝试参数化规则的方式总是会导致混淆,因为
make不可避免地使用符号的全局命名空间,这就是人们仍然将其构建分成正交子集的原因。您故意使您的构建受此全局命名空间的约束,同时试图保持构建的纯文本分隔。恕我直言,是时候重新评估您的构建过程了。 -
很抱歉没有为您当前的问题提供更多帮助,但我认为从长远来看,您会更喜欢另一种方法。
-
这不是制造问题。您的编译命令应使用
-c选项 ($(CC) -I$(perapp_srcdir) -c $< -o $@)。请注意,您使用的buildmake 变量是未定义的。 -
您可以递归地执行
make命令,而不是将makefile 包含在其他makefile 中。在您的主 makefile 中,子项目可以是目标,构建子项目的命令可以包含make命令。然后这个make命令将使用它自己的变量执行。make具有多种功能来支持这一点,包括将MAKE定义为正在使用的make可执行文件的路径,因此您可以使用$(MAKE)作为命令,这是一种传递给新@987654349 的方法@ 传递给起始make的相同开关,传递“全局”变量但不传递本地变量的方法,等等。
标签: c makefile compiler-errors