Go to: Links · Usecase · Response to Make criticism · My C++ Makefile project · Alternatives · CMake · Autotools
GNU Make is a tool which controls the generation of executables and other non-source files of a program from the program’s source files.
A Makefile
is the input file for programs called
make
. It’s a logic program that is both declarative
and procedural.
But why both? To understand this, we should look into the primary usecase for make: software compilation. Software compilation usually means running a series of instructions depending on what files have changed to produce other files. The goal is to make software compilation efficient during development: we don’t want to recompile parts of the program that is unchanged.
We can use declarative programming to specify what is effected by a file change, and use procedural programming to describe what needs to be done to create certain files.
Sometimes we cannot declare this dependency upfront, because this relation may depend on the content of the file. As an example object files may not only depend on
.c
files but on header files too.gcc -MMD
can help us by generating dependency files (.d
) that we can include in the Makefile. This approach can be useful even if don’t have a ready-made tool to generate the dependency files: we can generate this by ourselves too. Example: category pages for my notebook
Build system
make
is suitable for your project as a build
system: if you don’t need a meta build system.
If you:
But avoid make
if:
Task automation
In genaral make
is probably suitable for you
project as a task automation tool. Give your projects a
unified interface, save frequently used commands to execute
repetitive tasks quickly.
Good things:
Bad things:
$$$$
hell.PHONY
is uglymake
?First we have to define a scope to work in. If the problem requires deep understanding of the assembly code, we need to have deep understanding of the compilers. If all your interaction with the OS can be solved with the help of cmake modules and you don’t care about what happens in the background that is also a valid problem scope.
Make is suitable your task if you want to solve it with multiple tools following the UNIX philosophy.
Make commands aren’t documented
When using Make, the Makefile and other source code is all the documentation that is provided.
Definitely not self-documenting, but we can have something like this:
help:
@echo "usage: make [OPTIONS] <target>"
@echo " Options:"
@echo " > VERBOSE Show verbose output for Make rules. Default 1. Disable with 0."
@echo " > SANITIZER Compile with GCC sanitizer. Default none. Options: address, thread, etc."
@echo "Targets:"
@echo " debug: Builds all with debug flags"
@echo " release: Build with optimiser"
@echo " test: Build test executable"
Or autogenerate this from comments
Makes does not come with batteries included
Want to interact with a web API? You need to have cURL installed. Want to parse a text or configuration file? You’ll need awk or sed.
What is the alternative here? How does a tool like
invoke
solves this? pip install requests?
To check if an executable is installed we can use something like this:
CPPCHECK = cppcheck
define check_exec
@command -v $(1) >/dev/null || (echo ERROR: $(1) not found in path >&2; exit 1)
endef
cppcheck:
$(call check_exec, $(CPPCHECK))
$(CPPCHECK) $(CPPCHECKFLAGS) $(SRC) $(SRC_TESTS) $(INCLUDE)
(??? or use $(error text…)
)
Make does not support cross platform builds
Want to make the CLI cross platform? You’ll need to handle differences between Windows, Mac and Linux packages and their shell quirks.
That is correct.
Make is hard to debug
With this:
print-%:
@echo $* = $($*)
We can at least print the variables:
djen at xps in ~/example-cpp-project
make print-CXXFLAGS
CXXFLAGS = -std=c++17 -I/usr/include/png++
djen at xps in ~/example-cpp-project
make SANITIZER=thread print-CXXFLAGS
CXXFLAGS = -std=c++17 -I/usr/include/png++ -fsanitize=thread
Scaling
In Memfault’s codebase, we have around 100 tasks, most of which are namespaced under general top-level modules.
We can organise code in separate files
MyModule.include
and have them included.
If your project is less complex then the Linux kernel, there is chance that you can handle it.
Features:
Notable GnuMake features:
Portable syntaxes:
!=
Is CMake really the “cpp standard”? Check out the Rise of Meson.