Building Multiple Programs
This guide shows how to use one makefile to build multiple programs from one code base.
For example: you have code that is for a unit test project and code that is for
your main program. You can't compile it into one program as the tests and "main"
program each define a main
function. So you need to build two separate programs.
These instructions are based on the MakeComplex project from Week01 in the CS260 code repository. Use it if you want to be able to follow along exactly. The instructions assume you have a terminal open in that directory.
Examine the code in MakeComplex. There are two separate files with main functions - programA.cpp and programB.cpp. Those represent two separate programs that both happen to use code from Answer.cpp. We need to build the two files into two separate programs.
Create a file called Makefile and start with a basic set of targets for programA.
programA.exe: programA.cpp Answer.cpp Answer.h Templated.h g++ -g -Wall -o programA.exe programA.cpp Answer.cpp .PHONY: clean clean: rm -f programA.exe
As before, the target is named after the program to build, and it depends on all of the source files, both
.cpp
and.h
, that I might want to edit and would require the program to be rebuilt if I did. The commands useg++
and list all of the.cpp
files (but not .h files) to be compiled.Add a rule for programB.
.PHONY: programA.exe programA.exe: programA.cpp Answer.cpp Answer.h Templated.h g++ -g -Wall -o programA.exe programA.cpp Answer.cpp .PHONY: programB.exe programB.exe: programB.cpp Answer.cpp Answer.h Templated.h g++ -g -Wall -o programB.exe programB.cpp Answer.cpp .PHONY: clean clean: rm -f programA.exe programB.exe
The two
.exe
rules are similar, but note that each program only uses its own.cpp
file that definesmain
. They both use the shared files such asAnswer.cpp
.Normally make just runs the first rule it finds. So if you type
make
, only programA.exe will be built. To build programB.exe, you will have to do:make programB.exe
If you want to easily be able to make both program at once, you can add a phone all rule as the first one. It lists programA.exe and programB.exe as prerequisites - so those rules will be invoked automatically as part of building
all
. Theall
rules itself does not have any commands - all it does is trigger both program rules to run.Since
all
is the first rule, it is the one that runs if you runmake
with no arguments. You can always run justmake programA.exe
ormake programB.exe
if you want to specify just one..PHONY: all all: programA.exe programB.exe .PHONY: programA.exe programA.exe: programA.cpp Answer.cpp Answer.h Templated.h g++ -g -Wall -o programA.exe programA.cpp Answer.cpp .PHONY: programB.exe programB.exe: programB.cpp Answer.cpp Answer.h Templated.h g++ -g -Wall -o programB.exe programB.cpp Answer.cpp .PHONY: clean clean: rm -f programA.exe programB.exe
Once any significant amount of information is shared, it becomes good practice to use variables to move the repeated information to just one place. For example, we are going to want to pass the same flags to the compiler each time, so let's put that information into a variable.
CXXFLAGS = -g -Wall .PHONY: all all: programA.exe programB.exe .PHONY: programA.exe programA.exe: programA.cpp Answer.cpp Answer.h Templated.h g++ $(CXXFLAGS) -o programA.exe programA.cpp Answer.cpp .PHONY: programB.exe programB.exe: programB.cpp Answer.cpp Answer.h Templated.h g++ $(CXXFLAGS) -o programB.exe programB.cpp Answer.cpp .PHONY: clean clean: rm -f programA.exe programB.exe
The new first line defines the variable,
CXXFLAGS
, and the commands can simply use its value as$(CXXFLAGS)
instead of duplicating the list of flags. This is particularly useful once the list of flags you want to configure gets long.We can also use variables to avoid repeating the shared file names.
CXXFLAGS = -g -Wall PROGRAM_A_FILES = programA.exe PROGRAM_B_FILES = programB.exe SHARED_HEADERS = Answer.h Templated.h SHARED_FILES = Answer.cpp .PHONY: all all: programA.exe programB.exe .PHONY: programA.exe programA.exe: $(PROGRAM_A_FILES) $(SHARED_FILES) $(SHARED_HEADERS) g++ $(CXXFLAGS) -o programA.exe $(PROGRAM_A_FILES) $(SHARED_FILES) .PHONY: programB.exe programB.exe: $(PROGRAM_B_FILES) $(SHARED_FILES) $(SHARED_HEADERS) g++ $(CXXFLAGS) -o programB.exe $(PROGRAM_B_FILES) $(SHARED_FILES) .PHONY: clean clean: rm -f programA.exe programB.exe
With this sort of style, you can often control most things about the Makefile by editing variable definitions near the top, while the commands can sometimes get quite cryptic-looking but rarely require editing anyway.