2022-05-14

Makefile

What is Makefile

A Makefile is a build automation tool that has been used for decades to compile and link source code, generate executables, and manage dependencies in software development projects. It provides a convenient way to define and execute tasks, also known as "rules," which describe how to build specific targets and their dependencies. Makefiles rely on the make utility, a command-line tool that reads the Makefile and automatically executes the appropriate tasks.

Getting Started with Makefile

Installing Make

Before you can start using Makefiles, you need to install the make utility on your system. Most Unix-based systems, such as Linux and macOS, come with make pre-installed. However, if it's not already installed, you can use the following commands:

For Linux (Debian/Ubuntu):

$ sudo apt-get update
$ sudo apt-get install build-essential

For macOS (using Homebrew):

$ brew install make

For Windows, you can install make using the following methods:

  • Using WSL (Windows Subsystem for Linux): Install a Linux distribution from the Microsoft Store and follow the Linux installation instructions.
  • Using MSYS2 or Cygwin: Download and install one of these Unix-like environments for Windows, and then follow the installation instructions for Linux.

Creating Makefile

To create a Makefile, simply create a new file named Makefile (with no file extension) in the root directory of your project. You can use any text editor to write and edit the file.

Here's a simple example of a Makefile that compiles a C program:

Makefile
CC = gcc
CFLAGS = -Wall -Wextra

hello: hello.c
	$(CC) $(CFLAGS) -o hello hello.c

clean:
	rm -f hello

In this example, we define two variables (CC and CFLAGS) and use them in the rule for building the hello target. The clean rule is a phony target used to remove the generated hello executable.

Basic Makefile Syntax and Rules

A Makefile consists of rules, variable assignments, and comments. Here's a brief overview of the basic syntax:

Rules

A rule consists of a target, its dependencies, and a recipe. The format is as follows:

Makefile
target: dependencies
	recipe

The recipe is a series of shell commands indented with a tab character.

Variables

Variables in a Makefile are assigned using the = operator:

Makefile
VAR_NAME = value

You can use a variable in a rule by enclosing its name in parentheses and prefixing it with a dollar sign, like this: $(VAR_NAME).

Comments

Comments in a Makefile start with a # character and continue until the end of the line:

Makefile
# This is a comment

Phony

Phony targets are special targets that do not represent actual files but serve as a way to group related tasks. To define a phony target, add a .PHONY directive before the rule:

Makefile
.PHONY: clean


clean:
	rm -f hello

Executing Makefile

Once you have created a Makefile for your project, you can execute it using the make utility. By default, make looks for a file named Makefile in the current directory and executes the first rule found in the file. You can also specify a target to build by providing its name as an argument.

Run make to build the default target (usually the first target defined in the Makefile):

$ make

To build a specific target, provide its name as an argument to the make command:

$ make clean

In our example, this command will execute the clean rule, removing the hello executable.

You can specify multiple targets by providing their names separated by spaces:

$ make target1 target2 target3

If you want to execute a Makefile with a different name or located in a different directory, you can use the -f or --file option:

$ make -f path/to/MyMakefile

Advanced Makefile Techniques

Defining and Using Variables

Variables in Makefiles allow you to store and reuse values, making your Makefile more maintainable and easier to read. Variables can be assigned using one of the following operators:

  • =: Recursive variable assignment
  • :=: Simple variable assignment (non-recursive)
  • ?=: Conditional variable assignment (only if the variable is not already set)
  • +=: Appending to a variable

Here's an example illustrating the use of variables:

Makefile
CC := gcc
CFLAGS := -Wall -Wextra
SOURCES := main.c util.c
OBJECTS := $(SOURCES:.c=.o)

main: $(OBJECTS)
	$(CC) $(CFLAGS) -o $@ $^

%.o: %.c
	$(CC) $(CFLAGS) -c -o $@ $<

Conditional Statements and Loops

Makefiles support conditional statements and loops, which can be useful for customizing the behavior of your Makefile based on the environment or the target platform.

Conditional statements are written using ifeq, ifneq, ifdef, and ifndef directives:

Makefile
ifeq ($(OS),Windows_NT)
  EXE_EXT := .exe
else
  EXE_EXT :=
endif

hello: hello.c
	$(CC) $(CFLAGS) -o hello$(EXE_EXT) hello.c

Loops can be created using foreach and eval directives, as well as call and value functions:

Makefile
TARGETS := target1 target2 target3
RULES := $(foreach target,$(TARGETS),$(eval $(call create_rule,$(target))))

define create_rule
  $(1): $(1).c
	$(CC) $(CFLAGS) -o $(1) $(1).c
endef

$(RULES)

Pattern Rules and Wildcards

Pattern rules allow you to define generic rules for multiple targets based on file extensions or patterns. Wildcards can be used to match a group of files, simplifying your Makefile.

Here's an example using pattern rules and wildcards:

Makefile
SRCS := $(wildcard *.c)
OBJS := $(SRCS:.c=.o)

all: main

main: $(OBJS)
	$(CC) $(CFLAGS) -o $@ $^

%.o: %.c
	$(CC) $(CFLAGS) -c -o $@ $<

Managing Dependencies

Makefiles can automatically manage dependencies between targets, ensuring that only the necessary tasks are executed when a file changes.

To manage dependencies, you can use the -include directive to include the dependency files generated by the compiler:

Makefile
SRCS := $(wildcard *.c)
OBJS := $(SRCS:.c=.o)
DEPS := $(SRCS:.c=.d)

all: main

main: $(OBJS)
	$(CC) $(CFLAGS) -o $@ $^

%.o: %.c
	$(CC) $(CFLAGS) -MMD -c -o $@ $<

-include $(DEPS)

In this example, the -MMD flag tells the compiler to generate a dependency file for each source file, which is then included in the Makefile using the -include directive.

Ryusei Kakujo

researchgatelinkedingithub

Focusing on data science for mobility

Bench Press 100kg!