使用GNU Makefile自动解决依赖关系

时间:2013-06-06 19:16:27

标签: gcc g++ makefile gnu

我正在编写一个利用Makefile进行编译的软件,最初我为每个文件设置了一个规则,但是每当我添加一个新文件时,这被证明太麻烦了。为了尝试自动化这个过程,我做了一些研究,并了解到GCC / G ++可以使用-M标志自动构建Makefile规则。

使用简单的目录结构有很多这样的例子,但我理想的目录结构如下:

src/ 
    kernel.hpp kernel.cpp
    Types/
          String.cpp
          String.hpp
    Drivers/IO-Ports/
          CMOS.cpp
          CMOS.hpp
    ...

build/
    DEPS/
        kernel.d
        Types/String.d
        ...
    OBJ/
        kernel.o
        Types/String.o
        ...

我当前的Makefile:

CCHOME=/home/dan/opt/cross/bin
CC=@$(CCHOME)/i586-elf-g++
CFLAGS=-ffreestanding -O2 -Wextra -Wall -fno-exceptions -fno-rtti

KernelName=CPlusKern
QEMU=qemu-system-x86_64 -monitor stdio

SrcDIR=src
SourceDIRS:=$(shell find $(SrcDIR) -type d)
SrcFILES=$(shell find $(SrcDIR) -type f -name *.cpp)
HdrFILES=$(shell find $(SrcDIR) -type f -name *.hpp)

DepDIR=$(BuildDIR)/DEPS
DepFILES0=$(subst $(SrcDIR), $(DepDIR),$(SrcFILES))
DepFILES=$(subst .cpp,.d,$(DepFILES0))

ObjDIR=$(BuildDIR)/OBJ
ObjDIRS=$(subst $(SrcDIR),$(ObjDIR),$(SourceDIRS))
ObjFILES0=$(subst $(SrcDIR), $(ObjDIR),$(SrcFILES))
ObjFILES=$(subst .cpp,.o,$(ObjFILES0))

BuildDIR=build
BuildDIRS=$(BuildDIR) $(ObjDIR) $(DepDIR)


all: assemble compile run
image: assemble compile build-image run-image

debug:
    @echo "BuildDIRS: " $(BuildDIRS)
    @echo "DepFiles: " $(DepFILES)
    @echo "SrcFiles: " $(SrcFILES)
    @echo "ObjFiles: " $(ObjFILES)

./src/kernel.o: ./src/kernel.cpp
    @echo $(CC) $(CFLAGS) -MMD -MP -MF $< -o $(subst $(SrcDIR), $(ObjDIR),$@)

./src/kernel.cpp:

dir:
    @echo "Making Build Dirs..."
    @-mkdir -p $(BuildDIRS)

compile: dir $(ObjFILES)
#   @echo "Compiling Source Files: " $(SrcFILES)

assemble: dir boot.o
    @echo "Assembling Core Files..."

boot.o: $(SrcDIR)/boot.s
    @$(CCHOME)/i586-elf-as $(SrcDIR)/boot.s -o $(ObjDIR)/boot.o

build: %.o
    echo "Building Kernel Binary..."
    @$(CC) -T linker.ld -o $(KernelName).bin -ffreestanding -O2 -nostdlib $(SrcFILES)-lgcc

build-image: build
    @echo "Building Kernel Image..."
    @cp $(KernelName).bin isodir/boot/$(KernelName).bin
    @Scripts/MakeGrub.sh $(KernelName) isodir/boot/grub
    grub-mkrescue -o $(KernelName).iso isodir

%.o: %.cpp Makefile
    @echo "Building Object $@"
    $(CC) $(CFLAGS) -MMD -MP -MF $(subst $(SrcDIR),$(DepDIR),$@) -o $(subst $(SrcDIR), $(ObjDIR),$@)

run:
    @echo "Starting QEMU"
    @$(QEMU) -kernel $(KernelName).bin

run-image:
    @echo "Starting QEMU"
    @$(QEMU) -bios OVMF.fd -cdrom $(KernelName).iso

clean:
    @echo "Cleaning Build Directories..."
    -@rm -R $(BuildDIR) ./isodir
    -@$(RM) $(KernelName).bin $(KernelName).iso

我认为这可能会解决问题,但是会引发错误:

make: *** No rule to make target `build/OBJ/VGA.o', needed by `compile'. Stop.

我无法确定如何制定规则:

%.o: %.cpp Makefile
    $(CC) $(CFLAGS) -MMD -MP -MF $(subst $(SrcDIR),$(DepDIR),$@) -o $(subst $(SrcDIR), $(ObjDIR),$@)

适用于每个.cpp文件。据我所知,通配符不能用于规则定义。

我不确定这是否有帮助,但每个源文件的路径/名称都存储在$(SrcFILES)变量中。

请注意,以下是上述规则的扩展版本:

/home/dan/opt/cross/bin/i586-elf-g++ -ffreestanding -O2 -Wextra -Wall -fno-exceptions -fno-rtti -MMD -MP -MF src/kernel.cpp -o build/OBJ/kernel.o

为此实例生成的依赖项文件:

kernel.o: src/kernel.cpp src/kernel.hpp src/Globals.hpp \
 src/VGATerminal.hpp src/Types/String.hpp src/Types/../Globals.hpp \
 src/VGA.hpp src/IO/Read.hpp src/IO/Write.hpp \
 src/Drivers/IO-Ports/CMOS.hpp src/Drivers/IO-Ports/../../IO/Read.hpp \
 src/Drivers/IO-Ports/../../IO/Write.hpp

这是我在这里的第一篇文章,所以对我的问题的反馈表示赞赏:)

希望我能解决这个问题并重新开发我的代码。

编辑: @Beta提供的规则没有问题,我的所有Object文件都成功构建并输出到正确的位置。此规则甚至选择了build/OBJ/Drivers/IO-Ports/CMOS.obuild/OBJ/Drivers/PS2.o

所以现在我可以愉快地单独构建所有对象,如果我传递文件名但是我认为我仍然需要依赖性解析,这样我就不必为每个文件编写规则。

1 个答案:

答案 0 :(得分:0)

这可能需要几次迭代。

第一个问题是您有一个目标,build/OBJ/VGA.o并且没有相应的规则。规则

%.o: %.cpp Makefile
    ...

不适合;它可以从build/OBJ/VGA.o构建build/OBJ/VGA.cpp,但是没有这样的源文件。

因此,对于第一步,如果源文件是src/whatever/VGA.cpp,请尝试以下规则:

build/OBJ/%.o: src/whatever/%.cpp
    ...

告诉我们结果。

修改

好,现在试试这个:

build/OBJ/%.o: src/%.cpp
    ...