我已经结束了对我的Makefile的所有依赖项的输入工作。我已经阅读了一些指南,但我仍然不了解makefile中发生的变量扩展。我也不知道该怎么做。
我苦苦挣扎的部分是通常这样的设置。
src = $(wildcard *.c)
obj = $(src:.c=.o)
LDFLAGS = -lGL -lglut -lpng -lz -lm
myprog: $(obj)
$(CC) -o $@ $^ $(LDFLAGS)
我似乎无法将其改装到我的makefile中。
项目结构
| - src
| - boot
| - table.s
| - boot.s
| - machine
| - revb
| - memmap
| - vars.s
| - os
| - utils
| - ...lots here
我的计划是使特定于硬件的设备具有自己的文件夹,其中包括链接machine
文件夹所需的内部版本和文件。我也想将“功能”分组到一个文件夹中。
下面是我的Makefile。
CFLAGS = -march=rv32i -mabi=ilp32
CC = riscv32-unknown-linux-gnu-as
LINKER = riscv32-unknown-linux-gnu-ld
DUMP = riscv32-unknown-linux-gnu-objdump
COPY = riscv32-unknown-linux-gnu-objcopy
SRC_DIR = src
TARGET_DIR = target
MACHINE_FILES_DIR = machine
TARGET_MACHINE = revb
vizoros : boot.o table.o os.o hash.o machine
$(LINKER) $(TARGET_DIR)/boot.o $(TARGET_DIR)/table.o $(TARGET_DIR)/os.o $(TARGET_DIR)/hash.o -T $(TARGET_DIR)/memmap -o $(TARGET_DIR)/vizoros.elf
$(DUMP) -D $(TARGET_DIR)/vizoros.elf > $(TARGET_DIR)/vizoros.list
os.o : folders
$(CC) $(CFLAGS) $(SRC_DIR)/os/os.s -o $(TARGET_DIR)/os.o
$(DUMP) -D $(TARGET_DIR)/os.o > $(TARGET_DIR)/os.list
table.o : folders
$(CC) $(CFLAGS) $(SRC_DIR)/boot/table.s -o $(TARGET_DIR)/table.o
$(DUMP) -D $(TARGET_DIR)/table.o > $(TARGET_DIR)/table.list
boot.o : folders
$(CC) $(CFLAGS) $(SRC_DIR)/boot/boot.s -o $(TARGET_DIR)/boot.o
$(DUMP) -D $(TARGET_DIR)/boot.o > $(TARGET_DIR)/boot.list
hash.o : folders
$(CC) $(CFLAGS) $(SRC_DIR)/utils/hash.s -o $(TARGET_DIR)/hash.o
$(DUMP) -D $(TARGET_DIR)/hash.o > $(TARGET_DIR)/hash.list
machine:
cp -r $(SRC_DIR)/$(MACHINE_FILES_DIR)/$(TARGET_MACHINE) $(TARGET_DIR)
folders :
mkdir -p $(TARGET_DIR)
.phony: clean
clean:
rm -rf $(TARGET_DIR)
这是我尝试过的。
src = $(wildcard **/*.s)
obj = $(src:.s=.o)
vizoros : $(obj)
$(LINKER) ???
$(DUMP) -D $(TARGET_DIR)/vizoros.elf > $(TARGET_DIR)/vizoros.list
$(obj): $(src)
$(CC) $(CFLAGS) -o $(TARGET_DIR)/$(src)/$@ $^
$(DUMP) -D $(TARGET_DIR)/$(src)/$@ $^> $(TARGET_DIR)/$@ $^
编辑经过一些研究,我已经更新了我的Makefile。当我手动添加目标和源时,此常规流程将起作用,但是当我使用模式匹配时,终端中没有任何显示。上面的布局没有任何变化。我希望将“喜欢”分组的功能放置在一个文件夹中,即
boot.s
和table.s
放在/boot
中,我希望将那些“喜欢”分组的文件夹中的所有* .s文件构建为.o文件。
$(TARGET_DIR)/%.o : $(SRC_DIR)/%.s machine
@echo "compiling $<"
$(CC) $(CFLAGS) $< -o $@
$(DUMP) -D $< > [email protected]
这里有很多事情需要解决:)。我确实建议您回到开始并阅读GNU make manual中的一些介绍性章节,以获得更扎实的基础。
正如约翰在评论中提到的,make
实际上是一个相对简单直接的程序。每当您发现自己在想“哇,我不确定make
如何做到这一点”时,答案几乎肯定是它没有做到这一点。
让我们从您呈现的最终makefile开始。这里的第一件事是:
src = $(wildcard **/*.s)
GNU make使用简单的标准通配符扩展。它不支持扩展的通配符扩展,例如您在zsh
或类似版本中看到的。因此,**/*.s
不进行递归匹配:它与编写*/*.s
相同。如果要进行递归搜索,则必须使用类似以下内容:
src := $(shell find . -name \*.s)
接下来,此规则确实是错误的:
$(obj): $(src)
$(CC) $(CFLAGS) -o $(TARGET_DIR)/$(src)/$@ $^
这扩展到什么?假设src
为foo/foo.s bar/bar.s foo/bar/biz.s
,那么obj
将为foo/foo.o bar/bar.o foo/bar/biz.o
。然后,规则定义将扩展为:
foo/foo.o bar/bar.o foo/bar/biz.o: foo/foo.s bar/bar.s foo/bar/biz.s
[也许您认为make会以某种方式神奇地匹配目标和先决条件,但正如我上面提到的那样,make并不那么聪明。它做的很简单。在这种情况下(如在GNU make手册中可以看到的),具有多个目标的规则与多次编写相同的规则相同,因此上述内容与:
foo/foo.o: foo/foo.s bar/bar.s foo/bar/biz.s
...
bar/bar.o: foo/foo.s bar/bar.s foo/bar/biz.s
...
foo/bar/biz.o: foo/foo.s bar/bar.s foo/bar/biz.s
...
基本上是说每个.o
文件都取决于[[all .s
文件,因此,如果任何.s
文件发生了更改,则[[all] .o
文件被认为已过期。这不是您想要的。
$(CC) $(CFLAGS) -o $(TARGET_DIR)/$(src)/$@ $^
您要求制作的文件类似于foo/foo.o
,但您要求编译器创建的输出文件,$(TARGET_DIR)/$(src)/$@
是完全不同的(和src
?肯定不是您要替换的文件)。完整的源文件列表?)如果TARGET_DIR
为target
,它将扩展为target/foo/foo.s bar/bar.s foo/bar/biz.s/foo/foo.o
,这显然是错误的。
[除了可能的错字以外,制造配方建立目标与制造期望的目标并不相同总是(几乎)错误。这意味着您的编译器和链接器应始终完全使用列出该目录作为先决条件,您几乎也不想这样做。-o $@
,而不应始终使用-o dir/$@
或-o $(notdir $@)
或其他名称。如果要将对象文件放在其他位置,则必须操纵objs
的内容以包含所需文件的确切名称。我什至没有遇到您提供的第二个makefile的问题,其中还包括创建错误的文件(目标为
os.o
但编译行为-o $(TARGET_DIR)/os.o
),事实上您的目标文件没有列出其源文件作为先决条件,而目标文件do
总而言之,这里的问题实际上并不是变量扩展:问题在于了解make
规则的工作方式。或换一种说法,如果不使用任何变量或变量扩展就可以使makefile正常工作,那么您将达到95%的方式:添加变量将是微不足道的。
os.o
)编写一个简单正确的规则。然后考虑使用pattern rules编写通用规则,该规则将从.o
文件创建任何.s
文件。然后研究如何使用变量来避免多次编写相同的内容。