Make 对不同的目标文件使用相同的源文件

Make uses same source file for different object files

Make 为不同的目标文件选择相同的源文件。两者都是文件列表,只是文件名不同。在目标文件而不是源文件之间切换。

我已经在 Whosebug 上尝试了一些有关相关问题的答案,尽管这些解决方案对于所需的内容来说似乎过于复杂,有些不起作用,而另一些则需要将文件放在一个目录中。

我也尝试过一次编译这些文件(使用 gcc),但这给文件其余部分的链接带来了一些问题。

$(OBJFILES): $(SRCFILES)
    $(CC) $(CCFLAGS) -c $< -o $@

$(OBJFILES) 包含以下文件: src/kernel.o src/screen/screen_basic.o

$(SRCFILES) 包含这些文件: src/kernel.c src/screen/screen_basic.c

基本上,src/kernel.c 被编译为 src/kernel.osrc/screen/screen_basic.o,而 src/screen/screen_basic.c 永远不会被编译。

make 的 运行 是什么(我用变量名替换了 gcc 的选项以保持简短):

i686-elf-gcc $(CFLAGS) $(WARNINGS) -c src/kernel.c -o src/kernel.o

i686-elf-gcc $(CFLAGS) $(WARNINGS) -c src/kernel.c -o src/screen/screen_basic.o

我真的不知道你需要什么才能知道出了什么问题。因此,源文件(全部)位于 https://github.com/m44rtn/vireo-kernel。 很高兴知道这是对项目的重写。在之前的 'version' 中,我手动将文件名添加到 makefile 中,它工作得很好,但是当您必须添加新文件或移动它们时并不好。该 makefile 位于 master 分支(不再是默认分支)。

make版本是最新的:

GNU Make 4.2.1
Built for x86_64-pc-linux-gnu

所以,我希望它能很好地工作。我认为它只会编译列表中的所有文件。不幸的是,它没有。我真的不知道这里出了什么问题。

它将 kernel.c 编译为 kernel.o 和 screen_basic.o。我当然希望它能将 kernel.c 编译为 kernel.o 并将 screen_basic.c 编译为 screen_basic.o.

稍后,这两个文件被链接起来。但是,因为它们相同,所以链接器会抛出错误,因为所有内容都定义了两次,这并不理想。

我试图通过一次编译每个 C 文件来解决它,但这在将程序集文件与 C 文件链接时产生了一些问题(有时使其成为非 GRUB 多重引导,这是必须的,在我的情况)。

我不知道 makefile 有什么问题才会这样。

我尝试过的所有堆栈溢出解决方案:

一些解决方案涉及将所有文件放入根目录并仅使用:

%.o: %.c
       (..)

但是,这个项目会有很多文件。这使得将所有内容都放在同一目录中非常烦人,而且速度非常快。我认为这也不太奏效,但我不知道这是真的还是我的大脑在骗我。对不起。

我听说过一些关于 'static rules' 的事情:

$(OBJFILES): %.o: %.c
      (..)

这没有用,但我可能用错了。我不知道。

我希望 makefile 尽可能保持不变,因为它非常方便(它会自动检测所有文件)。

我真的希望我已经提供了足够的信息,而且这个问题还没有被问到。如果有,提前说声抱歉。

如果您需要更多信息,请询问! :)

--编辑-- 虽然我已经使用了五年,但我对这种方式很陌生。我一直用错了。有可能我的 makefile 非常丑陋或糟糕。我确实使用了一个例子来编写makefile。

考虑规则...

$(OBJFILES): $(SRCFILES)
        $(CC) $(CCFLAGS) -c $< -o $@

假设,为了论证,我们有...

SRCFILES := a.c b.c
OBJFILES := a.o b.o

如果您手动展开规则,它将变为...

a.o b.o: a.c b.c
        $(CC) $(CCFLAGS) -c $< -o $@

我认为(如果我错了请纠正我)你的印象是 make 会将此解释为 a.o 取决于 a.c 并且,b.o 取决于 b.c。事实并非如此。它实际上指出的是 a.o b.o 都依赖于 a.cb.c.

因此,当 make 尝试更新目标 a.o 时,它会看到 a.cb.c 的完整先决条件列表,并分配其中的第一个 a.c,到内置变量 $<。这就是为什么您总是看到 $(SRCFILES) 中第一个源文件被编译的原因。

解决此问题的最佳方法可能取决于您打算如何构建源文件层次结构和目标文件,但您可能想看看使用一个或多个 vpath 指令。

模式规则没有将所有对象放在根目录下,考虑

CFILES := path/to/a.c b.c
OBJFILES := $(foreach f,$(CFILES),$(f:%.c=%.o))

all: $(OBJFILES)

%.o: %.c
    $(CC) $(CCFLAGS) -c $< -o $@

这是你得到的:

cc -c path/to/a.c -o path/to/a.o
cc -c b.c -o b.o

以下不是推荐,而是一种 makefile 编程练习。

如果你有 $(SRCFILES) 并且想一次编译一个,你可以使用:

define compile
.o: .c
    $(CC) $(CCFLAGS) -c $$< -o $$@
endef

$(foreach src,$(SRCFILES),$(eval $(call compile, $(src:%.c=%))))

如果源和对象的列表不是按名称对应,而只是按在列表中的位置,则可以销毁CFILES列表

define compile
src := $(CFILES)
CFILES := $(wordlist 2, $(words $(CFILES)), $(CFILES))
: $(src)
    $(CC) $(CCFLAGS) -c $$< -o $$@
endef

$(foreach obj,$(OBJFILES),$(eval $(call compile, $(obj))))

或者您可以使用辅助列表,以保持 CFILES 不变:

helperlist := $(CFILES)

define compile
src := $(firstword $(helperlist))
helperlist := $(wordlist 2, $(words $(helperlist)), $(helperlist))
: $(src)
    $(CC) $(CCFLAGS) -c $$< -o $$@
endef

$(foreach obj,$(OBJFILES),$(eval $(call compile, $(obj))))