Getting started with makefiles

The make command allows for quick building of your C++ (and other language) projects. For large projects, this becomes very helpful to update portions of the project rather than recompiling everything. In this tutorial, I’ll give you a intro to using make.

Setup

We will start with 3 files that make up our program. Make a new directory and add these three files.

mkdir make-tutorial
cd make-tutorial
touch myclass.h myclass.cpp main.cpp
// myclass.h
class MyClass {
  public:
    void foo();
    int bar;
};
// myclass.cpp
#include "myclass.h"
#include <iostream>

using namespace std;

void MyClass::foo() {
  cout << "From Foo" << endl;
}
// main.cpp
#include "myclass.h"

int main()
{
  MyClass myClass;
  myClass.foo();
  return 0;
}

Automating the Basics

Let’s start by simplifying our compile flow of these three files. To compile them normally, we would do the following:

  g++ main.cpp myclass.cpp -o hellomake

Then we can run the program.

./hellomake

This is pretty simple, but we can make that a little easier with a make file. By default, when running the make command, the program will look for a file called makefile. Let’s start with that.

//  makefile
all:
    g++ main.cpp myclass.cpp -o hellomake

The syntax starts with the target. Here we have the target all which will be the default command for make to execute. We can then run the program likes so:

make
./hellomake

That make things a little easier 😀

Multiple Build Points

When projects get bigger, you will want multiple targets so that you can build portions of it rather than the whole project or the all target. Let’s use the current projects to show this with make. It will seem overly complex for this project, but hopefully you can see the potential.

Let’s create a new file called makefile-targets. Then add these two lines

all: hellomake

hellomake: main.o myclass.o
    g++ main.o myclass.o -o hellomake

This creates an all target that links to the hellomake target. This target will take two object files and build our whole project.

Next we will need to create targets to build those object files. Do that in this way:

myclass.o: myclass.cpp
    g++ -c myclass.cpp

main.o: main.cpp
    g++ -c main.cpp

The above will create our object files for our all target to use. Finally, it is good practice to create a clean target to remove object files:

clean:
    rm *o hellomake

The final file looks like this:

// makefile-target
all: hellomake

hellomake: main.o myclass.o
    g++ main.o myclass.o -o hellomake

myclass.o: myclass.cpp
    g++ -c myclass.cpp

main.o: main.cpp
    g++ -c main.cpp

clean:
    rm *o hellomake

We can then build our project likes so:

make -f makefile-target
./hellomake

Notice how we use the -f flag to specify our new file.

You can then rebuild a specific target like by adding an extra parameter to the make command:

rm myclass.o
make -f makefile-targets myclass.o hellomake

This allows us to rebuild only the myclass rather than the whole project.

Conclusion

There is much more to make than what I have shown you. Hopefully, this gives you a glimpse into the powerful tool of make.

If you really want to dig in, check out the docs: https://www.gnu.org/software/make/manual/make.html