summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/ci.yml8
-rw-r--r--.github/workflows/codecov.yml2
-rw-r--r--.github/workflows/codeql.yml2
-rw-r--r--Makefile157
-rw-r--r--README.md4
-rw-r--r--bench/bench.sh10
-rwxr-xr-xbuild/cc.sh (renamed from config/cc.sh)0
-rw-r--r--build/config.mk62
-rwxr-xr-xbuild/define-if.sh (renamed from config/cc-define.sh)11
-rw-r--r--build/deps.mk18
-rw-r--r--build/empty.c (renamed from config/empty.c)0
-rw-r--r--build/exports.mk (renamed from config/exports.mk)0
-rw-r--r--build/flags.mk (renamed from config/flags.mk)25
-rw-r--r--build/has/acl-get-entry.c (renamed from config/acl-get-entry.c)0
-rw-r--r--build/has/acl-get-file.c (renamed from config/acl-get-file.c)0
-rw-r--r--build/has/acl-get-tag-type.c (renamed from config/acl-get-tag-type.c)0
-rw-r--r--build/has/acl-is-trivial-np.c (renamed from config/acl-is-trivial-np.c)0
-rw-r--r--build/has/acl-trivial.c (renamed from config/acl-trivial.c)0
-rw-r--r--build/has/aligned-alloc.c (renamed from config/aligned-alloc.c)0
-rw-r--r--build/has/confstr.c (renamed from config/confstr.c)0
-rw-r--r--build/has/extattr-get-file.c (renamed from config/extattr-get-file.c)0
-rw-r--r--build/has/extattr-get-link.c (renamed from config/extattr-get-link.c)0
-rw-r--r--build/has/extattr-list-file.c (renamed from config/extattr-list-file.c)0
-rw-r--r--build/has/extattr-list-link.c (renamed from config/extattr-list-link.c)0
-rw-r--r--build/has/fdclosedir.c (renamed from config/fdclosedir.c)0
-rw-r--r--build/has/getdents.c (renamed from config/getdents.c)0
-rw-r--r--build/has/getdents64-syscall.c (renamed from config/getdents64-syscall.c)0
-rw-r--r--build/has/getdents64.c (renamed from config/getdents64.c)0
-rw-r--r--build/has/getprogname-gnu.c (renamed from config/getprogname-gnu.c)0
-rw-r--r--build/has/getprogname.c (renamed from config/getprogname.c)0
-rw-r--r--build/has/max-align-t.c (renamed from config/max-align-t.c)0
-rw-r--r--build/has/pipe2.c (renamed from config/pipe2.c)0
-rw-r--r--build/has/posix-spawn-addfchdir-np.c (renamed from config/posix-spawn-addfchdir-np.c)0
-rw-r--r--build/has/posix-spawn-addfchdir.c (renamed from config/posix-spawn-addfchdir.c)0
-rw-r--r--build/has/st-acmtim.c (renamed from config/st-acmtim.c)0
-rw-r--r--build/has/st-acmtimespec.c (renamed from config/st-acmtimespec.c)0
-rw-r--r--build/has/st-birthtim.c (renamed from config/st-birthtim.c)0
-rw-r--r--build/has/st-birthtimespec.c (renamed from config/st-birthtimespec.c)0
-rw-r--r--build/has/st-flags.c (renamed from config/st-flags.c)0
-rw-r--r--build/has/statx-syscall.c (renamed from config/statx-syscall.c)0
-rw-r--r--build/has/statx.c (renamed from config/statx.c)0
-rw-r--r--build/has/strerror-l.c (renamed from config/strerror-l.c)0
-rw-r--r--build/has/strerror-r-gnu.c (renamed from config/strerror-r-gnu.c)0
-rw-r--r--build/has/strerror-r-posix.c (renamed from config/strerror-r-posix.c)0
-rw-r--r--build/has/tm-gmtoff.c (renamed from config/tm-gmtoff.c)0
-rw-r--r--build/has/uselocale.c (renamed from config/uselocale.c)0
-rw-r--r--build/header.mk66
-rwxr-xr-xbuild/msg-if.sh21
-rwxr-xr-xbuild/msg.sh62
-rwxr-xr-xbuild/pkgconf.sh (renamed from config/pkgconf.sh)2
-rw-r--r--build/pkgs.mk33
-rw-r--r--build/prelude.mk122
-rw-r--r--build/use/libacl.c (renamed from config/libacl.c)0
-rw-r--r--build/use/libcap.c (renamed from config/libcap.c)0
-rw-r--r--build/use/libselinux.c (renamed from config/libselinux.c)0
-rw-r--r--build/use/liburing.c (renamed from config/liburing.c)0
-rw-r--r--build/use/oniguruma.c (renamed from config/oniguruma.c)0
-rw-r--r--config/config.mk63
-rw-r--r--config/deps.mk18
-rw-r--r--config/header.mk70
-rw-r--r--config/pkgs.mk42
-rw-r--r--config/prelude.mk170
-rwxr-xr-xconfigure137
-rw-r--r--docs/BUILDING.md211
-rw-r--r--docs/CHANGELOG.md30
-rw-r--r--tests/util.sh9
-rw-r--r--tests/xspawn.c15
67 files changed, 798 insertions, 572 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 3890bb0..e47600b 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -107,7 +107,7 @@ jobs:
gmake \
oniguruma
jobs=$(sysctl -n hw.ncpu)
- gmake -j$jobs config
+ ./configure MAKE=gmake
gmake -j$jobs check TEST_FLAGS="--sudo --verbose=skipped"
netbsd:
@@ -133,7 +133,7 @@ jobs:
pkgconf \
tcl-expect
jobs=$(sysctl -n hw.ncpu)
- make -j$jobs config
+ ./configure
make -j$jobs check TEST_FLAGS="--sudo --verbose=skipped"
dragonflybsd:
@@ -165,7 +165,7 @@ jobs:
run: |
chown -R action:action .
jobs=$(sysctl -n hw.ncpu)
- sudo -u action make config
+ sudo -u action ./configure
sudo -u action make -j$jobs check TEST_FLAGS="--sudo --verbose=skipped"
omnios:
@@ -198,5 +198,5 @@ jobs:
PATH="/usr/xpg4/bin:$PATH"
chown -R action:staff .
jobs=$(getconf NPROCESSORS_ONLN)
- sudo -u action gmake config
+ sudo -u action ./configure MAKE=gmake
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 2245f40..6aaace6 100644
--- a/.github/workflows/codecov.yml
+++ b/.github/workflows/codecov.yml
@@ -25,7 +25,7 @@ jobs:
- name: Generate coverage
run: |
- make config GCOV=y
+ ./configure --enable-gcov
make -j$(nproc) check TEST_FLAGS="--sudo"
gcov -abcfpu obj/*/*.o
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index c21fda5..a0b8fe3 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -41,7 +41,7 @@ jobs:
- name: Configure
run: |
- make -j$(nproc) config
+ ./configure
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
diff --git a/Makefile b/Makefile
index 981c322..d89f3b6 100644
--- a/Makefile
+++ b/Makefile
@@ -1,111 +1,99 @@
# 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 most BSD make implementations. To build bfs,
-# run
+# To build bfs, run
#
-# $ make config
+# $ ./configure
# $ make
# Utilities and GNU/BSD portability
-include config/prelude.mk
+include build/prelude.mk
# The default build target
default: bfs
.PHONY: default
# Include the generated build config, if it exists
--include ${CONFIG}
+-include gen/config.mk
-## Configuration phase (`make config`)
-
-# The configuration goal itself
-config::
- @+${MAKE} -sf config/config.mk
+## Configuration phase (`./configure`)
# 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: `%s %s` is no longer supported. ' "${MAKE}" $@ >&2
- @printf 'Use `%s config %s=y` instead.\n' "${MAKE}" $$(echo $@ | tr 'a-z' 'A-Z') >&2
+ @printf 'error: `%s %s` is no longer supported. Use `./configure --enable-%s` instead.\n' \
+ "${MAKE}" $@ $@ >&2
@false
-# Print an error if `make` is run before `make config`
-${CONFIG}::
+# Print an error if `make` is run before `./configure`
+gen/config.mk::
@if ! [ -e $@ ]; then \
- printf 'error: You must run `%s config` before `%s`.\n' "${MAKE}" "${MAKE}" >&2; \
+ printf 'error: You must run `./configure` before `%s`.\n' "${MAKE}" >&2; \
false; \
fi
## Build phase (`make`)
# The main binary
-bfs: ${BIN}/bfs
+bfs: bin/bfs
.PHONY: bfs
# All binaries
BINS := \
- ${BIN}/bfs \
- ${BIN}/tests/mksock \
- ${BIN}/tests/units \
- ${BIN}/tests/xspawnee \
- ${BIN}/tests/xtouch
+ bin/bfs \
+ bin/tests/mksock \
+ bin/tests/units \
+ bin/tests/xspawnee \
+ bin/tests/xtouch
all: ${BINS}
.PHONY: all
-# Group relevant flags together
-ALL_CFLAGS = ${CPPFLAGS} ${CFLAGS} ${DEPFLAGS}
-ALL_LDFLAGS = ${CFLAGS} ${LDFLAGS}
-
# The main binary
-${BIN}/bfs: ${LIBBFS} ${OBJ}/src/main.o
+bin/bfs: ${LIBBFS} obj/src/main.o
${BINS}:
@${MKDIR} ${@D}
- +${MSG} "[ LD ] ${TGT}" ${CC} ${ALL_LDFLAGS} ${.ALLSRC} ${LDLIBS} -o $@
+ +${MSG} "[ LD ] $@" ${CC} ${CFLAGS} ${LDFLAGS} ${.ALLSRC} ${LDLIBS} -o $@
${POSTLINK}
# Get the .c file for a .o file
-_CSRC = ${@:${OBJ}/%.o=%.c}
-CSRC = ${_CSRC:gen/%=${GEN}/%}
+CSRC = ${@:obj/%.o=%.c}
-# Depend on ${CONFIG} to make sure `make config` runs first, and to rebuild when
-# the configuration changes
-${OBJS}: ${CONFIG}
+# Rebuild when the configuration changes
+${OBJS}: gen/config.mk
@${MKDIR} ${@D}
- ${MSG} "[ CC ] ${_CSRC}" ${CC} ${ALL_CFLAGS} -c ${CSRC} -o $@
+ ${MSG} "[ CC ] ${CSRC}" ${CC} ${CPPFLAGS} ${CFLAGS} -c ${CSRC} -o $@
# Save the version number to this file, but only update version.c if it changes
-${GEN}/version.c.new::
+gen/version.c.new::
@${MKDIR} ${@D}
@printf 'const char bfs_version[] = "' >$@
@if [ "$$VERSION" ]; then \
printf '%s' "$$VERSION"; \
- elif test -d .git && command -v git >/dev/null 2>&1; then \
- git describe --always --dirty; \
+ elif test -e src/../.git && command -v git >/dev/null 2>&1; then \
+ git -C src/.. describe --always --dirty; \
else \
- echo "3.1.3"; \
+ echo "3.2"; \
fi | tr -d '\n' >>$@
@printf '";\n' >>$@
-${GEN}/version.c: ${GEN}/version.c.new
+gen/version.c: gen/version.c.new
@test -e $@ && cmp -s $@ ${.ALLSRC} && rm ${.ALLSRC} || mv ${.ALLSRC} $@
-${OBJ}/gen/version.o: ${GEN}/version.c
+obj/gen/version.o: gen/version.c
## Test phase (`make check`)
# Unit test binaries
UTEST_BINS := \
- ${BIN}/tests/units \
- ${BIN}/tests/xspawnee
+ bin/tests/units \
+ bin/tests/xspawnee
# Integration test binaries
ITEST_BINS := \
- ${BIN}/tests/mksock \
- ${BIN}/tests/xtouch
+ bin/tests/mksock \
+ bin/tests/xtouch
# Build (but don't run) test binaries
tests: ${UTEST_BINS} ${ITEST_BINS}
@@ -117,45 +105,45 @@ check: unit-tests integration-tests
# Run the unit tests
unit-tests: ${UTEST_BINS}
- ${MSG} "[TEST] tests/units" ${BIN}/tests/units
+ ${MSG} "[TEST] tests/units" bin/tests/units
.PHONY: unit-tests
-${BIN}/tests/units: \
+bin/tests/units: \
${UNIT_OBJS} \
${LIBBFS}
-${BIN}/tests/xspawnee: \
- ${OBJ}/tests/xspawnee.o
+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}
+check-default: bin/bfs ${ITEST_BINS}
+${MSG} "[TEST] bfs" \
- ./tests/tests.sh --make="${MAKE}" --bfs="${BIN}/bfs" ${TEST_FLAGS}
+ ./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}
+check-dfs check-ids check-eds: bin/bfs ${ITEST_BINS}
+${MSG} "[TEST] bfs -S ${@:check-%=%}" \
- ./tests/tests.sh --make="${MAKE}" --bfs="${BIN}/bfs -S ${@:check-%=%}" ${TEST_FLAGS}
+ ./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}
+check-j1 check-j2 check-j3 check-s: bin/bfs ${ITEST_BINS}
+${MSG} "[TEST] bfs -${@:check-%=%}" \
- ./tests/tests.sh --make="${MAKE}" --bfs="${BIN}/bfs -${@:check-%=%}" ${TEST_FLAGS}
+ ./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 \
+bin/tests/mksock: \
+ obj/tests/mksock.o \
${LIBBFS}
-${BIN}/tests/xtouch: \
- ${OBJ}/tests/xtouch.o \
+bin/tests/xtouch: \
+ obj/tests/xtouch.o \
${LIBBFS}
# `make distcheck` configurations
@@ -176,15 +164,17 @@ distcheck:
.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_asan := --enable-asan --enable-ubsan
+DISTCHECK_CONFIG_msan := --enable-msan --enable-ubsan CC=clang
+DISTCHECK_CONFIG_tsan := --enable-tsan --enable-ubsan CC=clang
DISTCHECK_CONFIG_m32 := EXTRA_CFLAGS="-m32" PKG_CONFIG_LIBDIR=/usr/lib32/pkgconfig
-DISTCHECK_CONFIG_release := RELEASE=y
+DISTCHECK_CONFIG_release := --enable-release
${DISTCHECKS}::
- +${MAKE} -rs BUILDDIR=${BUILDDIR}/$@ config ${DISTCHECK_CONFIG_${@:distcheck-%=%}}
- +${MAKE} -s BUILDDIR=${BUILDDIR}/$@ check TEST_FLAGS="--sudo --verbose=skipped"
+ @${MKDIR} $@
+ @+cd $@ \
+ && ../configure ${DISTCHECK_CONFIG_${@:distcheck-%=%}} \
+ && ${MAKE} -s check TEST_FLAGS="--sudo --verbose=skipped"
## Packaging (`make install`)
@@ -193,44 +183,49 @@ DEST_MANDIR := ${DESTDIR}${MANDIR}
install::
${Q}${MKDIR} ${DEST_PREFIX}/bin
- ${MSG} "[INSTALL] bin/bfs" \
- ${INSTALL} -m755 ${BIN}/bfs ${DEST_PREFIX}/bin/bfs
+ ${MSG} "[INST] bin/bfs" \
+ ${INSTALL} -m755 bin/bfs ${DEST_PREFIX}/bin/bfs
${Q}${MKDIR} ${DEST_MANDIR}/man1
- ${MSG} "[INSTALL] man/man1/bfs.1" \
+ ${MSG} "[INST] man/man1/bfs.1" \
${INSTALL} -m644 docs/bfs.1 ${DEST_MANDIR}/man1/bfs.1
${Q}${MKDIR} ${DEST_PREFIX}/share/bash-completion/completions
- ${MSG} "[INSTALL] completions/bfs.bash" \
+ ${MSG} "[INST] completions/bfs.bash" \
${INSTALL} -m644 completions/bfs.bash ${DEST_PREFIX}/share/bash-completion/completions/bfs
${Q}${MKDIR} ${DEST_PREFIX}/share/zsh/site-functions
- ${MSG} "[INSTALL] completions/bfs.zsh" \
+ ${MSG} "[INST] completions/bfs.zsh" \
${INSTALL} -m644 completions/bfs.zsh ${DEST_PREFIX}/share/zsh/site-functions/_bfs
${Q}${MKDIR} ${DEST_PREFIX}/share/fish/vendor_completions.d
- ${MSG} "[INSTALL] completions/bfs.fish" \
+ ${MSG} "[INST] completions/bfs.fish" \
${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
- ${RM} ${DEST_MANDIR}/man1/bfs.1
- ${RM} ${DEST_PREFIX}/bin/bfs
+ ${MSG} "[ RM ] completions/bfs.bash" \
+ ${RM} ${DEST_PREFIX}/share/bash-completion/completions/bfs
+ ${MSG} "[ RM ] completions/bfs.zsh" \
+ ${RM} ${DEST_PREFIX}/share/zsh/site-functions/_bfs
+ ${MSG} "[ RM ] completions/bfs.fish" \
+ ${RM} ${DEST_PREFIX}/share/fish/vendor_completions.d/bfs.fish
+ ${MSG} "[ RM ] man/man1/bfs.1" \
+ ${RM} ${DEST_MANDIR}/man1/bfs.1
+ ${MSG} "[ RM ] bin/bfs" \
+ ${RM} ${DEST_PREFIX}/bin/bfs
# Check that `make install` works and `make uninstall` removes everything
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
+ +${MAKE} install DESTDIR=pkg
+ +${MAKE} uninstall DESTDIR=pkg
+ bin/bfs pkg -not -type d -print -exit 1
+ ${RM} -r pkg
## Cleanup (`make clean`)
# Clean all build products
clean::
${MSG} "[ RM ] bin obj" \
- ${RM} -r ${BIN} ${OBJ}
+ ${RM} -r bin obj
# Clean everything, including generated files
distclean: clean
${MSG} "[ RM ] gen" \
- ${RM} -r ${GEN} ${DISTCHECKS}
+ ${RM} -r gen ${DISTCHECKS}
.PHONY: distclean
diff --git a/README.md b/README.md
index b95c16b..b9aad82 100644
--- a/README.md
+++ b/README.md
@@ -333,7 +333,7 @@ Once you have the dependencies, you can build <code>bfs</code>.
Download one of the [releases](https://github.com/tavianator/bfs/releases) or clone the [git repo](https://github.com/tavianator/bfs).
Then run
- $ make config
+ $ ./configure
$ make
This will build the `./bin/bfs` binary.
@@ -343,7 +343,7 @@ Run the test suite to make sure it works correctly:
If you're interested in speed, you may want to build the release version instead:
- $ make config RELEASE=y
+ $ ./configure --enable-release
$ make
Finally, if you want to install it globally, run
diff --git a/bench/bench.sh b/bench/bench.sh
index e4b5511..cf1ae49 100644
--- a/bench/bench.sh
+++ b/bench/bench.sh
@@ -221,7 +221,8 @@ setup() {
fi
echo "Building bfs ..."
- as-user make -s -j"$nproc" release all
+ as-user ./configure --enable-release
+ as-user make -s -j"$nproc" all
as-user mkdir -p bench/corpus
@@ -253,7 +254,12 @@ setup() {
echo "Building bfs $commit ..."
cd "$worktree"
as-user git checkout -qd "$commit" --
- as-user make -s -j"$nproc" release
+ if [ -e configure ]; then
+ as-user ./configure --enable-release
+ as-user make -s -j"$nproc"
+ else
+ as-user make -s -j"$nproc" release
+ fi
if [ -e ./bin/bfs ]; then
as-user cp ./bin/bfs "$bin/bfs-$commit"
else
diff --git a/config/cc.sh b/build/cc.sh
index 45d51ca..45d51ca 100755
--- a/config/cc.sh
+++ b/build/cc.sh
diff --git a/build/config.mk b/build/config.mk
new file mode 100644
index 0000000..24873ec
--- /dev/null
+++ b/build/config.mk
@@ -0,0 +1,62 @@
+# Copyright © Tavian Barnes <tavianator@tavianator.com>
+# SPDX-License-Identifier: 0BSD
+
+# Makefile that implements `./configure`
+
+include build/prelude.mk
+include build/exports.mk
+
+# All configuration steps
+config: gen/config.mk gen/config.h
+.PHONY: config
+
+# Makefile fragments generated by `./configure`
+MKS := \
+ gen/vars.mk \
+ gen/flags.mk \
+ gen/deps.mk \
+ gen/pkgs.mk
+
+# The main configuration file, which includes the others
+gen/config.mk: ${MKS}
+ ${MSG} "[ GEN] $@"
+ @printf '# %s\n' "$@" >$@
+ @printf 'include %s\n' ${.ALLSRC} >>$@
+ ${VCAT} gen/config.mk
+.PHONY: gen/config.mk
+
+# Saves the configurable variables
+gen/vars.mk::
+ @${MKDIR} ${@D}
+ ${MSG} "[ GEN] $@"
+ @printf '# %s\n' "$@" >$@
+ @printf 'PREFIX := %s\n' "$$XPREFIX" >>$@
+ @printf 'MANDIR := %s\n' "$$XMANDIR" >>$@
+ @printf 'OS := %s\n' "$${OS:-$$(uname)}" >>$@
+ @printf 'CC := %s\n' "$$XCC" >>$@
+ @printf 'INSTALL := %s\n' "$$XINSTALL" >>$@
+ @printf 'MKDIR := %s\n' "$$XMKDIR" >>$@
+ @printf 'PKG_CONFIG := %s\n' "$$XPKG_CONFIG" >>$@
+ @printf 'RM := %s\n' "$$XRM" >>$@
+ ${VCAT} $@
+
+# Sets the build flags. This depends on vars.mk and uses a recursive make so
+# that the default flags can depend on variables like ${OS}.
+gen/flags.mk: gen/vars.mk
+ @+XMAKEFLAGS="$$MAKEFLAGS" ${MAKE} -sf build/flags.mk $@
+.PHONY: gen/flags.mk
+
+# Check for dependency generation support
+gen/deps.mk: gen/flags.mk
+ @+XMAKEFLAGS="$$MAKEFLAGS" ${MAKE} -sf build/deps.mk $@
+.PHONY: gen/deps.mk
+
+# Auto-detect dependencies and their build flags
+gen/pkgs.mk: gen/flags.mk
+ @+XMAKEFLAGS="$$MAKEFLAGS" ${MAKE} -sf build/pkgs.mk $@
+.PHONY: gen/pkgs.mk
+
+# Compile-time feature detection
+gen/config.h: gen/config.mk
+ @+XMAKEFLAGS="$$MAKEFLAGS" ${MAKE} -sf build/header.mk $@
+.PHONY: gen/config.h
diff --git a/config/cc-define.sh b/build/define-if.sh
index edb5c87..295ead8 100755
--- a/config/cc-define.sh
+++ b/build/define-if.sh
@@ -3,17 +3,18 @@
# Copyright © Tavian Barnes <tavianator@tavianator.com>
# SPDX-License-Identifier: 0BSD
-# Output a C preprocessor definition based on whether a C source file could be
-# compiled successfully
+# Output a C preprocessor definition based on whether a command succeeds
set -eu
-SLUG="${1#config/}"
+SLUG="${1#build/}"
SLUG="${SLUG%.c}"
-MACRO="BFS_HAS_$(printf '%s' "$SLUG" | tr 'a-z-' 'A-Z_')"
+MACRO="BFS_$(printf '%s' "$SLUG" | tr '/a-z-' '_A-Z_')"
+shift
-if config/cc.sh "$1"; then
+if "$@"; then
printf '#define %s true\n' "$MACRO"
else
printf '#define %s false\n' "$MACRO"
+ exit 1
fi
diff --git a/build/deps.mk b/build/deps.mk
new file mode 100644
index 0000000..3db62b6
--- /dev/null
+++ b/build/deps.mk
@@ -0,0 +1,18 @@
+# Copyright © Tavian Barnes <tavianator@tavianator.com>
+# SPDX-License-Identifier: 0BSD
+
+# Makefile that generates gen/deps.mk
+
+include build/prelude.mk
+include gen/vars.mk
+include gen/flags.mk
+include build/exports.mk
+
+gen/deps.mk::
+ ${MSG} "[ GEN] $@"
+ @printf '# %s\n' "$@" >$@
+ @if build/cc.sh -MD -MP -MF /dev/null build/empty.c; then \
+ printf 'CPPFLAGS += -MD -MP\n'; \
+ fi >>$@ 2>$@.log
+ ${VCAT} $@
+ @printf -- '-include %s\n' ${OBJS:.o=.d} >>$@
diff --git a/config/empty.c b/build/empty.c
index 4fa9a5b..4fa9a5b 100644
--- a/config/empty.c
+++ b/build/empty.c
diff --git a/config/exports.mk b/build/exports.mk
index ed19134..ed19134 100644
--- a/config/exports.mk
+++ b/build/exports.mk
diff --git a/config/flags.mk b/build/flags.mk
index ee13f06..c911b22 100644
--- a/config/flags.mk
+++ b/build/flags.mk
@@ -3,8 +3,8 @@
# Makefile that generates gen/flags.mk
-include config/prelude.mk
-include ${GEN}/vars.mk
+include build/prelude.mk
+include gen/vars.mk
# Configurable flags
CPPFLAGS ?=
@@ -29,7 +29,7 @@ export XLDLIBS=${LDLIBS}
# Immutable flags
export BFS_CPPFLAGS= \
-Isrc \
- -I${GEN} \
+ -Igen \
-D__EXTENSIONS__ \
-D_ATFILE_SOURCE \
-D_BSD_SOURCE \
@@ -75,12 +75,13 @@ export TSAN_CFLAGS=${TSAN_CFLAGS,${_TSAN}}
export UBSAN_CFLAGS=${UBSAN_CFLAGS,${_UBSAN}}
SAN_CFLAGS,y := -fno-sanitize-recover=all
-NO_SAN := ${NOR,${_ASAN},${_LSAN},${_MSAN},${_TSAN},${_UBSAN}}
-SAN := ${NOT,${NO_SAN}}
+INSANE := ${NOT,${_ASAN}${_LSAN}${_MSAN}${_TSAN}${_UBSAN}}
+SAN := ${NOT,${INSANE}}
export SAN_CFLAGS=${SAN_CFLAGS,${SAN}}
# MSAN and TSAN both need all code to be instrumented
-NOLIBS ?= ${NOT,${NOR,${_MSAN},${_TSAN}}}
+YESLIBS := ${NOT,${_MSAN}${_TSAN}}
+NOLIBS ?= ${NOT,${YESLIBS}}
export XNOLIBS=${NOLIBS}
# gcov only intercepts fork()/exec() with -std=gnu*
@@ -100,14 +101,14 @@ export RELEASE_CPPFLAGS=${RELEASE_CPPFLAGS,${_RELEASE}}
export RELEASE_CFLAGS=${RELEASE_CFLAGS,${_RELEASE}}
# Set a variable
-SETVAR = printf '%s := %s\n' >>$@
+SETVAR = @printf '%s := %s\n' >>$@
# Append to a variable, if non-empty
-APPEND = append() { test -z "$$2" || printf '%s += %s\n' "$$1" "$$2" >>$@; }; append
+APPEND = @append() { test -z "$$2" || printf '%s += %s\n' "$$1" "$$2" >>$@; }; append
-${GEN}/flags.mk::
- ${MSG} "[ GEN] ${TGT}"
- printf '# %s\n' "${TGT}" >$@
+gen/flags.mk::
+ ${MSG} "[ GEN] $@"
+ @printf '# %s\n' "$@" >$@
${SETVAR} CPPFLAGS "$$BFS_CPPFLAGS"
${APPEND} CPPFLAGS "$$TSAN_CPPFLAGS"
${APPEND} CPPFLAGS "$$LINT_CPPFLAGS"
@@ -131,5 +132,5 @@ ${GEN}/flags.mk::
${APPEND} LDLIBS "$$EXTRA_LDLIBS"
${APPEND} LDLIBS "$$BFS_LDLIBS"
${SETVAR} NOLIBS "$$XNOLIBS"
- test "${OS}-${SAN}" != FreeBSD-y || printf 'POSTLINK = elfctl -e +noaslr $$@\n' >>$@
+ @test "${OS}-${SAN}" != FreeBSD-y || printf 'POSTLINK = elfctl -e +noaslr $$@\n' >>$@
${VCAT} $@
diff --git a/config/acl-get-entry.c b/build/has/acl-get-entry.c
index 3cce771..3cce771 100644
--- a/config/acl-get-entry.c
+++ b/build/has/acl-get-entry.c
diff --git a/config/acl-get-file.c b/build/has/acl-get-file.c
index 89fbf23..89fbf23 100644
--- a/config/acl-get-file.c
+++ b/build/has/acl-get-file.c
diff --git a/config/acl-get-tag-type.c b/build/has/acl-get-tag-type.c
index 2901956..2901956 100644
--- a/config/acl-get-tag-type.c
+++ b/build/has/acl-get-tag-type.c
diff --git a/config/acl-is-trivial-np.c b/build/has/acl-is-trivial-np.c
index 9ca9fc7..9ca9fc7 100644
--- a/config/acl-is-trivial-np.c
+++ b/build/has/acl-is-trivial-np.c
diff --git a/config/acl-trivial.c b/build/has/acl-trivial.c
index 7efc838..7efc838 100644
--- a/config/acl-trivial.c
+++ b/build/has/acl-trivial.c
diff --git a/config/aligned-alloc.c b/build/has/aligned-alloc.c
index 4460038..4460038 100644
--- a/config/aligned-alloc.c
+++ b/build/has/aligned-alloc.c
diff --git a/config/confstr.c b/build/has/confstr.c
index 58280b4..58280b4 100644
--- a/config/confstr.c
+++ b/build/has/confstr.c
diff --git a/config/extattr-get-file.c b/build/has/extattr-get-file.c
index ac9cf96..ac9cf96 100644
--- a/config/extattr-get-file.c
+++ b/build/has/extattr-get-file.c
diff --git a/config/extattr-get-link.c b/build/has/extattr-get-link.c
index c35be5b..c35be5b 100644
--- a/config/extattr-get-link.c
+++ b/build/has/extattr-get-link.c
diff --git a/config/extattr-list-file.c b/build/has/extattr-list-file.c
index e68a8bb..e68a8bb 100644
--- a/config/extattr-list-file.c
+++ b/build/has/extattr-list-file.c
diff --git a/config/extattr-list-link.c b/build/has/extattr-list-link.c
index 49f0ec2..49f0ec2 100644
--- a/config/extattr-list-link.c
+++ b/build/has/extattr-list-link.c
diff --git a/config/fdclosedir.c b/build/has/fdclosedir.c
index f4ad1f5..f4ad1f5 100644
--- a/config/fdclosedir.c
+++ b/build/has/fdclosedir.c
diff --git a/config/getdents.c b/build/has/getdents.c
index d0d4228..d0d4228 100644
--- a/config/getdents.c
+++ b/build/has/getdents.c
diff --git a/config/getdents64-syscall.c b/build/has/getdents64-syscall.c
index 4838c14..4838c14 100644
--- a/config/getdents64-syscall.c
+++ b/build/has/getdents64-syscall.c
diff --git a/config/getdents64.c b/build/has/getdents64.c
index 1abf36d..1abf36d 100644
--- a/config/getdents64.c
+++ b/build/has/getdents64.c
diff --git a/config/getprogname-gnu.c b/build/has/getprogname-gnu.c
index 6b97c5e..6b97c5e 100644
--- a/config/getprogname-gnu.c
+++ b/build/has/getprogname-gnu.c
diff --git a/config/getprogname.c b/build/has/getprogname.c
index 83dc8e8..83dc8e8 100644
--- a/config/getprogname.c
+++ b/build/has/getprogname.c
diff --git a/config/max-align-t.c b/build/has/max-align-t.c
index 96165ce..96165ce 100644
--- a/config/max-align-t.c
+++ b/build/has/max-align-t.c
diff --git a/config/pipe2.c b/build/has/pipe2.c
index 4cb43b5..4cb43b5 100644
--- a/config/pipe2.c
+++ b/build/has/pipe2.c
diff --git a/config/posix-spawn-addfchdir-np.c b/build/has/posix-spawn-addfchdir-np.c
index b870a53..b870a53 100644
--- a/config/posix-spawn-addfchdir-np.c
+++ b/build/has/posix-spawn-addfchdir-np.c
diff --git a/config/posix-spawn-addfchdir.c b/build/has/posix-spawn-addfchdir.c
index c52ff81..c52ff81 100644
--- a/config/posix-spawn-addfchdir.c
+++ b/build/has/posix-spawn-addfchdir.c
diff --git a/config/st-acmtim.c b/build/has/st-acmtim.c
index d687ab0..d687ab0 100644
--- a/config/st-acmtim.c
+++ b/build/has/st-acmtim.c
diff --git a/config/st-acmtimespec.c b/build/has/st-acmtimespec.c
index f747bc0..f747bc0 100644
--- a/config/st-acmtimespec.c
+++ b/build/has/st-acmtimespec.c
diff --git a/config/st-birthtim.c b/build/has/st-birthtim.c
index 4964571..4964571 100644
--- a/config/st-birthtim.c
+++ b/build/has/st-birthtim.c
diff --git a/config/st-birthtimespec.c b/build/has/st-birthtimespec.c
index 91a613f..91a613f 100644
--- a/config/st-birthtimespec.c
+++ b/build/has/st-birthtimespec.c
diff --git a/config/st-flags.c b/build/has/st-flags.c
index b1d0c32..b1d0c32 100644
--- a/config/st-flags.c
+++ b/build/has/st-flags.c
diff --git a/config/statx-syscall.c b/build/has/statx-syscall.c
index 87ec869..87ec869 100644
--- a/config/statx-syscall.c
+++ b/build/has/statx-syscall.c
diff --git a/config/statx.c b/build/has/statx.c
index 65f1674..65f1674 100644
--- a/config/statx.c
+++ b/build/has/statx.c
diff --git a/config/strerror-l.c b/build/has/strerror-l.c
index 3dcc4d7..3dcc4d7 100644
--- a/config/strerror-l.c
+++ b/build/has/strerror-l.c
diff --git a/config/strerror-r-gnu.c b/build/has/strerror-r-gnu.c
index 26ca0ee..26ca0ee 100644
--- a/config/strerror-r-gnu.c
+++ b/build/has/strerror-r-gnu.c
diff --git a/config/strerror-r-posix.c b/build/has/strerror-r-posix.c
index 41b2d30..41b2d30 100644
--- a/config/strerror-r-posix.c
+++ b/build/has/strerror-r-posix.c
diff --git a/config/tm-gmtoff.c b/build/has/tm-gmtoff.c
index 543df48..543df48 100644
--- a/config/tm-gmtoff.c
+++ b/build/has/tm-gmtoff.c
diff --git a/config/uselocale.c b/build/has/uselocale.c
index a712ff8..a712ff8 100644
--- a/config/uselocale.c
+++ b/build/has/uselocale.c
diff --git a/build/header.mk b/build/header.mk
new file mode 100644
index 0000000..a9157ad
--- /dev/null
+++ b/build/header.mk
@@ -0,0 +1,66 @@
+# Copyright © Tavian Barnes <tavianator@tavianator.com>
+# SPDX-License-Identifier: 0BSD
+
+# Makefile that generates gen/config.h
+
+include build/prelude.mk
+include gen/config.mk
+include build/exports.mk
+
+# All header fragments we generate
+HEADERS := \
+ gen/has/acl-get-entry.h \
+ gen/has/acl-get-file.h \
+ gen/has/acl-get-tag-type.h \
+ gen/has/acl-is-trivial-np.h \
+ gen/has/acl-trivial.h \
+ gen/has/aligned-alloc.h \
+ gen/has/confstr.h \
+ gen/has/extattr-get-file.h \
+ gen/has/extattr-get-link.h \
+ gen/has/extattr-list-file.h \
+ gen/has/extattr-list-link.h \
+ gen/has/fdclosedir.h \
+ gen/has/getdents.h \
+ gen/has/getdents64.h \
+ gen/has/getdents64-syscall.h \
+ gen/has/getprogname.h \
+ gen/has/getprogname-gnu.h \
+ gen/has/max-align-t.h \
+ gen/has/pipe2.h \
+ gen/has/posix-spawn-addfchdir.h \
+ gen/has/posix-spawn-addfchdir-np.h \
+ gen/has/st-acmtim.h \
+ gen/has/st-acmtimespec.h \
+ gen/has/st-birthtim.h \
+ gen/has/st-birthtimespec.h \
+ gen/has/st-flags.h \
+ gen/has/statx.h \
+ gen/has/statx-syscall.h \
+ gen/has/strerror-l.h \
+ gen/has/strerror-r-gnu.h \
+ gen/has/strerror-r-posix.h \
+ gen/has/tm-gmtoff.h \
+ gen/has/uselocale.h
+
+# Previously generated by pkgs.mk
+PKG_HEADERS := ${ALL_PKGS:%=gen/use/%.h}
+
+gen/config.h: ${PKG_HEADERS} ${HEADERS}
+ ${MSG} "[ GEN] $@"
+ @printf '// %s\n' "$@" >$@
+ @printf '#ifndef BFS_CONFIG_H\n' >>$@
+ @printf '#define BFS_CONFIG_H\n' >>$@
+ @cat ${.ALLSRC} >>$@
+ @printf '#endif // BFS_CONFIG_H\n' >>$@
+ @cat ${.ALLSRC:%=%.log} >gen/config.log
+ ${VCAT} $@
+.PHONY: gen/config.h
+
+# The short name of the config test
+SLUG = ${@:gen/%.h=%}
+
+${HEADERS}::
+ @${MKDIR} ${@D}
+ @build/define-if.sh ${SLUG} build/cc.sh build/${SLUG}.c >$@ 2>$@.log; \
+ build/msg-if.sh "[ CC ] ${SLUG}.c" test $$? -eq 0
diff --git a/build/msg-if.sh b/build/msg-if.sh
new file mode 100755
index 0000000..8112aea
--- /dev/null
+++ b/build/msg-if.sh
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+# Copyright © Tavian Barnes <tavianator@tavianator.com>
+# SPDX-License-Identifier: 0BSD
+
+# Print a success/failure indicator from a makefile:
+#
+# $ ./configure
+# [ CC ] use/liburing.c ✘
+# [ CC ] use/oniguruma.c ✔
+
+set -eu
+
+MSG="$1"
+shift
+
+if "$@"; then
+ build/msg.sh "$(printf '%-37s ✔' "$MSG")"
+else
+ build/msg.sh "$(printf '%-37s ✘' "$MSG")"
+fi
diff --git a/build/msg.sh b/build/msg.sh
new file mode 100755
index 0000000..a7da31b
--- /dev/null
+++ b/build/msg.sh
@@ -0,0 +1,62 @@
+#!/bin/sh
+
+# Copyright © Tavian Barnes <tavianator@tavianator.com>
+# SPDX-License-Identifier: 0BSD
+
+# Print a message from a makefile:
+#
+# $ make -s
+# $ make
+# [ CC ] src/main.c
+# $ make V=1
+# cc -Isrc -Igen -D...
+
+set -eu
+
+# Get the $MAKEFLAGS from the top-level make invocation
+MFLAGS="${XMAKEFLAGS-${MAKEFLAGS-}}"
+
+# Check if make should be quiet (make -s)
+is_quiet() {
+ # GNU make puts single-letter flags in the first word of $MAKEFLAGS,
+ # without a leading dash
+ case "${MFLAGS%% *}" in
+ -*) : ;;
+ *s*) return 0 ;;
+ esac
+
+ # BSD make puts each flag separately like -r -s -j 48
+ for flag in $MFLAGS; do
+ case "$flag" in
+ # Ignore things like --jobserver-auth
+ --*) continue ;;
+ # Skip variable assignments
+ *=*) break ;;
+ -*s*) return 0 ;;
+ esac
+ done
+
+ return 1
+}
+
+# Check if make should be loud (make V=1)
+is_loud() {
+ test "$XV"
+}
+
+MSG="$1"
+shift
+
+if ! is_quiet && ! is_loud; then
+ printf '%s\n' "$MSG"
+fi
+
+if [ $# -eq 0 ]; then
+ exit
+fi
+
+if is_loud; then
+ printf '%s\n' "$*"
+fi
+
+"$@"
diff --git a/config/pkgconf.sh b/build/pkgconf.sh
index 2fb2f1e..96e4bf1 100755
--- a/config/pkgconf.sh
+++ b/build/pkgconf.sh
@@ -41,7 +41,7 @@ if [ -z "$MODE" ]; then
CFLAGS=$("$0" --cflags "$LIB") || exit 1
LDFLAGS=$("$0" --ldflags "$LIB") || exit 1
LDLIBS=$("$0" --ldlibs "$LIB") || exit 1
- config/cc.sh $CFLAGS $LDFLAGS config/$LIB.c $LDLIBS || exit 1
+ build/cc.sh $CFLAGS $LDFLAGS build/use/$LIB.c $LDLIBS || exit 1
done
fi
diff --git a/build/pkgs.mk b/build/pkgs.mk
new file mode 100644
index 0000000..39b550d
--- /dev/null
+++ b/build/pkgs.mk
@@ -0,0 +1,33 @@
+# Copyright © Tavian Barnes <tavianator@tavianator.com>
+# SPDX-License-Identifier: 0BSD
+
+# Makefile that generates gen/pkgs.mk
+
+include build/prelude.mk
+include gen/vars.mk
+include gen/flags.mk
+include build/exports.mk
+
+HEADERS := ${ALL_PKGS:%=gen/use/%.h}
+
+gen/pkgs.mk: ${HEADERS}
+ ${MSG} "[ GEN] $@"
+ @printf '# %s\n' "$@" >$@
+ @gen() { \
+ printf 'PKGS := %s\n' "$$*"; \
+ printf 'CFLAGS += %s\n' "$$(build/pkgconf.sh --cflags "$$@")"; \
+ printf 'LDFLAGS += %s\n' "$$(build/pkgconf.sh --ldflags "$$@")"; \
+ printf 'LDLIBS := %s $${LDLIBS}\n' "$$(build/pkgconf.sh --ldlibs "$$@")"; \
+ }; \
+ gen $$(grep -l ' true$$' ${.ALLSRC} | sed 's|.*/\(.*\)\.h|\1|') >>$@
+ ${VCAT} $@
+
+.PHONY: gen/pkgs.mk
+
+# Convert gen/use/foo.h to foo
+PKG = ${@:gen/use/%.h=%}
+
+${HEADERS}::
+ @${MKDIR} ${@D}
+ @build/define-if.sh use/${PKG} build/pkgconf.sh ${PKG} >$@ 2>$@.log; \
+ build/msg-if.sh "[ CC ] use/${PKG}.c" test $$? -eq 0;
diff --git a/build/prelude.mk b/build/prelude.mk
new file mode 100644
index 0000000..5be26cb
--- /dev/null
+++ b/build/prelude.mk
@@ -0,0 +1,122 @@
+# Copyright © Tavian Barnes <tavianator@tavianator.com>
+# 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 ?= $^
+
+# Installation paths
+DESTDIR ?=
+PREFIX ?= /usr
+MANDIR ?= ${PREFIX}/share/man
+
+# Configurable executables
+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
+
+# Normalize ${V} to either "y" or ""
+export XV=${TRUTHY,${V}}
+
+# Suppress output unless V=1
+Q, := @
+Q := ${Q,${XV}}
+
+# Show full commands with `make V=1`, otherwise short summaries
+MSG = @build/msg.sh
+
+# cat a file if V=1
+VCAT,y := @cat
+VCAT, := @:
+VCAT := ${VCAT,${XV}}
+
+# All external dependencies
+ALL_PKGS := \
+ libacl \
+ libcap \
+ libselinux \
+ liburing \
+ oniguruma
+
+# List all object files here, as they're needed by both `./configure` 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}
diff --git a/config/libacl.c b/build/use/libacl.c
index de1fe50..de1fe50 100644
--- a/config/libacl.c
+++ b/build/use/libacl.c
diff --git a/config/libcap.c b/build/use/libcap.c
index 58e832c..58e832c 100644
--- a/config/libcap.c
+++ b/build/use/libcap.c
diff --git a/config/libselinux.c b/build/use/libselinux.c
index bca409d..bca409d 100644
--- a/config/libselinux.c
+++ b/build/use/libselinux.c
diff --git a/config/liburing.c b/build/use/liburing.c
index bea499a..bea499a 100644
--- a/config/liburing.c
+++ b/build/use/liburing.c
diff --git a/config/oniguruma.c b/build/use/oniguruma.c
index cb17596..cb17596 100644
--- a/config/oniguruma.c
+++ b/build/use/oniguruma.c
diff --git a/config/config.mk b/config/config.mk
deleted file mode 100644
index 280c6ac..0000000
--- a/config/config.mk
+++ /dev/null
@@ -1,63 +0,0 @@
-# Copyright © Tavian Barnes <tavianator@tavianator.com>
-# SPDX-License-Identifier: 0BSD
-
-# Makefile fragment that implements `make config`
-
-include config/prelude.mk
-include config/exports.mk
-
-# All configuration steps
-config: ${CONFIG} ${GEN}/config.h
-.PHONY: config
-
-# Makefile fragments generated by `make config`
-MKS := \
- ${GEN}/vars.mk \
- ${GEN}/flags.mk \
- ${GEN}/deps.mk \
- ${GEN}/pkgs.mk
-
-# The main configuration file, which includes the others
-${CONFIG}: ${MKS}
- ${MSG} "[ GEN] ${TGT}"
- @printf '# %s\n' "${TGT}" >$@
- @printf 'include $${GEN}/%s\n' ${MKS:${GEN}/%=%} >>$@
- ${VCAT} ${CONFIG}
-.PHONY: ${CONFIG}
-
-# Saves the configurable variables
-${GEN}/vars.mk::
- @${MKDIR} ${@D}
- ${MSG} "[ GEN] ${TGT}"
- @printf '# %s\n' "${TGT}" >$@
- @printf 'PREFIX := %s\n' "$$XPREFIX" >>$@
- @printf 'MANDIR := %s\n' "$$XMANDIR" >>$@
- @printf 'OS := %s\n' "$${OS:-$$(uname)}" >>$@
- @printf 'CC := %s\n' "$$XCC" >>$@
- @printf 'INSTALL := %s\n' "$$XINSTALL" >>$@
- @printf 'MKDIR := %s\n' "$$XMKDIR" >>$@
- @printf 'PKG_CONFIG := %s\n' "$$XPKG_CONFIG" >>$@
- @printf 'RM := %s\n' "$$XRM" >>$@
- @printf 'PKGS :=\n' >>$@
- ${VCAT} $@
-
-# Sets the build flags. This depends on vars.mk and uses a recursive make so
-# that the default flags can depend on variables like ${OS}.
-${GEN}/flags.mk: ${GEN}/vars.mk
- @+${MAKE} -sf config/flags.mk $@
-.PHONY: ${GEN}/flags.mk
-
-# Check for dependency generation support
-${GEN}/deps.mk: ${GEN}/flags.mk
- @+${MAKE} -sf config/deps.mk $@
-.PHONY: ${GEN}/deps.mk
-
-# Auto-detect dependencies and their build flags
-${GEN}/pkgs.mk: ${GEN}/flags.mk
- @+${MAKE} -sf config/pkgs.mk $@
-.PHONY: ${GEN}/pkgs.mk
-
-# Compile-time feature detection
-${GEN}/config.h: ${CONFIG}
- @+${MAKE} -sf config/header.mk $@
-.PHONY: ${GEN}/config.h
diff --git a/config/deps.mk b/config/deps.mk
deleted file mode 100644
index ac394a5..0000000
--- a/config/deps.mk
+++ /dev/null
@@ -1,18 +0,0 @@
-# Copyright © Tavian Barnes <tavianator@tavianator.com>
-# SPDX-License-Identifier: 0BSD
-
-# Makefile that generates gen/deps.mk
-
-include config/prelude.mk
-include ${GEN}/vars.mk
-include ${GEN}/flags.mk
-include config/exports.mk
-
-${GEN}/deps.mk::
- ${MSG} "[ GEN] ${TGT}"
- printf '# %s\n' "${TGT}" >$@
- if config/cc.sh -MD -MP -MF /dev/null config/empty.c; then \
- printf 'DEPFLAGS = -MD -MP\n'; \
- fi >>$@ 2>$@.log
- ${VCAT} $@
- printf -- '-include %s\n' ${OBJS:.o=.d} >>$@
diff --git a/config/header.mk b/config/header.mk
deleted file mode 100644
index ccc36d3..0000000
--- a/config/header.mk
+++ /dev/null
@@ -1,70 +0,0 @@
-# Copyright © Tavian Barnes <tavianator@tavianator.com>
-# SPDX-License-Identifier: 0BSD
-
-# Makefile that generates gen/config.h
-
-include config/prelude.mk
-include ${GEN}/config.mk
-include config/exports.mk
-
-# All header fragments we generate
-HEADERS := \
- ${GEN}/acl-get-entry.h \
- ${GEN}/acl-get-file.h \
- ${GEN}/acl-get-tag-type.h \
- ${GEN}/acl-is-trivial-np.h \
- ${GEN}/acl-trivial.h \
- ${GEN}/aligned-alloc.h \
- ${GEN}/confstr.h \
- ${GEN}/extattr-get-file.h \
- ${GEN}/extattr-get-link.h \
- ${GEN}/extattr-list-file.h \
- ${GEN}/extattr-list-link.h \
- ${GEN}/fdclosedir.h \
- ${GEN}/getdents.h \
- ${GEN}/getdents64.h \
- ${GEN}/getdents64-syscall.h \
- ${GEN}/getprogname.h \
- ${GEN}/getprogname-gnu.h \
- ${GEN}/max-align-t.h \
- ${GEN}/pipe2.h \
- ${GEN}/posix-spawn-addfchdir.h \
- ${GEN}/posix-spawn-addfchdir-np.h \
- ${GEN}/st-acmtim.h \
- ${GEN}/st-acmtimespec.h \
- ${GEN}/st-birthtim.h \
- ${GEN}/st-birthtimespec.h \
- ${GEN}/st-flags.h \
- ${GEN}/statx.h \
- ${GEN}/statx-syscall.h \
- ${GEN}/strerror-l.h \
- ${GEN}/strerror-r-gnu.h \
- ${GEN}/strerror-r-posix.h \
- ${GEN}/tm-gmtoff.h \
- ${GEN}/uselocale.h
-
-${GEN}/config.h: ${HEADERS}
- ${MSG} "[ GEN] ${TGT}"
- printf '// %s\n' "${TGT}" >$@
- printf '#ifndef BFS_CONFIG_H\n' >>$@
- printf '#define BFS_CONFIG_H\n' >>$@
- printf '#define BFS_USE_%s true\n' $$(printf '%s\n' ${PKGS} | tr 'a-z-' 'A-Z_') >>$@
- cat ${.ALLSRC} >>$@
- printf '#endif // BFS_CONFIG_H\n' >>$@
- cat ${.ALLSRC:%=%.log} >$@.log
- ${RM} ${.ALLSRC} ${.ALLSRC:%=%.log}
- ${VCAT} $@
-.PHONY: ${GEN}/config.h
-
-# The C source file to attempt to compile
-CSRC = ${@:${GEN}/%.h=config/%.c}
-
-${HEADERS}::
- config/cc-define.sh ${CSRC} >$@ 2>$@.log
- if ! [ "${IS_V}" ]; then \
- if grep -q 'true$$' $@; then \
- printf '[ CC ] %-${MSG_WIDTH}s ✔\n' ${CSRC}; \
- else \
- printf '[ CC ] %-${MSG_WIDTH}s ✘\n' ${CSRC}; \
- fi; \
- fi
diff --git a/config/pkgs.mk b/config/pkgs.mk
deleted file mode 100644
index 2c100ab..0000000
--- a/config/pkgs.mk
+++ /dev/null
@@ -1,42 +0,0 @@
-# Copyright © Tavian Barnes <tavianator@tavianator.com>
-# SPDX-License-Identifier: 0BSD
-
-# Makefile that generates gen/pkgs.mk
-
-include config/prelude.mk
-include ${GEN}/vars.mk
-include ${GEN}/flags.mk
-include config/exports.mk
-
-# External dependencies
-USE_PKGS := \
- ${GEN}/libacl.use \
- ${GEN}/libcap.use \
- ${GEN}/libselinux.use \
- ${GEN}/liburing.use \
- ${GEN}/oniguruma.use
-
-${GEN}/pkgs.mk: ${USE_PKGS}
- ${MSG} "[ GEN] ${TGT}"
- printf '# %s\n' "${TGT}" >$@
- gen() { \
- printf 'PKGS := %s\n' "$$*"; \
- printf 'CFLAGS += %s\n' "$$(config/pkgconf.sh --cflags "$$@")"; \
- printf 'LDFLAGS += %s\n' "$$(config/pkgconf.sh --ldflags "$$@")"; \
- printf 'LDLIBS := %s $${LDLIBS}\n' "$$(config/pkgconf.sh --ldlibs "$$@")"; \
- }; \
- gen $$(cat ${.ALLSRC}) >>$@
- ${VCAT} $@
-.PHONY: ${GEN}/pkgs.mk
-
-# Convert ${GEN}/foo.use to foo
-PKG = ${@:${GEN}/%.use=%}
-
-${USE_PKGS}::
- if config/pkgconf.sh ${PKG} 2>$@.log; then \
- printf '%s\n' ${PKG} >$@; \
- test "${IS_V}" || printf '[ CC ] %-${MSG_WIDTH}s ✔\n' config/${PKG}.c; \
- else \
- : >$@; \
- test "${IS_V}" || printf '[ CC ] %-${MSG_WIDTH}s ✘\n' config/${PKG}.c; \
- fi
diff --git a/config/prelude.mk b/config/prelude.mk
deleted file mode 100644
index e1e7a4d..0000000
--- a/config/prelude.mk
+++ /dev/null
@@ -1,170 +0,0 @@
-# Copyright © Tavian Barnes <tavianator@tavianator.com>
-# 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}
diff --git a/configure b/configure
new file mode 100755
index 0000000..d42dec3
--- /dev/null
+++ b/configure
@@ -0,0 +1,137 @@
+#!/bin/sh
+
+# Copyright © Tavian Barnes <tavianator@tavianator.com>
+# SPDX-License-Identifier: 0BSD
+
+# bfs build configuration script
+
+set -eu
+
+# Default to `make`
+MAKE="${MAKE:-make}"
+
+# Pass -j$(nproc) unless MAKEFLAGS is set
+if [ "${MAKEFLAGS+y}" ]; then
+ j=""
+else
+ j="-j$({ nproc || sysctl -n hw.ncpu || getconf _NPROCESSORS_ONLN || echo 1; } 2>/dev/null)"
+fi
+
+for arg; do
+ case "$arg" in
+ -h|--help)
+ cat <<EOF
+Usage:
+
+ \$ $0 [--enable-*|--disable-*] [CC=...] [CFLAGS=...] [...]
+ \$ $MAKE $j
+
+Variables set in the environment or on the command line will be picked up:
+
+ MAKE
+ The make implementation to use
+ CC
+ The C compiler to use
+
+ CPPFLAGS="-I... -D..."
+ CFLAGS="-W... -f..."
+ LDFLAGS="-L... -Wl,..."
+ Preprocessor/compiler/linker flags
+
+ LDLIBS="-l... -l..."
+ Dynamic libraries to link
+
+ EXTRA_{CPPFLAGS,CFLAGS,LDFLAGS,LDLIBS}
+ Adds to the default flags, instead of replacing them
+
+The default flags result in a plain debug build. Other build profiles include:
+
+ --enable-release
+ Enable optimizations, disable assertions
+ --enable-{asan,lsan,msan,tsan,ubsan}
+ Enable sanitizers
+ --enable-gcov
+ Enable code coverage instrumentation
+
+External dependencies are auto-detected by default, but you can --enable or
+--disable them manually:
+
+ --enable-libacl --disable-libacl
+ --enable-libcap --disable-libcap
+ --enable-libselinux --disable-libselinux
+ --enable-liburing --disable-liburing
+ --enable-oniguruma --disable-oniguruma
+
+Packaging:
+
+ --prefix=/path
+ Set the installation prefix (default: /usr)
+
+This script is a thin wrapper around a makefile-based configuration system.
+Any other arguments will be passed directly to the $MAKE invocation, e.g.
+
+ \$ $0 $j V=1
+EOF
+ exit 0
+ ;;
+
+ --enable-*|--disable-*)
+ case "$arg" in
+ --enable-*) yn=y ;;
+ --disable-*) yn=n ;;
+ esac
+
+ name="${arg#--*able-}"
+ NAME=$(printf '%s' "$name" | tr 'a-z-' 'A-Z_')
+ case "$name" in
+ libacl|libcap|libselinux|liburing|oniguruma)
+ shift
+ set -- "$@" "USE_$NAME=$yn"
+ ;;
+ release|asan|lsan|msan|tsan|ubsan|lint|gcov)
+ shift
+ set -- "$@" "$NAME=$yn"
+ ;;
+ *)
+ printf 'error: Unrecognized option "%s"\n\n' "$arg" >&2
+ printf 'Run %s --help for more information.\n' "$0" >&2
+ exit 1
+ ;;
+ esac
+ ;;
+
+ --prefix=*)
+ shift
+ set -- "$@" "PREFIX=${arg#*=}"
+ ;;
+
+ MAKE=*)
+ MAKE="${arg#*=}"
+ shift
+ ;;
+
+ # make flag (-j2) or variable (CC=clang)
+ -*|*=*)
+ continue
+ ;;
+
+ *)
+ printf 'error: Unrecognized option "%s"\n\n' "$arg" >&2
+ printf 'Run %s --help for more information.\n' "$0" >&2
+ exit 1
+ ;;
+ esac
+done
+
+# Get the relative path to the source tree based on how the script was run
+DIR=$(dirname -- "$0")
+
+# Set up symbolic links for out-of-tree builds
+for f in Makefile build completions docs src tests; do
+ test -e "$f" || ln -s "$DIR/$f" "$f"
+done
+
+# Set MAKEFLAGS to -j$(nproc) if it's unset
+export MAKEFLAGS="${MAKEFLAGS-$j}"
+
+$MAKE -rf build/config.mk "$@"
diff --git a/docs/BUILDING.md b/docs/BUILDING.md
index 4ed139c..cb33c51 100644
--- a/docs/BUILDING.md
+++ b/docs/BUILDING.md
@@ -1,110 +1,157 @@
Building `bfs`
==============
-Compiling
----------
-
-`bfs` uses [GNU Make](https://www.gnu.org/software/make/) as its build system.
A simple invocation of
- $ make config
+ $ ./configure
$ make
should build `bfs` successfully.
-As usual with `make`, you can run a [parallel build](https://www.gnu.org/software/make/manual/html_node/Parallel.html) with `-j`.
-For example, to use all your cores, run `make -j$(nproc)`.
-### Targets
-| Command | Description |
-|------------------|---------------------------------------------------------------|
-| `make config` | Configures the build system |
-| `make` | Builds just the `bfs` binary |
-| `make all` | Builds everything, including the tests (but doesn't run them) |
-| `make check` | Builds everything, and runs the tests |
-| `make install` | Installs `bfs` (with man page, shell completions, etc.) |
-| `make uninstall` | Uninstalls `bfs` |
-| `make clean` | Delete the build products |
-| `make distclean` | Delete all generated files, including the build configuration |
+Configuration
+-------------
+
+```console
+$ ./configure --help
+Usage:
+
+ $ ./configure [--enable-*|--disable-*] [CC=...] [CFLAGS=...] [...]
+ $ make
+
+...
+```
+
+### Variables
+
+Variables set in the environment or on the command line will be picked up:
+These variables specify binaries to run during the configuration and build process:
+
+<pre>
+<b>MAKE</b>=<i>make</i>
+ <a href="https://en.wikipedia.org/wiki/Make_(software)">make</a> implementation
+<b>CC</b>=<i>cc</i>
+ C compiler
+<b>INSTALL</b>=<i>install</i>
+ Copy files during <i>make install</i>
+<b>MKDIR</b>="<i>mkdir -p</i>"
+ Create directories
+<b>PKG_CONFIG</b>=<i>pkg-config</i>
+ Detect external libraries and required build flags
+<b>RM</b>="<i>rm -f</i>"
+ Delete files
+</pre>
+
+These flags will be used by the build process:
+
+<pre>
+<b>CPPFLAGS</b>="<i>-I... -D...</i>"
+<b>CFLAGS</b>="<i>-W... -f...</i>"
+<b>LDFLAGS</b>="<i>-L... -Wl,...</i>"
+ Preprocessor/compiler/linker flags
+
+<b>LDLIBS</b>="<i>-l... -l...</i>"
+ Dynamic libraries to link
+
+<b>EXTRA_</b>{<b>CPPFLAGS</b>,<b>CFLAGS</b>,<b>LDFLAGS</b>,<b>LDLIBS</b>}="<i>...</i>"
+ Adds to the default flags, instead of replacing them
+</pre>
### Build profiles
-The configuration system provides a few shorthand flags for handy configurations:
-
-| Command | Description |
-|-------------------------|-------------------------------------------------------------|
-| `make config RELEASE=y` | Build `bfs` with optimizations, LTO, and without assertions |
-| `make config ASAN=y` | Enable [AddressSanitizer] |
-| `make config LSAN=y` | Enable [LeakSanitizer] |
-| `make config MSAN=y` | Enable [MemorySanitizer] |
-| `make config TSAN=y` | Enable [ThreadSanitizer] |
-| `make config UBSAN=y` | Enable [UndefinedBehaviorSanitizer] |
-| `make config GCOV=y` | Enable [code coverage] |
-
-[AddressSanitizer]: https://github.com/google/sanitizers/wiki/AddressSanitizer
-[LeakSanitizer]: https://github.com/google/sanitizers/wiki/AddressSanitizerLeakSanitizer#stand-alone-mode
-[MemorySanitizer]: https://github.com/google/sanitizers/wiki/MemorySanitizer
-[ThreadSanitizer]: https://github.com/google/sanitizers/wiki/ThreadSanitizerCppManual
-[UndefinedBehaviorSanitizer]: https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html
-[code coverage]: https://gcc.gnu.org/onlinedocs/gcc/Gcov.html
-
-You can combine multiple profiles (e.g. `make config ASAN=y UBSAN=y`), but not all of them will work together.
-
-### Flags
-
-Other flags can be specified on the `make config` command line or in the environment.
-Here are some of the common ones; check the [`Makefile`](/Makefile) for more.
-
-| Flag | Description |
-|-------------------------------------|----------------------------------------------------|
-| `CC` | The C compiler to use, e.g. `make config CC=clang` |
-| `CFLAGS`<br>`EXTRA_CFLAGS` | Override/add to the default compiler flags |
-| `LDFLAGS`<br>`EXTRA_LDFLAGS` | Override/add to the linker flags |
-| `USE_LIBACL`<br>`USE_LIBCAP`<br>... | Enable/disable [optional dependencies] |
-| `TEST_FLAGS` | `tests.sh` flags for `make check` |
-| `BUILDDIR` | The build output directory (default: `.`) |
-| `DESTDIR` | The root directory for `make install` |
-| `PREFIX` | The installation prefix (default: `/usr`) |
-| `MANDIR` | The man page installation directory |
-
-[optional dependencies]: #dependencies
+The default flags result in a plain debug build.
+Other build profiles can be enabled:
+
+<pre>
+--enable-release
+ Enable optimizations, disable assertions
+
+--enable-<a href="https://github.com/google/sanitizers/wiki/AddressSanitizer">asan</a>
+--enable-<a href="https://github.com/google/sanitizers/wiki/AddressSanitizerLeakSanitizer#stand-alone-mode">lsan</a>
+--enable-<a href="https://github.com/google/sanitizers/wiki/MemorySanitizer">msan</a>
+--enable-<a href="https://github.com/google/sanitizers/wiki/ThreadSanitizerCppManual">tsan</a>
+--enable-<a href="https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html">ubsan</a>
+ Enable sanitizers
+
+--enable-<a href="https://gcc.gnu.org/onlinedocs/gcc/gcov/introduction-to-gcov.html">gcov</a>
+ Enable code coverage instrumentation
+</pre>
+
+You can combine multiple profiles (e.g. `./configure --enable-asan --enable-ubsan`), but not all of them will work together.
### Dependencies
`bfs` depends on some system libraries for some of its features.
-These dependencies are optional, and can be turned off in `make config` if necessary by setting the appropriate variable to `n` (e.g. `make config USE_ONIGURUMA=n`).
+External dependencies are auto-detected by default, but you can `--enable` or `--disable` them manually:
-| Dependency | Platforms | `make config` flag |
-|--------------|------------|--------------------|
-| [libacl] | Linux only | `USE_LIBACL` |
-| [libcap] | Linux only | `USE_LIBCAP` |
-| [liburing] | Linux only | `USE_LIBURING` |
-| [libselinux] | Linux only | `USE_LIBSELINUX` |
-| [Oniguruma] | All | `USE_ONIGURUMA` |
+<pre>
+--enable-<a href="https://savannah.nongnu.org/projects/acl">libacl</a> --disable-libacl
+--enable-<a href="https://sites.google.com/site/fullycapable/">libcap</a> --disable-libcap
+--enable-<a href="https://github.com/SELinuxProject/selinux">libselinux</a> --disable-libselinux
+--enable-<a href="https://github.com/axboe/liburing">liburing</a> --disable-liburing
+--enable-<a href="https://github.com/kkos/oniguruma">oniguruma</a> --disable-oniguruma
+</pre>
-[libacl]: https://savannah.nongnu.org/projects/acl
-[libcap]: https://sites.google.com/site/fullycapable/
-[libselinux]: https://github.com/SELinuxProject/selinux
-[liburing]: https://github.com/axboe/liburing
-[Oniguruma]: https://github.com/kkos/oniguruma
+[`pkg-config`] is used, if available, to detect these libraries and any additional build flags they may require.
+If this is undesireable, disable it by setting `PKG_CONFIG` to the empty string (`./configure PKG_CONFIG=""`).
-### Dependency tracking
+[`pkg-config`]: https://www.freedesktop.org/wiki/Software/pkg-config/
-The build system automatically tracks header dependencies with the `-M` family of compiler options (see `DEPFLAGS` in the [`Makefile`](/Makefile)).
-So if you edit a header file, `make` will rebuild the necessary object files ensuring they don't go out of sync.
+### Out-of-tree builds
-We also add a dependency on the current configuration, so you can change configurations and rebuild without having to `make clean`.
+You can set up an out-of-tree build by running the `configure` script from another directory, for example:
-We go one step further than most build systems by tracking the flags that were used for the previous compilation.
-That means you can change configurations without having to `make clean`.
-For example,
-
- $ make config
- $ make
- $ make config RELEASE=y
+ $ mkdir out
+ $ cd out
+ $ ../configure
$ make
-will build the project in debug mode and then rebuild it in release mode.
+
+Building
+--------
+
+### Targets
+
+The [`Makefile`](/Makefile) supports several different build targets:
+
+<pre>
+make
+ The default target; builds just the <i>bfs</i> binary
+make <b>all</b>
+ Builds everything, including the tests (but doesn't run them)
+
+make <b>check</b>
+ Builds everything, and runs all tests
+make <b>unit-tests</b>
+ Builds and runs the unit tests
+make <b>integration-tests</b>
+ Builds and runs the integration tests
+make <b>distcheck</b>
+ Builds and runs the tests in multiple different configurations
+
+make <b>install</b>
+ Installs bfs globally
+make <b>uninstall</b>
+ Uninstalls bfs
+
+make <b>clean</b>
+ Deletes all built files
+make <b>distclean</b>
+ Also deletes files generated by ./configure
+</pre>
+
+
+Troubleshooting
+---------------
+
+If the build fails or behaves unexpectedly, start by enabling verbose mode:
+
+ $ ./configure V=1
+ $ make V=1
+
+This will print the generated configuration and the exact commands that are executed.
+
+You can also check the file `gen/config.log`, which contains any errors from commands run during the configuration phase.
Testing
diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md
index 672c2b4..62b6480 100644
--- a/docs/CHANGELOG.md
+++ b/docs/CHANGELOG.md
@@ -1,6 +1,36 @@
3.*
===
+3.2
+---
+
+**May 2, 2024**
+
+### New features
+
+- New `-limit N` action that quits immediately after `N` results
+
+- Implemented `-context` (from GNU find) for matching SELinux contexts ([#27](https://github.com/tavianator/bfs/issues/27))
+
+- Implemented `-printf %Z` for printing SELinux contexts
+
+### Changes
+
+- The build system has been rewritten, and there is now a configure step:
+
+ $ ./configure
+ $ make
+
+ See `./configure --help` or [docs/BUILDING.md](/docs/BUILDING.md) for more details.
+
+- Improved platform support
+ - Implemented `-acl` on Solaris/Illumos
+ - Implemented `-xattr` on DragonFly BSD
+
+### Bug fixes
+
+- Fixed some rarely-used code paths that clean up after allocation failures
+
3.1.3
-----
diff --git a/tests/util.sh b/tests/util.sh
index 7dba9fb..3969db5 100644
--- a/tests/util.sh
+++ b/tests/util.sh
@@ -12,12 +12,9 @@ _realpath() (
)
# Globals
-TESTS=$(_realpath "$TESTS")
-if [ "${BUILDDIR-}" ]; then
- BIN=$(_realpath "$BUILDDIR/bin")
-else
- BIN=$(_realpath "$TESTS/../bin")
-fi
+ROOT=$(_realpath "$(dirname -- "$TESTS")")
+TESTS="$ROOT/tests"
+BIN="$ROOT/bin"
MKSOCK="$BIN/tests/mksock"
XTOUCH="$BIN/tests/xtouch"
UNAME=$(uname)
diff --git a/tests/xspawn.c b/tests/xspawn.c
index 7362aa5..785ea48 100644
--- a/tests/xspawn.c
+++ b/tests/xspawn.c
@@ -64,27 +64,20 @@ static bool check_use_path(bool use_posix) {
spawn.flags &= ~BFS_SPAWN_USE_POSIX;
}
- const char *builddir = getenv("BUILDDIR");
- dchar *bin = dstrprintf("%s/bin", builddir ? builddir : ".");
- ret &= bfs_pcheck(bin, "dstrprintf()");
- if (!ret) {
- goto destroy;
- }
-
- ret &= bfs_pcheck(bfs_spawn_addopen(&spawn, 10, bin, O_RDONLY | O_DIRECTORY, 0) == 0);
+ ret &= bfs_pcheck(bfs_spawn_addopen(&spawn, 10, "bin", O_RDONLY | O_DIRECTORY, 0) == 0);
ret &= bfs_pcheck(bfs_spawn_adddup2(&spawn, 10, 11) == 0);
ret &= bfs_pcheck(bfs_spawn_addclose(&spawn, 10) == 0);
ret &= bfs_pcheck(bfs_spawn_addfchdir(&spawn, 11) == 0);
ret &= bfs_pcheck(bfs_spawn_addclose(&spawn, 11) == 0);
if (!ret) {
- goto bin;
+ goto destroy;
}
// Check that $PATH is resolved in the parent's environment
char **envp;
ret &= bfs_pcheck(envp = envdup());
if (!ret) {
- goto bin;
+ goto destroy;
}
// Check that $PATH is resolved after the file actions
@@ -138,8 +131,6 @@ env:
free(*var);
}
free(envp);
-bin:
- dstrfree(bin);
destroy:
ret &= bfs_pcheck(bfs_spawn_destroy(&spawn) == 0);
out: