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)
# Linker
LINKER := ld
LINKERFLAGS := -m elf_i386 -T link.ld
# 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:
- Should I make the makefile as verbose as it is now?
- Is my coding style Ok?
- What are the things that I should keep in mind while writing makefiles?
2 Answers 2
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.
Comments
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.
-
\$\begingroup\$ 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 \$\endgroup\$aswin mohan– aswin mohan2015年12月04日 14:24:38 +00:00Commented Dec 4, 2015 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.