apenwarr-redo/redoconf/rc.sh
Avery Pennarun 6dae51f4d2 Experimental new redoconf C/C++ build/autoconfiguration system.
To test it out, try this:
	./do -j10 build
	cd docs/cookbook/c
	redo -j10 test

It should detect all the compilers on your system and make three
separate builds for each one: normal, debug, and optimized.  Then it
tries to run a test program under each one.

If there are windows cross compilers and you also have 'wine'
installed, it'll try running the test program under wine as well.

redoconf currently has no documentation other than the example program.
We'll fix that later.
2019-02-23 06:52: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 99
elif [ -n "$RC_QUEUE" ]; then
echo "Fatal: must call rc_save or rc_undo before ending." >&2
exit 99
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
}