#!/bin/sh
### Automatic build configuration script for Dinit.
### This script generates an "mconfig" file suitable for building on the current system.

## Initial preparation
set -eu
cd "$(dirname "$0")"

## Helper functions
# POSIX "echo" behaviour has unspecified behavior in some cases, for example when the first
# argument is "-n" or backslash ("\"). So, replace the shell built-in echo with a printf-based
# function. For more information see: http://www.etalabs.net/sh_tricks.html
echo()
{
    IFS=" " printf %s\\n "$*"
}

# Output a line of information, may be followed by call to sub_info to provide additional information
info()
{
    echo "$1"
}

# Output additional information (intended to follow call to info)
sub_info()
{
    echo "  ... $1"
}

# Output an error message, may be followed by a call to sub_error to provide additional information
error()
{
    >&2 echo "Error: $1"
    exit 1
}

# Output additional error information (intended to follow call to error)
sub_error()
{
    >&2 echo "  ... Error: $1"
    exit 1
}

# Output a warning, with an optional "extra information" field
# $1: Info, $2: Extra info (optional)
warning()
{
    >&2 echo "Warning: $1"
    [ -z "${2:-}" ] || >&2 echo "  ... $2"
}

# Produce (as output) a random number from 0-65535
randomnumber()
{
    awk 'BEGIN {
        srand()
        printf("%d\n", 65536 * rand())
    }'
}

# Create a temporary directory and output its full path
mktmpdir()
{
    rn1=`randomnumber`
    rn2=`randomnumber`
    dirname="${TMPDIR:-/tmp}/dinit-cfg-${rn1}-${rn2}"
    mkdir "$dirname"
    echo "$dirname"    
}

# Check whether the specified compiler ($1) seems to work
cxx_works()
{
    info "Checking whether \"$1\" is a working C++ compiler..."
    if eval $1 -o '"$configtmpdir"/testfile' '"$configtmpdir"/testfile.cc'; then
        rm -f "$configtmpdir"/testfile
        sub_info "Yes."
    else
        rm -f "$configtmpdir"/testfile*
        sub_error "It seems that \"$1\" is not a working C++ compiler. Please specify a compiler."
    fi
}

# Test if the compiler successfully compiles the specified source file. If the optional compilation
# argument is specified ($3) then pass it to the compiler along with the already-established
# arguments (this can be used to test if the compiler accepts the argument).
#   $1 - the name of the source file to compile
#   $2 - the name of the result object file
#   $3 - the argument to test (optional)
#   CXX - the name of the compiler
#   CXXFLAGS, CXXFLAGS_EXTRA - any predetermined flags
try_compile_source()
{
    eval $CXX $CXXFLAGS ${3:-} $CXXFLAGS_EXTRA '"$configtmpdir"/"$1"' \
            -c -o '"$configtmpdir"/"$2"' > /dev/null 2>&1
}

# Test if the specified object file can be linked into an executable. If the optional link argument
# is specified ($3) then pass it to the compiler driver along with the already-established
# arguments (this can be used to test if the compiler driver accepts the argument).
#   $1 - the name of the object file to link
#   $2 - the name of the result executable file
#   $3 - the argument to test (optional)
#   CXX - the name of the compiler
#   LDFLAGS, LDFLAGS_EXTRA - any predetermined flags
try_link_object()
{
    eval $CXX $LDFLAGS $LDFLAGS_EXTRA ${3:-} '"$configtmpdir"/"$1"' \
            -o '"$configtmpdir"/"$2"' > /dev/null 2>&1
}

# Test if the compiler accepts an argument (for compilation only); if so add it to named variable.
#   $1 - the name of the variable to potentially add the argument to
#   $2 - the argument to test/add
#   $3 - either "MANDATORY" or empty/not set; if MANDATORY, this function returns failure if the
#        argument isn't supported by the compiler. 
#   CXX - the name of the compiler
#   CXXFLAGS, CXXFLAGS_EXTRA, LDFLAGS, LDFLAGS_EXTRA - any predetermined flags
try_cxx_argument()
{
    info "Checking whether \"$2\" is accepted by the compiler..."
    if try_compile_source testfile.cc testfile.o "$2"; then
        rm "$configtmpdir"/testfile.o
        sub_info "Yes."
        eval "$1=\"\${$1} \$2\""
        eval "$1=\${$1# }"
        return 0
    else
        sub_info "No."
        if [ "${3:-}" = MANDATORY ]; then
            return 1
        fi
        return 0
    fi
}


# Test if an argument is supported during linking.
#   $1 - the name of the variable to potentially add the argument to
#   $2 - the argument to test/add
#   CXX - the name of the compiler
#   CXXFLAGS, CXXFLAGS_EXTRA, LDFLAGS, LDFLAGS_EXTRA - any predetermined flags
try_ld_argument()
{
    info "Checking whether \"$2\" is accepted as a link-time option..."
    if [ ! -e "$configtmpdir"/testfile.o ]; then
        # If the object file doesn't exist yet, need to compile
        if ! try_compile_source testfile.cc testfile.o; then
            sub_info "No (compilation failed)."
            return
        fi
    fi
    if try_link_object testfile.o testfile "$2"; then
        sub_info "Yes."
        rm "$configtmpdir"/testfile
        eval "$1=\"\${$1} \$2\""
        eval "$1=\${$1# }"
    else
        sub_info "No."
    fi
}

# Test if an argument is supported during both compiling and linking.
#   $1 - the name of the compiler-flags variable to potentially add the argument to, or a
#           dash ("-") if this is not required
#   $2 - the name of the link-flags variable to potentially add the argument to, or a dash ("-")
#           if this is not required
#   $3 - the argument to test/add
#   CXX - the name of the compiler
#   CXXFLAGS, CXXFLAGS_EXTRA, LDFLAGS, LDFLAGS_EXTRA - any predetermined flags
try_compile_link_argument()
{
    info "Checking whether \"$3\" is accepted as an option for both compiling and linking..."
    if try_compile_source testfile.cc testfile.o "$3" \
            && try_link_object testfile.o testfile "$3"; then
        sub_info "Yes."
        rm "$configtmpdir"/testfile.o
        rm "$configtmpdir"/testfile
        if [ "$1" != "-" ]; then
            eval "$1=\"\${$1} \$3\""
            eval "$1=\${$1# }"
        fi
        if [ "$2" != "-" ]; then
            eval "$2=\"\${$2} \$3\""
            eval "$2=\${$2# }"
        fi
    else
        sub_info "No."
    fi
}

# Test if the specified header is usable.
#   $1 - the name of the variable to set to 1 or 0 based on header availability
#   $2 - the header path
check_header()
{
    info "Checking whether header \"$2\" is available..."
    echo "#include <$2>" > "$configtmpdir/testheader.cc"
    if try_compile_source testheader.cc testheader.o; then
        sub_info "Yes."
        rm "$configtmpdir"/testheader.o
        eval "$1=1"
    else
        sub_info "No."
        eval "$1=0"
    fi
}

# Write the test code for testing exception handling with -fno-rtti
write_no_rtti_check()
{
    cat << _EOF > "$configtmpdir"/no-rtti-check.cc
#include <string>
#include <stdexcept>

int main()
{
    try {
        std::stoull("invalid for stoull");
    }
    catch (std::invalid_argument &exc) {
        return 0;
    }

    return -1;
}
_EOF
}

# Quote a string to preserve its original form after shell evaluation (except that an empty
# string remains as an empty string).
quote_for_shell()
{
    # The strategy: surround the string with '-quotes. Look for any such quotes embedded in the
    # string and replace them with the sequence "'\''", i.e. end ', literal-', begin '.
    result="'"
    remaining=$1
    smallest_prefix=${remaining%%\'*}
    while [ "$smallest_prefix" != "$remaining" ]; do
        result="$result$smallest_prefix'\''"
        remaining=${remaining#*\'}
        smallest_prefix=${remaining%%\'*}
    done
    result="$result$remaining'"
    if [ "$result" = "''" ]; then result=""; fi
    printf "%s" "$result"
}

# Quote a string for use in a make file.
quote_for_make()
{
    result=""
    remaining=$1
    smallest_prefix=${remaining%%\$*}
    while [ "$smallest_prefix" != "$remaining" ]; do
        result="$result$smallest_prefix\$\$"
        remaining=${remaining#*\$}
        smallest_prefix=${remaining%%\$*}
    done
    result="$result$remaining"
    printf "%s" "$result"	
}

usage()
{
    cat << _EOF
Usage: $0 [OPTION]...

Generates build configuration for Dinit.

Defaults are shown in [brackets].

  -h, --help                    This help message.
  -q, --quiet                   Don't print normal messages, just errors.
  -c, --clear                   Clear previously generated mconfig.

Target options:
  --platform=PLATFORM           Set the platform manually (for cross-compilation)
                                Note: for all cross-compiles please specify correct CXX and
                                CXX_FOR_BUILD.

Installation directories:
  --prefix=PREFIX               Main installation prefix [/usr]
  --exec-prefix=EPREFIX         Main executables prefix  [PREFIX]
  --bindir=BINDIR               Dinit executables        [EPREFIX/bin]
  --sbindir=SBINDIR             Dinit system executables [EPREFIX/sbin]
  --mandir=MANDIR               Dinit man-pages location [PREFIX/share/man]
  --syscontrolsocket=SOCKETPATH Dinitctl socket location [/run/dinitctl] on Linux systems
                                                         [/var/run/dinitctl] on other systems

Build and configuration options:
  --enable-strip                    Strip debug information in installation process [Enabled]
  --disable-strip                   Don't strip debug information in installation process
  --shutdown-prefix=PREFIX          Name prefix for shutdown, poweroff, reboot, soft-reboot, halt
                                    programs []
  --enable-shutdown                 Build shutdown, poweroff, reboot, soft-reboot, halt programs
                                    [Enabled only on Linux]
  --disable-shutdown                Don't build shutdown, poweroff, reboot, soft-reboot, halt
                                    programs
  --enable-cgroups                  Enable Cgroups support [Enabled only on Linux]
  --disable-cgroups                 Disable Cgroups support
  --enable-capabilities             Enable capabilities support
                                    [Enabled on Linux if libcap available]
  --disable-capabilities            Disable capabilities support
  --enable-ioprio                   Enable support for I/O priority setting (via Linux API)
                                    [Enabled on linux if linux/ioprio.h available]
  --disable-ioprio                  Disable support for I/O priority setting
  --enable-oom-adj                  Enable support for Linux "OOM" score adjustment
                                    [Enabled only on Linux] 
  --disable-oom-adj                 Disable support for Linux "OOM" score adjustment
  --enable-utmpx                    Enable manipulating the utmp/utmpx database via the related
                                    POSIX functions [Depends on system]
  --disable-utmpx                   Disable manipulating the utmp/utmpx database via the related
                                    POSIX functions
  --enable-initgroups               Enable initialization of supplementary groups for run-as
                                    [Enabled]
  --disable-initgroups              Disable initialization of supplementary groups for run-as
  --enable-auto-restart             Enable auto-restart for services by default (Deprecated;
                                    use --default-auto-restart=...)
  --disable-auto-restart            Disable auto-restart for services by default (Deprecated;
                                    use --default-auto-restart=...)
  --default-start-timeout=sec       Default start-timeout for services [60]
  --default-stop-timeout=sec        Default stop-timeout for services [10]
  --default-auto-restart=(never|on-failure|always)
                                    When to automatically restart services. This controls the
                                    default value for the "restart" service setting; see the
                                    dinit-service(5) man page for details. [always]

Build variables:
  Note: build variables can be passed in the environment, or as $0 argument (as "var=VALUE").
  Note: values for some options will be determined automatically, if not specified.
  Note: CXXFLAGS, TEST_CXXFLAGS, LDFLAGS and TEST_LDFLAGS by default will be set to options
    considered suitable for the platform, filtered to remove any options not supported by the
    compiler/linker. To disable this, specify the values explicitly (an empty string is accepted).
    To add options without removing the defaults, set the variable with _EXTRA appended to the
    name (eg CXXFLAGS_EXTRA).

  CXX                           C++ compiler
  CXX_FOR_BUILD                 C++ compiler generating code for the build system (for
                                cross-compiles).
  CXXFLAGS_FOR_BUILD            Flags to use when generating code for the build system.
                                  Defaults to the value of CXXFLAGS.
  CPPFLAGS_FOR_BUILD            Preprocessor flags to use when generating code for the build
                                system.
                                  Defaults to the value of CPPFLAGS.
  LDFLAGS_FOR_BUILD             Link flags to use when generating code for the build system.
                                  Defaults to the value of LDFLAGS.
  CXXFLAGS                      Flags to use when compiling C++ code.
  CXXFLAGS_EXTRA                Additional flags to use when compiling C++ code.
  TEST_CXXFLAGS                 Flags to use when compiling C++ code in tests.
  TEST_CXXFLAGS_EXTRA           Additional flags to use when compiling C++ code in tests.
  CPPFLAGS                      Preprocessor flags to use when compiling C++ code.
  LDFLAGS                       Link flags.
  LDFLAGS_EXTRA                 Additional link flags.
  LDFLAGS_BASE                  Link flags to use in addition to any link-time optimisation
                                (LTO)-related options. Set this to control link options without
                                disabling LTO. Ignored if LDFLAGS is set.
  TEST_LDFLAGS                  Link flags when building test executables.
  TEST_LDFLAGS_EXTRA            Additional link flags when building test executables.
  TEST_LDFLAGS_BASE             Link flags to use when building test executables, in addition to
                                LTO-releated options.
  
  LDFLAGS_LIBCAP                Additional Link flags to link capabilities support (libcap).

Note: build variable values representing commands or arguments must be suitably quoted for use in
shell commands (i.e. quoted twice if executing $0 via shell command line).  

See BUILD file for more information.
_EOF
    exit 0
}

## Don't take values from environment for these variables:
for var in PREFIX \
           EPREFIX \
           BINDIR \
           SBINDIR \
           MANDIR \
           SHUTDOWN_PREFIX \
           BUILD_SHUTDOWN \
           SUPPORT_CGROUPS \
           SUPPORT_CAPABILITIES \
           SUPPORT_IOPRIO \
           SUPPORT_OOM_ADJ \
           USE_UTMPX \
           USE_INITGROUPS \
           SYSCONTROLSOCKET \
           STRIPOPTS
do
    unset $var
done

## Flag parser
for arg in "$@"; do
    case "$arg" in
        -h|--help) usage ;;
        -q|--quiet)
            info() { true; }
            sub_info() { true; }
            warning() { true; }
        ;;
        -c|--clear) rm -f mconfig && exit 0 ;;
        --platform=*) PLATFORM="${arg#*=}" ;;
        --prefix=*) PREFIX="${arg#*=}" ;;
        --exec-prefix=*) EPREFIX="${arg#*=}" ;;
        --bindir=*) BINDIR="${arg#*=}" ;;
        --sbindir=*) SBINDIR="${arg#*=}" ;;
        --mandir=*) MANDIR="${arg#*=}" ;;
        --default-start-timeout=*) DEFAULT_START_TIMEOUT="${arg#*=}" ;;
        --default-stop-timeout=*) DEFAULT_STOP_TIMEOUT="${arg#*=}" ;;
        --syscontrolsocket=*) SYSCONTROLSOCKET="${arg#*=}" ;;
        --shutdown-prefix=*) SHUTDOWN_PREFIX="${arg#*=}" ;;
        --enable-shutdown|--enable-shutdown=yes) BUILD_SHUTDOWN=yes ;;
        --disable-shutdown|--enable-shutdown=no) BUILD_SHUTDOWN=no ;;
        --enable-cgroups|--enable-cgroups=yes) SUPPORT_CGROUPS=1 ;;
        --disable-cgroups|--enable-cgroups=no) SUPPORT_CGROUPS=0 ;;
        --enable-capabilities|--enable-capabilities=yes) SUPPORT_CAPABILITIES=1 ;;
        --disable-capabilities|--enable-capabilities=no) SUPPORT_CAPABILITIES=0 ;;
        --enable-ioprio|--enable-ioprio=yes) SUPPORT_IOPRIO=1 ;;
        --disable-ioprio|--enable-ioprio=no) SUPPORT_IOPRIO=0 ;;
        --enable-oom-adj|--enable-oom-adj=yes) SUPPORT_OOM_ADJ=1 ;;
        --disable-oom-adj|--enable-oom-adj=no) SUPPORT_OOM_ADJ=0 ;;
        --enable-utmpx|--enable-utmpx=yes) USE_UTMPX=1 ;;
        --disable-utmpx|--enable-utmpx=no) USE_UTMPX=0 ;;
        --enable-initgroups|--enable-initgroups=yes) USE_INITGROUPS=1 ;;
        --disable-initgroups|--enable-initgroups=no) USE_INITGROUPS=0 ;;
        --enable-auto-restart|--enable-auto-restart=yes) DEFAULT_AUTO_RESTART=ALWAYS ;; # Deprecated
        --disable-auto-restart|--enable-auto-restart=no) DEFAULT_AUTO_RESTART=NEVER ;; # Deprecated
        --enable-strip|--enable-strip=yes) STRIPOPTS="-s" ;;
        --disable-strip|--enable-strip=no) STRIPOPTS="" ;;
        --default-auto-restart=never) DEFAULT_AUTO_RESTART=NEVER ;;
        --default-auto-restart=always) DEFAULT_AUTO_RESTART=ALWAYS ;;
        --default-auto-restart=on-failure) DEFAULT_AUTO_RESTART=ON_FAILURE ;;
        CXX=*|CXX_FOR_BUILD=*|CXXFLAGS_FOR_BUILD=*|CPPFLAGS_FOR_BUILD=*\
        |LDFLAGS_FOR_BUILD=*|CXXFLAGS=*|CXXFLAGS_EXTRA=*|TEST_CXXFLAGS=*\
        |TEST_CXXFLAGS_EXTRA=*|LDFLAGS=*|LDFLAGS_EXTRA=*|TEST_LDFLAGS=*\
        |TEST_LDFLAGS_EXTRA=*|CPPFLAGS=*|LDFLAGS_BASE=*|TEST_LDFLAGS_BASE=*\
        |LDFLAGS_LIBCAP=*) eval "${arg%%=*}=\${arg#*=}" ;;
        *=*) warning "Unknown variable: ${arg%%=*}" ;;
        *) warning "Unknown argument: $arg" ;;
    esac
done

## Check platform is specified for a cross-build
if [ -n "${CXX_FOR_BUILD:-}" ]; then
    if [ -z "${PLATFORM:-}" ]; then
        warning "Platform not specified for cross-build, defaulting to \"unknown\"."
        PLATFORM=unknown
    fi
fi

## Defaults for variables not specified by user
: "${PLATFORM:=$(uname)}"
: "${PREFIX:="/usr"}"
: "${EPREFIX:="$PREFIX"}"
: "${BINDIR:="${EPREFIX%%/}/bin"}"
: "${SBINDIR:="${EPREFIX%%/}/sbin"}"
: "${MANDIR:="${PREFIX%%/}/share/man"}"
: "${SHUTDOWN_PREFIX:=""}"
: "${CXXFLAGS_EXTRA:=""}"
: "${TEST_CXXFLAGS_EXTRA:=""}"
: "${CPPFLAGS:=""}"
: "${LDFLAGS_EXTRA:=""}"
: "${TEST_LDFLAGS_EXTRA:=""}"
: "${DEFAULT_AUTO_RESTART:="ALWAYS"}"
: "${DEFAULT_START_TIMEOUT:="60"}"
: "${DEFAULT_STOP_TIMEOUT:="10"}"
: "${USE_INITGROUPS:="1"}"
if [ "$PLATFORM" = "Linux" ]; then
    : "${BUILD_SHUTDOWN:="yes"}"
    : "${SUPPORT_CGROUPS:="1"}"
    : "${SUPPORT_CAPABILITIES:="AUTO"}"
    : "${SUPPORT_IOPRIO:="AUTO"}"
    : "${SUPPORT_OOM_ADJ:="1"}"
    : "${SYSCONTROLSOCKET:="/run/dinitctl"}"
else
    : "${BUILD_SHUTDOWN:="no"}"
    : "${SUPPORT_CGROUPS:="0"}"
    : "${SUPPORT_CAPABILITIES:="0"}"
    : "${SUPPORT_IOPRIO:="0"}"
    : "${SUPPORT_OOM_ADJ:="0"}"
    : "${SYSCONTROLSOCKET:="/var/run/dinitctl"}"
fi

HAS_LTO=""

## Finalize $CXXFLAGS, $TEST_CXXFLAGS, $LDFLAGS, $TEST_LDFLAGS, $STRIPOPTS
if [ -z "${CXXFLAGS+IS_SET}" ]; then
    CXXFLAGS=""
    AUTO_CXXFLAGS="true"
else
    AUTO_CXXFLAGS="false"
fi
if [ -z "${TEST_CXXFLAGS+IS_SET}" ]; then
    TEST_CXXFLAGS=""
    AUTO_TEST_CXXFLAGS="true"
else
    AUTO_TEST_CXXFLAGS="false"
fi
if [ -z "${LDFLAGS+IS_SET}" ]; then
    LDFLAGS=""
    AUTO_LDFLAGS="true"
else
    AUTO_LDFLAGS="false"
fi
if [ -z "${TEST_LDFLAGS+IS_SET}" ]; then
    TEST_LDFLAGS=""
    AUTO_TEST_LDFLAGS="true"
else
    AUTO_TEST_LDFLAGS="false"
fi
if [ -z "${LDFLAGS_BASE+IS_SET}" ]; then
    LDFLAGS_BASE=""
    AUTO_LDFLAGS_BASE="true"
else
    AUTO_LDFLAGS_BASE="false"
fi
if [ -z "${TEST_LDFLAGS_BASE+IS_SET}" ]; then
    TEST_LDFLAGS_BASE=""
    AUTO_TEST_LDFLAGS_BASE="true"
else
    AUTO_TEST_LDFLAGS_BASE="false"
fi
if [ -z "${LDFLAGS_LIBCAP+IS_SET}" ]; then
    LDFLAGS_LIBCAP=""
    AUTO_LDFLAGS_LIBCAP="true"
else
    AUTO_LDFLAGS_LIBCAP="false"
fi

[ -z "${STRIPOPTS+IS_SET}" ] && STRIPOPTS="-s"

## Verify PLATFORM value
case "$PLATFORM" in
    # (Allow "unknown" platform as that may be set by cross-build, with warning already triggered) 
    Linux|FreeBSD|NetBSD|OpenBSD|Darwin|unknown) ;;
    *) warning "Platform \"$PLATFORM\" is unknown!" \
        "Known platforms are: Linux, FreeBSD, NetBSD, OpenBSD, Darwin"
    ;;
esac

## Create testfile.cc to test c++ compiler
configtmpdir=`mktmpdir`
echo "int main(int argc, char **argv) { return 0; }" > "$configtmpdir"/testfile.cc || error "Can't create temporary file"

## Find and test C++ compiler
if [ -z "${CXX:-}" ]; then
    info "Checking C++ compiler..."
    for guess in g++ clang++ c++; do
        if type "$guess" > /dev/null 2>&1; then
            CXX="$guess"
            sub_info "$CXX"
            break # Found
        fi
    done
    if [ -z "${CXX:-}" ]; then
       sub_error "No C++ compiler found!"
    fi
fi
cxx_works "$CXX"
if [ -n "${CXX_FOR_BUILD:-}" ]; then
    cxx_works "$CXX_FOR_BUILD"
fi

## Test compiler/linker supported arguments
if [ "$AUTO_CXXFLAGS" = "true" ]; then
    if ! try_cxx_argument CXXFLAGS -std=c++11 MANDATORY; then
        sub_error "The C++ compiler ($CXX) doesn't accept '-std=c++11' and may be too old."
    fi
    for argument in -Wall \
                    -Os \
                    -fno-plt
    do
        try_cxx_argument CXXFLAGS $argument
    done

    # -fno-rtti is a special case. Clang+libcxxrt is known for generating broken code when
    # disabling run-time type information with code which uses exceptions; details regarding the
    # issue can be found here: https://github.com/llvm/llvm-project/issues/66117
    info "Checking whether \"-fno-rtti\" is accepted by the compiler..."
    if [ -n "${CXX_FOR_BUILD:-}" ]; then
        # May not be able to execute the check when cross-compiling.
        sub_info "Cross-compilation detected. Disabling \"-fno-rtti\" to avoid a compiler bug on some platforms."
    else
        write_no_rtti_check
        if try_compile_source no-rtti-check.cc no-rtti-check.o -fno-rtti; then
            sub_info "Yes."
            info "Checking whether \"-fno-rtti\" breaks exceptions..."
            if try_link_object no-rtti-check.o no-rtti-check && "$configtmpdir"/no-rtti-check; then
                sub_info "No."
                CXXFLAGS="$CXXFLAGS -fno-rtti"
            else
                sub_info "Yes. Disabling -fno-rtti"
            fi
        else
            sub_info "No."
        fi
    fi
fi
if [ "$AUTO_LDFLAGS" = true ] && [ "$AUTO_CXXFLAGS" = true ]; then
    # -flto must work for both compiling and linking, but we don't want to add it to LDFLAGS as,
    # if LTO is used, CXXFLAGS will always be used alongside LDFLAGS.
    if try_compile_link_argument CXXFLAGS - -flto; then
        HAS_LTO="true"
    else
        HAS_LTO="false"
    fi
fi
if [ "$AUTO_LDFLAGS_BASE" = true ] && [ "$PLATFORM" = FreeBSD ]; then
    try_ld_argument LDFLAGS_BASE -lrt
fi
if [ "$SUPPORT_CAPABILITIES" != 0 ]; then
    if [ "$AUTO_LDFLAGS_LIBCAP" = true ]; then
        try_ld_argument LDFLAGS_LIBCAP -lcap
        if [ "$SUPPORT_CAPABILITIES" = AUTO ]; then
            if [ -z "$LDFLAGS_LIBCAP" ]; then
                SUPPORT_CAPABILITIES=0
            else
                SUPPORT_CAPABILITIES=1
            fi
        fi
    else
        # SUPPORT_CAPABILITIES may be set to AUTO, fix that:
        SUPPORT_CAPABILITES=1
    fi
else
    LDFLAGS_LIBCAP=""
fi

if [ "$SUPPORT_IOPRIO" = "AUTO" ]; then
    check_header SUPPORT_IOPRIO "linux/ioprio.h"
fi

# "XXX_MK" variables are for values already quoted for use in makefiles. The following will now be
# set: TEST_LDFLAGS_BASE_MK, LDFLAGS_MK, TEST_LDFLAGS_MK, TEST_CXXFLAGS_MK. For other build
# variables, we'll just do the quoting when we write the value to mconfig.

if [ "$AUTO_TEST_LDFLAGS_BASE" = true ]; then
    TEST_LDFLAGS_BASE_MK="\$(LDFLAGS_BASE)"
    established_TEST_LDFLAGS="$LDFLAGS_BASE"
else
    TEST_LDFLAGS_BASE_MK="$(quote_for_make "$TEST_LDFLAGS_BASE")"
    established_TEST_LDFLAGS="$TEST_LDFLAGS_BASE"
fi

# Determine LDFLAGS/TEST_LDFLAGS. In the case of TEST_LDFLAGS we may still add sanitisation
# options, shortly. 
if [ "$HAS_LTO" = true ]; then
    if [ "$AUTO_LDFLAGS" = true ]; then
        LDFLAGS_MK="\$(LDFLAGS_BASE) \$(CXXFLAGS)"
    else
        LDFLAGS_MK="$(quote_for_make "$LDFLAGS")"
    fi
    if [ "$AUTO_TEST_LDFLAGS" = true ]; then
        TEST_LDFLAGS_MK="\$(TEST_LDFLAGS_BASE) \$(TEST_CXXFLAGS)"
        established_TEST_LDFLAGS="$established_TEST_LDFLAGS $TEST_CXXFLAGS"
    else
        TEST_LDFLAGS_MK="$(quote_for_make "$TEST_LDFLAGS")"
        established_TEST_LDFLAGS="$TEST_LDFLAGS"
    fi
else
    # default LDFLAGS are just $(LDFLAGS_BASE)
    if [ "$AUTO_LDFLAGS" = true ]; then
        LDFLAGS_MK="\$(LDFLAGS_BASE)"
    else
        LDFLAGS_MK="$(quote_for_make "$LDFLAGS")"
    fi
    if [ "$AUTO_TEST_LDFLAGS" = true ]; then
        TEST_LDFLAGS_MK="\$(TEST_LDFLAGS_BASE)"
    else
        established_TEST_LDFLAGS="$TEST_LDFLAGS"
        TEST_LDFLAGS_MK="$(quote_for_make "$TEST_LDFLAGS")"
    fi
fi

# Default for test flags (TEST_CXXFLAGS, TEST_LDFLAGS) is to use the build flags
if [ "$AUTO_TEST_CXXFLAGS" = "true" ]; then
    TEST_CXXFLAGS_MK="\$(CXXFLAGS)"
    established_TEST_CXXFLAGS="$CXXFLAGS"
else
    TEST_CXXFLAGS_MK=$(quote_for_make "$TEST_CXXFLAGS")
    established_TEST_CXXFLAGS="$TEST_CXXFLAGS"
fi

# Check whether sanitizers can be used for tests
if [ "$AUTO_TEST_LDFLAGS" = "true" ] && [ "$AUTO_TEST_CXXFLAGS" = "true" ]; then
    if [ "$HAS_LTO" = true ]; then
        # Avoid doubling-up sanitizer options
        LDFLAGS_VAR=-
    else
        LDFLAGS_VAR=TEST_LDFLAGS_MK
    fi
    # (Note that we append directly to TEST_CXXFLAGS_MK/TEST_LDFLAGS_MK here)
    CXXFLAGS="$established_TEST_CXXFLAGS" CXXFLAGS_EXTRA="$TEST_CXXFLAGS_EXTRA" \
            LDFLAGS="$established_TEST_LDFLAGS" LDFLAGS_EXTRA="$TEST_LDFLAGS_EXTRA" \
            try_compile_link_argument TEST_CXXFLAGS_MK $LDFLAGS_VAR -fsanitize=address,undefined
fi

## Create mconfig
rm -r "$configtmpdir"
info "Creating mconfig..."
cat << _EOF > mconfig
## Auto-generated by "$0" for "$PLATFORM"
# All changes will be lost if "$0" is re-run.

# See BUILD for help on all variables.

# Installation path options.

BINDIR=$(quote_for_make "$(quote_for_shell "$BINDIR")")
SBINDIR=$(quote_for_make "$(quote_for_shell "$SBINDIR")")
MANDIR=$(quote_for_make "$(quote_for_shell "$MANDIR")")
SYSCONTROLSOCKET=$(quote_for_make "$(quote_for_shell "$SYSCONTROLSOCKET")")

# General build options.

CXX=$(quote_for_make "$CXX")
CXXFLAGS=$(quote_for_make "$CXXFLAGS")
CXXFLAGS_EXTRA=$(quote_for_make "$CXXFLAGS_EXTRA")
TEST_CXXFLAGS=$TEST_CXXFLAGS_MK
TEST_CXXFLAGS_EXTRA=$(quote_for_make "$TEST_CXXFLAGS_EXTRA")
CPPFLAGS=$(quote_for_make "$CPPFLAGS")
LDFLAGS_BASE=$(quote_for_make "$LDFLAGS_BASE")
LDFLAGS=$LDFLAGS_MK
LDFLAGS_EXTRA=$(quote_for_make "$LDFLAGS_EXTRA")
TEST_LDFLAGS_BASE=$TEST_LDFLAGS_BASE_MK
TEST_LDFLAGS=$TEST_LDFLAGS_MK
TEST_LDFLAGS_EXTRA=$(quote_for_make "$TEST_LDFLAGS_EXTRA")
STRIPOPTS=$(quote_for_make "$STRIPOPTS")

# Dependencies
LDFLAGS_LIBCAP=$(quote_for_make "$LDFLAGS_LIBCAP")

# Feature settings
BUILD_SHUTDOWN=$BUILD_SHUTDOWN
SUPPORT_CGROUPS=$SUPPORT_CGROUPS
USE_INITGROUPS=$USE_INITGROUPS
SUPPORT_CAPABILITIES=$SUPPORT_CAPABILITIES
SUPPORT_IOPRIO=$SUPPORT_IOPRIO
SUPPORT_OOM_ADJ=$SUPPORT_OOM_ADJ

# Optional settings
SHUTDOWN_PREFIX=$(quote_for_make "$(quote_for_shell "${SHUTDOWN_PREFIX:-}")")

# Service defaults
DEFAULT_AUTO_RESTART=$DEFAULT_AUTO_RESTART
DEFAULT_START_TIMEOUT=$DEFAULT_START_TIMEOUT
DEFAULT_STOP_TIMEOUT=$DEFAULT_STOP_TIMEOUT
_EOF
if [ -n "${USE_UTMPX:-}" ]; then
    echo "USE_UTMPX=$USE_UTMPX" >> mconfig
fi
if [ -n "${CXX_FOR_BUILD:-}" ]; then
    {
        echo ""
        echo "# For cross-compiling"
        echo "CXX_FOR_BUILD=$CXX_FOR_BUILD"
    } >> mconfig
fi
if [ -n "${CXXFLAGS_FOR_BUILD:-}" ]; then
    echo "CXXFLAGS_FOR_BUILD=$CXXFLAGS_FOR_BUILD" >> mconfig
fi
if [ -n "${CPPFLAGS_FOR_BUILD:-}" ]; then
    echo "CPPFLAGS_FOR_BUILD=$CPPFLAGS_FOR_BUILD" >> mconfig
fi
if [ -n "${LDFLAGS_FOR_BUILD:-}" ]; then
    echo "LDFLAGS_FOR_BUILD=$LDFLAGS_FOR_BUILD" >> mconfig
fi
sub_info "done."
info "Done!"
exit 0
