apenwarr-redo/redoconf/rc.sh
Avery Pennarun bdb8d8a27d redoconf: better handling of required vs optional detectors.
CC.rc was the only "mandatory" detection, which was weird and
inconsistent.  Instead, make it optional like the others, and have it
set a HAVE_CC variable appropriately (and have CXX.rc work the same
way).  Then, add a default.required.rc.od that checks the HAVE_* for
any variable and aborts if it is not available.

This allows us to fix confusing behaviour in allconfig.do, which would
try every compiler on the system, but redo would print a (non-fatal)
error message (and prevent redo-stamp optimization) when CC.rc failed
for any non-working compilers.  Now CC.rc just politely reports that it
didn't find a compiler.  Then we change all.rc.od to make CC.rc
mandatory.

Reported-by: Nathaniel Filardo <nwfilardo@gmail.com>
2019-02-23 16:51:25 -05:00

240 lines
5.1 KiB
Bash

# This script starts with $PWD=output dir, $S=input dir.
REDOCONF="$S/redoconf"
if [ ! -d "$S" ] || [ ! -f "$REDOCONF/default.do.sh" ]; then
echo "default.do.sh: \$S is not set correctly." >&2
exit 99
fi
. "$REDOCONF/utils.sh"
RC_TARGET="$1"
rm -f "$RC_TARGET.log"
redo-ifchange "$REDOCONF/rc.sh" "$REDOCONF/utils.sh"
_rc_exit_check() {
if [ -z "$RC_INCLUDE_RAN" ]; then
echo "Fatal: used redoconf/rc.sh but didn't call rc_include." >&2
exit 91
elif [ -n "$RC_QUEUE" ]; then
echo "Fatal: must call rc_save or rc_undo before ending." >&2
exit 92
fi
}
trap _rc_exit_check EXIT
rc_hook() {
# nothing by default; can be overridden
:
}
# Declare that a variable *named* $1 is used
# as input for the current script, and provide
# a help message in $2 for use with
# configure --help-flags.
helpmsg() {
# Nothing to do by default
rc_hook "$1"
}
# Assign the string $2 to the global variable
# *named* by $1.
replaceln() {
rc_hook "$1"
eval $1=\$2
}
# If $2 is nonempty, append a newline and $2 to
# the global variable *named* by $1
appendln() {
rc_hook "$1"
eval local tmp=\"\$$1\$NL\$2\"
eval $1='${tmp#$NL}'
}
# Write a command line that calls "uses $1",
# including proper sh-escaping.
rc_helpmsg() {
local cmd="helpmsg"
cmd="helpmsg $1 $(shquote "$2")"
eval "$cmd"
appendln RC_HELP_QUEUE "$cmd"
}
_rc_record() {
if ! contains_line "$RC_CHANGED" "$1"; then
local oldv=
eval oldv="\$$1"
eval "old$1=\$oldv"
RC_CHANGED="$RC_CHANGED$NL$1"
fi
}
# Write a command line that calls "appendln $1 $2",
# including properly sh-escaping $2.
rc_appendln() {
RC_LOG="$RC_LOG $1 += $(shquote "$2")$NL"
_rc_record "$1"
local cmd="appendln $1 $(shquote "$2")"
eval "$cmd"
appendln RC_QUEUE "$cmd"
}
# Write a command line that replaces $1 with $2,
# including properly sh-escaping $2.
# This is good for variables like CC and CXX, where
# appending isn't what we want.
rc_replaceln() {
RC_LOG="$RC_LOG $1 = $(shquote "$2")$NL"
_rc_record "$1"
local cmd="replaceln $1 $(shquote "$2")"
eval "$cmd"
appendln RC_QUEUE "$cmd"
}
# Read compiler variables from _init.rc and the listed .rc files.
# Runs redo-ifchange to generate the .rc files as needed.
rc_include() {
local d="" want="" target="$RC_TARGET" ops4="$PS4"
RC_INCLUDE_RAN=1
for d in rc/_init.rc "$@"; do
if [ "$d" = "${d%.rc}" ]; then
xecho "$0: rc_include: '$d' must end in .rc" >&2
exit 99
fi
if ! contains_line "$RC_INCLUDES" "$d"; then
want="$want$NL$d"
RC_INCLUDES="$RC_INCLUDES$NL$d"
fi
done
want="${want#$NL}"
if [ -n "$want" ]; then
xIFS="$IFS"
IFS="$NL"
set -f
redo-ifchange $want
for d in $want; do
IFS="$xIFS"
set +f
RC_TARGET="$d"
PS4="$PS4[$d] "
[ ! -e "$d" ] || { :; . "./$d"; }
PS4="$ops4"
done
IFS="$xIFS"
set +f
fi
unset RC_QUEUE
PS4="$ops4"
RC_TARGET="$target"
}
# Undo the currently enqueued rc_appendln and rc_replaceln
# actions, restoring the affected variables back to their
# original values.
rc_undo() {
local xIFS="$IFS" v=
IFS="$NL"
for k in $RC_CHANGED; do
eval v="\$old$k"
eval "$k=\$v"
done
unset RC_CHANGED
unset RC_QUEUE
unset RC_LOG
}
# Write the currently active rc_include,
# rc_replaceln, and rc_appendln commands to stdout,
# to produce the final .rc file.
rc_save() {
printf '%s' "${RC_LOG}" >&2
if [ -n "$RC_INCLUDES" ]; then
(
xIFS="$IFS"
IFS="$NL"
printf 'rc_include '
for d in $RC_INCLUDES; do
printf '%s ' "$d"
done
printf '\n'
)
fi
xecho "$RC_HELP_QUEUE"
xecho "$RC_QUEUE"
unset RC_QUEUE
unset RC_CHANGED
}
rc_compile() {
redo-ifchange "$REDOCONF/trycompile"
( . "$REDOCONF/trycompile" "$@" ) >>"$RC_TARGET.log" 2>&1
}
_pkg_config() {
(
IFS="$NL"
set -f
$PKG_CONFIG $PKG_CONFIG_FLAGS "$@"
)
}
_rc_pkg_add() {
local var="$1" vvar= want=
shift
if [ -n "$HAVE_PKG_CONFIG" ]; then
for d in "$@"; do
if _pkg_config --exists "$d"; then
want="$want $d"
fi
done
if [ -n "$want" ]; then
rc_appendln CPPFLAGS \
"$(rc_splitwords \
"$(_pkg_config --cflags $want)")"
vvar="$(rc_splitwords \
"$(_pkg_config --libs $want)")"
rc_appendln "$var" "$vvar"
fi
else
echo "(pkg-config is missing)" >&2
fi
}
# Determine whether packages listed in $2 all exists and are functional,
# by finding them using pkg-config and running the command in $3..$n.
#
# If the package works:
# HAVE_$1 is set to 1
# CPPFLAGS and the variable named by $1 are updated with compiler and
# linker flags, respectively.
# else:
# HAVE_$1 is set to blank.
#
# Returns success in either case. Check detection results in the
# HAVE_$1 variable if needed.
rc_pkg_detect() {
if ! contains_line "$RC_INCLUDES" rc/pkg-config.rc; then
echo "Error: include pkg-config.rc before using rc_pkg_*" >&2
return 1
fi
if [ "$#" -lt 3 ]; then
echo "Error: rc_pkg_detect needs a command to test." >&2
return 1
fi
local var="$1" vvar="" pkgs="$2"
shift
shift
eval vvar="\$$var"
rc_helpmsg "$var" "Extra linker options for '$pkgs'"
if [ -z "$vvar" ]; then
_rc_pkg_add "$var" $pkgs
eval vvar="\$$var"
fi
appendln LIBS "$vvar"
if ("$@"); then
rc_replaceln "HAVE_$var" 1
else
rc_undo
rc_replaceln "HAVE_$var" ""
rc_replaceln "$var" ""
fi
}