#!/bin/sh

# Copyright © Tavian Barnes <tavianator@tavianator.com>
# SPDX-License-Identifier: 0BSD

# bfs build configuration script

set -eu

# Save the ./configure command line for bfs --version
export CONFIG="$0 $*"

# 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

# Convert kebab-case to UPPER_CASE
toupper() {
    printf '%s' "$1" | tr 'a-z-' 'A-Z_'
}

# Report an argument parsing error
invalid() {
    printf 'error: Unrecognized option "%s"\n\n' "$1" >&2
    printf 'Run %s --help for more information.\n' "$0" >&2
    exit 1
}

for arg; do
    # --[(enable|disable|with|without)-]$name[=$value]
    value="${arg#*=}"
    name="${arg%%=*}"
    name="${name#--}"
    case "$arg" in
        --enable-*|--disable-*|--with-*|--without-*)
            name="${name#*-}"
            ;;
    esac
    NAME=$(printf '%s' "$name" | tr 'a-z-' 'A-Z_')

    # y/n modality
    case "$arg" in
        --enable-*|--with-*)
            case "$arg" in
                *=y|*=yes) yn=y ;;
                *=n|*=no) yn=n ;;
                *=*) invalid "$arg" ;;
                *) yn=y ;;
            esac
            ;;
        --disable-*|--without-*)
            case "$arg" in
                *=*) invalid "arg" ;;
                *) yn=n ;;
            esac
            ;;
    esac

    # Fix up --enable-lib* to --with-lib*
    case "$arg" in
        --enable-*|--disable-*)
            case "$name" in
                libacl|libcap|libselinux|liburing|oniguruma)
                    old="$arg"
                    case "$arg" in
                        --enable-*) arg="--with-${arg#--*-}" ;;
                        --disable-*) arg="--without-${arg#--*-}" ;;
                    esac
                    printf 'warning: Treating "%s" like "%s"\n' "$old" "$arg" >&2
                    ;;
            esac
        ;;
    esac

    case "$arg" in
        -h|--help)
            cat <<EOF
Usage:

  \$ $0 [--enable-*|--disable-*] [--with-*|--without-*] [CC=...] [...]
  \$ $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 build --with or
--without them explicitly:

  --with-libacl      --without-libacl
  --with-libcap      --without-libcap
  --with-libselinux  --without-libselinux
  --with-liburing    --without-liburing
  --with-oniguruma   --without-oniguruma

Packaging:

  --prefix=/path
      Set the installation prefix (default: /usr)
  --mandir=/path
      Set the man page directory (default: \$PREFIX/share/man)

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 "$name" in
                release|asan|lsan|msan|tsan|ubsan|lint|gcov)
                    shift
                    set -- "$@" "$NAME=$yn"
                    ;;
                *)
                    invalid "$arg"
                    ;;
            esac
            ;;

        --with-*|--without-*)
            case "$name" in
                libacl|libcap|libselinux|liburing|oniguruma)
                    shift
                    set -- "$@" "WITH_$NAME=$yn"
                    ;;
                *)
                    invalid "$arg"
                    ;;
            esac
            ;;

        --prefix=*|--mandir=*)
            shift
            set -- "$@" "$NAME=$value"
            ;;

        --infodir=*|--build=*|--host=*|--target=*)
            shift
            printf 'warning: Ignoring option "%s"\n' "$arg" >&2
            ;;

        MAKE=*)
            shift
            MAKE="$value"
            ;;

        # make flag (-j2) or variable (CC=clang)
        -*|*=*)
            continue
            ;;

        *)
            invalid "$arg"
            ;;
    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 "$@"