The term mk-files refers to a collection of *.mk files.

You need bmake or a recent NetBSD make. If in doubt use bmake.


Many years ago, when building large software projects, I used GNU make (or my own patched version of it), and had developed a set of macros to simplify developing complex build trees.

Since the early 90's my main development machines, run BSD (NetBSD to be precise, and more recently FreeBSD), and the BSD source tree is good example of a large software project. It quickly became clear that /usr/share/mk/*.mk were a great model, but at the time were quite tightly linked to building the BSD tree.

Much as I liked using NetBSD, my customers were more likely to be using SunOS, HP-UX etc, so I started on bmake and a portable collection of mk-files (mk.tar.gz). NetBSD provided much of the original structure.

Since then I've added a lot of features to NetBSD's make and hence to bmake which is kept closely in sync. The mk-files however have diverged quite a bit, though ideas are still picked up from NetBSD and FreeBSD.


The BSD build model is very simple. A directory produces one component, which is generally either a library or a program. Library makefiles include and programs include and they do the right thing.

A simple library makefile might look like:

LIB = sig

SRCS = \
        sigaction.c \
        sigcompat.c \

.include <>

a simple program makefile:

PROG = cat

SRCS = cat.c

.include <>

in such cases even the SRCS line is unnecessary as will default it to ${PROG}.c.

It is the sensible use of defaults and the plethora of macro modifiers provided by bmake that allow simple makefiles such as the above to just work on many different systems.


This section provides a brief description of some of the *.mk files.

The makefiles,,,, and are more or less equivalent to bsd.*.mk found in BSD, and when installed on non-BSD platforms get symlinked as bsd.*.mk as well.

The other makefiles (apart from can be used in conjunction with bsd.*.mk on BSD.

When bmake starts, it looks for and reads it before doing anything else. Thus, this is the place to setup the environment for everyone else.

In this distribution, avoids doing anything platform or site dependent. It is quite short, and includes a number of other files (which may or may not exists)
If it exists, is expected to do things like conditioning the environment. Since it will only be included by the initial instance of bmake, it should .export anything that sub-makes might need.

An example of how to clean the environment. See the file for all the details:

.if ${MAKE_VERSION} >= 20100606 && ${.MAKE.LEVEL} == 0
# we save any env var that starts with these

_env_vars != env | egrep '^(${MAKE_SAVE_ENV_PREFIX:ts|})' | sed 's,=.*,,'; echo
_export_list =
.for v in ${MAKE_SAVE_ENV_VARS:O:u}
.if !empty($v)
_export_list += $v
$v := ${$v}
# now clobber the environment

# list of vars that we handle specially below
_tricky_env_vars = MAKEOBJDIR
# export our selection - sans tricky ones
.export ${_export_list:${_tricky_env_vars:${M_ListToSkip}}}

# this next bit may need tweaking
.if defined(MAKEOBJDIR)
srctop := ${SRCTOP:U${SB_SRC:U${SB}/src}}
# we'll take care of MACHINE below
objtop := ${OBJTOP:U${objroot}${MACHINE}}
.if !empty(objtop)
# we would normally want something like (/bin/sh):
# the $$ below is how we achieve the same result here.
# since everything saved from the environment above
# has run through := we need to compensate for ${MACHINE}
MAKEOBJDIR = $${.CURDIR:S,${srctop},${objtop:S,${MACHINE},\${MACHINE},},}

# export these as-is, and do not track...
.export-env ${_tricky_env_vars}
# now evaluate for ourselves
.for v in ${_tricky_env_vars}
$v := ${$v}

Is used to set macros like HOST_TARGET, HOST_OS and host_os which are used to find the next step. Note: since 20130303 bmake provides .MAKE.OS set to the equivalent of HOST_OS.
Platform specific additions, such as or set things like HOST_LIBEXT = .dylib for Darwin or SHLIB_FULLVERSION = ${SHLIB_MAJOR} for SunOS 5. If there is no OS specific file, sys/ is used.
Any local.*.mk file is not part of the distribution. This provides a hook for sites to do extra setup without having to edit the distributed files.

The above arrangement makes it easy for the mk files to be part of a src tree on an NFS volume and to allow building on multiple platforms.

Inspired by FreeBSD's but more flexible. FreeBSD now have similar functionality in

It allows users to express their intent with respect to options MK_* by setting WITH_* or WITHOUT_*.

Note: WITHOUT_* wins if both are set, and makefiles can set NO_* to say they cannot handle that option, or even MK_* if they really need to.

This file is used to build a number of different libraries from the same SRCS.

An archive lib of .o files, this is the default
A profiled lib of .po files. Still an archive lib, but all the objects are built with profiling in mind - hence the different extension. It is skipped if MK_PROFILE is "no".
An archive of .so objects compiled for relocation. On NetBSD this is the input to lib${LIB}.${LD_so}, it is skipped if MK_PIC or MK_PICLIB are "no".

A shared library. The value of LD_so is very platform specific. For example:

# SunOS 5 and most other ELF systems

# Darwin

This library will only be built if SHLIB_MAJOR has a value, and MK_PIC is not set to "no".

There is a lot of platform specific tweaking in, largely the result of the original distributions trying to avoid interfering with the system's

This is included by both and and tries to include * of which:

does not exist unless you create it. It is a handy way for you to customize without touching the distributed files. For example, on a test machine I needed to build openssl but not install it, so put the following in

.if ${host_os} == "sunos"
LIBCRYPTO = ${OBJTOP}/openssl/lib/crypto/libcrypto${DLIBEXT}
LIBSSL = ${OBJTOP}/openssl/lib/ssl/libssl${DLIBEXT}
INCLUDES_libcrypto = -I${OBJ_libcrypto}

The makefile created an openssl dir in ${OBJ_libcrypto} to gather all the headers. did the rest.
contains logic to find any libs named in HOST_LIBS in HOST_LIBDIRS.

Each file above gets an opportunity to define things like:

LIBSSLFD        ?= ${OBJTOP}/ssl/lib/sslfd/libsslfd${DLIBEXT}
INCLUDES_libsslfd = -I${SRC_libsslfd}/h -I${OBJ_libslfd}

these are used by and will be explained below.

This file looks like line noise, and is best considered read-only. However it provides some very useful functionality, which simplifies the build.

Makefiles can use the LIB* macros defined via or anywhere else in various ways:

# indicate that we need to include headers from LIBCRYPTO
# this would result in ${INCLUDES_libcrypto} being added to CFLAGS.

# indicate that libsslfd must be built already.
# it also has the same effect as SRC_LIBS

# indicate that not only must libsslfd be built,
# but that we need to link with it.
# this is almost exactly equivalent to
# LDADD += -L${LIBSSLFD:H} -lsslfd
# and mostly serves to ensure that DPADD and LDADD are in sync.

Any library (referenced by its full path) in any of the above, is added to DPMAGIC_LIBS with the following results, for each lib foo.

Is set to indicate where the src for libfoo is. By default it is derived from LIBFOO by replacing ${OBJTOP} with ${SRCTOP}.
Not very exciting, is just the dir where libfoo lives.

What to add to CFLAGS to find the public headers. The default varies. If ${SRC_libfoo}/h exists, it is assumed to be the home of all public headers and thus the default is -I${SRC_libfoo}/h

Otherwise we make no assumptions and the default is -I${SRC_libfoo} -I${OBJ_libfoo}

This only applies to libs reference via DPLIBS. The default is -lfoo, LDADD_* provides a hook to instantiate other linker flags at the appropriate point without losing the benfits of DPLIBS.

Compiles the specified SRCS and links them and the nominated libraries into a program. Prog makefiles usually need to list the libraries that need to be linked. We prefer use of DPLIBS but the more traditional DPADD and LDADD work just as well. That is:


is equivalent to:

LDADD += -lcrypto

One of the cool aspects of BSD make, is its support for separating object files from the src tree. This is also the source of much confusion for people unfamiliar with it.

Traditionally one had to do a separate make obj pass through the tree. If MK_AUTO_OBJ is set we include

In fact if MKOBJDIRS is set to "auto", will set MK_AUTO_OBJ=yes and include since it is best done early.

Creates object dirs and leverages the .OBJDIR target introduced some years ago to NetBSD make, to use them.

Note that if is to be used it should be included early - before bmake has established .PATH, thus we include it from rather than

This is the traditional means of walking the tree. A makefile sets SUBDIR to the list of sub-dirs to visit.

If SUBDIR_MUST_EXIST is set, missing directories cause an error, otherwise a warning is issued. If you don't even want the warning, set MISSING_DIR=continue.

Traditionally, prints clues as it visits each subdir:

===> ssl
===> ssl/lib
===> ssl/lib/sslfd

you can suppress that - or enhance it by setting ECHO_DIR:

# suppress subdir noise
# print time stamps
ECHO_DIR=echo @ `date "+%s [%Y-%m-%d %T] "`

I prefer to use which makes irrelevant.

Deals with running (or generating) GNU autoconf configure scripts.

Deals with collecting dependencies. Another useful feature of BSD make is the separation of this sort of information into a .depend file. MKDEP_CMD needs to point to a suitable tool (like

If MK_AUTODEP is "yes" it sets MKDEP_MK to by default.

MKDEP_MK can also be set to which is more efficient but does not support an explicit depend target.

Leverages the -MD feature of recent GCC to collect dependency information as a side effect of compilation. With this GCC puts dependency info into a .d file.

Unfortunately GCC bases the name of the .d file on the name of the input rather than the output file, which causes problems when the same source is compiled different ways. The latest GCC supports -MF to name the .d file and -MT to control the name to put as the dependent.

Recent bmake allows dependencies for the .END target (run at the end if everything was successful), and uses this to post process the .d files into .depend.

A much simpler implementation than it uses -MF ${.TARGET:T}.d to avoid possible conflicts during parallel builds. This precludes the use of suffix rules to drive make depend, so handles that if specifically requested.

If bmake is 20160218 or newer, uses .dinclude to includes the *.d files directly thus avoiding the need to create a .depend file from them.

Normally included by (included by and etc), sets macros for default ownership etc.

It includes ${MAKECONF} if it is defined and exists.

Leverages bmake to compute optimal link order for libraries. This works nicely and makes refactoring a breeze - so long as you have no (or few) cicular dependencies between libraries.

Consider this experimental.

Deals with man pages.

This provides a means of fine grained control over warnings on a per ${MACHINE} or even file basis.

A makefile sets WARNINGS_SET to name a list of warnings and individual W_* macros can be used to tweak them. For example:

W_unused_sparc = -Wno-unused

would add all the warnings in ${HIGH_WARNINGS} to CFLAGS, but on sparc, -Wno-unused would replace -Wunused.

You should never need to edit, it will include and/or to pick up customizations.

Logic to simplify generating HTML (and PDF) documents from ReStructuredText.

Logic to build Python C interface modules using Cython

This makefile leverages two new features in bmake 20220126 and later.

First is the ablity to set target local variables (GNU make has done this for ages).

The second (only intersting if using meta mode) allows filtering commands before comparison with previous run to decide if a target is out-of-date.

In the past, making use of compiler wrappers like ccache, distcc or the newer icecc could get quite ugly. Using it could not be simpler.

This should be included by the top-level makefile. If you do:

make something-jobs

then will run:

make -j${JOB_MAX} someting > ${JOB_LOGDIR}/something.log 2>&1

this ensures you get a build log and JOB_MAX is assumed to be set optimally for the host.


The 20110505 and later versions of mk-files include a number of makefiles contributed by Juniper Networks, Inc. These allow the latest version of bmake to run in meta mode see and DIRDEPS_BUILD below.


When the meta mode was originally done, there was no distinction between META_MODE and DIRDEPS_BUILD, but as these were integrated into FreeBSD it became clear that META_MODE could be useful to many developers independently of DIRDEPS_BUILD.

Thus today we distinguish between the two. We have the following makefiles which are relevant to DIRDEPS_BUILD or META_MODE:


and the following are typically used for customization. See freebsd-meta-mode and netbsd-meta-mode:



You can use the content of mk.tar.gz without installing at all.

The script install-mk takes care of copying *.mk into a destination directory, and unless told not to, create bsd.*.mk links for etc.

If you just want to create the bsd.*.mk links in the directory where you unpacked the tar file, you can use:

./mk/install-mk ./mk
Revision:$Id: mk-files.txt,v 1.25 2023/07/14 23:51:11 sjg Exp $