Module 7: Build Automation with Make
- First read this page then start the module with the GitHub classroom link below.
- Github Classroom Link: https://classroom.github.com/a/qYmpO0uT
Compilation
When you compile a C++ program, the compiler often does the following steps:
- Preprocessing: Handles macros,
#includedirectives, and other preprocessor commands. - Compilation: Translates C++ code into assembly language. This is where a
.sfile can be generated if desired. - Assembly: Converts the assembly code into machine code, producing an object file (usually with a
.oor.objextension). - Linking: Combines multiple object files and resolves references to produce the final executable or shared library.

Exercise 1
Go to the exercise1 directory of your module 7 GitHub repository. Put your answers to the questions below in the README.md in your module 7 GitHub repository.
- Run the following commands to inspect the file type of each file.
$ file calculate.cpp $ file functions.cpp $ file functions.h - Read the contents of the
calculate.cpp,functions.cpp, andfunctions.hfile. - What is the purpose of including the
functions.hfile incalculate.cpp? - Run the following commands to run the preprocessing step.
$ g++ -E -P calculate.cpp > calculate.i $ g++ -E -P functions.cpp > functions.i - Read the contents of
calculate.iandfunctions.i. - What has changed compared to the
.cppversions of these files? - Now run the following commands to run the compilation step.
$ g++ -S calculate.i $ g++ -S functions.i - Run the following commands to inspect the file type of each file.
$ file calculate.s $ file functions.s - Read the contents of
calculate.sandfunctions.s. - What has changed compared to the
.iversions of these files? - Now run the following commands to run the assembly step.
$ g++ -c calculate.s $ g++ -c functions.s - Run the following commands to inspect the file type of each file.
$ file calculate.o $ file functions.o - Read the contents of the
calculate.oandfunctions.oby running.$ xxd -bits calculate.o $ xxd -bits functions.o - What has changed compared to the
.sversions of these files? - Now run the following command to run the linking step.
$ g++ calculate.o functions.o -o calculate - Why did we have to provide both
.ofiles at the same time when linking? - Run the following commands to inspect the file type of the executable.
$ file calculate - How is the
calculateexecutable file different from the.ofiles? - Run the executable.
$ ./calculate - In Linux (and Unix-like systems), the return value (or exit status) of the last command executed is stored in a special shell variable
?. You can access this value using$?.$ echo $? - Is the return value what you expected from running
calculate?
Build Automation Tools: Make
Makefile: The make utility reads a special file known as a makefile.
- The makefile (or Makefile) describes the files involved in the project and the dependencies among them.
- Each group of lines in a makefile has the following form.
target: dependencies <Tab> commands - In this context,
- target is a file to be created
- dependencies is a list of files on which the target depends
- commands is a list of commands used to (re)create the target.
- If any of the files in a dependency list is modified, make will recompile and/or relink the target.
- Important: a
Tabcharacter must precede the commands — not spaces.
Exercise 2
Go to the exercise2 directory of your module 8 GitHub repository. Put your answers to the questions below in the README.md in your module 8 GitHub repository.
- Review the
Makefilein theexercise2directory. - Run the
makecommand. Copy and paste the result of issuing the make command. - Run the
makecommand again. Copy and paste the result of issuing the make command. - Run the commands:
$ touch functions.h $ makeCopy and paste the result of issuing both commands.
- What did the
touchcommand do in this case (man touch)?
Exercise 3
Go to the exercise3 directory of your module 8 GitHub repository. Put your answers to the questions below in the README.md in your module 8 GitHub repository.
- One way to avoid having all files be recompiled each time is to make use of object files.
- To create an object file for the hello.cpp source file, you could issue the command
g++ -c hello.cpp(but do not do so here). - The result would be a file
hello.o. This file is not executable because no linking has taken place, only compilation — the result of using the-cflag. - Create a rule in your
Makefilefor creatinghello.osimilar to the following (remember tab, not spaces!):hello.o: hello.cpp functions.h g++ -g -Wall -c hello.cpp - Notice that the dependencies for
hello.oarehello.cppandfunctions.h. - The
hello.cppsource file as a dependency should be obvious. The function.h is also a dependency because it is included in the file hello.cpp. - Now create similar rules for creating main.o and factorial.o. (What dependencies do these two need? Make sure to modify the corresponding commands in the current Makefile as well.)
- Run
make. List all.ofiles created by the make command. - Run
make. Explain the result. - Run
make main.o. Copy and paste the result of issuing this command. - Run
$ make factorial.o $ ls -alIs there an executable present for running main?
- Create another rule as the last rule in Makefile that will create the target
hellobased on the dependencieshello.o,main.o, andfactorial.o. Hint:g++ hello.o main.o factorial.o -o hello - Run:
$ make hello $ ./helloCopy and paste the output from issuing both commands.
- Run:
$ rm *.o hello $ make $ ./helloCopy and paste the output from issuing the commands.
- Explain the previous output.
- Make the necessary modification to have
hellobe the default target created bymake. - Run:
$ touch functions.h $ makeCopy and paste the output from issuing the commands.
- Explain the previous output.
- Run:
$ touch hello.cpp $ makeCopy and paste the output from issuing the commands.
- Explain the previous output.
- Run:
rm *.o hello - Could we get make to handle cleanup work like this for us? Yes — the target does not have to be a file-to-be-created. In the examples we have seen thus far, the command has been a compile command which naturally creates a file. We can issue other command-prompt commands that do not result in a created file. The typical way to have make handle the cleanup is to create a new rule at the end of your makefile. The rule should have a target called
clean(no dependencies) with the command/bin/rm -f *.o hello. Add this rule to the endMakefile. - Run:
$ make $ make clean - Copy and paste the output from issuing the commands.
- Should the
cleantarget be first in the makefile? Explain.