# Generic C and C++ Makefile project template
# Contains targets for `release', `debug', and `clean'.
PROJECT = memfd_secret-shim
AUTHOR = Avril ( Flanchan) <flanchan@cumallover.me>
DESCRIPTION =
VERSION = 0.0.0r1
SRC = src
# Exclude `src/test/*` from search.
SRC_C = $( shell find $( SRC) / -type f -name \* .c -a -not -path src/test/\* )
SRC_CXX = $( shell find -O2 $( SRC) / -type f -name \* .cpp -or -name \* .cxx -a -not -path src/test/\* )
INCLUDE = include
# If PCH should be auto-included for all TUs, set to 1.
INCLUDE_PCH_GLOBAL ?= 0
# Files to be auto-included by all TUs (after PCH global, if `INCLUDE_PCH_GLOBAL == 1`.)
INCLUDE_GLOBAL =
# Link to these libraries dynamicalls
SHARED_LIBS =
# Link to these libraries statically
STATIC_LIBS =
# Prefix for un/install targets
i f e q ( $( PREFIX ) , )
PREFIX := /usr/local
e n d i f
INCLUDE_PREFIX := $( PROJECT) /
# Default archivers
AR ?= ar
RANLIB ?= ranlib
# Use gcc-{ar,ranlib} when using gcc
i f e q ( $( CXX ) , g + + )
AR:= gcc-ar
RANLIB:= gcc-ranlib
e n d i f
# Pre-compile these headers
PCH_HEADERS +=
# PCH_HEADERS depend on these header files
PCH_INCLUDES +=
# Compile-time default program features (see `Features application` below.)
FEATURES ?=
# Build constants
CONSTANTS += _GNU_SOURCE
# Testing
# Can be {release,debug}.{a,so}
TARGET ?= debug.a
TEST_LDFLAGS += -lfmt -lstdc++ -Wl,-z,now -Wl,-z -Wl,relro
TEST_CFLAGS += -Og -g -fwhole-program
o v e r r i d e __COMMA = ,
override __VERSION_SPLIT : = $( subst ., ,$ ( VERSION ) )
override __VERSION_REVISION : =$( word 3,$ ( __VERSION_SPLIT ) ) 0
VERSION_MAJOR := $( word 1,$( __VERSION_SPLIT) )
VERSION_MINOR := $( word 2,$( __VERSION_SPLIT) )
VERSION_BUGFIX := $( word 3,$( __VERSION_SPLIT) )
VERSION_REVISION := $( word 2,$( subst r, ,$( __VERSION_REVISION) ) )
override __VERSION_SPLIT : = MAJOR :$( word 1,$ ( __VERSION_SPLIT ) ) MINOR :$( word 2,$ ( __VERSION_SPLIT ) ) BUGFIX :$( word 1,$ ( subst r , ,$ ( __VERSION_REVISION ) ) ) REVISION :$( word 2,$ ( subst r , ,$ ( __VERSION_REVISION ) ) ) REVISION_STRING :$( word 3,$ ( __VERSION_SPLIT ) )
COMMON_FLAGS += -W -Wall
COMMON_FLAGS += $( addprefix -D,$( CONSTANTS) )
COMMON_FLAGS += -pipe -Wstrict-aliasing -fno-strict-aliasing $( addprefix -I,$( INCLUDE) )
COMMON_FLAGS += $( addprefix -D_VERSION_,$( subst :,= ,$( __VERSION_SPLIT) ) ) '-D_VERSION="$(VERSION)"'
# PCH targets for `%_p.hh` -> `%_p.hh.gch`, add `-include $(PCH_HEADERS)` to general `COMMON_FLAGS`, and add $(PCH_OUT) as a requirement for all `%.c/pp` targets as well. (where `PCH_OUT= $(addsuffix .gch,$(PCH_HEADERS))`.)
# Target arch & CPU. Set to blank for generic
ARCH ?= native
CPU ?= native
# Enable OpenMP and loop parallelisation? (dyn-links to openmp)
PARALLEL ?= no
# Enable CPU-specific features
CPU_FLAGS ?=
BINFLAGS +=
DEBUG_BINFLAGS +=
RELEASE_BINFLAGS += -fuse-linker-plugin
OPT_FLAGS ?= -fgraphite \
-floop-interchange -ftree-loop-distribution -floop-strip-mine -floop-block \
-fno-stack-check
# Static and shared common flags
SHARED_FLAGS += -fPIC
SHARED_RELEASE_FLAGS +=
SHARED_DEBUG_FLAGS +=
STATIC_FLAGS +=
STATIC_RELEASE_FLAGS += -ffat-lto-objects
STATIC_DEBUG_FLAGS +=
# Features application
## Tell program which features are enabled via `FEATURE_<feature name in UPPER_SNAKE_CASE>`.
override __FEATURES=$(shell echo "$(FEATURES)" | tr '[ : lower :]' '[:upper :]' | tr '-' '_ ')
COMMON_FLAGS += $( addsuffix = 1,$( addprefix -DFEATURE_,$( __FEATURES) ) )
## Specific feature additions
# ## Example:
#ifneq (,$(findstring fast-math,$(FEATURES)))
# # fast-math feature: Apply `-ffast-math`
# COMMON_FLAGS+=-ffast-math
#endif
# Arch and optimisation
i f n e q ( $( ARCH ) , )
OPT_FLAGS += $( addprefix -march= ,$( ARCH) )
e n d i f
i f n e q ( $( CPU ) , )
OPT_FLAGS += $( addprefix -mtune= ,$( CPU) )
e n d i f
i f e q ( $( PARALLEL ) , y e s )
SHARED_FLAGS += -fopenmp
SHARED_RELEASE_FLAGS += -floop-parallelize-all -ftree-parallelize-loops= 4
e n d i f
COMMON_FLAGS += $( addprefix -m,$( CPU_FLAGS) )
CXX_OPT_FLAGS ?= $( OPT_FLAGS) -felide-constructors
CSTD ?= gnu2x
CXXSTD ?= gnu++23
# Build Options
STRIP = strip
RELEASE_COMMON_FLAGS += -fno-bounds-check
DEBUG_COMMON_FLAGS += -ggdb -gz -ftrapv -fbounds-check
i f n e q ( $( TARGET_SPEC_FLAGS ) , n o )
RELEASE_CFLAGS?= -O3 -flto $( OPT_FLAGS)
RELEASE_CXXFLAGS?= -O3 -flto $( CXX_OPT_FLAGS)
RELEASE_LDFLAGS?= -Wl,-O3 -Wl,-flto
DEBUG_CFLAGS?= -Og
DEBUG_CXXFLAGS?= -Og
DEBUG_LDFLAGS?=
e n d i f
DEBUG_CFLAGS += -DDEBUG $( DEBUG_COMMON_FLAGS)
DEBUG_CXXFLAGS += -DDEBUG $( DEBUG_COMMON_FLAGS) -fasynchronous-unwind-tables
RELEASE_CFLAGS += -DRELEASE $( RELEASE_COMMON_FLAGS)
RELEASE_CXXFLAGS += -DRELEASE $( RELEASE_COMMON_FLAGS)
# Objects
OBJ_C = $( addprefix obj/c/,$( SRC_C:.c= .o) )
OBJ_CXX = $( addprefix obj/cxx/,$( SRC_CXX:.cpp= .o) )
OBJ = $( OBJ_C) $( OBJ_CXX)
# Pre-compiled header objects
PCH_HEADERS += $( shell find -O2 $( INCLUDE) -type f -name \* _p.hh -or -name \* _p.h)
PCH_OUTPUT_LOCATION =
PCH_OUTPUT = $( addprefix $( PCH_OUTPUT_LOCATION) ,$( addsuffix .gch,$( PCH_HEADERS) ) )
PCH_BUILD_COMMON_FLAGS += -H
#-D_PCH_BUILD=1
PCH_BUILD_CFLAGS += -x c-header $( PCH_BUILD_COMMON_FLAGS)
PCH_BUILD_CXXFLAGS += -x c++-header $( PCH_BUILD_COMMON_FLAGS)
PCH_CHEADERS = $( filter %.h,$( PCH_HEADERS) )
PCH_CXXHEADERS = $( filter %.hh,$( PCH_HEADERS) )
PCH_USE_COMMON_FLAGS += -Winvalid-pch
i f e q ( $( INCLUDE_PCH_GLOBAL ) , 1 )
# Globally included
PCH_USE_CFLAGS += $( addprefix -include ,$( realpath $( PCH_CHEADERS) ) )
PCH_USE_CXXFLAGS += $( addprefix -include ,$( realpath $( PCH_CXXHEADERS) ) )
e l s e
# Seperately include them
i f n e q ( $( PCH_OUTPUT_LOCATION ) , )
override PCH_OUTPUT_LOCATION : =$( dir $ ( PCH_OUTPUT_LOCATION ) )
# XXX: We don't wan't to be using PCH_OUTPUT_LOCATION really...
COMMON_FLAGS := $( addprefix -I,$( dir $( shell find -O3 $( PCH_OUTPUT_LOCATION) -mindepth 1 -type d -printf %p/ ) ) ) $( COMMON_FLAGS)
e n d i f
e n d i f
COMMON_FLAGS += $( addprefix -include ,$( INCLUDE_GLOBAL) )
PCH_USE_CFLAGS += $( PCH_USE_COMMON_FLAGS)
PCH_USE_CXXFLAGS += $( PCH_USE_COMMON_FLAGS)
# Compiler Flags
CFLAGS += $( COMMON_FLAGS) --std= $( CSTD)
CXXFLAGS += $( COMMON_FLAGS) --std= $( CXXSTD)
LDFLAGS += $( addsuffix .a,$( addprefix -l:lib,$( STATIC_LIBS) ) ) $( addprefix -l,$( SHARED_LIBS) )
# PGO (unused for lib targets)
PROF_FLAGS = -D_PGO_GEN -fprofile-generate
PGO_OBJ_C = $( addprefix prof/c/,$( SRC_C:.c= .o) )
PGO_OBJ_CXX = $( addprefix prof/cxx/,$( SRC_CXX:.c= .o) )
PGO_OBJ = $( PGO_OBJ_C) $( PGO_OBJ_CXX)
PROF_ITERATIONS = 10
PROF_LOCATION ?= /tmp/$( PROJECT) -pgo
PROF_LARGE_BOUND = 10240
PROF_SMALL_BOUND = 1024
# Phonies
# XXX: This doesn't force them to run in series for some reason?
.PHONY : release
release : | dirs
$( MAKE) lib$( PROJECT) .a
@$( MAKE) clean-rebuild >> /dev/null
@$( MAKE) dirs >> /dev/null
$( MAKE) lib$( PROJECT) .so
.PHONY : debug
debug : | dirs
$( MAKE) lib$( PROJECT) -debug.a
@$( MAKE) clean-rebuild >> /dev/null
@$( MAKE) dirs >> /dev/null
$( MAKE) lib$( PROJECT) -debug.so
# Rebuild both release and debug targets from scratch
.PHONY : all
all : | clean
@$( MAKE) release
@$( MAKE) clean-rebuild
@$( MAKE) debug
.PHONY : test
test : $( PROJECT ) -test
-strace ./$<
-valgrind ./$<
# Targets
.PHONY : pch
pch : | dirs $( PROJECT ) -pch
@echo 'WARNING: When building target $(PROJECT)-pch, the PCH file(s) will not be built with the default auto-opts enabled by targets of kind `debug` or `release`. This should only be used when full manually overriding all compile and linker flags' >& 2
$(PROJECT)-pch : $( PCH_OUTPUT )
@echo ""
@echo 'PCH: $(PCH_OUTPUT)' '<-' '$(PCH_HEADERS)'
@echo " with: $( PCH_INCLUDES) "
@echo 'C: $(PCH_CHEADERS)'
@echo 'C++: $(PCH_CXXHEADERS)'
# Invoking target `pch` itself is not necissary, they are build automatically where they are used.
dirs :
@mkdir -p $( addprefix { obj$( __COMMA) prof} /c{ $( __COMMA) xx} /,$( shell find $( SRC) / -type d) )
$(PCH_OUTPUT_LOCATION)%.hh.gch : CXXFLAGS +=$( PCH_BUILD_CXXFLAGS )
$(PCH_OUTPUT_LOCATION)%.hh.gch : %.hh $( PCH_INCLUDES )
@mkdir -p $( dir $@ )
$( CXX) $( CXXFLAGS) -o $@ -c $<
$(PCH_OUTPUT_LOCATION)%.h.gch : CFLAGS +=$( PCH_BUILD_CFLAGS )
$(PCH_OUTPUT_LOCATION)%.h.gch : %.h $( PCH_INCLUDES )
@mkdir -p $( dir $@ )
$( CC) $( CFLAGS) -o $@ -c $<
obj/c/%.o : CFLAGS += $( PCH_USE_CFLAGS )
obj/c/%.o : %.c $( PCH_OUTPUT )
$( CC) -c $< $( CFLAGS) -o $@
obj/cxx/%.o : CXXFLAGS += $( PCH_USE_CXXFLAGS )
obj/cxx/%.o : %.cpp $( PCH_OUTPUT )
$( CXX) -c $< $( CXXFLAGS) -o $@
prof/c/%.o : CFLAGS += $( PCH_USE_CFLAGS )
prof/c/%.o : %.c $( PCH_OUTPUT )
$( CC) -c $< $( CFLAGS) -o $@ $( PROF_FLAGS)
#$(LDFLAGS)
prof/cxx/%.o : CXXFLAGS += $( PCH_USE_CXXFLAGS )
prof/cxx/%.o : %.cpp $( PCH_OUTPUT )
$( CXX) -c $< $( CXXFLAGS) -o $@ $( PROF_FLAGS)
#$(LDFLAGS)
lib$(PROJECT)-release.a : CFLAGS += $( RELEASE_CFLAGS ) $( STATIC_FLAGS ) $( STATIC_RELEASE_FLAGS )
lib$(PROJECT)-release.a : CXXFLAGS += $( RELEASE_CXXFLAGS ) $( STATIC_FLAGS ) $( STATIC_RELEASE_FLAGS )
lib$(PROJECT)-release.a : LDFLAGS += $( RELEASE_LDFLAGS )
lib$(PROJECT)-release.a : $( OBJ )
$( AR) rcs $@ $^
$( RANLIB) $@
lib$(PROJECT)-debug.a : CFLAGS += $( DEBUG_CFLAGS ) $( STATIC_FLAGS ) $( STATIC_DEBUG_FLAGS )
lib$(PROJECT)-debug.a : CXXFLAGS += $( DEBUG_CXXFLAGS ) $( STATIC_FLAGS ) $( STATIC_DEBUG_FLAGS )
lib$(PROJECT)-debug.a : LDFLAGS += $( DEBUG_LDFLAGS )
lib$(PROJECT)-debug.a : $( OBJ )
$( AR) rcs $@ $^
$( RANLIB) $@
lib$(PROJECT)-release.so : CFLAGS += $( RELEASE_CFLAGS ) $( SHARED_FLAGS ) $( SHARED_RELEASE_FLAGS )
lib$(PROJECT)-release.so : CXXFLAGS += $( RELEASE_CXXFLAGS ) $( SHARED_FLAGS ) $( SHARED_RELEASE_FLAGS )
lib$(PROJECT)-release.so : LDFLAGS += $( RELEASE_LDFLAGS )
lib$(PROJECT)-release.so : BINFLAGS += $( RELEASE_BINFLAGS )
lib$(PROJECT)-release.so : $( OBJ )
$( CXX) -shared $^ $( BINFLAGS) $( CXXFLAGS) -o $@ $( LDFLAGS)
$( STRIP) $@
lib$(PROJECT)-debug.so : CFLAGS += $( DEBUG_CFLAGS ) $( SHARED_FLAGS ) $( SHARED_DEBUG_FLAGS )
lib$(PROJECT)-debug.so : CXXFLAGS += $( DEBUG_CXXFLAGS ) $( SHARED_FLAGS ) $( SHARED_DEBUG_FLAGS )
lib$(PROJECT)-debug.so : LDFLAGS += $( DEBUG_LDFLAGS )
lib$(PROJECT)-debug.so : BINFLAGS += $( DEBUG_BINFLAGS )
lib$(PROJECT)-debug.so : $( OBJ )
$( CXX) -shared $^ $( BINFLAGS) $( CXXFLAGS) -o $@ $( LDFLAGS)
lib$(PROJECT).a : lib $( PROJECT ) -release .a
ln -f $< $@
lib$(PROJECT).so : LDFLAGS += -Wl , -soname , lib $( PROJECT ) .so .$( VERSION_MAJOR )
lib$(PROJECT).so : lib $( PROJECT ) -release .so
ln -f $< $@ .$( VERSION)
ln -sf $@ .$( VERSION) $@ .$( VERSION_MAJOR)
ln -sf $@ .$( VERSION_MAJOR) $@
clean-source :
find -O2 { obj,prof} / -type f -exec rm { } +
clean-rebuild : clean -source
find $( INCLUDE) $( PCH_OUTPUT_LOCATION) -type f -name \* .gch -exec rm { } +
clean : clean -rebuild
rm -f lib$( PROJECT) { ,-{ release,debug,pgo} } .{ a,so{ ,.*} }
rm -f $( PROJECT) -test
clean-full : clean
rm -rf { obj,prof}
install :
install -d $( DESTDIR) $( PREFIX) /lib/
install -m 644 lib$( PROJECT) .a $( DESTDIR) $( PREFIX) /lib/
install -s -m 755 lib$( PROJECT) .so.$( VERSION) $( DESTDIR) $( PREFIX) /lib/
ln -sf lib$( PROJECT) .so.$( VERSION) $( DESTDIR) $( PREFIX) /lib/lib$( PROJECT) .so.$( VERSION_MAJOR)
ln -sf lib$( PROJECT) .so.$( VERSION_MAJOR) $( DESTDIR) $( PREFIX) /lib/lib$( PROJECT) .so
install -d $( DESTDIR) $( PREFIX) /include/$( INCLUDE_PREFIX)
install -m 644 $( wildcard $( INCLUDE) /*.*) $( DESTDIR) $( PREFIX) /include/$( INCLUDE_PREFIX)
uninstall :
-rm $( DESTDIR) $( PREFIX) /lib/lib$( PROJECT) .{ a,so{ ,.*} }
cd $( INCLUDE) && find . -type f | xargs -I { } rm " $( DESTDIR) $( PREFIX) /include/ $( INCLUDE_PREFIX) {} "
[ [ -d " $( DESTDIR) $( PREFIX) /include/ $( INCLUDE_PREFIX) " ] ] && \
rmdir $( DESTDIR) $( PREFIX) /include/$( INCLUDE_PREFIX) || :
#TODO: We can make the target between `{debug,release}{.a,.so}` configurable by call. e.g. `TARGET=release.a make test`
$(PROJECT)-test : lib $( PROJECT ) -$( TARGET )
$( CC) $( CFLAGS) $( TEST_CFLAGS) src/test/*.c -o $@ -l:$< $( LDFLAGS) $( TEST_LDFLAGS)