Makefile for a x86 operating system in C++

I recently had submitted my bashscript for building an OS for review and got valuable feedback, so I rewrote the entire script for a makefile:

Makefile

# Make File for Bulding Ultron

# Sets up the Assembler
AS := nasm
ASFLAGS := -f elf
ASFILES := $(wildcard kernel/boot/*.s kernel/arch/*.s) ASOBJECTS :=$(ASFILES:.s=.s.o)

# Compile C++ Files
CC := g++
CCFLAGS := -c -nostdlib -nostdinc -fno-builtin -fno-stack-protector -ffreestanding -m32 -I./sdk/include
CCFILES := $(wildcard *.cc sdk/lib/*.cc) CCOBJECTS :=$(CCFILES:.cc=.o)

# The Final Product will be ultron.img in the build directory
all: ultron.img

# Ultron.img : It is the Final Product formed by linking all the object files together
ultron.img : $(ASOBJECTS)$(CCOBJECTS)
$(LINKER)$(LINKERFLAGS) -o build/$@$^

# Run the Kernel : First Make the Kernel and then run it
run: ultron.img
qemu-system-i386 -kernel 'build/ultron.img'

# Assemble the Assembly Files into object Files
# This Assembles all the Files into thier respective object files
# $< : Names the first prerequiste #$@ : Names the current Target
%.s.o: %.s
$(AS)$(ASFLAGS) $< -o$@

# Compile the C++ Files into corresponding object Files and place it in same directory
%.o: %.cc
$(CC)$(CCFLAGS) -o $@$<

# Clean this Mess
clean: $(ASOBJECTS)$(CCOBJECTS)
$(RM)$^


This is my first makefile and I have a couple of questions:

1. Should I make the makefile as verbose as it is now?
2. Is my coding style Ok?
3. What are the things that I should keep in mind while writing makefiles?

Dependency Propagation

One thing that's missing is dependency propagation. So each .o file will be recompiled only if its corresponding .cc file has changed. This is good, I don't want to have to recompile foo.o just because I changed bar.cc. However, what if both foo.cc and bar.cc include some other file quux.h, and you changed some of the function signatures. Currently, neither foo.o nor bar.o will be recompiled! This is bad. However, you don't want to manually have to go through and add:

bar.o : quux.h
foo.o : quux.h


Since that is tedious, error-prone, and unmaintanable. You want to make the compiler do this for you.You can start with reading about automatic prerequisites, but rather than follow the guidelines there, note that gcc can both build the .o files and generate the .d files concurrently with -M (check out all the -M* arguments).

Targets

run and clean should be .PHONY targets.

I also like to get in the habit of setting .DEFAULT_GOAL instead of just making sure that all is at the top. You never know.

Very helpful for the most part. But I would omit these:

# $< : Names the first prerequiste #$@ : Names the current Target


People who don't know what those mean likely won't understand anything about the makefile anyway, not sure the comments will help. Those are very commonly used.

• Thanks . I was experiencing the problem with dependency propagation. I would change the Function signature in the Header and to see the Output I would have to run make clean , and then compile again , which was pretty tedious . I will Update the MakeFile Soon :D – aswin mohan Dec 4 '15 at 14:24

Traditionally, the make variable CC is used for the C compiler and CXX for the C++ compiler. I would recommend changing

CC := g++


to

CXX := g++


Similaly, CFLAGS is traditionally used for C compiler flags and CXXFLAGS is used for C++ compiler flags. I would recommend changing

CCFLAGS := ....


to

CXXFLAGS := ....


In the same vein, linker flags are usually defined using LDFLAGS. I would recommend changing

LINKERFLAGS := -m elf_i386 -T link.ld


to

LDFLAGS := -m elf_i386 -T link.ld


You can see the names of the traditional make variables at https://www.gnu.org/software/make/manual/html_node/Implicit-Variables.html.