Makefiles are great for standardizing builds.

General Rules

Use only tab characters in makefiles. Otherwise, you get an error.

Makefile Example

Tips

Add echos for key variables

Random How-Tos

Basic Substitution

You can substitute text in variables very simply:

VAR_SRC_FILE_NAME = test.c
VAR_OBJ_FILE_NAME = $(VAR_SRC_FILE_NAME:.c=.o)

Automatic Variables

See here as well.

Variable Description
$@ Rule target
$< First dependency
$^ All dependencies, space-separated
$* The stem which an implicit rule matches. For example, if the rule is %.o: %.c and you want to build app.o from app.c, $* will be ‘app’

Source File List to Object File List

If you want to generate an object file for each source file and put them in a special object file directory, do this:

SOURCES = src/a.cpp src/b.cpp src/c.cpp
OBJECTS = $(patsubst %.c, obj/%.o, $(notdir $(SOURCES)))

Ignoring Errors in Commands

Sometimes a command will cause an error, but you kinda expect that and want to keep going. A Stackoverflow answer shows you how to ignore errors in commands - you put a dash in front of the command like so:

build_target: $(PRE_REQS) 
    -this_command_causes_an_error_but_is_ignored $(PRE_REQS)

Source files in multiple subfolders

Sometimes you have source files in multiple subdirectories of a main directory and you don’t want to make a dozen rules for a dozen different subdirectories.

SRC_DIRS = $(sort $(dir $(wildcard ../src/*/)))
vpath %.c $(SRC_DIRS)

This will:

  1. Generate a list of subdirectories in the ../src
  2. Set the VPATH to look for .c files in each of the src subdirectories

VPATH is a built-in makefile feature which assigns a list of search directories to look for specific files or types. In this case, if you have a rule like this:

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

make will now search for .c files in each of the vpath subdirectories and you get to have one rule like this instead of this:

obj/%.o: subdir1/%.c:
    $(CC) - $(CFLAGS) -c $< -o $@
    
obj/%.o: subdir2/%.c:
    $(CC) - $(CFLAGS) -c $< -o $@    

Filtering out an item from a list based on an implicit rule

Okay, so imagine you’re creating unit tests and you have 20 different source files in your application and you want to test the functions in one source file at a time. You will mock out the remaining functions for testing purposes. However, when you generate the mocks, you generate mocks for all source files. You can’t link the mock object file in the same executable as the actual source object file because all of the functions in the source file are repeated in the mock. So, you have to remove the mock object file from being linked into the unit test executable. Let’s say you have a rule to generate the executable from object files - this is how you structure the rule to remove the mock object file from being linked. Assume $(MOCK_OBJ_FILES) contains all of the mock object files and ut_%.o is the unit test object file for the source file you want to test.

$(BIN_DIR)/test_%: $(OBJ_DIR)/ut_%.o $(MOCK_OBJ_FILES)
	$(LINKER) $(filter-out $(OBJ_DIR)/Mock$*.o,$^) $(LINKFLAGS) -o $@
    

The use of the $* variable matches the stem of the implicit rule. For the $(OBJ_DIR)/ut_%.o you can use the percent sign, but that won’t work inside a function like filter-out, so you have to use the automatic variable $* instead. Note: You CANNOT match the implicit stem in the dependencies for the rule - you have to put them all in and then filter the dependencies when executing the link command

Getting a list of all subdirectories recursively

If you have need a list of all subdirectories all the way down starting from a root directory, try this:

SUBDIRS = $(shell find $(ROOT_DIR) -type d)

Getting a list of all source files recursively

If you have a bunch of source files all over a directory (maybe with subdirectories or whatnot) and you want to find all of the, say, .c files in that directory recursively, you do this:

SOURCES := $(shell find $(SOURCEDIR) -name '*.c')

This should find all .c files recursively under $(SOURCEDIR).

foreach - Include Directories

If you have a list of include directories and want to turn that list into a list of flags you have to pass the compiler, use this:

INCDIRS = ./include /lib/include
INCFLAGS = $(foreach d,$(INCDIRS),-I$d)

Resources