コンパイルに 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
...
私の現在のメイクファイル:
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はエラーをスローする:
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 が提供するルールは問題なく機能しました。すべてのオブジェクト ファイルが正常にビルドされ、適切な場所に出力されます。このルールは、build/OBJ/Drivers/IO-Ports/CMOS.o
とbuild/OBJ/Drivers/PS2.o
.
したがって、ファイル名を渡せばすべてのオブジェクトを個別にビルドできますが、ファイルごとにルールを作成する必要がないように依存関係の解決が必要だと思います。