COMP 2012 Object-Oriented Programming and Data Structures

Lab 1 Makefile and Separate Compilation

Welcome Back

In this lab, we are going to practice separate compilation using Makefile. We are going to design a reception bot for visitors using C++ code. You are given some source codes (.h/.cpp), which are almost complete. You will learn about the basic usage of Makefile, work with the Makefile and add a few lines in some files. Before implementation, you could read the Makefile lecture notes. The source files are available HERE.

1. What is Separate Compilation?

Suppose you have a program that consists of 3 cpp files: file1.cpp, file2.cpp, and file3.cpp.

With the g++ command, one can compile the program with the following command that produces an executable named a.out

g++ -o a.out file1.cpp file2.cpp file3.cpp

Any changes to any 3 files (e.g., just editing a line in file1.cpp) will require the g++ compiler to recompile all 3 cpp files to generate the updated executable.

This is inefficient. To alleviate this problem, we can make use of separate compilations.

To perform separate compilations, we use the following commands instead.

g++ -c file1.cpp
g++ -c file2.cpp
g++ -c file3.cpp
g++ -o a.out file1.o file2.o file3.o

Each of the first 3 commands will individually compile fileN.cpp into fileN.o - an object file (which is not executable) containing the compiled code for the corresponding cpp file. The last command then simply links those object files together to generate the final executable a.out.

Now, say, if a line is edited in file1.cpp, we just need to execute:

g++ -c file1.cpp
g++ -o a.out file1.o file2.o file3.o

And an updated executable would be generated because the existing file2.o and file3.o can simply be reused.


2. Makefile

Makefile allows us to create a set of compilation rules for your programming project. It is very useful for separate compilation. A Makefile consists of a number of rules. The rules have the following syntax:

[Name of rule (target) #1]: [List of dependent files/targets, separated by a space]
[Press a tab character here][Command 1 to be executed]
[Press a tab character here][Command 2 to be executed]
...
[Press a tab character here][Command N to be executed]
[Empty lines....]
[Name of rule (target) #2]: [List of dependent files/targets, separated by a space]
[Press a tab character here][Command 1 to be executed]
[Press a tab character here][Command 2 to be executed]
...
[Press a tab character here][Command N to be executed]
[Empty lines....]

The name of a rule is also called its target. When a rule is executed, all its dependent files/targets will first be generated using the rules of the same names (e.g., a dependent file "main.exe" can be generated by the rule named "main.exe") unless the files are already there and up-to-date. Then, after all dependent files are generated, the commands under the rule will be executed.

The first rule in a Makefile will be executed by default when you simply run the command make (without mentioning the target/rule name), In the given Makefile, it will be the rule "all". Note that, however, the first target is generally not necessarily called "all"; it can be anything you want.

We usually write a rule to clean up a folder by removing all the temporary files (e.g. executable and object files). In the given Makefile, to execute that, you will have to run the command make clean.

You need these three commands in this lab.

To create an object file (e.g., main.o) from a source file (e.g., main.cpp):

g++ -std=c++11 -o Output filename -c Source file name

To link the object files together to make an executable:

g++ -std=c++11 -o Output filename Object file name 1 Object file name 2 ... Object file name N

To delete files:

On Windows:
del File name pattern
Or, on Linux/MacOS:
rm -f File name pattern

Note: it is possible to detect which OS is being used and pick the command automatically in the Makefile, but it is slightly complicated, so just write the command that works for the OS you are using (please edit the provided Makefile accordingly yourself).

2.1 Meta Rule
You may notice that we have many .cpp files and most of them are doing the same line. To save up the copy and paste work, we can define a meta-rule instead. The following meta rule is used to generate any_name.o file:
%.o: %.cpp
	g++ -std=c++11 -c $< -o $@
The % symbol means any name. So the rule simply says any .o file depends on its corresponding .cpp file. The command to be executed under this rule uses g++ to compile the .cpp source file to its .o object file.
  • -c $< : Specify the source file name %.cpp as the first dependent file.
  • -o $@ : Specify the output file name %.o as the target.

Note: Due to a convention, we usually write the makefile file name "Makefile" with a capital "M". For details, read this discussion.


3. Add the source code

We use VS Code for this semester. You may refer to our VS Code tutorial page for setup and usage instructions. You may also refer to our How to add "-std=c++11" to VS Code for help with compiling.

Download the source files. Unzip/extract the zip file and open the extracted folder. See Creating a project and using the terminal for custom compilation command section of our VS Code tutorial on how to open the folder (you don't need to create a new folder because the folder is already there after the extraction). You may also find the instructions there useful in the latter part of this lab, where you will need to enter some commands in the terminal in VS Code.


4. Read the .cpp/.h files

There are many files to read, while most of them contain several lines of code only. You are suggested to begin with the files main.cpp, openningMessage.cpp, QA.cpp.

//main.cpp
#include "openningMessage.h"

int main() {
    sayHello();
    return 0;
}
//openningMessage.cpp
#include <iostream>

using namespace std;

int sayHello() {
    cout << "Hello, visitors!" << endl;
    cout << "Welcome to HKUST 30th Anniversary!" << endl;
    return 0;
}
//QA.cpp
#include <iostream>
#include <string>

using namespace std;

int QA() {
    cout << "What do you want to check (1: Today's event, 2: History of HKUST, 3: Quit)? ";
    string reply;
    while (cin >> reply) {
        if (reply == "1") {
            cout << "Today's event" << endl;
        } else if (reply == "2") {
            cout << "History of HKUST" << endl;
        } else if (reply == "3") {
            break;
        } else {
            cout << "Please enter the number 1, 2 or 3 (1: Today's event, 2: History of HKUST, 3: Quit)" << endl;
        }

        // repeat the message
        cout << "What do you want to check (1: Today's event, 2: History of HKUST, 3: Quit)? ";
    }

    cout << "Have a nice day in HKUST!!!" << endl;

    return 0;
}

So, for example, main.exe needs different functions in openningMessage and QA c++ files. You need to deduce the dependency and write them in the Makefile.


5. Open the Makefile in VS Code

You should see:

CPPFLAGS = -std=c++11
all: main.exe

main.exe: openningMessage.o main.o
	g++ -o $@ $(CPPFLAGS) openningMessage.o main.o


%.o: %.cpp
	g++ $(CPPFLAGS) -c $< -o $@

clean:
  del  *.o *.exe
# On Windows, use: del *.o *.exe
# On Linux or MacOS, use: rm -f *.o *.exe

Note: change "del *.o *.exe" to "rm -f *.o *.exe" under the "clean" rule if you are using Linux or MacOS.

Beware that the indentations are all just single tab characters as described in the previous section.

Extra: noted that we are only dealing with ".cpp" files here, how about ".h" files? You could read "make depend section" in the the Makefile lecture notes! For Windows users, since makedepend is a Unix command, we need to download the makedepend.exe here. Unzip the downloaded zip files and you will be able to find the makedepend.exe in UnxUtils\usr\local\wbin. You may now add the rule to the Makefile.

depend:
	path\to\UnxUtils\usr\local\wbin\makedepend -o.o $(SRCS)
                      

Note: by default the win32 version prints .obj, so we need to use the -o.o option. Besides makedepend, you might also try the similar feature built into GCC with the -MM flag (i.e. g++ -MM *.cpp) without downloading anything. The difference between g++ -MM and makedepend is that makedepend change the Makefile automatically and g++ -MM only print the dependency on screen (we need to copy those dependencies to the Makefile manually).


Lab tasks

Task 1: Make and run the exe files

In the terminal (see the tutorial on how to open the terminal in VS Code), enter the "make" commands to compile the project using the Makefile. Afterwards, you can enter the command "./main.exe" to run the generated programs.

The program "main.exe" should show:

Hello, visitors!
Welcome to HKUST 30th Anniversary!

Task 2: Modify the Makefile to include function in QA.cpp

Reception bot program should allow visitors to ask some questions. Modify the Makefile to include the Q&A function. You should update the rule of main.exe in Makefile to include QA object file. Also remember to include the header file of QA in main.cpp and add the function QA() in the main function of the main.cpp. Visitors can then ask some questions by entering number 1 or 2 and quit the program by entering number 3.

When you run main.exe, it shows:

Hello, visitors!
Welcome to HKUST 30th Anniversary!
What do you want to check (1: Today's event, 2: History of HKUST, 3: Quit)?

Task 3: Update the openningMessage.cpp and Make main.exe Again

The bot should also introduce itself to the visitors. Please edit the openningMessage.cpp to enable the bot to introduce itself. The bot should say "I am your reception bot." at the end of the openning messages.

Once editing, you should notice that only g++ -std=c++11 -c openningMessage.cpp -o openningMessage.o is run (g++ -std=c++11 -c main.cpp -o main.o will not be executed) when you execute make.

If succeed, you would get the result like
Hello, visitors!
Welcome to HKUST 30th Anniversary!
I am your reception bot.
What do you want to check (1: Today's event, 2: History of HKUST, 3: Quit)?


Submission Deadline and Guidelines:

The lab assignment is due on 10 minutes after the end of your lab session.
We will use an online grading system ZINC to grade your lab work. Therefore, you are supposed to upload the following files in a zip file to ZINC:
- Makefile
- main.cpp
- openningMessage.cpp
You can submit your codes multiple times to ZINC before the deadline. Only the last submission will be graded.

Page maintained by
  • Wai TONG
  • Last Modified:
Homepage