I’m happy to have a new contributor to my project, who did some cleanup to Makefile (thanks, @buoto!). I thought it’s a good time to write a few words on how the make tool works.

Windows users: to check if you have make installed on your machine type in your command line: make --version . If you don’t, I suggest downloading Gow, which will equip your Windows with most useful Linux commands. Windows 10 users can use Windows Subsystem for Linux feature (how to enable).

What is make tool for?

Reading my tutorials, you could notice I compile many of my examples by hand, i.e. I run commands like this every time:

$ clang++ -std=c++14 timepoints.cpp -o timepoints.exe && ./timepoints.exe

It takes time, is repetitive, it’s easy to forget some option or make a typo somewhere. Another problem is, the set of commands working on one machine may not work on another because of e.g. different OS installed.

make provides a unified environment and a framework for building projects. Not only it helps with managing compilation instructions, but also keeps track of what dependencies were already built and what can be reused. For example, C++ files are compiled into object files ( .o ). If nothing changed in a particular module, then the corresponding code doesn’t have to be recompiled. The bigger the project the more time you save.

Basic Makefile

We have a simple “Hello World” file. We usually compile it like this:

$ g++ main.cpp -o example $ ./example Hello, make!

Now we would like to use the new method. Create Makefile in your folder:

# This is a sample Makefile build: g++ main.cpp -o example

The syntax is really easy. You put target name, they semicolon. After that, in new lines (starting with a tab!) go commands. If you run make build, g++ will compile the file. You will see all the commands as if you typed them yourself.

Using macros

You can use variables in your Makefile. Some of them are already provided for you, like CXX (c++ compiler tool) or OS. To set variable name, just use normal assignment. To get variable value, use the dollar sign and braces, e.g. $(BIN_DIR) .

# This is a sample Makefile CXX = clang++ BIN_DIR = bin EXECUTABLE = example build: @echo Building on $(OS): mkdir -p $(BIN_DIR) $(CXX) main.cpp -o $(BIN_DIR)/$(EXECUTABLE) run: ./$(BIN_DIR)/$(EXECUTABLE)

What has changed:

We have set the compiler to clang++ (you can use anything) build target shows what OS we are using (@ at the beginning will suppress writing command to stdout) Executable is put in bin directory now We have a new rule to run the executable

We can order our tool to build multiple targets at a time, so we will run our program afterward:

$ make build run Building on Windows_NT: mkdir -p bin clang++ main.cpp -o bin/example ./bin/example Hello, make!

Get all the news! Did you like the content? You can get summaries regularly. A message or two per month. Also, try the social websites! First Name: Email address: Leave this field empty if you're human:

Dependencies

Let’s say we need to extract some functionality to another file. Here are the modifications:

main.cpp

#include <iostream> #include "greetings.hpp" int main() { greet(); return 0; }

greetings.hpp

#ifndef GREETINGS #define GREETINGS #include <iostream> void greet(); #endif

greetings.cpp

#include "greetings.hpp" void greet() { std::cout << "Hello, make!" << std::endl; }

Now if we run make build run new files won’t be taken into consideration:

$ make build run Building on Windows_NT: mkdir -p bin clang++ main.cpp -o bin/example C:\Users\kantoniak\AppData\Local\Temp\main-724567.o:(.text+0x67): undefined reference to `greet()' clang++.exe: error: linker command failed with exit code 1 (use -v to see invocation) make: *** [build] Error 1

We have to update Makefile with dependencies. Basically, we say that particular rule needs another to finish before. For example, to build executable, we need source files to be compiled and then we just link all of them.

# This is a sample Makefile CXX = clang++ OBJ_DIR = obj BIN_DIR = bin EXECUTABLE = example $(OBJ_DIR)/greetings.o: greetings.cpp greetings.hpp mkdir -p $(OBJ_DIR) $(CXX) -c greetings.cpp -o $(OBJ_DIR)/greetings.o $(OBJ_DIR)/main.o: main.cpp greetings.hpp mkdir -p $(OBJ_DIR) $(CXX) -c main.cpp -o $(OBJ_DIR)/main.o build: $(OBJ_DIR)/main.o $(OBJ_DIR)/greetings.o @echo Building on $(OS): mkdir -p $(BIN_DIR) $(CXX) $(OBJ_DIR)/main.o $(OBJ_DIR)/greetings.o -o $(BIN_DIR)/$(EXECUTABLE) clean: rm -rf $(OBJ_DIR)/* rm -rf $(BIN_DIR)/* run: ./$(BIN_DIR)/$(EXECUTABLE)

It means that before build target will be made, it needs obj/main.o and obj/greetings.o. Actually, these files get compiled before:

$ make build run clang++ -c main.cpp -o obj/main.o clang++ -c greetings.cpp -o obj/greetings.o Building on Windows_NT: clang++ obj/main.o obj/greetings.o -o bin/example ./bin/example Hello, make!

Need more?

The basics are covered. But make tool is much more, and there are even more tools extending its functionality, adding GUIs, etc. You will find other resources here: