【问题标题】:How to use common variable names in sub Makefiles when I'm building multiple targets? (Makefile)构建多个目标时,如何在子 Makefile 中使用公共变量名? (生成文件)
【发布时间】: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 $&lt; -o $@)。请注意,您使用的 build make 变量是未定义的。
  • 您可以递归地执行make 命令,而不是将makefile 包含在其他makefile 中。在您的主 makefile 中,子项目可以是目标,构建子项目的命令可以包含 make 命令。然后这个make 命令将使用它自己的变量执行。 make 具有多种功能来支持这一点,包括将MAKE 定义为正在使用的make 可执行文件的路径,因此您可以使用$(MAKE) 作为命令,这是一种传递给新@987654349 的方法@ 传递给起始 make 的相同开关,传递“全局”变量但不传递本地变量的方法,等等。

标签: c makefile compiler-errors


【解决方案1】:

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

查看该页面,您可以看到 make 变量 recipes 仅在 make 将要运行该配方时才始终扩展,这发生在所有 makefile 已被解析之后.

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

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

【讨论】:

  • 谢谢,好像有点高级的话题,等有时间再看。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-07-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多