# Nuke built-in rules. .SUFFIXES: # This is the name that our final executable will have. # Change as needed. override OUTPUT := kernel # Target architecture to build for. Default to x86_64. ARCH := x86_64 # Install prefix; /usr/local is a good, standard default pick. PREFIX := /usr/local # Check if the architecture is supported. ifeq ($(filter $(ARCH),aarch64 loongarch64 riscv64 x86_64),) $(error Architecture $(ARCH) not supported) endif # User controllable toolchain and toolchain prefix. TOOLCHAIN := TOOLCHAIN_PREFIX := ifneq ($(TOOLCHAIN),) ifeq ($(TOOLCHAIN_PREFIX),) TOOLCHAIN_PREFIX := $(TOOLCHAIN)- endif endif # User controllable C compiler command. ifneq ($(TOOLCHAIN_PREFIX),) CC := $(TOOLCHAIN_PREFIX)gcc else CC := cc endif # User controllable C++ compiler command. CXX := $(TOOLCHAIN_PREFIX)c++ # User controllable linker command. LD := $(TOOLCHAIN_PREFIX)ld # Defaults overrides for variables if using "llvm" as toolchain. ifeq ($(TOOLCHAIN),llvm) CC := clang CXX := clang++ LD := ld.lld endif # User controllable C flags. CFLAGS := -g -O3 -ffast-math -pipe -Werror -Wall -fomit-frame-pointer -funroll-loops -fno-exceptions -mllvm -force-vector-width=64 -mllvm -inline-threshold=1000 # User controllable C++ flags. We default to same as C flags. CXXFLAGS := $(CFLAGS) # User controllable C/C++ preprocessor flags. We set none by default. CPPFLAGS := ifeq ($(ARCH),x86_64) # User controllable nasm flags. NASMFLAGS := -g endif # User controllable linker flags. We set none by default. LDFLAGS := # Ensure the dependencies have been obtained. ifneq ($(filter-out clean distclean,$(MAKECMDGOALS)),) ifeq ($(wildcard .deps-obtained),) $(error Please run the ./get-deps script first) endif endif # Check if CC is Clang. override CC_IS_CLANG := $(shell ! $(CC) --version 2>/dev/null | grep -q '^Target: '; echo $$?) # Check if CXX is Clang. override CXX_IS_CLANG := $(shell ! $(CXX) --version 2>/dev/null | grep -q '^Target: '; echo $$?) # Internal flags shared by both C and C++ compilers. override SHARED_FLAGS := \ -Wall \ -Wextra \ -nostdinc \ -ffreestanding \ -fno-stack-protector \ -fno-stack-check \ -flto \ -fno-PIC -I uacpi/include \ -ffunction-sections \ -fdata-sections # Internal C/C++ preprocessor flags that should not be changed by the user. override CPPFLAGS_C := \ -I src \ -I limine-protocol/include -I flanterm/src -I nanoprintf -I stb -I src/klibc/c \ -isystem freestnd-c-hdrs/$(ARCH)/include \ $(CPPFLAGS) \ -MMD \ -MP # Internal C++ only preprocessor flags. override CPPFLAGS_CXX := \ $(CPPFLAGS_C) \ -isystem freestnd-cxx-hdrs/$(ARCH)/include ifeq ($(ARCH),x86_64) # Internal nasm flags that should not be changed by the user. override NASMFLAGS := \ $(patsubst -g,-g -F dwarf,$(NASMFLAGS)) \ -Wall endif # Architecture specific internal flags. ifeq ($(ARCH),x86_64) ifeq ($(CC_IS_CLANG),1) override CC += \ -target x86_64-unknown-none-elf endif ifeq ($(CXX_IS_CLANG),1) override CXX += \ -target x86_64-unknown-none-elf endif override SHARED_FLAGS += \ -m64 \ -march=x86-64 \ -mabi=sysv \ -mno-80387 \ -mno-mmx \ -mno-sse \ -mno-sse2 \ -mno-red-zone \ -mcmodel=kernel override LDFLAGS += \ -m elf_x86_64 override NASMFLAGS := \ -f elf64 \ $(NASMFLAGS) endif ifeq ($(ARCH),aarch64) ifeq ($(CC_IS_CLANG),1) override CC += \ -target aarch64-unknown-none-elf endif ifeq ($(CXX_IS_CLANG),1) override CXX += \ -target aarch64-unknown-none-elf endif override SHARED_FLAGS += \ -mcpu=generic \ -march=armv8-a+nofp+nosimd \ -mgeneral-regs-only override LDFLAGS += \ -m aarch64elf endif ifeq ($(ARCH),riscv64) ifeq ($(CC_IS_CLANG),1) override CC += \ -target riscv64-unknown-none-elf endif ifeq ($(CXX_IS_CLANG),1) override CXX += \ -target riscv64-unknown-none-elf endif override SHARED_FLAGS += \ -march=rv64imac_zicsr_zifencei \ -mabi=lp64 \ -mno-relax override LDFLAGS += \ -m elf64lriscv \ --no-relax endif ifeq ($(ARCH),loongarch64) ifeq ($(CC_IS_CLANG),1) override CC += \ -target loongarch64-unknown-none-elf endif ifeq ($(CXX_IS_CLANG),1) override CXX += \ -target loongarch64-unknown-none-elf endif override SHARED_FLAGS += \ -march=loongarch64 \ -mabi=lp64s \ -mfpu=none \ -msimd=none override LDFLAGS += \ -m elf64loongarch endif # Internal C flags that should not be changed by the user. override CFLAGS += \ -std=gnu11 \ $(SHARED_FLAGS) # Internal C++ flags that should not be changed by the user. override CXXFLAGS += \ -std=c++26 -Wno-c23-extensions \ -fno-rtti \ -fno-exceptions \ $(SHARED_FLAGS) # Internal linker flags that should not be changed by the user. override LDFLAGS += \ -nostdlib \ -static \ -z max-page-size=0x1000 \ --gc-sections \ -T linker-scripts/$(ARCH).lds # Use "find" to glob all *.c, *.cpp, *.S, and *.asm files in the tree # (except the src/arch/* directories, as those are gonna be added # in the next step). override SRCFILES := $(shell find -L src cc-runtime/src flanterm/src uacpi/source -type f -not -path 'src/arch/*' 2>/dev/null | LC_ALL=C sort) # Add architecture specific files, if they exist. override SRCFILES += $(shell find -L src/arch/$(ARCH) -type f 2>/dev/null | LC_ALL=C sort) # Obtain the object and header dependencies file names. override CFILES := $(filter %.c,$(SRCFILES)) override CXXFILES := $(filter %.cpp,$(SRCFILES)) override ASFILES := $(filter %.S,$(SRCFILES)) ifeq ($(ARCH),x86_64) override NASMFILES := $(filter %.asm,$(SRCFILES)) endif override OBJ := $(addprefix obj-$(ARCH)/,$(CFILES:.c=.c.o) $(CXXFILES:.cpp=.cpp.o) $(ASFILES:.S=.S.o)) ifeq ($(ARCH),x86_64) override OBJ += $(addprefix obj-$(ARCH)/,$(NASMFILES:.asm=.asm.o)) endif override HEADER_DEPS := $(addprefix obj-$(ARCH)/,$(CFILES:.c=.c.d) $(CXXFILES:.cpp=.cpp.d) $(ASFILES:.S=.S.d)) # Default target. This must come first, before header dependencies. .PHONY: all all: bin-$(ARCH)/$(OUTPUT) # Include header dependencies. -include $(HEADER_DEPS) # Link rules for the final executable. bin-$(ARCH)/$(OUTPUT): GNUmakefile linker-scripts/$(ARCH).lds $(OBJ) mkdir -p "$(dir $@)" $(LD) $(LDFLAGS) $(OBJ) -o $@ # Compilation rules for *.c files. obj-$(ARCH)/%.c.o: %.c GNUmakefile mkdir -p "$(dir $@)" $(CC) $(CFLAGS) $(CPPFLAGS_C) -c $< -o $@ # Compilation rules for *.cpp files. obj-$(ARCH)/%.cpp.o: %.cpp GNUmakefile mkdir -p "$(dir $@)" $(CXX) $(CXXFLAGS) $(CPPFLAGS_CXX) -c $< -o $@ # Compilation rules for *.S files. obj-$(ARCH)/%.S.o: %.S GNUmakefile mkdir -p "$(dir $@)" $(CC) $(CFLAGS) $(CPPFLAGS_C) -c $< -o $@ ifeq ($(ARCH),x86_64) # Compilation rules for *.asm (nasm) files. obj-$(ARCH)/%.asm.o: %.asm GNUmakefile mkdir -p "$(dir $@)" nasm $(NASMFLAGS) $< -o $@ endif # Remove object files and the final executable. .PHONY: clean clean: rm -rf bin-$(ARCH) obj-$(ARCH) # Remove everything built and generated including downloaded dependencies. .PHONY: distclean distclean: rm -rf bin-* obj-* .deps-obtained .cache compile_commands.json freestnd-c-hdrs freestnd-cxx-hdrs cc-runtime limine-protocol # Install the final built executable to its final on-root location. .PHONY: install install: all install -d "$(DESTDIR)$(PREFIX)/share/$(OUTPUT)" install -m 644 bin-$(ARCH)/$(OUTPUT) "$(DESTDIR)$(PREFIX)/share/$(OUTPUT)/$(OUTPUT)-$(ARCH)" # Try to undo whatever the "install" target did. .PHONY: uninstall uninstall: rm -f "$(DESTDIR)$(PREFIX)/share/$(OUTPUT)/$(OUTPUT)-$(ARCH)" -rmdir "$(DESTDIR)$(PREFIX)/share/$(OUTPUT)"