Advanced GNU Make

Let our advance worrying become advance thinking and planning. -- Winston Churchill

GNU Make is often included within a complex ecosystem of Integrated Development Environments, build systems (such as boost.build, CMake, ninja, SCons, and/or waf), and compilers. Some people say Make is starting to show its age and there are better tools now. Maybe that is correct, but there is still a need for it on some legacy projects.

Previously I wrote an article on GNU Make, but I thought I’d enhance it a touch. That write-up assumed the files all lived in a single directory (and to make it more simple still, just a single file). Most real world projects are far more complex. Today the author will expand a bit more into the world of Makefiles. Much of what follows was inspired by:

Try as I might, I couldn’t figure out how to get wildcards to work on my system! Wildcards are one of the more confusing aspects of GNU Make.

My environment is Ubuntu 20.04.

Implementation Notes

Bottom-line up front: here is the link to my implementation of various GNU Makefiles on github.

In the hellomake example the C math library is introduced (see the variable LIBS). For details see askubuntu – note that this assumes you’ve installed the build-essential packages for ubuntu.

We are always looking for ways to do better at keypuncher so please don’t hesitate to contact us! However, before asking for assistance please be sure you understand this article well. Especially review the material on automatic variables thoroughly. Make likes to keep those variables very compact which can lead to some confusion for both first-time and veteran users.

Tabs in Makefiles

A reminder -- Makefiles are very touchy about tabs! From the Make manual:

Please note: you need to put a tab character at the beginning of every recipe line! This is an obscurity that catches the unwary.

See also stackoverflow and YouTube.

Regarding .o Or .obj Files

C and C++ compilers use intermediary files called object files as a step in the compilation process. According to Wikipedia:

An object file is a computer file containing object code, that is, machine code output of an assembler or compiler.

If you are not interested in the gory details of compilers nor linkers – that’s OK! You don’t have to be intimately familiar with language design to proceed. Just know that GNU Make cares about object files and leave it at that.

Boilerplate Items

VPATH

VPATH is a variable and vpath is a directive. See here: https://www.gnu.org/software/make/manual/make.html#General-Search

Automatic Variables

Automatic variables are discussed here: https://stackoverflow.com/questions/3220277/what-do-the-makefile-symbols-and-mean and here: https://www.gnu.org/software/make/manual/make.html#Automatic-Variables

Implicit Rules

These are pretty central to GNU Make. From https://www.gnu.org/software/make/manual/make.html#Implicit-Rules

Implicit rules tell make how to use customary techniques so that you do not have to specify them in detail when you want to use them. For example, there is an implicit rule for C compilation. File names determine which implicit rules are run. For example, C compilation typically takes a .c file and makes a .o file. So make applies the implicit rule for C compilation when it sees this combination of file name endings.

A chain of implicit rules can apply in sequence; for example, make will remake a .o file from a .y file by way of a .c file.”

And from https://makefiletutorial.com/

The important variables used by implicit rules are:

CC: Program for compiling C programs; default cc
CXX
: Program for compiling C++ programs; default g++
CFLAGS
: Extra flags to give to the C compiler

LDFLAGS: Extra flags to give to compilers when to invoke the linker

Pattern Rules

Again from the Make manual: https://www.gnu.org/software/make/manual/make.html#Pattern-Match:

A target pattern is composed of a ‘%’ between a prefix and a suffix, either or both of which may be empty. The pattern matches a file name only if the file name starts with the prefix and ends with the suffix, without overlap. The text between the prefix and the suffix is called the stem. Thus, when the pattern ‘%.o’ matches the file name test.o, the stem is ‘test’. The pattern rule prerequisites are turned into actual file names by substituting the stem for the character ‘%’. Thus, if in the same example one of the prerequisites is written as ‘%.c’, it expands to ‘test.c’

See also https://www.gnu.org/software/make/manual/make.html#Pattern-Match and https://www.gnu.org/software/make/manual/make.html#Pattern-Rules.

Note that Suffix Rules https://www.gnu.org/software/make/manual/make.html#Suffix-Rules are considered obsolete.

Functions

https://www.gnu.org/software/make/manual/make.html#Functions

GNU Make is a far cry from a full programming language and has limited support for function calls. Make is rather picky about including function calls within other function calls and I find it best to use another language like Python when you want to start using nested functions https://en.wikipedia.org/wiki/Nested_function. In practice I rarely use GNU Make function calls.

Conditional Functions

https://www.gnu.org/software/make/manual/make.html#Conditional-Functions

Conditional Functions may be useful to some: but I don’t find them so. Again, you are better off using a scripting language, IMHO: be very careful about using a shell scripting language like bash to do anything complex. I find that when I have to break a bash script into multiple files I am better off asking myself, “would a scripting language like Python or TypeScript be better?”

See https://careerkarma.com/blog/what-is-a-scripting-language/

General Makefile Tips

ALL_CFLAGS

I like to define an ALL_CFLAGS as suggested in this section: https://www.gnu.org/software/make/manual/make.html#Command-Variables

If there are C compiler options that must be used for proper compilation of certain files, do not include them in CFLAGS. Users expect to be able to specify CFLAGS freely themselves. Instead, arrange to pass the necessary options to the C compiler independently of CFLAGS, by writing them explicitly in the compilation commands or by defining an implicit rule, like this:

CFLAGS = -g
ALL_CFLAGS = -I. $(CFLAGS)
.c.o:
        $(CC) -c $(CPPFLAGS) $(ALL_CFLAGS) $<

Do include the ‘-g’ option in CFLAGS, because that is not required for proper compilation. You can consider it a default that is only recommended. If the package is set up so that it is compiled with GCC by default, then you might as well include ‘-O’ in the default value of CFLAGS as well.

Library Paths

Library Search: https://www.gnu.org/software/make/manual/make.html#Libraries_002fSearch

Cleaning Up

See here: https://www.gnu.org/software/make/manual/make.html#Cleanup

Parallel Execution

TLDR; version: use the make command-line argument -j (means “jobs”) to split the the work up into multiple processes. A good rule of thumb for -j is to set it equal to one or two less than the number of CPU cores. See here: https://www.gnu.org/software/make/manual/make.html#Parallel and here: https://www.gnu.org/software/make/manual/make.html#Job-Slots

Opinions On Handling Intermediate Files

https://stackoverflow.com/questions/47447369/gnu-make-removing-intermediate-files

https://stackoverflow.com/questions/5426934/makefile-unexpectedly-removes-target

https://www.gnu.org/software/make/manual/html_node/Special-Targets.html

Appending More To a Variable

https://www.gnu.org/software/make/manual/make.html#Appending

Empty targets

https://www.gnu.org/software/make/manual/make.html#Empty-Targets

Splitting Long Lines

https://www.gnu.org/software/make/manual/make.html#Splitting-Lines and an exception: https://www.gnu.org/software/make/manual/make.html#Splitting-Recipe-Lines

Including Other Makefiles

https://www.gnu.org/software/make/manual/make.html#Include

Playing Make

To understand how GNU Make itself sees the world: https://www.gnu.org/software/make/manual/make.html#How-Make-Works and: https://www.gnu.org/software/make/manual/make.html#Reading-Makefiles and: https://www.gnu.org/software/make/manual/make.html#Parsing-Makefiles

Previous
Previous

Synergy Introduction

Next
Next

GNU Readline