Understanding Dependence in Makefile

May 7, 2020

Introduction

make is a simple and useful tool that allows you building your own code projects. It can be used in any programming language. It’s also used in coding for repetitive computer tasks. For example, if you have multiple shell tasks that listed one by one. You can use make to organize your work flow.

If you’ve downloaded some open source code or joined a new project as a developer. To run the entire project, you have to start building it first. Nearly all open source projects have a Makefile for organizing code. The Makefile is not easy to understand. The Makefile official manual is so long it’s hard to understand. You can’t modify the Makefile for your mind if you’re a newbie. You can then Google the Makefile tutorial and copy&paste some codes within your Makefile. But you don’t know how Makefile works, you just let your code run.

This post will take you 10 minutes to understand the make core concepts. Afterwards, you will create your own Makefile to do some basic tasks and figure out how make works.

Core conception

Dependence is the make’s core. Understanding this is the most important principle in make. In the Makefile every task depends on another prerequisites that can be files, tasks, or some other form of rules. If you want to do a task, you must define your target and its prerequisites. We call this a dependence. A simple Makefile consists of “dependencies” with the following shape:

foo: bar

The above Makefile is do nothing, because you bar target is undefined. Target should depend on a defined prerequisite or just define how it works. The following target is a defined to run echo command.

foo:
    echo "hello world"  #noticed: you must use 'tab' to start this line.

A realistic example

Next, we will give you a realistic example. Suppose we have a simple C project, which contains the following files:

.
├── http.c
├── http.h
├── main.c
├── socket.c
├── socket.h

In our project, we have the following dependencies:
http.c include http.h and socket.h
socket.c include socket.h
main.c include socket.h and http.h.

To build an executing file using gcc, we must compile the source to the object and then link them to the be an executing file. we must compile the object first. Here are our rules:

http.o: http.c http.h socket.h
    gcc -c http.c
socket.o: socket.h
    gcc -c socket.c
main.o: main.c http.h socket.h
    gcc -c main.c

Link all object files to be a execute file:

exe: http.o socket.o main.o
    gcc -o exe http.o socket.o main.o

Now, you can type make exe to build your own project.