# Copyright © Tavian Barnes # SPDX-License-Identifier: 0BSD # Common makefile utilities. Compatible with both GNU make and most BSD makes. # BSD make will chdir into ${.OBJDIR} by default, unless we tell it not to .OBJDIR: . # We don't use any suffix rules .SUFFIXES: # GNU make has $^ for the full list of targets, while BSD make has $> and the # long-form ${.ALLSRC}. We could write $^ $> to get them both, but that would # break if one of them implemented support for the other. So instead, bring # BSD's ${.ALLSRC} to GNU. .ALLSRC ?= $^ # For out-of-tree builds, e.g. # # $ make config BUILDDIR=/path/to/build/dir # $ make BUILDDIR=/path/to/build/dir BUILDDIR ?= . # Shorthand for build subdirectories BIN := ${BUILDDIR}/bin GEN := ${BUILDDIR}/gen OBJ := ${BUILDDIR}/obj # GNU make strips a leading ./ from target names, so do the same for BSD make BIN := ${BIN:./%=%} GEN := ${GEN:./%=%} OBJ := ${OBJ:./%=%} # The configuration file generated by `make config` CONFIG := ${GEN}/config.mk # Installation paths DESTDIR ?= PREFIX ?= /usr MANDIR ?= ${PREFIX}/share/man # GNU make supports `export VAR`, but BSD make requires `export VAR=value`. # Sadly, GNU make gives a recursion error on `export VAR=${VAR}`. _BUILDDIR := ${BUILDDIR} export BUILDDIR=${_BUILDDIR} # Configurable executables; can be overridden with # # $ make config CC=clang CC ?= cc INSTALL ?= install MKDIR ?= mkdir -p PKG_CONFIG ?= pkg-config RM ?= rm -f # GNU and BSD make have incompatible syntax for conditionals, but we can do a # lot with just nested variable expansion. We use "y" as the canonical # truthy value, and "" (the empty string) as the canonical falsey value. # # To normalize a boolean, use ${TRUTHY,${VAR}}, which expands like this: # # VAR=y ${TRUTHY,${VAR}} => ${TRUTHY,y} => y # VAR=1 ${TRUTHY,${VAR}} => ${TRUTHY,1} => y # VAR=n ${TRUTHY,${VAR}} => ${TRUTHY,n} => [empty] # VAR=other ${TRUTHY,${VAR}} => ${TRUTHY,other} => [empty] # VAR= ${TRUTHY,${VAR}} => ${TRUTHY,} => [emtpy] # # Inspired by https://github.com/wahern/autoguess TRUTHY,y := y TRUTHY,1 := y # Boolean operators are also implemented with nested expansion NOT,y := NOT, := y # Support up to 5 arguments AND,y := y AND,y,y := y AND,y,y,y := y AND,y,y,y,y := y AND,y,y,y,y,y := y # NOR can be defined without combinatorial explosion. # OR is just ${NOT,${NOR,...}} NOR, := y NOR,, := y NOR,,, := y NOR,,,, := y NOR,,,,, := y # Normalize ${V} to either "y" or "" IS_V := ${TRUTHY,${V}} # Suppress output unless V=1 Q, := @ Q := ${Q,${IS_V}} # The current target, with ${BUILDDIR} stripped for shorter messages TGT = ${@:${BUILDDIR}/%=%} # Show full commands with `make V=1`, otherwise short summaries MSG = @msg() { \ MSG="$$1"; \ shift; \ test "${IS_V}" || printf '%s\n' "$$MSG"; \ test "$${1:-}" || return 0; \ test "${IS_V}" && printf '%s\n' "$$*"; \ "$$@"; \ }; \ msg # Maximum width of a short message, to align the ✔/✘ MSG_WIDTH := 33 # cat a file if V=1 VCAT,y := @cat VCAT, := @: VCAT := ${VCAT,${IS_V}} # List all object files here, as they're needed by both `make config` and `make` # All object files except the entry point LIBBFS := \ ${OBJ}/src/alloc.o \ ${OBJ}/src/bar.o \ ${OBJ}/src/bfstd.o \ ${OBJ}/src/bftw.o \ ${OBJ}/src/color.o \ ${OBJ}/src/ctx.o \ ${OBJ}/src/diag.o \ ${OBJ}/src/dir.o \ ${OBJ}/src/dstring.o \ ${OBJ}/src/eval.o \ ${OBJ}/src/exec.o \ ${OBJ}/src/expr.o \ ${OBJ}/src/fsade.o \ ${OBJ}/src/ioq.o \ ${OBJ}/src/mtab.o \ ${OBJ}/src/opt.o \ ${OBJ}/src/parse.o \ ${OBJ}/src/printf.o \ ${OBJ}/src/pwcache.o \ ${OBJ}/src/stat.o \ ${OBJ}/src/thread.o \ ${OBJ}/src/trie.o \ ${OBJ}/src/typo.o \ ${OBJ}/src/xregex.o \ ${OBJ}/src/xspawn.o \ ${OBJ}/src/xtime.o \ ${OBJ}/gen/version.o # Unit test objects UNIT_OBJS := \ ${OBJ}/tests/alloc.o \ ${OBJ}/tests/bfstd.o \ ${OBJ}/tests/bit.o \ ${OBJ}/tests/ioq.o \ ${OBJ}/tests/main.o \ ${OBJ}/tests/trie.o \ ${OBJ}/tests/xspawn.o \ ${OBJ}/tests/xtime.o # All object files OBJS := \ ${OBJ}/src/main.o \ ${OBJ}/tests/mksock.o \ ${OBJ}/tests/xspawnee.o \ ${OBJ}/tests/xtouch.o \ ${LIBBFS} \ ${UNIT_OBJS}