diff options
-rw-r--r-- | .github/workflows/ci.yml | 43 | ||||
-rw-r--r-- | .github/workflows/codecov.yml | 2 | ||||
-rw-r--r-- | .github/workflows/codeql.yml | 2 | ||||
-rw-r--r-- | Makefile | 167 | ||||
-rw-r--r-- | README.md | 4 | ||||
-rw-r--r-- | bench/bench.sh | 7 | ||||
-rwxr-xr-x | build/cc.sh (renamed from config/cc.sh) | 0 | ||||
-rw-r--r-- | build/config.mk | 62 | ||||
-rwxr-xr-x | build/define-if.sh (renamed from config/define-if.sh) | 2 | ||||
-rw-r--r-- | build/deps.mk | 18 | ||||
-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) | 18 | ||||
-rw-r--r-- | build/has/acl-get-entry.c (renamed from config/has/acl-get-entry.c) | 0 | ||||
-rw-r--r-- | build/has/acl-get-file.c (renamed from config/has/acl-get-file.c) | 0 | ||||
-rw-r--r-- | build/has/acl-get-tag-type.c (renamed from config/has/acl-get-tag-type.c) | 0 | ||||
-rw-r--r-- | build/has/acl-is-trivial-np.c (renamed from config/has/acl-is-trivial-np.c) | 0 | ||||
-rw-r--r-- | build/has/acl-trivial.c (renamed from config/has/acl-trivial.c) | 0 | ||||
-rw-r--r-- | build/has/aligned-alloc.c (renamed from config/has/aligned-alloc.c) | 0 | ||||
-rw-r--r-- | build/has/confstr.c (renamed from config/has/confstr.c) | 0 | ||||
-rw-r--r-- | build/has/extattr-get-file.c (renamed from config/has/extattr-get-file.c) | 0 | ||||
-rw-r--r-- | build/has/extattr-get-link.c (renamed from config/has/extattr-get-link.c) | 0 | ||||
-rw-r--r-- | build/has/extattr-list-file.c (renamed from config/has/extattr-list-file.c) | 0 | ||||
-rw-r--r-- | build/has/extattr-list-link.c (renamed from config/has/extattr-list-link.c) | 0 | ||||
-rw-r--r-- | build/has/fdclosedir.c (renamed from config/has/fdclosedir.c) | 0 | ||||
-rw-r--r-- | build/has/getdents.c (renamed from config/has/getdents.c) | 5 | ||||
-rw-r--r-- | build/has/getdents64-syscall.c (renamed from config/has/getdents64-syscall.c) | 5 | ||||
-rw-r--r-- | build/has/getdents64.c (renamed from config/has/getdents64.c) | 5 | ||||
-rw-r--r-- | build/has/getmntent-1.c | 9 | ||||
-rw-r--r-- | build/has/getmntent-2.c | 10 | ||||
-rw-r--r-- | build/has/getmntinfo.c | 10 | ||||
-rw-r--r-- | build/has/getprogname-gnu.c (renamed from config/has/getprogname-gnu.c) | 0 | ||||
-rw-r--r-- | build/has/getprogname.c (renamed from config/has/getprogname.c) | 0 | ||||
-rw-r--r-- | build/has/max-align-t.c (renamed from config/has/max-align-t.c) | 0 | ||||
-rw-r--r-- | build/has/pipe2.c (renamed from config/has/pipe2.c) | 0 | ||||
-rw-r--r-- | build/has/posix-spawn-addfchdir-np.c (renamed from config/has/posix-spawn-addfchdir-np.c) | 0 | ||||
-rw-r--r-- | build/has/posix-spawn-addfchdir.c (renamed from config/has/posix-spawn-addfchdir.c) | 0 | ||||
-rw-r--r-- | build/has/st-acmtim.c (renamed from config/has/st-acmtim.c) | 0 | ||||
-rw-r--r-- | build/has/st-acmtimespec.c (renamed from config/has/st-acmtimespec.c) | 0 | ||||
-rw-r--r-- | build/has/st-birthtim.c (renamed from config/has/st-birthtim.c) | 0 | ||||
-rw-r--r-- | build/has/st-birthtimespec.c (renamed from config/has/st-birthtimespec.c) | 0 | ||||
-rw-r--r-- | build/has/st-flags.c (renamed from config/has/st-flags.c) | 0 | ||||
-rw-r--r-- | build/has/statx-syscall.c (renamed from config/has/statx-syscall.c) | 0 | ||||
-rw-r--r-- | build/has/statx.c (renamed from config/has/statx.c) | 0 | ||||
-rw-r--r-- | build/has/strerror-l.c (renamed from config/has/strerror-l.c) | 0 | ||||
-rw-r--r-- | build/has/strerror-r-gnu.c (renamed from config/has/strerror-r-gnu.c) | 0 | ||||
-rw-r--r-- | build/has/strerror-r-posix.c (renamed from config/has/strerror-r-posix.c) | 0 | ||||
-rw-r--r-- | build/has/string-to-flags.c | 9 | ||||
-rw-r--r-- | build/has/strtofflags.c | 9 | ||||
-rw-r--r-- | build/has/timegm.c | 9 | ||||
-rw-r--r-- | build/has/tm-gmtoff.c (renamed from config/has/tm-gmtoff.c) | 0 | ||||
-rw-r--r-- | build/has/uselocale.c (renamed from config/has/uselocale.c) | 0 | ||||
-rw-r--r-- | build/header.mk | 72 | ||||
-rwxr-xr-x | build/msg-if.sh | 21 | ||||
-rwxr-xr-x | build/msg.sh | 62 | ||||
-rwxr-xr-x | build/pkgconf.sh (renamed from config/pkgconf.sh) | 2 | ||||
-rw-r--r-- | build/pkgs.mk | 33 | ||||
-rw-r--r-- | build/prelude.mk | 122 | ||||
-rw-r--r-- | build/use/libacl.c (renamed from config/use/libacl.c) | 0 | ||||
-rw-r--r-- | build/use/libcap.c (renamed from config/use/libcap.c) | 0 | ||||
-rw-r--r-- | build/use/libselinux.c (renamed from config/use/libselinux.c) | 0 | ||||
-rw-r--r-- | build/use/liburing.c (renamed from config/use/liburing.c) | 0 | ||||
-rw-r--r-- | build/use/oniguruma.c (renamed from config/use/oniguruma.c) | 0 | ||||
-rw-r--r-- | config/config.mk | 63 | ||||
-rw-r--r-- | config/deps.mk | 18 | ||||
-rw-r--r-- | config/header.mk | 69 | ||||
-rw-r--r-- | config/pkgs.mk | 36 | ||||
-rw-r--r-- | config/prelude.mk | 162 | ||||
-rwxr-xr-x | configure | 137 | ||||
-rw-r--r-- | docs/BUILDING.md | 211 | ||||
-rw-r--r-- | docs/CHANGELOG.md | 30 | ||||
-rw-r--r-- | src/bar.c | 133 | ||||
-rw-r--r-- | src/bfstd.c | 16 | ||||
-rw-r--r-- | src/dstring.c | 4 | ||||
-rw-r--r-- | src/dstring.h | 4 | ||||
-rw-r--r-- | src/mtab.c | 9 | ||||
-rw-r--r-- | src/prelude.h | 15 | ||||
-rw-r--r-- | src/stat.c | 34 | ||||
-rw-r--r-- | src/xtime.c | 38 | ||||
-rw-r--r-- | tests/util.sh | 9 | ||||
-rw-r--r-- | tests/xspawn.c | 15 | ||||
-rw-r--r-- | tests/xtime.c | 12 |
82 files changed, 1063 insertions, 660 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3890bb0..1d4196e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,6 +41,11 @@ jobs: run: | make -j$(nproc) distcheck + - uses: actions/upload-artifact@v4 + with: + name: linux-config.log + path: distcheck-*/gen/config.log + macos: name: macOS @@ -73,7 +78,6 @@ jobs: with: operating_system: freebsd version: "14.0" - sync_files: runner-to-vm run: | sudo pkg install -y \ @@ -85,6 +89,11 @@ jobs: sudo mount -t fdescfs none /dev/fd make -j$(nproc) distcheck + - uses: actions/upload-artifact@v4 + with: + name: freebsd-config.log + path: distcheck-*/gen/config.log + openbsd: name: OpenBSD @@ -98,7 +107,6 @@ jobs: with: operating_system: openbsd version: "7.5" - sync_files: runner-to-vm run: | sudo pkg_add \ @@ -107,9 +115,14 @@ jobs: gmake \ oniguruma jobs=$(sysctl -n hw.ncpu) - gmake -j$jobs config + ./configure MAKE=gmake gmake -j$jobs check TEST_FLAGS="--sudo --verbose=skipped" + - uses: actions/upload-artifact@v4 + with: + name: openbsd-config.log + path: gen/config.log + netbsd: name: NetBSD @@ -123,7 +136,6 @@ jobs: with: operating_system: netbsd version: "10.0" - sync_files: runner-to-vm run: | PATH="/sbin:/usr/sbin:$PATH" @@ -133,9 +145,14 @@ jobs: pkgconf \ tcl-expect jobs=$(sysctl -n hw.ncpu) - make -j$jobs config + ./configure make -j$jobs check TEST_FLAGS="--sudo --verbose=skipped" + - uses: actions/upload-artifact@v4 + with: + name: netbsd-config.log + path: gen/config.log + dragonflybsd: name: DragonFly BSD @@ -149,7 +166,6 @@ jobs: with: release: "6.4.0" usesh: true - copyback: false prepare: | pkg install -y \ @@ -165,9 +181,14 @@ 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" + - uses: actions/upload-artifact@v4 + with: + name: dragonfly-config.log + path: gen/config.log + omnios: name: OmniOS @@ -181,7 +202,6 @@ jobs: with: release: "r151048" usesh: true - copyback: false prepare: | pkg install \ @@ -198,5 +218,10 @@ 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" + + - uses: actions/upload-artifact@v4 + with: + name: omnios-config.log + path: gen/config.log 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 @@ -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 @@ -168,23 +156,25 @@ DISTCHECKS := \ # Test multiple configurations distcheck: - @+${MAKE} -s distcheck-asan - @+test "$$(uname)" = Darwin || ${MAKE} -s distcheck-msan - @+${MAKE} -s distcheck-tsan - @+test "$$(uname)-$$(uname -m)" != Linux-x86_64 || ${MAKE} -s distcheck-m32 - @+${MAKE} -s distcheck-release + @+${MAKE} distcheck-asan + @+test "$$(uname)" = Darwin || ${MAKE} distcheck-msan + @+${MAKE} distcheck-tsan + @+test "$$(uname)-$$(uname -m)" != Linux-x86_64 || ${MAKE} distcheck-m32 + @+${MAKE} distcheck-release .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} 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 @@ -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 1d379ca..cf1ae49 100644 --- a/bench/bench.sh +++ b/bench/bench.sh @@ -221,7 +221,7 @@ setup() { fi echo "Building bfs ..." - as-user make -s -j"$nproc" RELEASE=y + as-user ./configure --enable-release as-user make -s -j"$nproc" all as-user mkdir -p bench/corpus @@ -254,9 +254,8 @@ setup() { echo "Building bfs $commit ..." cd "$worktree" as-user git checkout -qd "$commit" -- - ls - if [ -e config ]; then - as-user make -s -j"$nproc" config RELEASE=1 + if [ -e configure ]; then + as-user ./configure --enable-release as-user make -s -j"$nproc" else as-user make -s -j"$nproc" release 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/define-if.sh b/build/define-if.sh index 059c1ac..295ead8 100755 --- a/config/define-if.sh +++ b/build/define-if.sh @@ -7,7 +7,7 @@ set -eu -SLUG="${1#config/}" +SLUG="${1#build/}" SLUG="${SLUG%.c}" MACRO="BFS_$(printf '%s' "$SLUG" | tr '/a-z-' '_A-Z_')" shift 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 837c30f..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 \ @@ -101,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" @@ -132,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/has/acl-get-entry.c b/build/has/acl-get-entry.c index 3cce771..3cce771 100644 --- a/config/has/acl-get-entry.c +++ b/build/has/acl-get-entry.c diff --git a/config/has/acl-get-file.c b/build/has/acl-get-file.c index 89fbf23..89fbf23 100644 --- a/config/has/acl-get-file.c +++ b/build/has/acl-get-file.c diff --git a/config/has/acl-get-tag-type.c b/build/has/acl-get-tag-type.c index 2901956..2901956 100644 --- a/config/has/acl-get-tag-type.c +++ b/build/has/acl-get-tag-type.c diff --git a/config/has/acl-is-trivial-np.c b/build/has/acl-is-trivial-np.c index 9ca9fc7..9ca9fc7 100644 --- a/config/has/acl-is-trivial-np.c +++ b/build/has/acl-is-trivial-np.c diff --git a/config/has/acl-trivial.c b/build/has/acl-trivial.c index 7efc838..7efc838 100644 --- a/config/has/acl-trivial.c +++ b/build/has/acl-trivial.c diff --git a/config/has/aligned-alloc.c b/build/has/aligned-alloc.c index 4460038..4460038 100644 --- a/config/has/aligned-alloc.c +++ b/build/has/aligned-alloc.c diff --git a/config/has/confstr.c b/build/has/confstr.c index 58280b4..58280b4 100644 --- a/config/has/confstr.c +++ b/build/has/confstr.c diff --git a/config/has/extattr-get-file.c b/build/has/extattr-get-file.c index ac9cf96..ac9cf96 100644 --- a/config/has/extattr-get-file.c +++ b/build/has/extattr-get-file.c diff --git a/config/has/extattr-get-link.c b/build/has/extattr-get-link.c index c35be5b..c35be5b 100644 --- a/config/has/extattr-get-link.c +++ b/build/has/extattr-get-link.c diff --git a/config/has/extattr-list-file.c b/build/has/extattr-list-file.c index e68a8bb..e68a8bb 100644 --- a/config/has/extattr-list-file.c +++ b/build/has/extattr-list-file.c diff --git a/config/has/extattr-list-link.c b/build/has/extattr-list-link.c index 49f0ec2..49f0ec2 100644 --- a/config/has/extattr-list-link.c +++ b/build/has/extattr-list-link.c diff --git a/config/has/fdclosedir.c b/build/has/fdclosedir.c index f4ad1f5..f4ad1f5 100644 --- a/config/has/fdclosedir.c +++ b/build/has/fdclosedir.c diff --git a/config/has/getdents.c b/build/has/getdents.c index d0d4228..579898f 100644 --- a/config/has/getdents.c +++ b/build/has/getdents.c @@ -4,7 +4,6 @@ #include <dirent.h> int main(void) { - struct dirent de; - getdents(3, &de, 1024); - return 0; + char buf[1024]; + return getdents(3, (void *)buf, sizeof(buf)); } diff --git a/config/has/getdents64-syscall.c b/build/has/getdents64-syscall.c index 4838c14..7642d93 100644 --- a/config/has/getdents64-syscall.c +++ b/build/has/getdents64-syscall.c @@ -6,7 +6,6 @@ #include <unistd.h> int main(void) { - struct dirent64 de; - syscall(SYS_getdents64, 3, &de, 1024); - return 0; + char buf[1024]; + return syscall(SYS_getdents64, 3, (void *)buf, sizeof(buf)); } diff --git a/config/has/getdents64.c b/build/has/getdents64.c index 1abf36d..d8e8062 100644 --- a/config/has/getdents64.c +++ b/build/has/getdents64.c @@ -4,7 +4,6 @@ #include <dirent.h> int main(void) { - struct dirent64 de; - getdents64(3, &de, 1024); - return 0; + char buf[1024]; + return getdents64(3, (void *)buf, sizeof(buf)); } diff --git a/build/has/getmntent-1.c b/build/has/getmntent-1.c new file mode 100644 index 0000000..9854dcd --- /dev/null +++ b/build/has/getmntent-1.c @@ -0,0 +1,9 @@ +// Copyright © Tavian Barnes <tavianator@tavianator.com> +// SPDX-License-Identifier: 0BSD + +#include <mntent.h> +#include <stdio.h> + +int main(void) { + return !getmntent(stdin); +} diff --git a/build/has/getmntent-2.c b/build/has/getmntent-2.c new file mode 100644 index 0000000..71f0220 --- /dev/null +++ b/build/has/getmntent-2.c @@ -0,0 +1,10 @@ +// Copyright © Tavian Barnes <tavianator@tavianator.com> +// SPDX-License-Identifier: 0BSD + +#include <stdio.h> +#include <sys/mnttab.h> + +int main(void) { + struct mnttab mnt; + return getmntent(stdin, &mnt); +} diff --git a/build/has/getmntinfo.c b/build/has/getmntinfo.c new file mode 100644 index 0000000..90ef5fb --- /dev/null +++ b/build/has/getmntinfo.c @@ -0,0 +1,10 @@ +// Copyright © Tavian Barnes <tavianator@tavianator.com> +// SPDX-License-Identifier: 0BSD + +#include <stddef.h> +#include <sys/types.h> +#include <sys/mount.h> + +int main(void) { + return getmntinfo(NULL, MNT_WAIT); +} diff --git a/config/has/getprogname-gnu.c b/build/has/getprogname-gnu.c index 6b97c5e..6b97c5e 100644 --- a/config/has/getprogname-gnu.c +++ b/build/has/getprogname-gnu.c diff --git a/config/has/getprogname.c b/build/has/getprogname.c index 83dc8e8..83dc8e8 100644 --- a/config/has/getprogname.c +++ b/build/has/getprogname.c diff --git a/config/has/max-align-t.c b/build/has/max-align-t.c index 96165ce..96165ce 100644 --- a/config/has/max-align-t.c +++ b/build/has/max-align-t.c diff --git a/config/has/pipe2.c b/build/has/pipe2.c index 4cb43b5..4cb43b5 100644 --- a/config/has/pipe2.c +++ b/build/has/pipe2.c diff --git a/config/has/posix-spawn-addfchdir-np.c b/build/has/posix-spawn-addfchdir-np.c index b870a53..b870a53 100644 --- a/config/has/posix-spawn-addfchdir-np.c +++ b/build/has/posix-spawn-addfchdir-np.c diff --git a/config/has/posix-spawn-addfchdir.c b/build/has/posix-spawn-addfchdir.c index c52ff81..c52ff81 100644 --- a/config/has/posix-spawn-addfchdir.c +++ b/build/has/posix-spawn-addfchdir.c diff --git a/config/has/st-acmtim.c b/build/has/st-acmtim.c index d687ab0..d687ab0 100644 --- a/config/has/st-acmtim.c +++ b/build/has/st-acmtim.c diff --git a/config/has/st-acmtimespec.c b/build/has/st-acmtimespec.c index f747bc0..f747bc0 100644 --- a/config/has/st-acmtimespec.c +++ b/build/has/st-acmtimespec.c diff --git a/config/has/st-birthtim.c b/build/has/st-birthtim.c index 4964571..4964571 100644 --- a/config/has/st-birthtim.c +++ b/build/has/st-birthtim.c diff --git a/config/has/st-birthtimespec.c b/build/has/st-birthtimespec.c index 91a613f..91a613f 100644 --- a/config/has/st-birthtimespec.c +++ b/build/has/st-birthtimespec.c diff --git a/config/has/st-flags.c b/build/has/st-flags.c index b1d0c32..b1d0c32 100644 --- a/config/has/st-flags.c +++ b/build/has/st-flags.c diff --git a/config/has/statx-syscall.c b/build/has/statx-syscall.c index 87ec869..87ec869 100644 --- a/config/has/statx-syscall.c +++ b/build/has/statx-syscall.c diff --git a/config/has/statx.c b/build/has/statx.c index 65f1674..65f1674 100644 --- a/config/has/statx.c +++ b/build/has/statx.c diff --git a/config/has/strerror-l.c b/build/has/strerror-l.c index 3dcc4d7..3dcc4d7 100644 --- a/config/has/strerror-l.c +++ b/build/has/strerror-l.c diff --git a/config/has/strerror-r-gnu.c b/build/has/strerror-r-gnu.c index 26ca0ee..26ca0ee 100644 --- a/config/has/strerror-r-gnu.c +++ b/build/has/strerror-r-gnu.c diff --git a/config/has/strerror-r-posix.c b/build/has/strerror-r-posix.c index 41b2d30..41b2d30 100644 --- a/config/has/strerror-r-posix.c +++ b/build/has/strerror-r-posix.c diff --git a/build/has/string-to-flags.c b/build/has/string-to-flags.c new file mode 100644 index 0000000..027d72c --- /dev/null +++ b/build/has/string-to-flags.c @@ -0,0 +1,9 @@ +// Copyright © Tavian Barnes <tavianator@tavianator.com> +// SPDX-License-Identifier: 0BSD + +#include <stddef.h> +#include <util.h> + +int main(void) { + return string_to_flags(NULL, NULL, NULL); +} diff --git a/build/has/strtofflags.c b/build/has/strtofflags.c new file mode 100644 index 0000000..73ecbcb --- /dev/null +++ b/build/has/strtofflags.c @@ -0,0 +1,9 @@ +// Copyright © Tavian Barnes <tavianator@tavianator.com> +// SPDX-License-Identifier: 0BSD + +#include <stddef.h> +#include <unistd.h> + +int main(void) { + return strtofflags(NULL, NULL, NULL); +} diff --git a/build/has/timegm.c b/build/has/timegm.c new file mode 100644 index 0000000..6e2d155 --- /dev/null +++ b/build/has/timegm.c @@ -0,0 +1,9 @@ +// Copyright © Tavian Barnes <tavianator@tavianator.com> +// SPDX-License-Identifier: 0BSD + +#include <time.h> + +int main(void) { + struct tm tm = {0}; + return (int)timegm(&tm); +} diff --git a/config/has/tm-gmtoff.c b/build/has/tm-gmtoff.c index 543df48..543df48 100644 --- a/config/has/tm-gmtoff.c +++ b/build/has/tm-gmtoff.c diff --git a/config/has/uselocale.c b/build/has/uselocale.c index a712ff8..a712ff8 100644 --- a/config/has/uselocale.c +++ b/build/has/uselocale.c diff --git a/build/header.mk b/build/header.mk new file mode 100644 index 0000000..8412fd5 --- /dev/null +++ b/build/header.mk @@ -0,0 +1,72 @@ +# 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-syscall.h \ + gen/has/getdents64.h \ + gen/has/getmntent-1.h \ + gen/has/getmntent-2.h \ + gen/has/getmntinfo.h \ + gen/has/getprogname-gnu.h \ + gen/has/getprogname.h \ + gen/has/max-align-t.h \ + gen/has/pipe2.h \ + gen/has/posix-spawn-addfchdir-np.h \ + gen/has/posix-spawn-addfchdir.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-syscall.h \ + gen/has/statx.h \ + gen/has/strerror-l.h \ + gen/has/strerror-r-gnu.h \ + gen/has/strerror-r-posix.h \ + gen/has/string-to-flags.h \ + gen/has/strtofflags.h \ + gen/has/timegm.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 365ed61..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/use/$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/use/libacl.c b/build/use/libacl.c index de1fe50..de1fe50 100644 --- a/config/use/libacl.c +++ b/build/use/libacl.c diff --git a/config/use/libcap.c b/build/use/libcap.c index 58e832c..58e832c 100644 --- a/config/use/libcap.c +++ b/build/use/libcap.c diff --git a/config/use/libselinux.c b/build/use/libselinux.c index bca409d..bca409d 100644 --- a/config/use/libselinux.c +++ b/build/use/libselinux.c diff --git a/config/use/liburing.c b/build/use/liburing.c index bea499a..bea499a 100644 --- a/config/use/liburing.c +++ b/build/use/liburing.c diff --git a/config/use/oniguruma.c b/build/use/oniguruma.c index cb17596..cb17596 100644 --- a/config/use/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 50ad5e8..0000000 --- a/config/header.mk +++ /dev/null @@ -1,69 +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}/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] ${TGT}" - printf '// %s\n' "${TGT}" >$@ - printf '#ifndef BFS_CONFIG_H\n' >>$@ - printf '#define BFS_CONFIG_H\n' >>$@ - cat ${.ALLSRC} >>$@ - printf '#endif // BFS_CONFIG_H\n' >>$@ - cat ${.ALLSRC:%=%.log} >$@.log - ${VCAT} $@ -.PHONY: ${GEN}/config.h - -# The short name of the config test -SLUG = ${@:${GEN}/%.h=%} - -${HEADERS}:: - ${MKDIR} ${@D} - if config/define-if.sh ${SLUG} config/cc.sh config/${SLUG}.c >$@ 2>$@.log; then \ - test "${IS_V}" || printf '[ CC ] %-${MSG_WIDTH}s ✔\n' ${SLUG}.c; \ - else \ - test "${IS_V}" || printf '[ CC ] %-${MSG_WIDTH}s ✘\n' ${SLUG}.c; \ - fi diff --git a/config/pkgs.mk b/config/pkgs.mk deleted file mode 100644 index 898f262..0000000 --- a/config/pkgs.mk +++ /dev/null @@ -1,36 +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 - -HEADERS := ${ALL_PKGS:%=${GEN}/use/%.h} - -${GEN}/pkgs.mk: ${HEADERS} - ${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 $$(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} - if config/define-if.sh use/${PKG} config/pkgconf.sh ${PKG} >$@ 2>$@.log; then \ - test "${IS_V}" || printf '[ CC ] %-${MSG_WIDTH}s ✔\n' use/${PKG}.c; \ - else \ - test "${IS_V}" || printf '[ CC ] %-${MSG_WIDTH}s ✘\n' use/${PKG}.c; \ - fi diff --git a/config/prelude.mk b/config/prelude.mk deleted file mode 100644 index 109e60c..0000000 --- a/config/prelude.mk +++ /dev/null @@ -1,162 +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 - -# 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 := 30 - -# cat a file if V=1 -VCAT,y := @cat -VCAT, := @: -VCAT := ${VCAT,${IS_V}} - -# All external dependencies -ALL_PKGS := \ - libacl \ - libcap \ - libselinux \ - liburing \ - oniguruma - -# 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 ----- @@ -6,6 +6,7 @@ #include "atomic.h" #include "bfstd.h" #include "bit.h" +#include "diag.h" #include "dstring.h" #include <errno.h> #include <fcntl.h> @@ -15,9 +16,30 @@ #include <string.h> #include <sys/ioctl.h> +/** + * Sentinel values for bfs_bar::refcount. + */ +enum { + /** The bar is currently uninitialized. */ + BFS_BAR_UNINIT = 0, + /** The bar is being initialized/destroyed. */ + BFS_BAR_BUSY = 1, + /** Values >= this are valid refcounts. */ + BFS_BAR_READY = 2, +}; + struct bfs_bar { - int fd; + /** + * A file descriptor to the TTY. Must be atomic because signal handlers + * can race with bfs_bar_hide(). + */ + atomic int fd; + /** A reference count protecting `fd`. */ + atomic unsigned int refcount; + + /** The width of the TTY. */ atomic unsigned int width; + /** The height of the TTY. */ atomic unsigned int height; }; @@ -26,27 +48,71 @@ static struct bfs_bar the_bar = { .fd = -1, }; +/** Try to acquire a reference to bar->fd. */ +static int bfs_bar_getfd(struct bfs_bar *bar) { + unsigned int refs = load(&bar->refcount, relaxed); + do { + if (refs < BFS_BAR_READY) { + errno = EAGAIN; + return -1; + } + } while (!compare_exchange_weak(&bar->refcount, &refs, refs + 1, acquire, relaxed)); + + return load(&bar->fd, relaxed); +} + +/** Release a reference to bar->fd. */ +static void bfs_bar_putfd(struct bfs_bar *bar) { + int fd = load(&bar->fd, relaxed); + + unsigned int refs = fetch_sub(&bar->refcount, 1, release); + bfs_assert(refs >= BFS_BAR_READY); + + if (refs == 2) { + close_quietly(fd); + store(&bar->fd, -1, relaxed); + store(&bar->refcount, BFS_BAR_UNINIT, release); + } +} + /** Get the terminal size, if possible. */ static int bfs_bar_getsize(struct bfs_bar *bar) { #ifdef TIOCGWINSZ - struct winsize ws; - if (ioctl(bar->fd, TIOCGWINSZ, &ws) != 0) { + int fd = bfs_bar_getfd(bar); + if (fd < 0) { return -1; } - store(&bar->width, ws.ws_col, relaxed); - store(&bar->height, ws.ws_row, relaxed); - return 0; + struct winsize ws; + int ret = ioctl(fd, TIOCGWINSZ, &ws); + if (ret == 0) { + store(&bar->width, ws.ws_col, relaxed); + store(&bar->height, ws.ws_row, relaxed); + } + + bfs_bar_putfd(bar); + return ret; #else errno = ENOTSUP; return -1; #endif } -/** Async Signal Safe puts(). */ -static int ass_puts(int fd, const char *str) { - size_t len = strlen(str); - return xwrite(fd, str, len) == len ? 0 : -1; +/** Write a string to the status bar (async-signal-safe). */ +static int bfs_bar_write(struct bfs_bar *bar, const char *str, size_t len) { + int fd = bfs_bar_getfd(bar); + if (fd < 0) { + return -1; + } + + int ret = xwrite(fd, str, len) == len ? 0 : -1; + bfs_bar_putfd(bar); + return ret; +} + +/** Write a string to the status bar (async-signal-safe). */ +static int bfs_bar_puts(struct bfs_bar *bar, const char *str) { + return bfs_bar_write(bar, str, strlen(str)); } /** Number of decimal digits needed for terminal sizes. */ @@ -70,22 +136,24 @@ static char *ass_itoa(char *str, unsigned int n) { /** Update the size of the scrollable region. */ static int bfs_bar_resize(struct bfs_bar *bar) { - char esc_seq[12 + ITOA_DIGITS] = - "\0337" // DECSC: Save cursor - "\033[;"; // DECSTBM: Set scrollable region + char esc_seq[12 + ITOA_DIGITS]; + + char *cur = stpcpy(esc_seq, + "\0337" // DECSC: Save cursor + "\033[;" // DECSTBM: Set scrollable region + ); // DECSTBM takes the height as the second argument unsigned int height = load(&bar->height, relaxed); - char *ptr = esc_seq + strlen(esc_seq); - ptr = ass_itoa(ptr, height - 1); + cur = ass_itoa(cur, height - 1); - strcpy(ptr, + cur = stpcpy(cur, "r" // DECSTBM "\0338" // DECRC: Restore the cursor "\033[J" // ED: Erase display from cursor to end ); - return ass_puts(bar->fd, esc_seq); + return bfs_bar_write(bar, esc_seq, cur - esc_seq); } #ifdef SIGWINCH @@ -102,7 +170,7 @@ static void sighand_winch(int sig) { /** Reset the scrollable region and hide the bar. */ static int bfs_bar_reset(struct bfs_bar *bar) { - return ass_puts(bar->fd, + return bfs_bar_puts(bar, "\0337" // DECSC: Save cursor "\033[r" // DECSTBM: Reset scrollable region "\0338" // DECRC: Restore cursor @@ -138,13 +206,14 @@ static int bfs_bar_printf(struct bfs_bar *bar, const char *format, ...) { return -1; } - int ret = ass_puts(bar->fd, str); + int ret = bfs_bar_write(bar, str, dstrlen(str)); dstrfree(str); return ret; } struct bfs_bar *bfs_bar_show(void) { - if (the_bar.fd >= 0) { + unsigned int refs = BFS_BAR_UNINIT; + if (!compare_exchange_strong(&the_bar.refcount, &refs, BFS_BAR_BUSY, acq_rel, relaxed)) { errno = EBUSY; goto fail; } @@ -153,16 +222,18 @@ struct bfs_bar *bfs_bar_show(void) { ctermid(term); if (strlen(term) == 0) { errno = ENOTTY; - goto fail; + goto unref; } - the_bar.fd = open(term, O_RDWR | O_CLOEXEC); - if (the_bar.fd < 0) { - goto fail; + int fd = open(term, O_RDWR | O_CLOEXEC); + if (fd < 0) { + goto unref; } + store(&the_bar.fd, fd, relaxed); + store(&the_bar.refcount, BFS_BAR_READY, release); if (bfs_bar_getsize(&the_bar) != 0) { - goto fail_close; + goto put; } reset_before_death_by(SIGABRT); @@ -192,9 +263,11 @@ struct bfs_bar *bfs_bar_show(void) { return &the_bar; -fail_close: - close_quietly(the_bar.fd); - the_bar.fd = -1; +put: + bfs_bar_putfd(&the_bar); + return NULL; +unref: + store(&the_bar.refcount, 0, release); fail: return NULL; } @@ -233,7 +306,5 @@ void bfs_bar_hide(struct bfs_bar *bar) { #endif bfs_bar_reset(bar); - - xclose(bar->fd); - bar->fd = -1; + bfs_bar_putfd(bar); } diff --git a/src/bfstd.c b/src/bfstd.c index f8ce871..0ac3a72 100644 --- a/src/bfstd.c +++ b/src/bfstd.c @@ -637,8 +637,14 @@ error: return NULL; } +#if BFS_HAS_STRTOFFLAGS +# define BFS_STRTOFFLAGS strtofflags +#elif BFS_HAS_STRING_TO_FLAGS +# define BFS_STRTOFFLAGS string_to_flags +#endif + int xstrtofflags(const char **str, unsigned long long *set, unsigned long long *clear) { -#if BSD && !__GNU__ +#ifdef BFS_STRTOFFLAGS char *str_arg = (char *)*str; #if __OpenBSD__ @@ -649,11 +655,7 @@ int xstrtofflags(const char **str, unsigned long long *set, unsigned long long * bfs_fflags_t set_arg = 0; bfs_fflags_t clear_arg = 0; -#if __NetBSD__ - int ret = string_to_flags(&str_arg, &set_arg, &clear_arg); -#else - int ret = strtofflags(&str_arg, &set_arg, &clear_arg); -#endif + int ret = BFS_STRTOFFLAGS(&str_arg, &set_arg, &clear_arg); *str = str_arg; *set = set_arg; @@ -663,7 +665,7 @@ int xstrtofflags(const char **str, unsigned long long *set, unsigned long long * errno = EINVAL; } return ret; -#else // !BSD +#else // !BFS_STRTOFFLAGS errno = ENOTSUP; return -1; #endif diff --git a/src/dstring.c b/src/dstring.c index b5bf3d3..86ab646 100644 --- a/src/dstring.c +++ b/src/dstring.c @@ -174,7 +174,7 @@ int dstrxcpy(dchar **dest, const char *src, size_t len) { return 0; } -char *dstrprintf(const char *format, ...) { +dchar *dstrprintf(const char *format, ...) { va_list args; va_start(args, format); @@ -184,7 +184,7 @@ char *dstrprintf(const char *format, ...) { return str; } -char *dstrvprintf(const char *format, va_list args) { +dchar *dstrvprintf(const char *format, va_list args) { // Guess a capacity to try to avoid reallocating dchar *str = dstralloc(2 * strlen(format)); if (!str) { diff --git a/src/dstring.h b/src/dstring.h index 9ea7eb9..14e1d3e 100644 --- a/src/dstring.h +++ b/src/dstring.h @@ -244,7 +244,7 @@ int dstrxcpy(dchar **dest, const char *str, size_t len); * The created string, or NULL on failure. */ attr(printf(1, 2)) -char *dstrprintf(const char *format, ...); +dchar *dstrprintf(const char *format, ...); /** * Create a dynamic string from a format string and a va_list. @@ -257,7 +257,7 @@ char *dstrprintf(const char *format, ...); * The created string, or NULL on failure. */ attr(printf(1, 0)) -char *dstrvprintf(const char *format, va_list args); +dchar *dstrvprintf(const char *format, va_list args); /** * Format some text onto the end of a dynamic string. @@ -13,11 +13,11 @@ #include <string.h> #include <sys/types.h> -#if !defined(BFS_USE_MNTENT) && BFS_USE_MNTENT_H +#if !defined(BFS_USE_MNTENT) && BFS_HAS_GETMNTENT_1 # define BFS_USE_MNTENT true -#elif !defined(BFS_USE_MNTINFO) && BSD +#elif !defined(BFS_USE_MNTINFO) && BFS_HAS_GETMNTINFO # define BFS_USE_MNTINFO true -#elif !defined(BFS_USE_MNTTAB) && __SVR4 +#elif !defined(BFS_USE_MNTTAB) && BFS_HAS_GETMNTENT_2 # define BFS_USE_MNTTAB true #endif @@ -27,7 +27,6 @@ # include <stdio.h> #elif BFS_USE_MNTINFO # include <sys/mount.h> -# include <sys/ucred.h> #elif BFS_USE_MNTTAB # include <stdio.h> # include <sys/mnttab.h> @@ -148,7 +147,7 @@ struct bfs_mtab *bfs_mtab_parse(void) { bfs_statfs *mntbuf; int size = getmntinfo(&mntbuf, MNT_WAIT); - if (size < 0) { + if (size <= 0) { error = errno; goto fail; } diff --git a/src/prelude.h b/src/prelude.h index ddeacbd..72f88b0 100644 --- a/src/prelude.h +++ b/src/prelude.h @@ -127,21 +127,6 @@ extern const char bfs_version[]; # define __has_attribute(attr) false #endif -// Platform detection - -// Get the definition of BSD if available -#if BFS_USE_SYS_PARAM_H -# include <sys/param.h> -#endif - -#ifndef __GLIBC_PREREQ -# define __GLIBC_PREREQ(maj, min) false -#endif - -#ifndef __NetBSD_Prereq__ -# define __NetBSD_Prereq__(maj, min, patch) false -#endif - // Fundamental utilities /** @@ -62,7 +62,7 @@ int bfs_fstatat_flags(enum bfs_stat_flags flags) { ret |= AT_SYMLINK_NOFOLLOW; } -#if defined(AT_NO_AUTOMOUNT) && (!__GNU__ || __GLIBC_PREREQ(2, 35)) +#ifdef AT_NO_AUTOMOUNT ret |= AT_NO_AUTOMOUNT; #endif @@ -297,27 +297,21 @@ int bfs_stat(int at_fd, const char *at_path, enum bfs_stat_flags flags, struct b return bfs_stat_tryfollow(at_fd, at_path, at_flags, flags, buf); } - // Check __GNU__ to work around https://lists.gnu.org/archive/html/bug-hurd/2021-12/msg00001.html -#if defined(AT_EMPTY_PATH) && !__GNU__ - static atomic bool has_at_ep = true; - if (load(&has_at_ep, relaxed)) { - at_flags |= AT_EMPTY_PATH; - int ret = bfs_stat_explicit(at_fd, "", at_flags, buf); - if (ret != 0 && errno == EINVAL) { - store(&has_at_ep, false, relaxed); - } else { - return ret; - } - } -#endif - - struct stat statbuf; - if (fstat(at_fd, &statbuf) == 0) { - bfs_stat_convert(buf, &statbuf); - return 0; - } else { +#if BFS_USE_STATX + // If we have statx(), use it with AT_EMPTY_PATH for its extra features + at_flags |= AT_EMPTY_PATH; + return bfs_stat_explicit(at_fd, "", at_flags, buf); +#else + // Otherwise, just use fstat() rather than fstatat(at_fd, ""), to save + // the kernel the trouble of copying in the empty string + struct stat sb; + if (fstat(at_fd, &sb) != 0) { return -1; } + + bfs_stat_convert(buf, &sb); + return 0; +#endif } const struct timespec *bfs_stat_time(const struct bfs_stat *buf, enum bfs_stat_field field) { diff --git a/src/xtime.c b/src/xtime.c index 91ed915..eb11afa 100644 --- a/src/xtime.c +++ b/src/xtime.c @@ -5,6 +5,7 @@ #include "xtime.h" #include "bfstd.h" #include "diag.h" +#include "sanity.h" #include <errno.h> #include <limits.h> #include <sys/time.h> @@ -12,13 +13,13 @@ #include <unistd.h> int xmktime(struct tm *tm, time_t *timep) { - *timep = mktime(tm); + time_t time = mktime(tm); - if (*timep == -1) { + if (time == -1) { int error = errno; struct tm tmp; - if (!localtime_r(timep, &tmp)) { + if (!localtime_r(&time, &tmp)) { bfs_bug("localtime_r(-1): %s", xstrerror(errno)); return -1; } @@ -30,9 +31,38 @@ int xmktime(struct tm *tm, time_t *timep) { } } + *timep = time; + return 0; +} + +// FreeBSD is missing an interceptor +#if BFS_HAS_TIMEGM && !(__FreeBSD__ && SANITIZE_MEMORY) + +int xtimegm(struct tm *tm, time_t *timep) { + time_t time = timegm(tm); + + if (time == -1) { + int error = errno; + + struct tm tmp; + if (!gmtime_r(&time, &tmp)) { + bfs_bug("gmtime_r(-1): %s", xstrerror(errno)); + return -1; + } + + if (tm->tm_year != tmp.tm_year || tm->tm_yday != tmp.tm_yday + || tm->tm_hour != tmp.tm_hour || tm->tm_min != tmp.tm_min || tm->tm_sec != tmp.tm_sec) { + errno = error; + return -1; + } + } + + *timep = time; return 0; } +#else + static int safe_add(int *value, int delta) { if (*value >= 0) { if (delta > INT_MAX - *value) { @@ -147,6 +177,8 @@ overflow: return -1; } +#endif // !BFS_HAS_TIMEGM + /** Parse a decimal digit. */ static int xgetdigit(char c) { int ret = c - '0'; 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: diff --git a/tests/xtime.c b/tests/xtime.c index a7c63d2..d9d6c5c 100644 --- a/tests/xtime.c +++ b/tests/xtime.c @@ -137,6 +137,7 @@ static bool check_one_xtimegm(const struct tm *tm) { return ret; } +#if !BFS_HAS_TIMEGM /** Check an overflowing xtimegm() call. */ static bool check_xtimegm_overflow(const struct tm *tm) { struct tm copy = *tm; @@ -154,6 +155,7 @@ static bool check_xtimegm_overflow(const struct tm *tm) { return ret; } +#endif /** xtimegm() tests. */ static bool check_xtimegm(void) { @@ -173,11 +175,13 @@ static bool check_xtimegm(void) { ret &= check_one_xtimegm(&tm); } +#if !BFS_HAS_TIMEGM // Check integer overflow cases - check_xtimegm_overflow(&(struct tm) { .tm_sec = INT_MAX, .tm_min = INT_MAX }); - check_xtimegm_overflow(&(struct tm) { .tm_min = INT_MAX, .tm_hour = INT_MAX }); - check_xtimegm_overflow(&(struct tm) { .tm_hour = INT_MAX, .tm_mday = INT_MAX }); - check_xtimegm_overflow(&(struct tm) { .tm_mon = INT_MAX, .tm_year = INT_MAX }); + ret &= check_xtimegm_overflow(&(struct tm) { .tm_sec = INT_MAX, .tm_min = INT_MAX }); + ret &= check_xtimegm_overflow(&(struct tm) { .tm_min = INT_MAX, .tm_hour = INT_MAX }); + ret &= check_xtimegm_overflow(&(struct tm) { .tm_hour = INT_MAX, .tm_mday = INT_MAX }); + ret &= check_xtimegm_overflow(&(struct tm) { .tm_mon = INT_MAX, .tm_year = INT_MAX }); +#endif return ret; } |