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:
https://spin.atomicobject.com/2016/08/26/makefile-c-projects/
https://www.cs.colby.edu/maxwell/courses/tutorials/maketutor/
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. Somake
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; defaultcc
: Program for compiling C++ programs; default
CXXg++
: Extra flags to give to the C compiler
CFLAGS…
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 specifyCFLAGS
freely themselves. Instead, arrange to pass the necessary options to the C compiler independently ofCFLAGS
, 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 ofCFLAGS
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
Credits
Managing Projects With GNU Make, Author: Robert Mecklenburg.
https://riptutorial.com/Download/makefile.pdf
http://makepp.sourceforge.net/2.0/makepp_cookbook.html
https://www.gnu.org/software/make/manual/make.html#Catalogue-of-Rules
https://www.gnu.org/software/make/manual/make.html#Implicit-Variables
https://stackoverflow.com/questions/3220277/what-do-the-makefile-symbols-and-mean