【问题标题】:How to resolve GNU make producing a "circular main.c" error如何解决 GNU make 产生“循环 main.c”错误
【发布时间】:2026-01-16 13:55:01
【问题描述】:

//makefile.conf

.SUFFIXES : .c .o

cc = gcc
CFLAG = -c
OFLAG = -o
O2FLAG = -O2
WPIFLAG = -lwringPi
RM = rm -rf

TARGET_SRCS = main.c
TARGET_OBJS = $(TARGET_SRCS:$.c=$.o)
TARGET_NAMES = $(TARGET_SRCS:$.c=$)
BINARY_NAME = LED_TEST

// 生成文件

include makefile.conf

$(TARGET_OBJS) : $(TARGET_SRCS)
        $(CC) $(O2FLAG) $(CFLAG) $(OFLAG) $^

我试图弄清楚 gnu make 的工作原理以及如何使用它。然而,我是初学者。

我正在尝试在我的作业中使用 make(不是强制性的,只是我的热情)来运行通过使用 Wiringpi 点亮 LED 的简单代码。

其实只有一个main.c,我要做的是运行的makefile

gcc -O2 -c -o main.o main.c

gcc -o main main.o -lwiringPi

由于我上一个代码不起作用,(不断循环 main.c

我试图制作一个只运行的代码

gcc -O2 -c -o main.o main.c

但我仍然收到循环 main.c 错误,我不知道这是什么意思。

我试图查找 gnu make 手册,但我想通了,这将需要我一生的时间才能理解。

所以我正在尝试查看代码并用我看到的内容制作一个。

我想我了解makefile.conf的概念,但还是不明白.SUFFIXES的功能。

我理解为注意到make我将用.c和.o制定规则,makefile.conf中的代码定义了将在makefile中使用的变量。

我怎样才能更正代码?当实际的“分配”只用了五分钟,包括添加 cmets。

【问题讨论】:

  • 顺便提一下,关于循环依赖的原始消息的原因是因为您对函数使用了错误的语法:这两个:$(TARGET_SRCS:$.c=$.o)$(TARGET_SRCS:$.c=$) 都是错误的,因为模式字符是% 不是$。这些应该是:$(TARGET_SRCS:%.c=%.o)$(TARGET_SRCS:%.c=%)。然而,第一个无论如何都会意外地工作,所以我认为你没有准确地粘贴你在这里实际使用的 makefile。

标签: c linux gcc makefile


【解决方案1】:

第一个 makefile:

main:
    gcc -O2 -c -o main.o main.c
    gcc -o main main.o -lwiringPi

当它完美运行时,第二个 makefile:

main: main.o
    gcc -o main main.o -lwiringPi

main.o: main.c
    gcc -O2 -c -o main.o main.c

当它完美运行时,第三个 makefile:

main: main.o
    gcc -o $@ $^ -lwiringPi

main.o: main.c
    gcc -O2 -c -o $@ $<

当它完美运行时,您将为更高级的技术做好准备。

【讨论】:

  • 谢谢。从所有这些cmets中,现在我意识到我一直很着急。像往常一样,我应该从基础开始。
【解决方案2】:

如果您真的是新手,在您需要额外功能之前,从简单的 makefile 开始通常会很有帮助。

main: main.c
        gcc -O2 -o main -lwiringPi main.c

注意gcc 之前的空格是一个制表符。

一旦掌握了窍门,您就可以替换各种项目,以使您的“制定规则”更易于复制和维护。例如,%^ 表示“每个依赖源,因此规则的重写将是

main: main.c
        gcc -O2 -o main -lwiringPi $^

有时,您可能希望提供对编译器的简单重新配置,因此如果您有十几个规则,并且想在一个地方全部配置编译器

CC=gcc

main: main.c
        $(CC) -O2 -o main -lwiringPi $^

会在生成时将CC 变量扩展为值gcc。这种扩展的实用性有一个上限,例如,如果某事物是“同一事物中的一个”,您可能不想为该单个项目声明一个变量。例如,您的WPIFLAG 可能始终是必需的,并且可能不是很成功地可重新配置。也许加载器标志变量更有意义。

LDFLAGS=-lwiringPi -lm -lwhatever

和一个编译器标志变量

CFLAGS=-O2 -Werror -Wfatal-errors

这会导致更明智的

main: main.c
        $(CC) $(CFLAGS) -o main $(LDFLAGS) $^

最后,在这种情况下,您可以将目标 main 替换为另一个特殊变量。 $@ 表示“正在构建的目标”

main: main.c
        $(CC) $(CFLAGS) -o $@ $(LDFLAGS) $^

请注意,对于对象文件,您将所有对象列为依赖于所有源。如果你想支持对象构建的独立规则,你需要做一些不同的事情

main: main.o
        $(CC) $(CFLAGS) -o $@ $(LDFLAGS) $^

您需要为每个对象制定规则。

main.o: main.c
        $(CC) $(CFLAGS) -c -o $@ $(LDFLAGS) $^

但是为每个对象键入此规则将变得很费力。要自动执行此规则,您将使用基于文件后缀和后缀规则的模式。

.SUFFIXES : .o .c

.c.o :
        $(CC) $(CFLAGS) -c $<

请注意,上述规则依赖于 $(CC) 的默认行为,即在使用 -c 标志编译 something.c 时生成 something.o。如果你想让输出文件明确

.SUFFIXES : .o .c

.c.o :
        $(CC) $(CFLAGS) -c -o $@ $<

此后缀规则充当宏。当有人需要thing.o 时,如果thing.c 存在,它将从thing.c 构建它,即使thing.c 没有明确的规则

有了这个,您就可以收集原始main 目标上的所有对象。 (我们将删除 CFLAGS,因为不会发生编译,只会进行链接)

main: main.o other.o first.o list.o end.o
        $(CC) -o $@ $(LDFLAGS) $^

但有些人发现列出对象很痛苦,喜欢将它们放在变量中

main: $(OBJS)
        $(CC) -o $@ $(LDFLAGS) $^

这意味着你需要声明和设置OBJS

OBJS = main.o other.o first.o list.o end.o

但是跟踪中间文件有点奇怪,所以为什么不跟踪实际来源

SOURCES = main.c other.c first.c list.c end.c

好的,但是我们如何从SOURCES 获得所需的OBJS?我们将取消引用SOURCES,将后缀修改为.o

OBJS = ${SOURCES:.c=.o}

最终结果

SOURCES = main.c other.c first.c list.c end.c
OBJS = ${SOURCES:.c=.o}

CC=gcc 
CFLAGS=-O2 -Werror -Wfatal-errors
LDFLAGS=-lwiringPi -lm -lwhatever


.SUFFIXES : .o .c

.c.o :
        $(CC) $(CFLAGS) -c -o $@ $<

main: ${OBJS}
        $(CC) -o $@ $(LDFLAGS) $^

【讨论】:

  • 您的原始文件不起作用的部分原因是因为在所有变量选择中,您删除了-o main 的扩展文本,将其替换为-o,它正在选择第一个目标文件,这可能是-o main.o,这没有意义,因为它也是一个来源。因此出现错误。
  • 我还推荐 O'Reilly 出版商的 Manage projects with Make,作者 Andrew Oram 和 Steve Talbott。它比较薄,易于阅读,而且如果你仔细看的话,使用过的副本很多。
  • 感激不尽。我意识到我太不耐烦了,无法真正理解什么是什么。非常感谢。完全理解您的答案需要一些时间,我敢肯定这会对我有很大帮助。
  • 顺便说一句,我仍然无法在互联网上找到有关 -O2 选项的信息。你能告诉我那是什么吗??
  • -O2 选项是gcc 编译器的一个选项。运气好的话,man gccinfo gcc 将帮助您了解这意味着什么(我不知道 ;-))。很高兴你的问题得到了解决。支持对您有帮助的答案并“接受”明确解决您的问题的答案被认为是一种很好的形式。这样做会给作家们带来他们在这里明显获得的声誉点。祝你好运。
最近更新 更多