Complex Recipes
This page contains examples of what else can be done with Makefiles. These techniques are not required for your assignment Makefiles.
One of the things make can do is only rebuild files that have changed.
To use this feature, we need to explicitly compile individual files in to object code, then merge the results. Here is a recipe that does this:
all:
#make a directory to hold the build product
mkdir -p build
#build MyProgram.exe to that directory
g++ -g -c -o build/main.o main.cpp
g++ -g -c -o build/Answer.o Answer.cpp
g++ -g -o build/MyProgram.exe build/main.o build/Answer.o
The two lines that include -c
say to "only compile this code, do not link it into
an executable yet". Each line compiles one of the .cpp files into an object (.o) file.
The final line builds the executable from the two object files made on the preceding
lines.
That recipe still builds all of the files at once, every time we run the recipe. To compile individual parts, we need to break it up into multiple rules:
all: makeDirectory buildMain buildAnswer
g++ -g -o build/MyProgram.exe build/main.o build/Answer.o
buildMain:
g++ -g -c -o build/main.o main.cpp
buildAnswer:
g++ -g -c -o build/Answer.o Answer.cpp
makeDirectory:
mkdir -p build
Now we can build just one of the object files by calling something like:
make -f MyMake buildMain
If we run the all
rule (or don't specify a rule and get it by default),
the prerequisites say we need to first run makeDirectory
the two build____
rules.
But to build all
, this still will recompile everything. The next step is to
rename the rules so they match the name of the file they build. When make
encounters
a rule, it checks to see if there is a file with that name. If so, it does not run the
rule.
build/MyProgram.exe: makeDirectory build/main.o build/Answer.o
g++ -g -o build/MyProgram.exe build/main.o build/Answer.o
build/main.o:
g++ -g -c -o build/main.o main.cpp
build/Answer.o:
g++ -g -c -o build/Answer.o Answer.cpp
makeDirectory:
mkdir -p build
Now if you run the default rule to make the full program, it finds and uses the existing .o files. You should only see one g++ command be executed.
But there is a problem - if you change a file, the build will not know that it needs to recompile that file. Try changing main to print something extra, running make, and then running the program. You won't see the new code as it is being ignored.
To fix this, we need to mark each rule with its prerequisites. For the object files, the prerequisites are the .cpp files:
build/MyProgram.exe: makeDirectory build/main.o build/Answer.o
g++ -g -o build/MyProgram.exe build/main.o build/Answer.o
build/main.o: main.cpp
g++ -g -c -o build/main.o main.cpp
build/Answer.o: Answer.cpp
g++ -g -c -o build/Answer.o Answer.cpp
makeDirectory:
mkdir -p build
The line build/main.o: main.cpp
says that build/main.o
depends on main.cpp
.
If main.cpp is more recently changed than build/main.o
, we need to rebuild it.
Now, if you run the makefile, the default rule knows that build/main.o
is a
prerequisite and so it evaluates that rule. Make can tell that build/main.o
depends
on main.cpp which has been updated, so it rebuilds that. Now, since build/main.o
is newer than build/MyProgram.exe
, make will rebuild that as well. However Answer.o,
which was never changed, gets reused as is.
We also might want to add any .h files that .cpp files include as prerequisites - that way if we change them, the .cpp gets recompiled even if we don't change the .cpp files. This is especially important if the .h file has templated code that may not exist in a .cpp file!
build/MyProgram.exe: makeDirectory build/main.o build/Answer.o
g++ -g -o build/MyProgram.exe build/main.o build/Answer.o
build/main.o: main.cpp Answer.h
g++ -g -c -o build/main.o main.cpp
build/Answer.o: Answer.cpp Answer.h
g++ -g -c -o build/Answer.o Answer.cpp
makeDirectory:
mkdir -p build
That makefile may seem a bit tedious. And it is. Imagine writing a separate rule for each one of 300 .cpp files for how to turn it into a .o file. But there are tricks that can be used to automatically define a rule for any .cpp file to turn it into a .o file. Here is such a recipe. The weird rule in the middle handles both .o files.
build/MyProgram.exe: makeDirectory build/main.o build/Answer.o
g++ -g -o build/MyProgram.exe build/main.o build/Answer.o
build/%.o: %.c
g++ -c -o [email protected] $<
makeDirectory:
mkdir -p build
If you want to learn how tricks like this work, try this detailed tutorial: https://makefiletutorial.com/