Useful Shortcuts for Makefiles ############################## When starting out new projects, Makefiles can be faster and easier to configure than cmake. Makefiles take an opposing world-view to CMake. Where CMake tries to auto-detect install and configure options, make works best for a static build tree with fixed dependencies configured explicitly by the user. Not second-guessing, autodetecting or otherwise overriding the user's environment is make's speciality. There are many `good guides online `_. Syntax Example -------------- Here's an example of good practices from the `quark project `_: .. code-block:: make # See LICENSE file for copyright and license details # quark - simple web server .POSIX: include config.mk COMPONENTS = data http sock util all: quark data.o: data.c data.h http.h util.h config.mk http.o: http.c config.h http.h util.h config.mk main.o: main.c arg.h data.h http.h sock.h util.h config.mk sock.o: sock.c sock.h util.h config.mk util.o: util.c util.h config.mk quark: config.h $(COMPONENTS:=.o) $(COMPONENTS:=.h) main.o config.mk $(CC) -o $@ $(CPPFLAGS) $(CFLAGS) $(COMPONENTS:=.o) main.o $(LDFLAGS) config.h: cp config.def.h $@ clean: rm -f quark main.o $(COMPONENTS:=.o) dist: rm -rf "quark-$(VERSION)" mkdir -p "quark-$(VERSION)" cp -R LICENSE Makefile arg.h config.def.h config.mk quark.1 \ $(COMPONENTS:=.c) $(COMPONENTS:=.h) main.c "quark-$(VERSION)" tar -cf - "quark-$(VERSION)" | gzip -c > "quark-$(VERSION).tar.gz" rm -rf "quark-$(VERSION)" install: all mkdir -p "$(DESTDIR)$(PREFIX)/bin" cp -f quark "$(DESTDIR)$(PREFIX)/bin" chmod 755 "$(DESTDIR)$(PREFIX)/bin/quark" mkdir -p "$(DESTDIR)$(MANPREFIX)/man1" cp quark.1 "$(DESTDIR)$(MANPREFIX)/man1/quark.1" chmod 644 "$(DESTDIR)$(MANPREFIX)/man1/quark.1" uninstall: rm -f "$(DESTDIR)$(PREFIX)/bin/quark" rm -f "$(DESTDIR)$(MANPREFIX)/man1/quark.1" Notice that explicit dependencies ensure the project is properly rebuilt when relevant files are edited. Also, special make substituions are used: * ``$@`` = the name of the rule's target (e.g. the ``config.h`` rule) * ``$^`` = all dependencies of the rule * ``$(VAR:a=b)`` = substitute trailing ``a`` with ``b`` in each word. General Rules ------------- The example above doesn't specifically say how to compile ``.c`` files to ``.o`` files. Instead, it relies on the default rule. We reproduce this below, along with some other useful ones for modern software: .. code-block:: make .SUFFIXES: c cx f cu .c.o: $(CC) $(CFLAGS) -c $< .f.o: $(FC) $(FFLAGS) -c $< .cc.o: $(CXX) $(CXXFLAGS) -c $< .cu.o: $(NVCC) $(NVCCFLAGS) -c $< External Packages ----------------- The quark example above uses a small, separate ``config.mk`` file to let the user point the build at all system dependencies: .. code-block:: make # quark version VERSION = 0 # Customize below to fit your system # paths PREFIX = /usr/local MANPREFIX = $(PREFIX)/share/man # flags CPPFLAGS = -DVERSION=\"$(VERSION)\" -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=700 -D_BSD_SOURCE CFLAGS = -std=c99 -pedantic -Wall -Wextra -Os LDFLAGS = -s # compiler and linker CC = cc GNU Make defines the `shell function `_. This can be helpful for adding limited auto-detection to ``config.mk``. For example, ``$(shell pkg-config --libs zlib)``. Interesting Tricks ------------------ GCC can create Makefiles listing user header files with ``-MMD``. The way to use this is `like this `_: .. code-block:: make CXX = g++ CXXFLAGS = -g -Wall -MMD # The MMD flag causes x.d, etc. to be output OBJECTS = x.o y.o z.o # object files forming executable DEPENDS = ${OBJECTS:.o=.d} # substitutes ".o" with ".d" EXEC = a.out # executable name ${EXEC} : ${OBJECTS} # link step ${CXX} ${OBJECTS} -o ${EXEC} -include ${DEPENDS} # include x.d, y.d, and z.d The ``-`` prefix on include prevents an error from being thrown if the include command fails.