Kahibaro
Discord Login Register

Introduction to Make

What Make Is and Why It Matters in HPC

make is a classic build automation tool. In HPC, it is still one of the most common ways to:

At its core, make:

This chapter focuses on basic, practical usage of make for small to medium scientific codes.

Basic Concepts: Targets, Dependencies, and Rules

A Makefile consists of rules. Each rule usually has:

General form:

target: dependencies
<TAB>recipe

Important details:

A minimal example:

myprog: main.o helper.o
    gcc -O2 -o myprog main.o helper.o
main.o: main.c
    gcc -O2 -c main.c
helper.o: helper.c
    gcc -O2 -c helper.c

Behavior:

This automatic decision-making is what saves time in HPC development: large codes often have many source files, and you only want to rebuild what actually changed.

A First Simple Makefile for an HPC-Style Program

Consider a simple C program split into multiple files:

A simple Makefile:

CC     = gcc
CFLAGS = -O2 -Wall
myprog: main.o solver.o io.o
    $(CC) $(CFLAGS) -o myprog main.o solver.o io.o
main.o: main.c
    $(CC) $(CFLAGS) -c main.c
solver.o: solver.c
    $(CC) $(CFLAGS) -c solver.c
io.o: io.c
    $(CC) $(CFLAGS) -c io.c

Usage:

Variables in Makefiles

Variables make Makefiles flexible and easier to maintain.

Defining variables:

CC     = mpicc
CFLAGS = -O3 -march=native
LDFLAGS =
LIBS   = -lm

Using variables:

myprog: main.o solver.o io.o
    $(CC) $(LDFLAGS) -o myprog main.o solver.o io.o $(LIBS)
%.o: %.c
    $(CC) $(CFLAGS) -c $<

Notes:

You can override variables on the command line:

make CC=icc CFLAGS="-O3 -xHost"

Pattern Rules and Automatic Variables

Typing a separate rule for every .c file is repetitive. Pattern rules solve this.

Pattern Rules

A pattern rule with % can apply to many files:

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

This means:

Then you only need per-program link rules:

myprog: main.o solver.o io.o
    $(CC) $(CFLAGS) -o myprog main.o solver.o io.o

Useful Automatic Variables

Within a rule, make provides automatic variables:

Examples:

%.o: %.c
    $(CC) $(CFLAGS) -c $<
myprog: main.o solver.o io.o
    $(CC) $(CFLAGS) -o $@ $^

Here:

Multiple Targets and Phony Targets

Not all targets have to be real files. Some are just commands you want to run.

Phony Targets

Use phony targets for actions like cleaning build files:

.PHONY: clean
clean:
    rm -f myprog *.o

Why .PHONY?

Common phony targets in HPC development:

Typical HPC-Conscious Makefile Variants

HPC work often needs different build types:

You can add separate targets that reuse most of the same rules.

Debug vs Optimized

CC = gcc
CFLAGS_DEBUG   = -O0 -g -Wall
CFLAGS_RELEASE = -O3 -march=native -DNDEBUG
.PHONY: debug release
debug: CFLAGS = $(CFLAGS_DEBUG)
debug: myprog
release: CFLAGS = $(CFLAGS_RELEASE)
release: myprog
myprog: main.o solver.o io.o
    $(CC) $(CFLAGS) -o $@ $^
%.o: %.c
    $(CC) $(CFLAGS) -c $<

Usage:

This pattern is very common for HPC codes shared by multiple users.

MPI vs Serial

CC_SERIAL = gcc
CC_MPI    = mpicc
CFLAGS_COMMON = -O2 -Wall
CFLAGS_SERIAL = $(CFLAGS_COMMON)
CFLAGS_MPI    = $(CFLAGS_COMMON) -DMPI_ENABLED
.PHONY: serial mpi
serial: CC = $(CC_SERIAL)
serial: CFLAGS = $(CFLAGS_SERIAL)
serial: myprog
mpi: CC = $(CC_MPI)
mpi: CFLAGS = $(CFLAGS_MPI)
mpi: myprog

Here:

Dependency Management Basics

Large HPC codes often depend on header files (.h), which can trigger many recompilations.

Minimal explicit dependencies:

main.o: main.c solver.h io.h
solver.o: solver.c solver.h
io.o: io.c io.h

This ensures that changing solver.h triggers recompilation of main.o and solver.o.

More advanced automatic dependency generation uses compiler options (e.g., -MMD -MP with GCC/Clang), but the details can be postponed to more advanced build-system material.

Running Make and Common Options

Typical usage patterns:

  make
  make myprog
  make debug
  make clean

Useful options:

Note: On shared login nodes of HPC clusters, be careful with make -j if it spawns heavy compilation jobs; consider compiling on dedicated build nodes if available.

Good Practices for Make in HPC Contexts

Some practical tips:

Minimal Complete Example

A compact Makefile for a small HPC-style C project:

# Compiler and flags
CC      = mpicc
CFLAGS  = -O3 -Wall
LDFLAGS =
LIBS    = -lm
# Program and sources
PROG    = myprog
SRCS    = main.c solver.c io.c
OBJS    = $(SRCS:.c=.o)
# Default target
$(PROG): $(OBJS)
    $(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
# Pattern rule for object files
%.o: %.c
    $(CC) $(CFLAGS) -c $<
# Utility targets
.PHONY: clean debug
debug: CFLAGS = -O0 -g -Wall
debug: clean $(PROG)
clean:
    rm -f $(PROG) $(OBJS)

Usage:

This pattern is a solid starting point for many small HPC exercises and prototype codes.

Views: 10

Comments

Please login to add a comment.

Don't have an account? Register now!