为什么这个 Makefile 是错误的?仅当编译器错误之前出现链接器错误时,链接器才会失败

Why is this Makefile wrong? Linker fails only if preceded by compiler error

提问人:Lineath 提问时间:2/16/2023 更新时间:2/16/2023 访问量:72

问:

这是示例程序,以便于解释。有这 4 个文件,main.c、sum.c 和 header.h。最后是 Makefile。

main.c

#include <stdio.h>
#include "header.h"

int main()
{
    int a = 1;
    int b = 1;
    int c = sum(a, b);
    
    printf("Sum: %i\n",c);

    return (0);
}

sum.c

#include "header.h"

int sum(int a, int b)
{
    return (a + b);
}

标头.h

int sum(int a, int b);

生成文件

TEST_SRCS = *.c 
TEST_OBJS = $(TEST_SRCS:%.c=%.o)

%.o : %.c *.h
    gcc -c $^

assemble_test: $(TEST_OBJS);
    
test: assemble_test 
    gcc -o test *.o
    ./test

clean:
    @rm -rf *.o *.gch test
    @echo "Cleaned"

重现步骤:

  • 调用测试规则:使测试>一切正常。

  • 故意引入任何错误,例如两个逗号:

printf("Sum: %i\n",,c);
  • 调用测试规则:make test > 获取正常语法错误。

  • 删除错误。保存文件。

  • 调用测试规则:make test > 无论如何都会出现链接器错误。

    gcc -o test *.o
    Undefined symbols for architecture x86_64:
      "_main", referenced from:
         implicit entry/start for main executable
    ld: symbol(s) not found for architecture x86_64
    clang: error: linker command failed with exit code 1 (use -v to see invocation)
    make: *** [test] Error 1
    
  • 每次都使用干净的规则重新开始。

有一段时间了,我无法弄清楚问题所在。老实说,我是 Makefiles 的新手,所以我一定做错了什么。

关于测试规则调用assemble_test。这是因为更大的 Makefile 的一部分,我需要不同的规则来编译对象,所以不要介意。

你能帮我了解发生了什么吗?

谢谢。

c makefile linker-errors 头文件

评论


答:

4赞 MadScientist 2/16/2023 #1

对目标文件使用通配符是错误的。通配符匹配运行时磁盘上已存在的文件。这无济于事,因为显然它们不会匹配尚不存在的文件,而 makefile 的全部意义在于创建不存在的文件。make

所以这个:

TEST_SRCS = *.c 
TEST_OBJS = $(TEST_SRCS:%.c=%.o)

留下字符串的值和字符串的值(也许您认为通配符会以某种方式立即扩展?不,事实并非如此)。TEST_SRCS*.cTEST_OBJS*.o

然后这一行:

assemble_test: $(TEST_OBJS);

使目标作为先决条件列出:这将扩展到目录中已存在的所有对象文件。但当然,它不会扩展到您想要创建但尚不存在的目标文件。assemble_test*.o

这里还有很多其他问题:例如,您不想编译,因为这会扩展到所有先决条件,但您只想编译源文件,而不是头文件。而且添加似乎很奇怪,使事情变得更加困难。$^assemble_test

你可能想要这个,假设你使用的是 GNU make:

TEST_SRCS := $(wildcard *.c )
TEST_OBJS = $(TEST_SRCS:%.c=%.o)

%.o : %.c *.h
        gcc -c $<

test: $(TEST_OBJS)
        gcc -o $@ $^
        ./$@

通过使用该函数,您可以扩展源 glob 的值,然后再将结果替换为 。wildcard.o

评论

0赞 sideshowbarker 2/17/2023
评论已移至聊天室;请不要在这里继续讨论。在在此下方发表评论之前,请查看评论的目的。不要求澄清或提出改进建议的评论通常属于 Meta Stack Overflow 或 Stack Overflow Chat 中的答案。继续讨论的评论可能会被删除。
0赞 Lineath 2/17/2023
通配符功能解决了这个问题,要学这么多...... - 关于 .h 作为编译的先决条件,我需要了解 .h 的可能修改,如果是这样,请重新编译目标文件。这正在按预期工作。- 关于打电话给assemble_test,你告诉我的方式让我抱怨无事可做,我希望能够多次调用测试。非常感谢您的解决方案!
0赞 MadScientist 2/17/2023
我没有说你不应该添加标题作为先决条件。我说你不应该把它作为参数传递给编译器。如果你使用,那么 扩展到所有先决条件,所以如果你在make中有先决条件,你将调用编译器,这是错误的。您希望使用 which 仅扩展到第一个先决条件,而不是所有先决条件,因此您的编译器将作为正确的 which 调用。gcc -c $^$^foo.c foo.h bar.h baz.hgcc -c foo.c foo.h bar.h baz.h$<gcc -c foo.c
0赞 MadScientist 2/17/2023
关于,如果你要做的是构建一次测试并运行它多次,你应该这样做:哪个将构建测试程序,然后哪个将运行测试程序,然后用于运行它。您还可以添加到您的makefile中以使其更清晰。assemble_testtest: $(TEST_OBJS) ; gcc -o $@ $^run: test ; ./$<make run.PHONY: run