A non-recursive make build system designed with two goals in mind: minimalism and efficiency. Supports C and C++ projects using gcc and/or clang. The only dependency required is GNU Make of at least v3.82.
See the nrmake-example repository for an example of how to use this build system.
For each "module" (i.e.,executable, static lib, shared lib) you want to
build, you simply need to create a file called Module.mk
in the
directory. The simplest Module.mk
requires a single line.
$(call add-executable-module,$(get-path))
This builds an executable with the same name as the directory and puts
it in the <root>/bin
directory. For documentation on how to provide
individual CPPFLAGS
, LDFLAGS
, CXXFLAGS
, etc., see
nrmake/module_example.mk
.
The best way to use nrmake in your project is to include it as a git submodule. From your project's root directory, do the following:
git submodule add -- https://gitlab.com/btmcg/nrmake.git cd nrmake git checkout v2.2.1 cd .. ln --relative --symbolic nrmake/Makefile
Then add Module.mk
files to the directory of each executable or
library you would like to build. See the example one-line file cited
above or use nrmake-example as an example. A
description of all supported variables are listed in
nrmake/module_example.mk
.
<root>/benchmark/
- Project benchmarking code, ideally for use with google-benchmark. Not required.
<root>/nrmake/
- Build-related makefiles provided by this repository.
<root>/src/
- Project source code.
<root>/test/
- Project unit testing code, ideally for use with catch. Not required.
<root>/third_party/
- Third party dependencies for project, such as catch and google-benchmark libraries. The goal being to keep the entire project self-contained. Not required.
<root>/bin/
- Final location of all of the project's compiled executables.
<root>/include/
- Final location of all of the project's exported headers.
<root>/lib/
- Final location of all of the project's compiled libraries.
DEBUG
- Disables optimizations and removes the
NDEBUG
flag. By default, all compile- and link-time optimizations are turned on and-DNDEBUG
is set. COMPILER
- Supports either
clang
orgcc
(defaultgcc
). ASAN
- Compiles with address sanitization static analysis.
MSAN
- Compiles with memory sanitization static analysis (currently only
supported by
clang
). UBSAN
- Compiles with undefined behavior sanitization static analysis.
PGO_GEN
- Builds binaries with profile-guided optimization instrumentation. The profile data will be written after running a compiled binary.
PGO_USE
- Uses the profile data generated by
PGO_GEN=1
.
make help
- Show available targets and descriptions.
make -j
- Builds the project using
gcc
with full optimizations and places all binaries and libraries inbin/
andlib/
. make dist
- Generate a distributable tarball package containing
bin
,include
, andlib
. It will be placed in the root directory. make COMPILER=clang DEBUG=1 -j
- Builds the project using clang with optimizations turned off (and
NDEBUG
defined). make tidy
- Runs
clang-tidy
on the source tree. make benchmark
- Builds the benchmarking code and executes
bin/benchmark-runner
. make COMPILER=clang DEBUG=1 ASAN=1 test -j
- Builds the testing code with
clang
and address sanitization turned on.
all
- Builds every module in the tree, including the "special" targets
test-runner
andbenchmark-runner
. This is built by default when no arguments are given tomake
. benchmark
- Builds (if necessary) the benchmarking code (assuming
google-benchmark has been installed) and executes
bin/benchmark-runner
. clean
- Removes all build artifacts from the tree; this includes: object
code, libraries, and executables. Top-level
bin/
andlib/
directories are preserved. dist
- Create a tarball for distribution. All files in
bin/
,include/
, andlib/
will be included. distclean
- Calls
clean
and additionally removes dependency files, the version file,bin/
,lib/
, andinclude/
directories. format
- Runs
clang-format
onsrc/
,test/
, andbenchmark/
directories (if they exist). Assumes a.clang-format
file exists in root. help
- Shows version of nrmake as well as defined targets and modules.
list-modules
- Prints to stdout every module the build system is aware of, along with its associated build and link flags.
tags
- Runs ctags on the
src/
directory. test
- Builds (if necessary) the unit testing code (assuming catch is
installed) and executes
bin/test-runner
. tidy
- Runs
clang-tidy
onsrc/
. (Assumes a.clang-tidy
file exists in root.)
nrmake was designed with catch and google-benchmark in mind. Including these two projects is fairly simple.
catch
is best included as a submodule with your project's test code
in <root>/test
. To make the header available to your code, an edit
of nrmake/third_party.mk
is required. Boilerplate provided.
git submodule add -- https://github.com/catchorg/Catch2.git third_party/catch2/2.13.2 cd third_party/catch2/2.13.2 git checkout v2.13.2 cd - vim nrmake/third_party.mk
google-benchmark needs to be compiled for both gcc
and clang
.
The following steps will install the header and libraries in separate
directories under <root>/third_party
. To make the library available
to your code, an edit of nrmake/third_party.mk
is required.
Boilerplate is provided.
# from your repository root git clone --branch=v1.5.2 --depth=1 https://github.com/google/benchmark.git gb cd gb cmake \ -DBENCHMARK_ENABLE_LTO:BOOL=ON \ -DBENCHMARK_ENABLE_TESTING:BOOL=OFF \ -DCMAKE_BUILD_TYPE:STRING=RELEASE \ -DCMAKE_CXX_COMPILER:STRING=g++ \ -DCMAKE_INSTALL_PREFIX:PATH=../../third_party/google-benchmark/gcc-10.2.0/1.5.2 \ -DGCC_AR:STRING=gcc-ar \ -DGCC_RANLIB:STRING=gcc-ranlib \ -S . -B _build cmake --build _build --config Release --target install --parallel # now build with clang rm -rf _build cmake \ -DBENCHMARK_ENABLE_LTO:BOOL=ON \ -DBENCHMARK_ENABLE_TESTING:BOOL=OFF \ -DBENCHMARK_USE_LIBCXX:BOOL=ON \ -DCMAKE_BUILD_TYPE:STRING=RELEASE \ -DCMAKE_CXX_COMPILER:STRING=clang++ \ -DCMAKE_INSTALL_PREFIX:PATH=../../third_party/google-benchmark/clang-10.0.1/1.5.2 \ -DLLVMAR_EXECUTABLE:STRING=llvm-ar \ -DLLVMNM_EXECUTABLE:STRING=llvm-nm \ -DLLVMRANLIB_EXECUTABLE:STRING=llvm-ranlib \ -S . -B _build cmake --build _build --config Release --target install --parallel cd .. rm -rf gb vim nrmake/third_party.mk
After years of using less-than-efficient build systems (GNU Make-based or otherwise) in various jobs and personal projects, I wanted to create a simple environment that I could replicate over and over again that would do exactly what I needed it to do. I wanted it to use make (due to its ubiquity), require zero dependencies (including additional build binaries or libraries), correctly handle internal dependency graphs, and provide a mechanism for running unit tests and benchmarks. Every time I started a new project, I didn't want to waste time thinking about how to build and structure the code and tests, I just wanted to get some prototype on the disk. What started as a Makefile that I would copy to each new project turned into more of a "system" (or collection of .mk files) that provided various features that I used on a regular basis. I finally decided to make this repo public, write this README, and provide this code for anyone else like me that has suffered with clumsy C++ build systems in the past.