From c4c063e9844f2bd2271b2e3391f59f872c66f69a Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Tue, 16 Apr 2024 18:43:49 -0400 Subject: build: Refactor configuration We now use a recursive make invocation to do the work of `make config`. The new implementation is also compatible with GNU make 3.81 found on macOS. --- Makefile | 369 ++++++--------------------------------------------------------- 1 file changed, 33 insertions(+), 336 deletions(-) (limited to 'Makefile') diff --git a/Makefile b/Makefile index 7cddc51..e9efef3 100644 --- a/Makefile +++ b/Makefile @@ -2,285 +2,27 @@ # SPDX-License-Identifier: 0BSD # This Makefile implements the configuration and build steps for bfs. It is -# portable to both GNU make and the BSD make implementations (how that works -# is documented below). To build bfs, run +# portable to both GNU make and most BSD make implementations. To build bfs, +# run # # $ make config # $ make +# Utilities and GNU/BSD portability +include config/prelude.mk + # The default build target default: bfs .PHONY: default -# 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 ?= $^ - -# GNU and BSD make have incompatible syntax for conditionals, but we can do a -# lot with just recursive variable expansion. Inspired by -# https://github.com/wahern/autoguess -TRUTHY,y := y -TRUTHY,1 := y - -# Normalize ${V} to either "y" or "" -IS_V := ${TRUTHY,${V}} - -# Suppress output unless V=1 -Q, := @ -Q := ${Q,${IS_V}} - -# 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 - -# Platform detection -OS != uname -ARCH != uname -m - -# 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:./%=%} - -# Installation paths -DESTDIR ?= -PREFIX ?= /usr -MANDIR ?= ${PREFIX}/share/man - -# Configurable executables; can be overridden with -# -# $ make config CC=clang -CC ?= cc -INSTALL ?= install -MKDIR ?= mkdir -p -PKG_CONFIG ?= pkg-config -RM ?= rm -f - -# Configurable flags - -CPPFLAGS ?= -CFLAGS ?= \ - -g \ - -Wall \ - -Wformat=2 \ - -Werror=implicit \ - -Wimplicit-fallthrough \ - -Wmissing-declarations \ - -Wshadow \ - -Wsign-compare \ - -Wstrict-prototypes -LDFLAGS ?= -LDLIBS ?= - -EXTRA_CPPFLAGS ?= -EXTRA_CFLAGS ?= -EXTRA_LDFLAGS ?= -EXTRA_LDLIBS ?= - -GIT_VERSION != test -d .git && command -v git >/dev/null 2>&1 && git describe --always --dirty || echo 3.1.3 -VERSION ?= ${GIT_VERSION} - -# Immutable flags -export BFS_CPPFLAGS= \ - -D__EXTENSIONS__ \ - -D_ATFILE_SOURCE \ - -D_BSD_SOURCE \ - -D_DARWIN_C_SOURCE \ - -D_DEFAULT_SOURCE \ - -D_GNU_SOURCE \ - -D_LARGEFILE64_SOURCE \ - -D_POSIX_PTHREAD_SEMANTICS \ - -D_FILE_OFFSET_BITS=64 \ - -D_TIME_BITS=64 -export BFS_CFLAGS= -std=c17 -pthread - -# Platform-specific system libraries -LDLIBS,DragonFly := -lposix1e -LDLIBS,Linux := -lrt -LDLIBS,NetBSD := -lutil -LDLIBS,SunOS := -lsocket -lnsl -_BFS_LDLIBS := ${LDLIBS,${OS}} -export BFS_LDLIBS=${_BFS_LDLIBS} - -# Build profiles -ASAN ?= n -LSAN ?= n -MSAN ?= n -TSAN ?= n -UBSAN ?= n -GCOV ?= n -LINT ?= n -RELEASE ?= n - -export ASAN_CFLAGS= -fsanitize=address -export LSAN_CFLAGS= -fsanitize=leak -export MSAN_CFLAGS= -fsanitize=memory -fsanitize-memory-track-origins -export UBSAN_CFLAGS= -fsanitize=undefined - -# https://github.com/google/sanitizers/issues/342 -export TSAN_CPPFLAGS= -DBFS_USE_TARGET_CLONES=0 -export TSAN_CFLAGS= -fsanitize=thread - -export SAN=${ASAN}${LSAN}${MSAN}${TSAN}${UBSAN} -export SAN_CFLAGS= -fno-sanitize-recover=all - -# MSAN and TSAN both need all code to be instrumented -export NOLIBS= ${MSAN}${TSAN} - -# gcov only intercepts fork()/exec() with -std=gnu* -export GCOV_CFLAGS= --coverage -std=gnu17 - -export LINT_CPPFLAGS= -D_FORTIFY_SOURCE=3 -DBFS_LINT -export LINT_CFLAGS= -Werror -O2 - -export RELEASE_CPPFLAGS= -DNDEBUG -export RELEASE_CFLAGS= -O3 -flto=auto - -# Save the new value of these variables, before they potentially get overridden -# by `-include ${CONFIG}` below - -_XPREFIX := ${PREFIX} -_XMANDIR := ${MANDIR} - -_XOS := ${OS} -_XARCH := ${ARCH} - -_XCC := ${CC} -_XINSTALL := ${INSTALL} -_XMKDIR := ${MKDIR} -_XRM := ${RM} - -_XCPPFLAGS := ${CPPFLAGS} -_XCFLAGS := ${CFLAGS} -_XLDFLAGS := ${LDFLAGS} -_XLDLIBS := ${LDLIBS} - -# 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} -_PKG_CONFIG := ${PKG_CONFIG} - -export BUILDDIR=${_BUILDDIR} -export PKG_CONFIG=${_PKG_CONFIG} - -export XPREFIX=${_XPREFIX} -export XMANDIR=${_XMANDIR} - -export XOS=${_XOS} -export XARCH=${_XARCH} - -export XCC=${_XCC} -export XINSTALL=${_XINSTALL} -export XMKDIR=${_XMKDIR} -export XRM=${_XRM} - -export XCPPFLAGS=${_XCPPFLAGS} -export XCFLAGS=${_XCFLAGS} -export XLDFLAGS=${_XLDFLAGS} -export XLDLIBS=${_XLDLIBS} - -# The configuration file generated by `make config` -CONFIG := ${GEN}/config.mk +# Include the generated build config, if it exists -include ${CONFIG} ## Configuration phase (`make config`) -# Makefile fragments generated by `make config` -MKS := \ - ${GEN}/vars.mk \ - ${GEN}/deps.mk \ - ${GEN}/objs.mk \ - ${GEN}/pkgs.mk - -# cat a file if V=1 -VCAT,y := @cat -VCAT, := @: -VCAT := ${VCAT,${IS_V}} - # The configuration goal itself -config: ${MKS} - ${MSG} "[ GEN] ${CONFIG}" - @printf 'include $${GEN}/%s\n' ${.ALLSRC:${GEN}/%=%} >${CONFIG} - ${VCAT} ${CONFIG} -.PHONY: config - -# Saves the configurable variables -${GEN}/vars.mk: - @${XMKDIR} ${@D} - ${MSG} "[ GEN] $@" - @config/vars.sh >$@ - ${VCAT} $@ -.PHONY: ${GEN}/vars.mk - -# Check for dependency generation support -${GEN}/deps.mk: ${GEN}/vars.mk - ${MSG} "[ GEN] $@" - @+${MAKE} -rs -f config/deps.mk TARGET=$@ - ${VCAT} $@ - @printf -- '-include %s\n' ${OBJS:.o=.d} >>$@ -.PHONY: ${GEN}/deps.mk - -# Lists file.o: file.c dependencies -${GEN}/objs.mk: - @${XMKDIR} ${@D} - ${MSG} "[ GEN] $@" - @for obj in ${OBJS:${OBJ}/%.o=%}; do printf '$${OBJ}/%s.o: %s.c\n' "$$obj" "$$obj"; done >$@ -.PHONY: ${GEN}/objs.mk - -# External dependencies -PKG_MKS := \ - ${GEN}/libacl.mk \ - ${GEN}/libcap.mk \ - ${GEN}/libselinux.mk \ - ${GEN}/liburing.mk \ - ${GEN}/oniguruma.mk - -# Auto-detect dependencies and their build flags -${GEN}/pkgs.mk: ${PKG_MKS} - ${MSG} "[ GEN] $@" - @printf 'include $${GEN}/%s\n' ${.ALLSRC:${GEN}/%=%} >$@ - @+${MAKE} -rs -f config/pkgs.mk TARGET=$@ - ${VCAT} $@ -.PHONY: ${GEN}/pkgs.mk - -# Auto-detect dependencies -${PKG_MKS}: ${GEN}/vars.mk - @+${MAKE} -rs -f config/pkg.mk TARGET=$@ - @if [ "${IS_V}" ]; then \ - cat $@; \ - elif grep -q PKGS $@; then \ - printf '[ GEN] %-18s [y]\n' $@; \ - else \ - printf '[ GEN] %-18s [n]\n' $@; \ - fi -.PHONY: ${PKG_MKS} +config:: + @+${MAKE} -sf config/config.mk # bfs used to have flag-like targets (`make release`, `make asan ubsan`, etc.). # Direct users to the new configuration system. @@ -313,36 +55,6 @@ BINS := \ all: ${BINS} .PHONY: all -# 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/version.o \ - ${OBJ}/src/xregex.o \ - ${OBJ}/src/xspawn.o \ - ${OBJ}/src/xtime.o - # Group relevant flags together ALL_CFLAGS = ${CPPFLAGS} ${CFLAGS} ${DEPFLAGS} ALL_LDFLAGS = ${CFLAGS} ${LDFLAGS} @@ -355,22 +67,6 @@ ${BINS}: +${MSG} "[ LD ] $@" ${CC} ${ALL_LDFLAGS} ${.ALLSRC} ${LDLIBS} -o $@ ${POSTLINK} -# All object files -OBJS := \ - ${OBJ}/src/main.o \ - ${OBJ}/tests/alloc.o \ - ${OBJ}/tests/bfstd.o \ - ${OBJ}/tests/bit.o \ - ${OBJ}/tests/ioq.o \ - ${OBJ}/tests/main.o \ - ${OBJ}/tests/mksock.o \ - ${OBJ}/tests/trie.o \ - ${OBJ}/tests/xspawn.o \ - ${OBJ}/tests/xspawnee.o \ - ${OBJ}/tests/xtime.o \ - ${OBJ}/tests/xtouch.o \ - ${LIBBFS} - # Get the .c file for a .o file CSRC = ${@:${OBJ}/%.o=%.c} @@ -383,14 +79,20 @@ ${OBJS}: ${CONFIG} # Save the version number to this file, but only update VERSION if it changes ${GEN}/NEWVERSION:: @${MKDIR} ${@D} - @printf '%s\n' '${VERSION}' >$@ + @if [ "$$VERSION" ]; then \ + printf '%s\n' "$$VERSION"; \ + elif test -d .git && command -v git >/dev/null 2>&1; then \ + git describe --always --dirty; \ + else \ + echo "3.1.3"; \ + fi >$@ ${GEN}/VERSION: ${GEN}/NEWVERSION @test -e $@ && cmp -s $@ ${.ALLSRC} && rm ${.ALLSRC} || mv ${.ALLSRC} $@ # Rebuild version.c whenever the version number changes ${OBJ}/src/version.o: ${GEN}/VERSION -${OBJ}/src/version.o: CPPFLAGS := ${CPPFLAGS} -DBFS_VERSION='"${VERSION}"' +${OBJ}/src/version.o: CPPFLAGS := ${CPPFLAGS} -DBFS_VERSION='"$$(cat ${GEN}/VERSION)"' ## Test phase (`make check`) @@ -418,14 +120,7 @@ unit-tests: ${UTEST_BINS} .PHONY: unit-tests ${BIN}/tests/units: \ - ${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 \ + ${UNIT_OBJS} \ ${LIBBFS} ${BIN}/tests/xspawnee: \ @@ -463,20 +158,20 @@ ${BIN}/tests/xtouch: \ ${LIBBFS} # `make distcheck` configurations -DISTCHECKS := distcheck-asan distcheck-tsan distcheck-release - -# Don't use msan on macOS -IS_DARWIN,Darwin := y -IS_DARWIN := ${IS_DARWIN,${OS}} -DISTCHECK_MSAN, := distcheck-msan -DISTCHECKS += ${DISTCHECK_MSAN,${IS_DARWIN}} - -# Only add a 32-bit build on 64-bit Linux -DISTCHECK_M32,Linux,x86_64 := distcheck-m32 -DISTCHECKS += ${DISTCHECK_M32,${OS},${ARCH}} +DISTCHECKS := \ + distcheck-asan \ + distcheck-msan \ + distcheck-tsan \ + distcheck-m32 \ + distcheck-release # Test multiple configurations -distcheck: ${DISTCHECKS} +distcheck: + @+${MAKE} -s distcheck-asan + @+test "$$(uname)" = Darwin || ${MAKE} -s distcheck-msan + @+${MAKE} -s distcheck-tsan + @+test "$$(uname)-$$(uname -m)" != Linux-x86_64 || ${MAKE} -s distcheck-m32 + @+${MAKE} -s distcheck-release .PHONY: distcheck # Per-distcheck configuration @@ -530,9 +225,11 @@ check-install:: # Clean all build products clean:: - ${RM} -r ${BIN} ${OBJ} + ${MSG} "[ RM ] bin obj" \ + ${RM} -r ${BIN} ${OBJ} # Clean everything, including generated files distclean: clean - ${RM} -r ${GEN} ${DISTCHECKS} + ${MSG} "[ RM ] gen" \ + ${RM} -r ${GEN} ${DISTCHECKS} .PHONY: distclean -- cgit v1.2.3