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.exeAs before, the target is named after the program to build, and it depends on all of the source files, both
.cppand.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.cppfiles (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.exeThe two
.exerules are similar, but note that each program only uses its own.cppfile 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.exeIf 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. Theallrules itself does not have any commands - all it does is trigger both program rules to run.Since
allis the first rule, it is the one that runs if you runmakewith no arguments. You can always run justmake programA.exeormake programB.exeif 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.exeOnce 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.exeThe 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.exeWith 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.