summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@tavianator.com>2024-04-08 11:27:11 -0400
committerTavian Barnes <tavianator@tavianator.com>2024-04-09 17:15:23 -0400
commitc31577d102d87455f3f12086be4c0e2159fa5d35 (patch)
tree864c7c199e5b846dcf497de8b667d6c6f8c550b9
parent5e0b721d0d929223e4308406480a1f1ca9e3f4dc (diff)
build: Add a separate configuration step
-rw-r--r--.github/workflows/ci.yml23
-rw-r--r--.github/workflows/codecov.yml3
-rw-r--r--.github/workflows/codeql.yml1
-rw-r--r--.gitignore2
-rw-r--r--GNUmakefile372
-rw-r--r--Makefile517
-rw-r--r--README.md4
-rwxr-xr-xconfig/cc.sh12
-rw-r--r--config/empty.c6
-rw-r--r--config/libacl.c6
-rw-r--r--config/libcap.c6
-rw-r--r--config/liburing.c6
-rw-r--r--config/oniguruma.c6
-rwxr-xr-xconfig/pkg.sh44
-rwxr-xr-xconfig/pkgconf.sh64
-rw-r--r--docs/BUILDING.md83
-rw-r--r--docs/USAGE.md2
-rw-r--r--src/config.h11
-rw-r--r--src/main.c1
-rw-r--r--src/parse.c2
-rw-r--r--src/version.c6
-rw-r--r--tests/run.sh2
-rw-r--r--tests/tests.mk10
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
diff --git a/.gitignore b/.gitignore
index 4ded7c4..84e47fc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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