# 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)
```