Homework 3: Make Your Makefile
Now it's time to make your own Makefile
! The Makefile
will contain all the compilation and linking steps to make an executable called our-movie
. You and your partner (together or individually) should complete the written questions in Part 1 before working on this part.
In the asciimation
directory, you'll find a file called our-movie.cpp
, which contains a main
function. There are also three pairs of header/implementation files:
sprite.cpp
andsprite.hpp
, which you'll use to create ASCII-art–based “characters”asciimation.cpp
andasciimation.hpp
, which you'll use to coordinate the “characters” on the screenncurses-helper.cpp
andncurses-helper.hpp
, which are wrappers around thencurses
library (see below) that we've written for you, but that you shouldn't have to modify.
Reminder: Compiling by Hand
If you compiled this code by hand, you would have to run several complex commands, specifically:
clang++ -g -std=c++17 -c -pedantic -Wall -Wextra boundingbox.cpp
clang++ -g -std=c++17 -c -pedantic -Wall -Wextra boundingbox-test.cpp
clang++ -g -std=c++17 -c -pedantic -Wall -Wextra sprite.cpp
clang++ -g -std=c++17 -c -pedantic -Wall -Wextra sprite-test.cpp
clang++ -g -std=c++17 -c -pedantic -Wall -Wextra display.cpp
clang++ -g -std=c++17 -c -pedantic -Wall -Wextra display-test.cpp
clang++ -g -std=c++17 -c -pedantic -Wall -Wextra asciimation.cpp
clang++ -g -std=c++17 -c -pedantic -Wall -Wextra our-movie.cpp
to build all the object files (boundingbox.o
, boundingbox-test.o
, sprite.o
, sprite-test.o
, display.o
, display-test.o
, asciimation.o
, and our-movie.o
); and, finally,
clang++ -g -std=c++17 -o boundingbox-test boundingbox-test.o boundingbox.o -ltestinglogger
clang++ -g -std=c++17 -o sprite-test sprite-test.o sprite.o boundingbox.o -ltestinglogger
clang++ -g -std=c++17 -o display-test display-test.o display.o -lncurses
clang++ -g -std=c++17 -o our-movie our-movie.o asciimation.o sprite.o display.o boundingbox.o -lncurses
to build three test executables (boundingbox-test
, sprite-test
, and display-test
) and the final executable (our-movie
). Note that the -ltestinglogger
flag is needed to link the testing library into the test executables, and the -lncurses
flag is needed to link the ncurses
library into the our-movie
executable.
Remembering all the different arguments for clang++
is a pain, and you will also have to figure which commands you need to rerun if you make changes to one or more files. You could keep detailed notes about which files depend on each other, like this:
boundingbox.cpp
usesboundingbox.hpp
boundingbox-test.cpp
usesboundingbox.hpp
sprite.cpp
usessprite.hpp
andboundingbox.hpp
sprite-test.cpp
usessprite.hpp
andboundingbox.hpp
display.cpp
usesdisplay.hpp
display-test.cpp
usesdisplay.hpp
asciimation.cpp
usesasciimation.hpp
,display.hpp
,sprite.hpp
, andboundingbox.hpp
our-movie.cpp
usesasciimation.hpp
,display.hpp
,sprite.hpp
, andboundingbox.hpp
and
boundingbox-test
needs to be relinked ifboundingbox-test.o
orboundingbox.o
changessprite-test
needs to be relinked ifsprite-test.o
orsprite.o
orboundingbox.o
changesdisplay-test
needs to be relinked ifdisplay-test.o
ordisplay.o
changesour-movie
needs to be relinked ifour-movie.o
orasciimation.o
orsprite.o
ordisplay.o
orboundingbox.o
changes
But keeping track of all this, remembering all the commands, and running them at the proper time is tedious and error-prone, which is why we use build systems like make
.
A “makefile” for the project allows you to figure out commands and dependencies once and then let the make
program decide what needs to be rebuilt for later builds.
Your Task: Create Your Makefile and Use It
Make Your Makefile
Create a makefile for your project (which, by convention, is called Makefile
when there is just one) in the asciimation
directory with all of the commands you'll need to compile the our-movie
executable. You will be graded on how “well” your Makefile performs; for example, compiling only what's necessary for any given change and doing those compilations correctly.
Include both the all:
and clean:
targets in your Makefile
.
If you're looking for an example you can use as a starting point, the segfault-generator
directory contains an example of a very basic Makefile
, as did the make
exercise that you saw at the start of the written part of this assignment (which also had more advanced examples, with variables).
Note that you do not have to comment your Makefile
as we did in our example. We added those comments for your benefit! For basic makefiles like the ones we will use in CS 70, it's probably not necessary to add comments. (Of course you can always include comments if you want.)
Make sure you read the hints below for some very useful information about Makefiles!
Test Your Makefile
Once you've written your Makefile
, you should be able to run cs70-make
or make
in the asciimation
directory to generate any of the .o
files, and to link the .o
files to make the three executables: boundingbox-test
, sprite-test
, and our-movie
.
Why would I use
cs70-make
rather thanmake
?See the hints below for an explanation.
Meh. The programs don't do anything yet. The tests fail.
You didn't think you'd be done with the assignment after just making a Makefile, did you?
Helpful Hints
It's just called Makefile
By default, the make
program looks for a file called Makefile
. If you wanted to have lots of make files, you could use the extension .mak
, but, by default, the name is Makefile
with no file extension. And yes, it's a capital M
at the front.
Test Your Makefile
(Use cs70-make
)
Once you write your Makefile
, you should be able to successfully run cs70-make
or make
to generate any of the .o
files, and to link the .o
files together into the executable our-movie
.
Why would I use
cs70-make
rather thanmake
?The
cs70-make
program explains everything its doing and why, which can be helpful for checking that yourMakefile
is working properly.It's also often more likely to give an error when you've made a mistake in your
Makefile
. Sometimes, regularmake
assumes that you're an expert and you meant to do something weird.
Indent with Tabs (in Makefiles)
The syntax for Makefiles requires that the line below the target and dependencies begins with a literal tab character followed by the command it should run. Check the example Makefile
you were given for examples. Note that in the bottom bar in VS Code there is a convenient way to switch between using tabs/spaces for a given file. (If you have the Makefile support that VS Code offers to install, it will automatically set the indentation character to a tab in Makefiles.)
Headers that Include Headers
Remember that a header file can include another header file. If a .cpp
file includes a .hpp
file that includes another .hpp
file, it depends on both of them! That is, if either header file changes, the .cpp
file should be recompiled to stay up-to-date.
Fancy Makefile Tricks Are Optional
You don't have to use the fancy tricks described on the Makefile
resource page (or any of the terrifying shortcuts found on the internet)—if your Makefile
doesn't use any tricks fancier than the example provided for segfaultGenerator
, that's fine.
Using the ncurses
Library
ASCIImation uses a library called ncurses
to display characters to the screen. You don't need to compile ncurses
, but you do need to link your executable against ncurses
or you'll be missing symbols. So, at the end of the line you put in your Makefile to compile and link your executable, add -lncurses
. If generateSegfault
required ncurses
(which it doesn't, but just as a hypothetical), you'd do the following:
clang++ -o generateSegfault -g -std=c++11 -pedantic -Wall -Wextra generateSegfault.o helloSayer.o -lncurses
(When logged in, completion status appears here.)