#!/bin/sh
set -eu
cd -- "$(dirname -- "$0")"
self=./$(basename -- "$0")
. ../build_util.sh
# Get configuration from the environment
CC=${CC:-c99}
CFLAGS=${CFLAGS:-}
CPPFLAGS=${CPPFLAGS:-}
append_cppflag -D_DEFAULT_SOURCE
LDFLAGS=${LDFLAGS:-}
LDLIBS=
JOBS=${JOBS:-1}
LTO=${LTO:-false}
PGO=${PGO:-false}
STATIC=${STATIC:-false}
NATIVE=${NATIVE:-false}
CONFIG=${CONFIG:-release}
PREFIX=$(preadlinkf "${DESTDIR+${DESTDIR%/}}${PREFIX:-/usr/local}")
typecheck bool LTO PGO STATIC NATIVE
typecheck uint JOBS
# Target specific values
STD=c99
BIN=prog-c
[ -f ../VERSION ] && version=$(cat ../VERSION) || version=$(git_version ..)
append_cppflag \
-DPROG_NAME="$(dquote "$BIN" 2)" \
-DPROG_VERSION="$(dquote "$version" 2)"
LDLIBS='-pthread -lm'
USE_FEATURE1=${USE_FEATURE1:-true}
USE_FEATURE2=${USE_FEATURE2:-false}
# Prepare the source list (must be done before any cclean call)
SRC=$(printf '%s\n' *.c)
# gperf files detection
gperf=$(listfiles . -name '*.gperf' | sed 's#^\./##; s#\.gperf$#.c#')
if [ "$gperf" ]
then
! command -v gperf >/dev/null && die "gperf: command not found"
SRC=$SRC$gperf
fi
# lex files detection
lex=$(listfiles . -name '*.lex' | sed 's#^\./##; s#\.lex$#.c#')
if [ "$lex" ]
then
SRC=$SRC$lex
fi
# Remove duplicates inserted by gperf/lex detection
SRC=$(pecho "$SRC" | awk '!seen[$0]++')
# Conditional features processing
SRC=$(pecho "$SRC" | grep -Evx '(feature1|feature2)\.c')
if "$USE_FEATURE1"
then
append_cppflag -DUSE_FEATURE1
# LDLIBS="$LDLIBS -lfeat1"
appendnl SRC feature1.c
fi
if "$USE_FEATURE2"
then
append_cppflag -DUSE_FEATURE2
# LDLIBS="$LDLIBS -lfeat2"
appendnl SRC feature2.c
fi
# Argument parsing
if "$PGO"
then
[ $# -eq 0 ] && die "PGO is useless without a command to run"
else
if [ $# -eq 1 ]
then
case "$(tolower "$1")" in
clean)
cclean
;;
install)
pb_install -m 755 "$PREFIX"/bin/ "$BIN"
pb_install -m 644 "$PREFIX"/share/doc/"$PROJECT"/README_"$BIN" README
;;
uninstall)
uninstall \
"$PREFIX/bin/$BIN" \
"$PREFIX"/share/doc/"$PROJECT"/README_"$BIN"
;;
help)
pb_usage 0
;;
*)
die "$1: unknown action"
;;
esac
exit
elif [ $# -gt 1 ]
then
pb_usage 1
fi
fi
# Detect compiler vendor
case "$(tolower "$CC")" in
*gcc*)
cctype=gcc
;;
*clang*)
cctype=clang
;;
*)
cctype=c99
[ "$STD" != c99 ] && die "$STD: c99 only support the c99 standard"
;;
esac
# Apply PGO with workload specified by "$@"
if "$PGO"
then
xtest_cflag -fprofile-generate
case "$cctype" in
gcc)
PGO=false CFLAGS="$CFLAGS -fprofile-generate" \
LDFLAGS="$LDFLAGS -fprofile-generate" "$self"
(
cd - >/dev/null
"$@"
)
cclean
append_ldflag -fprofile-use
append_cflag -fprofile-use
;;
clang)
PGO=false CFLAGS="$CFLAGS -fprofile-generate=$(dquote "$PWD")" \
LDFLAGS="$LDFLAGS -fprofile-generate=$(dquote "$PWD")" "$self"
(
cd - >/dev/null
"$@"
)
cclean
llvm-profdata merge -output=default.profdata default_*.profraw
append_ldflag -fprofile-use="$PWD"
append_cflag -fprofile-use="$PWD"
;;
*)
die "Unknown PGO supporting compiler"
;;
esac
fi
# Process CONFIG
case "$(tolower "$CONFIG")" in
debug)
test_append_cflag -g3 || append_cflag -g
test_append_cflag -Og || append_cflag -O0
test_append_cflag -fsanitize=address &&
append_ldflag -fsanitize=address
test_append_cflag -fsanitize=undefined &&
append_ldflag -fsanitize=undefined
;;
release)
test_append_cflag -O3 || append_cflag -O1
append_cppflag -DNDEBUG
append_ldflag -s
test_append_ldflag -Wl,-O1
;;
*)
die "$CONFIG: unknown config"
;;
esac
# Diverse CFLAG/LDFLAG additions
"$NATIVE" && xtest_append_cflag -march=native
"$STATIC" && xtest_append_ldflag -static
[ "$CC" != c99 ] && xtest_append_cflag -std="$STD"
test_append_cflag -pedantic -Wall -Wextra # -Wconversion
test_append_ldflag -Wl,-z,defs
[ "${LD:-}" ] && test_append_ldflag -fuse-ld="${LD#ld.}"
# Apply LTO
if "$LTO"
then
xtest_cflag -flto
case "$cctype" in
gcc)
append_ldflag -flto="$JOBS" "$CFLAGS"
append_cflag -flto -fno-fat-lto-objects
;;
clang)
append_cflag -flto=thin
append_ldflag "$CFLAGS"
;;
*)
die "Unknown LTO supporting compiler"
;;
esac
fi
pb_make