# Makefile Guidelines Writing portable Makefiles is important for simplifying development and production work flows. Even though much of the software world has settled on Linux, there is no singular Linux environment. And even if a specific release of a specific distribution is agreed upon, needless idiosyncrasies and dependencies can still cause headaches. This note draws upon the [GNU style guide for Makefile conventions](https://www.gnu.org/prep/standards/standards.html#Makefile-Conventions), which offers a good blueprint for writing portable Makefiles. While this note DOES NOT recommend GNU autotools as a default framework for building C and C++ software, the widespread prevalence of autotools has ensured that the GNU Makefile style is effectively standard practice, particularly for targets, installation variables, and basic compilation flag variables. # Targets ## all Compile all components. The `all` target MUST NOT install anything outside the build directory. The `all` target should be the default target; that is, the target built when no target is specified. The default target is the first target defined in the Makefile, even if the definition is empty. ```Makefile # start of Makefile all: ``` Alternatively, with GNU Make `.DEFAULT_GOAL:` can be specified anywhere in the Makefile. ```Makefile # anywhere in Makefile .DEFAULT_GOAL: all ``` ## install Compile all components (if stale or not yet compiled) and copy the executables, libraries, etc to their destination files. Intermediate destination directories may not yet exist. The Makefile should use `mkdir -p $(@D)` or the `-D` switch to `install(1)`. Do not strip executables when installing. Packaging frameworks normally have their own facilities for stripping binaries. A Makefile can optionally provide an `install-strip` target for users that want the Makefile to strip binaries. ## uninstall Delete all installed files, leaving any directories intact. Files in the build directory are untouched. ## clean Delete all files that are normally created by the `all` target. Does not delete any generated configuration files. ## distclean Delete all files created by building or configuring. ## dist Create a distribution tar file for this program, such as the tar files published by open source projects. This often includes generation of configure scripts and similar files. The tar file name should be the program name, optionally suffixed with release version information. The tar file should unpack into a subdirectory with the same name as the basename of the tar file. A tar file named `foo.tgz` should extract into `foo/`; a tar file named `foo-1.0.tgz` should extract into `foo-1.0/`. ## check Execute regression, unit, and feature tests. If any tests fail the target rule should also fail so that the make invocation fails. Tests SHOULD NOT require installation of any program components other than external dependencies. The ability to run tests without installation is especially important during development, and particularly when writing tests. Some packaging frameworks support running tests, and installation is not normally expected or allowed given that components were likely installed into a DESTDIR tree. Likewise, while continuous integration frameworks may allow installation before running tests, it's cleaner not to require it. To support in-tree execution of tests, LD_LIBRARY_PATH, PATH, and similar environment variables can be specified and adjusted accordingly from the check target rules. Libraries and executables should be implemented such that other runtime paths can be specified using command-line arguments or environment variables. Such capabilities are usually a good idea, anyhow. Preferably tests should not depend on external testing frameworks. An exception to the rule would be a framework like Perl 5 Test::More, which is strongly normative and likely already available in any build environment for Perl modules. # Compilation Flags Some of the following are an extrapolation of the [implicit variables](https://www.gnu.org/software/make/manual/html_node/Implicit-Variables.html) defined by GNU Make, [some patterns](https://www.gnu.org/prep/standards/html_node/Command-Variables.html#Command-Variables) specified in the GNU style guide, and [usages](https://github.com/git/git/blob/master/Makefile) in high-profile open source projects like Git. When a project builds multiple components such as shared libraries, loadable modules, and an executable, each with complex and possibly conflicting build requirements, the following variables may be insufficient or inappropriate. Use discretion, but try to maintain enough flexibility that the user, when necessary, can control all the flags for each build rule. Normally that will only require a simple extension and adaptation of the FOO prefix pattern. In such cases a component variable like `FOO_CFLAGS` might supercede, supplement, or reference `ALL_CFLAGS`, rather than be referenced within `ALL_CFLAGS`. The user SHOULD NOT be required to invoke component build targets separately in order for user-specified flags to be applied appropriately. Where the prefix pattern is unwieldly, the alternative is provide a separate complete Makefile for each component that needs to be built separately to avoid variable conflicts. ## CFLAGS Optional flags to the C compiler that are NOT REQUIRED for proper compilation. This often includes debug flags like `-g`, diagnostic flags like `-Wall`, and optimization flags like `-O2`. The Makefile can set a default set of recommended flags--especially common when using autoconf--but the user should be free to override this variable. If the user specifies an empty string compilation should still work. Dependency, feature, or component compiler flags should be independently specifiable as `FOO_CFLAGS`. Example default: `CFLAGS = -g -O2 -Wall` ## MYCFLAGS While `CFLAGS` can default to unrequired flags, they may nonetheless be recommended flags. To allow users to supplement recommended flags it's common to allow the user to specify `MYCFLAGS`. This can be especially useful during development. The Makefile should never specify a default value. ## CXXFLAGS C++ compiler analog to `CFLAGS`. ## CPPFLAGS Optional flags to the C preprocessor. These flags may be necessary to proper compilation, such as providing the location of header dependencies with `-I/some/project/include`. Normally this variable should be entirely user-specified. `CPPFLAGS` is a catch-all variable that users expect to be able to use freely. Dependency, feature, or component preprocessor flags should be independently specifiable as `FOO_CPPFLAGS`. For example, if libevent is a dependency, allow the user to specify the required preprocessor include paths using `LIBEVENT_CPPFLAGS`. Some newer utilities, like `pkg-config`, conflate `CFLAGS` and `CPPFLAGS`. Do not make this mistake. The function of these variables, as well as the components of the compiler toolchain they target, are distinct. Usually the conflation is benign, until it isn't and it then becomes a serious headache. ## LDFLAGS Extra flags to give to compilers when they are supposed to invoke the linker. This is a catch-all. Dependency, feature, or component linker flags should be independently specifiable as `FOO_LDFLAGS`. The Makefile SHOULD NOT normally specify a default value. This SHOULD NOT include the `-l` flags for linking in libraries. See `LIBS` and `FOO_LIBS`, below. Separating LIBS from LDFLAGS makes it easier for the user to supplement linker flags while relying on the default flags for dependency library names. ## LIBS Compiler `-l` flags to link in library dependencies. This is a catch-all. Dependency, feature, or component `-l` flags should be independently specifiable as `FOO_LIBS`. ## SOFLAGS Required C compiler flags for generating a dynamic shared object library. On most platforms, including Linux, this will simply be `-shared`. macOS is the outlier often requiring something like `-dynamiclib -undefined dynamic_lookup`. Some projects also use this variable for specifying flags like `-fPIC`. But it's not as common for a Makefile to use `SOFLAGS` when building intermediate object files, and providing -fPIC at the linking stage is too late. These days it's usually desirable to compile everything as position independent code (PIC), and so -fPIC is better placed in `ALL_CFLAGS`. If a distinction between PIC (`-fPIC`) and static code (`-static`) is needed, then provide `FOO_CFLAGS` or `FOO_SOFLAGS` for the relevant components. Example default: `SOFLAGS = -shared` ## FOO_CFLAGS A variable that allows the user to specify C compiler flags separately for each dependency, feature, or component, such as `LIBEVENT_CFLAGS`, `ICU_CFLAGS`, or `BIN_CFLAGS`. For dependencies `FOO_CPPFLAGS` and `FOO_LDFLAGS` are usually more relevant. ## FOO_CPPFLAGS C preprocessor flags for a dependency, feature, or component. Normally the Makefile SHOULD NOT specify a default value for dependencies. Users will typically use this variable to specify a non-standard include path. For features a default value is expected. Makefiles SHOULD NOT automatically include `-I$(includedir)`. See reasoning and exceptions at [includedir](#includedir). Example dependency usage: `make ICU_CPPFLAGS="-I/usr/local/icu/include"` Example feature default: `ATTACHMENT_CPPFLAGS = -DWITH_ATTACHMENT=1` ## FOO_LDFLAGS Extra compiler flags necessary for linking against a particular dependency or for enabling some feature. For dependencies the Makefile SHOULD NOT normally specify a default value. This SHOULD NOT include the `-l` flags for linking in the library. See `LDFLAGS` for why; see `FOO_LIBS` for how. Makefiles SHOULD NOT automatically include `-L$(libdir)`. See reasoning and exceptions at [libdir](#libdir). Example usage: `make ICU_LDFLAGS="-L/usr/local/icu/lib -Wl,-rpath,/usr/local/icu/lib"` ## FOO_LIBS Compiler `-l` flags to link in a library dependency. Example default: `ICU_LIBS = -licui18n -licuuc -licudata` ## ALL_CFLAGS C compiler flags required for proper compilation, plus references to all other compiler flags relevant to all build targets. This is not normally specified by the user, but in a pinch this variable allows them specify all the C compiler flags of their choosing, completely overriding the Makefile defaults. Example default: `ALL_CFLAGS = -fPIC $(FOO_CFLAGS) $(CFLAGS) $(MYCFLAGS)` ## ALL_CXXFLAGS C++ compiler analog to `ALL_CFLAGS`. ## ALL_CPPFLAGS Required C preprocessor flags, plus references to all other preprocess flags relevant to all build targets. This often includes defines like `-D_GNU_SOURCE` or include paths to local headers like `-Iext`. Note that there's no `MYCPPFLAGS` as normally the Makefile SHOULD NOT specify a default `CPPFLAGS` value. Makefiles SHOULD NOT automatically include `-I$(includedir)`. See reasoning and exceptions at [includedir](#includedir). Example default: `ALL_CPPFLAGS = -D_GNU_SOURCE -Iext $(LIBEVENT_CPPFLAGS) $(CPPFLAGS)` ## ALL_LDFLAGS Required compiler link flags, plus references to all other compiler link flags relevant to all build targets. This SHOULD NOT include `-l` flags for linking in libraries. See `LDFLAGS` for why; see `FOO_LIBS` for how. Makefiles SHOULD NOT automatically include `-L$(libdir)`. See reasoning and exceptions at [libdir](#libdir). Example default: `ALL_LDFLAGS = $(ICU_LDFLAGS) $(LIBEVENT_LDFLAGS) $(LDFLAGS)` ## ALL_LIBS Aggregation of all the `-l` linker flags. Example default: `ALL_LIBS = $(ICU_LIBS) $(LIBEVENT_LIBS) $(LIBS) -lpthread -lm` # Installation Directories The following omit the use of `$(exec_prefix)`. For most uses `$(exec_prefix)` would be confusing boilerplate when writing simple Makefiles, but would be appropriate for projects maintained to open source portability standards. Some other intermediate directory variables, like $(datarootdir), are omitted for similar reasons. It might be tempting to simply hardcode variables like `$(bindir)` as `$(prefix)/bin`. However, * During development the ability to specify specific installation directories can be very useful; and * There are other scenarios, like multiarch, where a particular subdirectory name might need to be different. ## DESTDIR `DESTDIR` is a variable that allows a user to stage installation underneath a temporary subtree. This is especially useful (and often necessary) for packaging. The Makefile should prepend `DESTDIR` to each installed target file _without_ an intervening directory separator. ```Makefile $(DESTDIR)$(libdir)/libfoo.so: libfoo.so $(INSTALL_DATA) $^ $@ ``` Because some programs may internalize variables like `libdir` during compilation for use at runtime, it's important that temporary staging directories be specified using `DESTDIR`, not by adjusting `prefix`. ## prefix A prefix used in constructing the default values of the installation directory variables listed below. Default: `/usr/local` ## bindir The directory for installing executable programs that users can run. Normally interactive shell tools. Default: `$(prefix)/bin` ## sbindir The directory for installing executable programs that can be run from the shell. GNU says `sbindir` is for programs generally useful to system administrators. At a minimum this encompasses daemons and programs for starting and stopping daemons. For other programs, determining whether use is restricted to system administrators, or event defining what is meant by system administrator, can be a matter of opinion. Except for daemon executables and similar programs, employ discretion. Few people will complain if other programs are installed in `$(bindir)`. Default: `$(prefix)/sbin` ## libexecdir These are auxiliary programs intended to be invoked directly by other programs (e.g. daemons), rather than invoked from a shell. It is analogous to `libdir`, but where the interface uses stdio rather than the C ABI. If invocation from an interactive shell is expected, `libexecdir` is probably wrong. Default: `$(prefix)/libexec` ## datadir The directory for installing idiosyncratic read-only architecture-independent data files required by the program. This includes licensing documentation, but more importantly architecture-independent libraries for languages like Perl or Lua. For configuration files see sysconfdir. Often datadir is suffixed with the program name inside the Makefile; e.g. `$(datadir)/program/database.txt`. Variables like mandir are also usually defined in terms of datadir (e.g. `$(datadir)/man`), so it's uncommon for a user to specify a program-specific suffix themselves. GNU style specifies datarootdir as a supplement to datadir, but like `exec_prefix` this distinction is omitted. For supporting a user-specified, program-specific datadir then it makes sense to distinguish datarootdir and datadir. Default: `$(prefix)/share`. ## sysconfdir The directory for installing read-only data files that pertain to a single machine, such as files for configuring a host. Like `datadir`, `sysconfdir` is often suffixed with a program name by the Makefile if more than a single configuration file is supported, rather than relying on the user to specify a path specialized to the program. However, it's also common for users to specify non-default sysconfdir paths, especially for aggregating into a subtree all the configuration files of a large multi-package framework. If a conflict of concerns causes a configuration file to be located at `/etc/foo/foo/bar.conf`, for instance, it's the user's fault for needlessly specializing `sysconfdir`. If the Makefile installs two configuration files at `/etc/foo.conf` and `/etc/bar.conf`, it's the Makefile's fault for not manually suffixing `sysconfdir` with the program name. Default: `$(prefix)/etc` ## localstatedir The directory for installing data files which the programs modify while they run, that pertain to one specific machine, and which are expected to persist across a reboot. The Makefile is normally expected to suffix `locatestatedir` with the program name. Default: `$(prefix)/var`. ## runstatedir The directory for installing data files which the programs modify while they run, that pertain to one specific machine, and which SHOULD NOT persist across a reboot. For example, `/var/run/foo.pid`. The Makefile is normally expected to suffix `runstatedir` with the program name if more than a single runtime file is created. Default: `$(prefix)/var/run` ## includedir The directory for installing header files to be included by user programs. The Makefile is normally expected to suffix `includedir` with the program name when more than a single header is installed. Makefiles SHOULD NOT automatically include `-I$(includedir)` in `CPPFLAGS` or `ALL_CPPFLAGS` as it makes it difficult for the user to control the visibility of dependencies. During development multiple versions of the same dependency might be available and employed. If `-I$(includedir)` is added by default, make sure it comes last in the ordering of `ALL_CPPFLAGS` such that any user-specified `CPPFLAGS` takes priority. Default: `$(prefix)/include` ## libdir The directory for object files and libraries of object code. Do not install executables here; they probably ought to go in `$(libexecdir)` instead. Makefiles SHOULD NOT automatically include `-L$(libdir)` in `LDFLAGS` or `ALL_LDFLAGS`. See reasoning and exceptions at [includedir](#includedir). Default: `$(prefix)/lib` # Autotools ## Autoconf Autoconf is usually unnecessary for most projects that don't target portability to a wide number of environments (operating systems, historical releases of a particular system, etc). Other than understanding the syntax and maintaining the boilerplate, generating the configure script and dependencies can be a headache, especially given that best practice dictates they not be committed to a project's source repository. ### ./bootstrap Autoconf is (just barely) useable standalone, independent of libtool and automake. However, automake is needed for generating some auxiliary files, like install-sh, expected by autoconf. For the most recent versions of autoconf and automake the following `./bootstrap` file should suffice. ```sh #!/bin/sh # NB: change to whatever AC_CONFIG_AUX_DIR specifies. CONFIG_AUX_DIR=build-aux INSTALL_SH="${CONFIG_AUX_DIR}/install-sh" trap "rm -fr autom4te.cache" EXIT aclocal autoheader if [ ! -f "${INSTALL_SH}" ]; then mkdir -p "${INSTALL_SH%/*}" automake --foreign -a -c || true [ -f "${INSTALL_SH}" ] || exit 1 fi autoconf printf "OK\n" >&2 ``` ### ./configure & config.h When autoconf is used, it's important to continue to allow the user to supplement and override all flags, especially preprocessor feature macro definitions. The default preprocessor code generated by autoconf for `config.h` does not permit overriding feature macro definitions when invoking make. Place this preamble at the top of `configure.ac` to make autoconf feature macros overrideable. ``` # Redefine AH_TEMPLATE so that feature macros can be overridden from CPPFLAGS m4_define([AH_TEMPLATE], [AH_VERBATIM([$1], m4_text_wrap([$2 */], [ ], [/* ])[ @%:@ifndef ]_m4_expand([$1])[ @%:@undef ]_m4_expand([$1])[ @%:@endif])]) ``` In application code, test feature macros using boolean evaluation expressions, not with the `#ifdef` directive or `defined` operator. ```c /* CORRECT */ #if HAVE_EVENTFD ... #endif /* WRONG */ #ifdef HAVE_EVENTFD ... #endif /* WRONG */ #if defined HAVE_EVENTFD ... #endif ``` Note that in arithmetic and boolean preprocessor expressions an undefined macro will evalute as 0. Long ago some non-standard proprietary compilers failed to implement this behavior, which may explain the GNU recommendation to test for definedness, but that's a distant memory. ## Automake Just don't. Building most components is already trivial in modern environments, even outside GNU/Linux environments. When specialized build rules are required, automake will compound the complexity. Automake also effectively locks you into libtool, whereas autoconf is still useable standalone. The one thing automake has going for it is that automake-built projects will usually be buildabe out-of-tree (e.g. from a separate object directory), and will support all the right flags in the proper manner expected of packagers who cross-compile. The former is easily accomplished with the proper use of `$(top_srcdir)`. For cross-compiling, using the make variable patterns specified in this note should suffice. ## libtool These days building shared libraries is trivial on all extant POSIX-like platforms, even when not relying on GCC or clang. To generate a shared library or module use `-shared` everywhere but macOS. For macOS the appropriate flags for shared libraries are usually `-dynamiclib -undefined dynamic_lookup`. libtool static archives (.la) are anachronistic, unnecessary, and [cause problems with cross-compiling](http://www.metastatic.org/text/libtool.html). # Example C library The following is a simplification of the build for libsmtp, excluding the Lua modules build rules. ```Makefile all: CFLAGS = -g -O2 -Wall OPENSSL_CFLAGS = OPENSSL_CPPFLAGS = OPENSSL_LDFLAGS = OPENSSL_LIBS = -lssl -lcrypto ALL_CFLAGS = -fPIC -fvisibility=hidden -std=gnu99 $(OPENSSL_CFLAGS) $(CFLAGS) $(MYCFLAGS) ALL_CPPFLAGS = -D_REENTRANT -D_GNU_SOURCE $(OPENSSL_CPPFLAGS) $(CPPFLAGS) ALL_LDFLAGS = $(OPENSSL_LDFLAGS) $(LDFLAGS) ALL_LIBS = $(LIBS) $(OPENSSL_LIBS) -lpthread SOFLAGS = -shared INSTALL = install INSTALL_DATA = $(INSTALL) -m644 INSTALL_PROGRAM = $(INSTALL) -m755 MKDIR = mkdir MKDIR_P = $(MKDIR) -p RAGEL = ragel prefix = /usr/local libdir = $(prefix)/lib includedir = $(prefix)/include OBJS = smtp.o http.o obstack.o socket.o dns.o mime.o uri.o SRCS = $(OBJS:%.o=%.c) %.c: %.rl $(RAGEL) -C -o $@ $< %.o: %.c $(CC) -c -o $@ $^ $(ALL_CPPFLAGS) $(ALL_CFLAGS) libsmtp.so: $(OBJS) $(CC) -o $@ $^ $(SOFLAGS) $(ALL_LDFLAGS) $(ALL_LIBS) $(DESTDIR)$(libdir)/libsmtp.so: libsmtp.so $(MKDIR_P) $(@D) $(INSTALL_PROGRAM) $< $@ $(DESTDIR)$(includedir)/libsmtp/smtp.h: smtp.h $(MKDIR_P) $(@D) $(INSTALL_DATA) $< $@ $(DESTDIR)$(includedir)/libsmtp/http.h: http.h $(MKDIR_P) $(@D) $(INSTALL_DATA) $< $@ $(DESTDIR)$(includedir)/libsmtp/uri.h: uri.h $(MKDIR_P) $(@D) $(INSTALL_DATA) $< $@ PACKAGE_TARNAME = libsmtp PACKAGE_VERSION = $(or $(VERSION), $(shell cat VERSION), $(error No VERSION)) PACKAGE_TARBALL = $(PACKAGE_TARNAME)-$(PACKAGE_VERSION).tar.gz PACKAGE_TREEISH = HEAD libsmtp.spec: libsmtp.spec.in sed -e 's/@VERSION@/$(PACKAGE_VERSION)/g' < $< > $@ $(PACKAGE_TARBALL): libsmtp.spec git archive --format=tar --prefix=$(@F:%.tar.gz=%)/ $(PACKAGE_TREEISH) . >| $(@:%.gz=%) tar -rhf $(@:%.gz=%) --transform='s#^#$(@F:%.tar.gz=%)/#' libsmtp.spec gzip -c < $(@:%.gz=%) > $@ all: libsmtp.so install: $(DESTDIR)$(libdir)/libsmtp.so install: $(DESTDIR)$(includedir)/libsmtp/smtp.h install: $(DESTDIR)$(includedir)/libsmtp/http.h install: $(DESTDIR)$(includedir)/libsmtp/uri.h uninstall: $(RM) -f $(DESTDIR)$(includedir)/libsmtp/{smtp.h,http.h,uri.h} $(RM) -f $(DESTDIR)$(libdir)/libsmtp.so clean: $(RM) -f *.o libsmtp.so dist: $(PACKAGE_TARBALL) distclean: $(RM) -f libsmtp*.tar libsmtp*.tar.gz $(RM) -f libsmtp.spec check: $(MAKE) -C regress check RPMBUILD = rpmbuild RPMBUILD_FLAGS = --clean rpm: $(PACKAGE_TARBALL) $(RPMBUILD) $(RPMBUILD_FLAGS) $(RPMBUILD_MYFLAGS) -ta $(PACKAGE_TARBALL) ```