diff options
author | Tavian Barnes <tavianator@tavianator.com> | 2024-04-08 11:27:11 -0400 |
---|---|---|
committer | Tavian Barnes <tavianator@tavianator.com> | 2024-04-09 17:15:23 -0400 |
commit | c31577d102d87455f3f12086be4c0e2159fa5d35 (patch) | |
tree | 864c7c199e5b846dcf497de8b667d6c6f8c550b9 | |
parent | 5e0b721d0d929223e4308406480a1f1ca9e3f4dc (diff) |
build: Add a separate configuration step
-rw-r--r-- | .github/workflows/ci.yml | 23 | ||||
-rw-r--r-- | .github/workflows/codecov.yml | 3 | ||||
-rw-r--r-- | .github/workflows/codeql.yml | 1 | ||||
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | GNUmakefile | 372 | ||||
-rw-r--r-- | Makefile | 517 | ||||
-rw-r--r-- | README.md | 4 | ||||
-rwxr-xr-x | config/cc.sh | 12 | ||||
-rw-r--r-- | config/empty.c | 6 | ||||
-rw-r--r-- | config/libacl.c | 6 | ||||
-rw-r--r-- | config/libcap.c | 6 | ||||
-rw-r--r-- | config/liburing.c | 6 | ||||
-rw-r--r-- | config/oniguruma.c | 6 | ||||
-rwxr-xr-x | config/pkg.sh | 44 | ||||
-rwxr-xr-x | config/pkgconf.sh | 64 | ||||
-rw-r--r-- | docs/BUILDING.md | 83 | ||||
-rw-r--r-- | docs/USAGE.md | 2 | ||||
-rw-r--r-- | src/config.h | 11 | ||||
-rw-r--r-- | src/main.c | 1 | ||||
-rw-r--r-- | src/parse.c | 2 | ||||
-rw-r--r-- | src/version.c | 6 | ||||
-rw-r--r-- | tests/run.sh | 2 | ||||
-rw-r--r-- | tests/tests.mk | 10 |
23 files changed, 757 insertions, 432 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 51d06fb..78aa196 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -53,12 +53,13 @@ jobs: run: | brew install \ bash \ - expect + expect \ + make - name: Run tests run: | jobs=$(sysctl -n hw.ncpu) - make -j$jobs distcheck + gmake -j$jobs distcheck freebsd: name: FreeBSD @@ -79,8 +80,8 @@ jobs: pkg install -y \ bash \ expect \ - gmake \ oniguruma \ + pkgconf \ sudo \ tcl-wrapper pw useradd -n action -m -G wheel -s /usr/local/bin/bash @@ -89,7 +90,7 @@ jobs: run: | chown -R action:action . - sudo -u action gmake -j$(nproc) distcheck + sudo -u action make -j$(nproc) distcheck openbsd: name: OpenBSD @@ -119,6 +120,7 @@ jobs: run: | chown -R action:action . jobs=$(sysctl -n hw.ncpu) + doas -u action gmake config doas -u action gmake -j$jobs check TEST_FLAGS="--sudo=doas --verbose=skipped" netbsd: @@ -141,8 +143,8 @@ jobs: pkg_add \ bash \ clang \ - gmake \ oniguruma \ + pkgconf \ sudo \ tcl-expect useradd -m -G wheel -g =uid action @@ -152,7 +154,8 @@ jobs: PATH="/sbin:/usr/sbin:$PATH" chown -R action:action . jobs=$(sysctl -n hw.ncpu) - sudo -u action gmake -j$jobs check CC=clang LDFLAGS="-rpath /usr/pkg/lib" TEST_FLAGS="--sudo --verbose=skipped" + sudo -u action make config CC=clang + sudo -u action make -j$jobs check TEST_FLAGS="--sudo --verbose=skipped" dragonflybsd: name: DragonFly BSD @@ -173,8 +176,8 @@ jobs: pkg install -y \ bash \ expect \ - gmake \ oniguruma \ + pkgconf \ sudo \ tcl-wrapper pw useradd -n action -m -G wheel -s /usr/local/bin/bash @@ -183,7 +186,8 @@ jobs: run: | chown -R action:action . jobs=$(sysctl -n hw.ncpu) - sudo -u action gmake -j$jobs check TEST_FLAGS="--sudo --verbose=skipped" + sudo -u action make config + sudo -u action make -j$jobs check TEST_FLAGS="--sudo --verbose=skipped" omnios: name: OmniOS @@ -215,4 +219,5 @@ jobs: PATH="/usr/xpg4/bin:$PATH" chown -R action:staff . jobs=$(getconf NPROCESSORS_ONLN) - sudo -u action gmake -j$jobs check LDFLAGS="-Wl,-rpath,/opt/ooce/lib/amd64" TEST_FLAGS="--sudo --verbose=skipped" + sudo -u action gmake config + sudo -u action gmake -j$jobs check TEST_FLAGS="--sudo --verbose=skipped" diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml index d1dc351..2abe531 100644 --- a/.github/workflows/codecov.yml +++ b/.github/workflows/codecov.yml @@ -25,7 +25,8 @@ jobs: - name: Generate coverage run: | - make -j$(nproc) gcov check TEST_FLAGS="--sudo" + make config GCOV=y + make -j$(nproc) check TEST_FLAGS="--sudo" gcov -abcfpu obj/*/*.o - uses: codecov/codecov-action@v3 diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index c50b266..a2c224a 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -47,6 +47,7 @@ jobs: - name: Build run: | + make config make -j$(nproc) all - name: Perform CodeQL Analysis @@ -1,2 +1,4 @@ /bin/ +/gen/ /obj/ +/distcheck-*/ diff --git a/GNUmakefile b/GNUmakefile deleted file mode 100644 index 8685ca3..0000000 --- a/GNUmakefile +++ /dev/null @@ -1,372 +0,0 @@ -# Copyright © Tavian Barnes <tavianator@tavianator.com> -# SPDX-License-Identifier: 0BSD - -ifneq ($(wildcard .git),) -VERSION := $(shell git describe --always 2>/dev/null) -endif - -ifndef VERSION -VERSION := 3.1.3 -endif - -ifndef OS -OS := $(shell uname) -endif - -ifndef ARCH -ARCH := $(shell uname -m) -endif - -CC ?= gcc -INSTALL ?= install -MKDIR ?= mkdir -p -RM ?= rm -f - -export BUILDDIR ?= . -DESTDIR ?= -PREFIX ?= /usr -MANDIR ?= $(PREFIX)/share/man - -BIN := $(BUILDDIR)/bin -OBJ := $(BUILDDIR)/obj - -DEFAULT_CFLAGS := \ - -g \ - -Wall \ - -Wformat=2 \ - -Werror=implicit \ - -Wimplicit-fallthrough \ - -Wmissing-declarations \ - -Wshadow \ - -Wsign-compare \ - -Wstrict-prototypes - -CFLAGS ?= $(DEFAULT_CFLAGS) -LDFLAGS ?= -DEPFLAGS ?= -MD -MP -MF $(@:.o=.d) - -LOCAL_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 \ - -DBFS_VERSION=\"$(VERSION)\" - -LOCAL_CFLAGS := -std=c17 -pthread -LOCAL_LDFLAGS := -LOCAL_LDLIBS := - -ASAN := $(filter asan,$(MAKECMDGOALS)) -LSAN := $(filter lsan,$(MAKECMDGOALS)) -MSAN := $(filter msan,$(MAKECMDGOALS)) -TSAN := $(filter tsan,$(MAKECMDGOALS)) -UBSAN := $(filter ubsan,$(MAKECMDGOALS)) - -ifdef ASAN -LOCAL_CFLAGS += -fsanitize=address -SANITIZE := y -endif - -ifdef LSAN -LOCAL_CFLAGS += -fsanitize=leak -SANITIZE := y -endif - -ifdef MSAN -# msan needs all code instrumented -NOLIBS := y -LOCAL_CFLAGS += -fsanitize=memory -fsanitize-memory-track-origins -SANITIZE := y -endif - -ifdef TSAN -# tsan needs all code instrumented -NOLIBS := y -# https://github.com/google/sanitizers/issues/342 -LOCAL_CPPFLAGS += -DBFS_USE_TARGET_CLONES=0 -LOCAL_CFLAGS += -fsanitize=thread -SANITIZE := y -endif - -ifdef UBSAN -LOCAL_CFLAGS += -fsanitize=undefined -SANITIZE := y -endif - -ifdef SANITIZE -LOCAL_CFLAGS += -fno-sanitize-recover=all -endif - -ifndef NOLIBS -USE_ONIGURUMA := y -endif - -ifdef USE_ONIGURUMA -LOCAL_CPPFLAGS += -DBFS_USE_ONIGURUMA=1 - -ONIG_CONFIG := $(shell command -v onig-config 2>/dev/null) -ifdef ONIG_CONFIG -ONIG_CFLAGS := $(shell $(ONIG_CONFIG) --cflags) -ONIG_LDLIBS := $(shell $(ONIG_CONFIG) --libs) -else -ONIG_LDLIBS := -lonig -endif - -LOCAL_CFLAGS += $(ONIG_CFLAGS) -LOCAL_LDLIBS += $(ONIG_LDLIBS) -endif # USE_ONIGURUMA - -ifeq ($(OS),Linux) -ifndef NOLIBS -USE_ACL := y -USE_LIBCAP := y -USE_LIBURING := y -endif - -ifdef USE_ACL -LOCAL_LDLIBS += -lacl -else -LOCAL_CPPFLAGS += -DBFS_USE_SYS_ACL_H=0 -endif - -ifdef USE_LIBCAP -LOCAL_LDLIBS += -lcap -else -LOCAL_CPPFLAGS += -DBFS_USE_SYS_CAPABILITY_H=0 -endif - -ifdef USE_LIBURING -LOCAL_CPPFLAGS += -DBFS_USE_LIBURING=1 -LOCAL_LDLIBS += -luring -endif - -LOCAL_LDFLAGS += -Wl,--as-needed -LOCAL_LDLIBS += -lrt -endif # Linux - -ifeq ($(OS),NetBSD) -LOCAL_LDLIBS += -lutil -endif - -ifeq ($(OS),DragonFly) -LOCAL_LDLIBS += -lposix1e -endif - -ifeq ($(OS),SunOS) -LOCAL_LDLIBS += -lsocket -lnsl -endif - -ifneq ($(filter gcov,$(MAKECMDGOALS)),) -LOCAL_CFLAGS += --coverage -# gcov only intercepts fork()/exec() with -std=gnu* -LOCAL_CFLAGS := $(patsubst -std=c%,-std=gnu%,$(LOCAL_CFLAGS)) -endif - -ifneq ($(filter lint,$(MAKECMDGOALS)),) -LOCAL_CPPFLAGS += \ - -D_FORTIFY_SOURCE=3 \ - -DBFS_LINT -LOCAL_CFLAGS += -Werror -O2 -endif - -ifneq ($(filter release,$(MAKECMDGOALS)),) -LOCAL_CPPFLAGS += -DNDEBUG -CFLAGS := $(DEFAULT_CFLAGS) -O3 -flto=auto -endif - -ALL_CPPFLAGS = $(LOCAL_CPPFLAGS) $(CPPFLAGS) $(EXTRA_CPPFLAGS) -ALL_CFLAGS = $(ALL_CPPFLAGS) $(LOCAL_CFLAGS) $(CFLAGS) $(EXTRA_CFLAGS) $(DEPFLAGS) -ALL_LDFLAGS = $(ALL_CFLAGS) $(LOCAL_LDFLAGS) $(LDFLAGS) $(EXTRA_LDFLAGS) -ALL_LDLIBS = $(LOCAL_LDLIBS) $(LDLIBS) $(EXTRA_LDLIBS) - -# Default make target -bfs: $(BIN)/bfs -.PHONY: bfs - -# Goals that are treated like flags by this makefile -FLAG_GOALS := asan lsan msan tsan ubsan gcov lint release - -# These are the remaining non-flag goals -GOALS := $(filter-out $(FLAG_GOALS),$(MAKECMDGOALS)) - -# Build the default goal if only flag goals are specified -FLAG_PREREQS := -ifndef GOALS -FLAG_PREREQS += bfs -endif - -# Make sure that "make release" builds everything, but "make release obj/src/main.o" doesn't -$(FLAG_GOALS): $(FLAG_PREREQS) - @: -.PHONY: $(FLAG_GOALS) - -all: bfs tests -.PHONY: all - -$(BIN)/%: - @$(MKDIR) $(@D) - +$(CC) $(ALL_LDFLAGS) $^ $(ALL_LDLIBS) -o $@ -ifeq ($(OS) $(SANITIZE),FreeBSD y) - elfctl -e +noaslr $@ -endif - -$(OBJ)/%.o: %.c $(OBJ)/FLAGS - @$(MKDIR) $(@D) - $(CC) $(ALL_CFLAGS) -c $< -o $@ - -# Save the full set of flags to rebuild everything when they change -$(OBJ)/FLAGS.new: - @$(MKDIR) $(@D) - @echo $(CC) : $(ALL_CFLAGS) : $(ALL_LDFLAGS) : $(ALL_LDLIBS) >$@ -.PHONY: $(OBJ)/FLAGS.new - -# Only update obj/FLAGS if obj/FLAGS.new is different -$(OBJ)/FLAGS: $(OBJ)/FLAGS.new - @test -e $@ && cmp -s $@ $< && rm $< || mv $< $@ - -# 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 - -# The main executable -$(BIN)/bfs: $(OBJ)/src/main.o $(LIBBFS) - -# Testing utilities -TEST_UTILS := \ - $(BIN)/tests/mksock \ - $(BIN)/tests/xspawnee \ - $(BIN)/tests/xtouch - -$(BIN)/tests/mksock: $(OBJ)/tests/mksock.o $(LIBBFS) - -$(BIN)/tests/xspawnee: $(OBJ)/tests/xspawnee.o - -$(BIN)/tests/xtouch: $(OBJ)/tests/xtouch.o $(LIBBFS) - -# All test binaries -TESTS := $(BIN)/tests/units $(TEST_UTILS) - -$(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 \ - $(LIBBFS) - -# Build all the test binaries -tests: $(TESTS) -.PHONY: tests - -# Run the unit tests -unit-tests: $(BIN)/tests/units $(BIN)/tests/xspawnee - $< -.PHONY: unit-tests - -# The different flag combinations we check -INTEGRATIONS := default dfs ids eds j1 j2 j3 s -INTEGRATION_TESTS := $(INTEGRATIONS:%=check-%) - -check-default: $(BIN)/bfs $(TEST_UTILS) - +./tests/tests.sh --make="$(MAKE)" --bfs="$<" $(TEST_FLAGS) - -check-dfs check-ids check-eds: check-%: $(BIN)/bfs $(TEST_UTILS) - +./tests/tests.sh --make="$(MAKE)" --bfs="$< -S $*" $(TEST_FLAGS) - -check-j1 check-j2 check-j3 check-s: check-%: $(BIN)/bfs $(TEST_UTILS) - +./tests/tests.sh --make="$(MAKE)" --bfs="$< -$*" $(TEST_FLAGS) - -# Run the integration tests -integration-tests: $(INTEGRATION_TESTS) -.PHONY: integration-tests - -# Run all the tests -check: unit-tests integration-tests -.PHONY: check - -# Custom test flags for distcheck -DISTCHECK_FLAGS := -s TEST_FLAGS="--sudo --verbose=skipped" - -distcheck: - +$(MAKE) -B asan ubsan check $(DISTCHECK_FLAGS) -ifneq ($(OS),Darwin) - +$(MAKE) -B msan ubsan check CC=clang $(DISTCHECK_FLAGS) -endif - +$(MAKE) -B tsan ubsan check CC=clang $(DISTCHECK_FLAGS) -ifeq ($(OS) $(ARCH),Linux x86_64) - +$(MAKE) -B check EXTRA_CFLAGS="-m32" ONIG_CONFIG= USE_LIBURING= $(DISTCHECK_FLAGS) -endif - +$(MAKE) -B release check $(DISTCHECK_FLAGS) - +$(MAKE) -B check $(DISTCHECK_FLAGS) - +$(MAKE) check-install $(DISTCHECK_FLAGS) -.PHONY: distcheck - -clean: - $(RM) -r $(BIN) $(OBJ) -.PHONY: clean - -install: - $(MKDIR) $(DESTDIR)$(PREFIX)/bin - $(INSTALL) -m755 $(BIN)/bfs $(DESTDIR)$(PREFIX)/bin/bfs - $(MKDIR) $(DESTDIR)$(MANDIR)/man1 - $(INSTALL) -m644 docs/bfs.1 $(DESTDIR)$(MANDIR)/man1/bfs.1 - $(MKDIR) $(DESTDIR)$(PREFIX)/share/bash-completion/completions - $(INSTALL) -m644 completions/bfs.bash $(DESTDIR)$(PREFIX)/share/bash-completion/completions/bfs - $(MKDIR) $(DESTDIR)$(PREFIX)/share/zsh/site-functions - $(INSTALL) -m644 completions/bfs.zsh $(DESTDIR)$(PREFIX)/share/zsh/site-functions/_bfs - $(MKDIR) $(DESTDIR)$(PREFIX)/share/fish/vendor_completions.d - $(INSTALL) -m644 completions/bfs.fish $(DESTDIR)$(PREFIX)/share/fish/vendor_completions.d/bfs.fish -.PHONY: install - -uninstall: - $(RM) $(DESTDIR)$(PREFIX)/share/bash-completion/completions/bfs - $(RM) $(DESTDIR)$(PREFIX)/share/zsh/site-functions/_bfs - $(RM) $(DESTDIR)$(PREFIX)/share/fish/vendor_completions.d/bfs.fish - $(RM) $(DESTDIR)$(MANDIR)/man1/bfs.1 - $(RM) $(DESTDIR)$(PREFIX)/bin/bfs -.PHONY: uninstall - -check-install: - +$(MAKE) install DESTDIR=$(BUILDDIR)/pkg - +$(MAKE) uninstall DESTDIR=$(BUILDDIR)/pkg - $(BIN)/bfs $(BUILDDIR)/pkg -not -type d -print -exit 1 - $(RM) -r $(BUILDDIR)/pkg -.PHONY: check-install - -.SUFFIXES: - --include $(wildcard $(OBJ)/*/*.d) diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..326ee87 --- /dev/null +++ b/Makefile @@ -0,0 +1,517 @@ +# Copyright © Tavian Barnes <tavianator@tavianator.com> +# 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 +# +# $ make config +# $ make + +# 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 ?= $^ + +# 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 + +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 + +# Auto-detected library dependencies. Can be set manually with +# +# $ make config USE_LIBURING=n USE_ONIGURUMA=y +USE_LIBACL ?= +USE_LIBCAP ?= +USE_LIBURING ?= +USE_ONIGURUMA ?= + +# 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} + +_XUSE_LIBACL := ${USE_LIBACL} +_XUSE_LIBCAP := ${USE_LIBCAP} +_XUSE_LIBURING := ${USE_LIBURING} +_XUSE_ONIGURUMA := ${USE_ONIGURUMA} + +# 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} + +export XUSE_LIBACL=${_XUSE_LIBACL} +export XUSE_LIBCAP=${_XUSE_LIBCAP} +export XUSE_LIBURING=${_XUSE_LIBURING} +export XUSE_ONIGURUMA=${_XUSE_ONIGURUMA} + +# The configuration file generated by `make config` +CONFIG := ${GEN}/config.mk +-include ${CONFIG} + +## Configuration phase (`make config`) + +# External dependencies +PKGS := \ + ${GEN}/libacl.mk \ + ${GEN}/libcap.mk \ + ${GEN}/liburing.mk \ + ${GEN}/oniguruma.mk + +# Makefile fragments generated by `make config` +MKS := \ + ${GEN}/vars.mk \ + ${GEN}/deps.mk \ + ${GEN}/objs.mk \ + ${PKGS} + +# The configuration goal itself +config: ${MKS} + @printf 'include $${GEN}/%s\n' ${MKS:${GEN}/%=%} >${CONFIG} +.PHONY: config + +# Saves the configurable variables +${GEN}/vars.mk:: + @${XMKDIR} ${@D} + @printf 'PREFIX := %s\n' "$$XPREFIX" >$@ + @printf 'MANDIR := %s\n' "$$XMANDIR" >>$@ + @printf 'OS := %s\n' "$$XOS" >>$@ + @printf 'ARCH := %s\n' "$$XARCH" >>$@ + @printf 'CC := %s\n' "$$XCC" >>$@ + @printf 'INSTALL := %s\n' "$$XINSTALL" >>$@ + @printf 'MKDIR := %s\n' "$$XMKDIR" >>$@ + @printf 'RM := %s\n' "$$XRM" >>$@ + @printf 'CPPFLAGS := %s\n' "$$BFS_CPPFLAGS" >>$@ + @test "${TSAN}" != y || printf 'CPPFLAGS += %s\n' "$$TSAN_CPPFLAGS" >>$@ + @test "${LINT}" != y || printf 'CPPFLAGS += %s\n' "$$LINT_CPPFLAGS" >>$@ + @test "${RELEASE}" != y || printf 'CPPFLAGS += %s\n' "$$RELEASE_CPPFLAGS" >>$@ + @test -z "$$XCPPFLAGS" || printf 'CPPFLAGS += %s\n' "$$XCPPFLAGS" >>$@ + @test -z "$$EXTRA_CPPFLAGS" || printf 'CPPFLAGS += %s\n' "$$EXTRA_CPPFLAGS" >>$@ + @printf 'CFLAGS := %s\n' "$$BFS_CFLAGS" >>$@ + @test "${ASAN}" != y || printf 'CFLAGS += %s\n' "$$ASAN_CFLAGS" >>$@ + @test "${LSAN}" != y || printf 'CFLAGS += %s\n' "$$LSAN_CFLAGS" >>$@ + @test "${MSAN}" != y || printf 'CFLAGS += %s\n' "$$MSAN_CFLAGS" >>$@ + @test "${TSAN}" != y || printf 'CFLAGS += %s\n' "$$TSAN_CFLAGS" >>$@ + @test "${UBSAN}" != y || printf 'CFLAGS += %s\n' "$$UBSAN_CFLAGS" >>$@ + @case "${SAN}" in *y*) printf 'CFLAGS += %s\n' "$$SAN_CFLAGS" >>$@ ;; esac + @test "${GCOV}" != y || printf 'CFLAGS += %s\n' "$$GCOV_CFLAGS" >>$@ + @test "${LINT}" != y || printf 'CFLAGS += %s\n' "$$LINT_CFLAGS" >>$@ + @test "${RELEASE}" != y || printf 'CFLAGS += %s\n' "$$RELEASE_CFLAGS" >>$@ + @test -z "$$XCFLAGS" || printf 'CFLAGS += %s\n' "$$XCFLAGS" >>$@ + @test -z "$$EXTRA_CFLAGS" || printf 'CFLAGS += %s\n' "$$EXTRA_CFLAGS" >>$@ + @printf 'LDFLAGS := %s\n' "$$XLDFLAGS" >>$@ + @test -z "$$EXTRA_LDFLAGS" || printf 'LDFLAGS += %s\n' "$$EXTRA_LDFLAGS" >>$@ + @printf 'LDLIBS := %s\n' "$$XLDLIBS" >>$@ + @test -z "$$EXTRA_LDLIBS" || printf 'LDLIBS += %s\n' "$$EXTRA_LDLIBS" >>$@ + @test -z "$$BFS_LDLIBS" || printf 'LDLIBS += %s\n' "$$BFS_LDLIBS" >>$@ + @case "${OS}-${SAN}" in FreeBSD-*y*) printf 'POSTLINK = elfctl -e +noaslr $$@\n' >>$@ ;; esac + @cat $@ + +# Check for dependency generation support +${GEN}/deps.mk:: + @${MKDIR} ${@D} + @if config/cc.sh -MD -MP -MF /dev/null config/empty.c; then \ + echo 'DEPFLAGS = -MD -MP -MF $${@:.o=.d}'; \ + fi 2>$@.log | tee $@ + @printf -- '-include %s\n' ${OBJS:.o=.d} >>$@ + +# Lists file.o: file.c dependencies +${GEN}/objs.mk:: + @${MKDIR} ${@D} + @for obj in ${OBJS:${OBJ}/%.o=%}; do printf '$${OBJ}/%s.o: %s.c\n' "$$obj" "$$obj"; done >$@ + +# Auto-detect dependencies and their build flags +${PKGS}:: + @${MKDIR} ${@D} + @config/pkg.sh ${@:${GEN}/%.mk=%} >$@ 2>$@.log + @cat $@ + +# bfs used to have flag-like targets (`make release`, `make asan ubsan`, etc.). +# Direct users to the new configuration system. +asan lsan msan tsan ubsan gcov lint release:: + @printf 'error: `make %s` is no longer supported. ' $@ >&2 + @printf 'Use `make config %s=y` instead.\n' $$(echo $@ | tr '[a-z]' '[A-Z]') >&2 + @false + +## Build phase (`make`) + +# The main binary +bfs: ${BIN}/bfs +.PHONY: bfs + +# All binaries +BINS := \ + ${BIN}/bfs \ + ${BIN}/tests/mksock \ + ${BIN}/tests/units \ + ${BIN}/tests/xspawnee \ + ${BIN}/tests/xtouch + +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} + +# The main binary +${BIN}/bfs: ${LIBBFS} ${OBJ}/src/main.o + +${BINS}: + @${MKDIR} ${@D} + +${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} + +# Depend on ${CONFIG} to make sure `make config` runs first, and to rebuild when +# the configuration changes +${OBJS}: ${CONFIG} + @${MKDIR} ${@D} + ${CC} ${ALL_CFLAGS} -c ${@:${OBJ}/%.o=%.c} -o $@ + +# Save the version number to this file, but only update VERSION if it changes +${GEN}/NEWVERSION:: + @${MKDIR} ${@D} + @printf '%s\n' '${VERSION}' >$@ + +${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}"' + +# Clean all build products +clean:: + ${RM} -r ${BIN} ${OBJ} + +# Clean everything, including generated files +distclean: clean + ${RM} -r ${GEN} +.PHONY: distclean + +## Test phase (`make check`) + +# Unit test binaries +UTEST_BINS := \ + ${BIN}/tests/units \ + ${BIN}/tests/xspawnee + +# Integration test binaries +ITEST_BINS := \ + ${BIN}/tests/mksock \ + ${BIN}/tests/xtouch + +# Build (but don't run) test binaries +tests: ${UTEST_BINS} ${ITEST_BINS} +.PHONY: tests + +# Run all the tests +check: unit-tests integration-tests +.PHONY: check + +# Run the unit tests +unit-tests: ${UTEST_BINS} + ${BIN}/tests/units +.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 \ + ${LIBBFS} + +${BIN}/tests/xspawnee: \ + ${OBJ}/tests/xspawnee.o + +# The different flag combinations we check +INTEGRATIONS := default dfs ids eds j1 j2 j3 s +INTEGRATION_TESTS := ${INTEGRATIONS:%=check-%} + +# Check just `bfs` +check-default: ${BIN}/bfs ${ITEST_BINS} + +./tests/tests.sh --make="${MAKE}" --bfs="${BIN}/bfs" ${TEST_FLAGS} + +# Check the different search strategies +check-dfs check-ids check-eds: ${BIN}/bfs ${ITEST_BINS} + +./tests/tests.sh --make="${MAKE}" --bfs="${BIN}/bfs -S ${@:check-%=%}" ${TEST_FLAGS} + +# Check various flags +check-j1 check-j2 check-j3 check-s: ${BIN}/bfs ${ITEST_BINS} + +./tests/tests.sh --make="${MAKE}" --bfs="${BIN}/bfs -${@:check-%=%}" ${TEST_FLAGS} + +# Run the integration tests +integration-tests: ${INTEGRATION_TESTS} +.PHONY: integration-tests + +${BIN}/tests/mksock: \ + ${OBJ}/tests/mksock.o \ + ${LIBBFS} + +${BIN}/tests/xtouch: \ + ${OBJ}/tests/xtouch.o \ + ${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}} + +# Test multiple configurations +distcheck: ${DISTCHECKS} +.PHONY: distcheck + +# Per-distcheck configuration +DISTCHECK_CONFIG_asan := ASAN=y UBSAN=y +DISTCHECK_CONFIG_msan := MSAN=y UBSAN=y CC=clang +DISTCHECK_CONFIG_tsan := TSAN=y UBSAN=y CC=clang +DISTCHECK_CONFIG_m32 := EXTRA_CFLAGS="-m32" PKG_CONFIG_PATH=/usr/lib32/pkgconfig USE_LIBURING=n +DISTCHECK_CONFIG_release := RELEASE=y + +${DISTCHECKS}:: + +${MAKE} -rs BUILDDIR=${BUILDDIR}/$@ config ${DISTCHECK_CONFIG_${@:distcheck-%=%}} + +${MAKE} -s BUILDDIR=${BUILDDIR}/$@ check TEST_FLAGS="--sudo --verbose=skipped" + +## Packaging (`make install`) + +DEST_PREFIX := ${DESTDIR}${PREFIX} +DEST_MANDIR := ${DESTDIR}${MANDIR} + +install:: + ${MKDIR} ${DEST_PREFIX}/bin + ${INSTALL} -m755 ${BIN}/bfs ${DEST_PREFIX}/bin/bfs + ${MKDIR} ${DEST_MANDIR}/man1 + ${INSTALL} -m644 docs/bfs.1 ${DEST_MANDIR}/man1/bfs.1 + ${MKDIR} ${DEST_PREFIX}/share/bash-completion/completions + ${INSTALL} -m644 completions/bfs.bash ${DEST_PREFIX}/share/bash-completion/completions/bfs + ${MKDIR} ${DEST_PREFIX}/share/zsh/site-functions + ${INSTALL} -m644 completions/bfs.zsh ${DEST_PREFIX}/share/zsh/site-functions/_bfs + ${MKDIR} ${DEST_PREFIX}/share/fish/vendor_completions.d + ${INSTALL} -m644 completions/bfs.fish ${DEST_PREFIX}/share/fish/vendor_completions.d/bfs.fish + +uninstall:: + ${RM} ${DEST_PREFIX}/share/bash-completion/completions/bfs + ${RM} ${DEST_PREFIX}/share/zsh/site-functions/_bfs + ${RM} ${DEST_PREFIX}/share/fish/vendor_completions.d/bfs.fish |