Makefile for Projects with Subdirectories
Abstract
In order to manage our project easily, we may tend to make
for help. Usually, our projects will contians many subdirectories. So writing a makefile which can always compile our projects successfully when our project add, delete or modify some sub-dirs is quite important.
Background
Take my private projects kmusb
for example.
It file structure is like:
1 | . |
I know the structure is awful. But let’s forget about it first, since our final goal is to write a universal makefile that can compile our project, no matter what file structure they have.
For the above project:
main
function is contianed inkmsub.c
arg_parser/
,semihost/
are 2 directories that we write our code in thembin/
contains the output fileobj/
contains all*.obj
files and list them just like*.c
files in the project.data/
is a folder that we store the data created when the program is running
Practice
Practice 1 - Write All Targets Directly
To write a most simple makefile, we can code like:
1 | TARGET = kmusb |
We write all the targets directly.
.PHONY
: A phony target is one that is not really the name of a file; rather it is just a name for a recipe to be executed when you make an explicit request.There are two reasons to use a phony target: to avoid a conflict with a file of the same name, and to improve performance. [1]
Pracetice 2 - Use variables
It’s unconvinient to write all the target directly. If we modify our project later, we need to change a lot in the makefile. Just like the variable CC
, we can also define varibles to store the subfolders.
1 | TARGET = kmusb |
foreach
: It causes one piece of text to be used repeatedly, each time with a different substitution performed on it. It resembles the for command in the shell sh and the foreach command in the C-shell csh.[2]
- Systax:
$(foreach var,list,text)
$(@D)
: automatic variables. The directory part of the file name of the target, with the trailing slash removed. If the value of$@
isdir/foo.o
then$(@D)
isdir
. This value is.
if$@
does not contain a slash.
Here we add a variable SUBDIR
and add our subfolders in it. Then we can use make
syntax like wildcard
, foreach
and so on to get all *.c
and *.h
file in the project. As for the obj/
folder, to better maintain all the *.obj
, we will create folders with the same name as the subfolders in the projects under obj/
.
Besides, we add a target echoes
to help us debug our makefile.
Now, our makefile can compile the projects with subfolders easily. Just change the contents in SUBDIR
.
Practice 3 - Complete the makefile
From above actions, we wrote a makefile that can adapt to various of projects. This part just some small modification to make our makefile better to look.
1 | TARGET = kmusb |
Let’s leave alone the include library I added, since our main goal is to write a makefile to complie projects with subfolders. This makefile is copied from my projects.
You see, I adjust the way to write .PHONY
.
This is a trick that I got from busybox. We add contents to the .PHONY
wehen we write a new target is easier to maintain the makefile when it grows bigger and bigger.