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.
This commit is contained in:
Avery Pennarun 2019-02-03 01:14:51 -05:00
commit 6dae51f4d2
67 changed files with 1777 additions and 1 deletions

View file

@ -1,4 +1,4 @@
export NO_SLOW_TESTS=1
for d in */all.do; do
for d in */all.do */test.do; do
echo "${d%.do}"
done | xargs redo-ifchange

6
docs/cookbook/c/.gitignore vendored Normal file
View file

@ -0,0 +1,6 @@
hello
/when.c
/allconfig
/arches
/out
/out.*

1
docs/cookbook/c/all.do Normal file
View file

@ -0,0 +1 @@
redo-ifchange all.each

4
docs/cookbook/c/all.h Normal file
View file

@ -0,0 +1,4 @@
#include "main.h"
#include "libhello/hello.h"
#include "monotime.h"
#include <stdio.h>

4
docs/cookbook/c/all.hpp Normal file
View file

@ -0,0 +1,4 @@
#include <algorithm>
#include <string>
#include <list>
#include <map>

1
docs/cookbook/c/all.od Normal file
View file

@ -0,0 +1 @@
redo-ifchange "hello world"

23
docs/cookbook/c/all.rc.od Normal file
View file

@ -0,0 +1,23 @@
. ./redoconf.rc
rc_include \
rc/CC.rc \
rc/CXX.rc \
rc/libqt4.rc \
rc/libgtk2.rc \
rc/Wextra.rc \
rc/Wall.rc \
rc/libm.rc \
rc/rt.autolib.rc \
rc/libpng.rc \
rc/clock_gettime.func.rc \
rc/mach_time.h.rc \
rc/windows.h.rc \
rc/posix.rc \
rc/printf_lld.rc \
rc/extra.rc \
rc/all.h.precompiled.rc \
rc/all.hpp.precompiled.rc
rc_appendln LIBS "$LIBRT"
rc_appendln LIBS "$LIBM"
rc_save

View file

@ -0,0 +1,27 @@
redo-ifchange arches configure redoconf/utils.sh
config() {
local dir="$1" arch="$2"
shift
shift
[ -d "$dir" ] || mkdir "$dir"
(
cd "$dir" &&
../configure --host="$arch" "$@" &&
redo-ifchange rc/CC.rc &&
echo "$dir"
) || :
}
for d in $(cat arches); do
if [ "$d" = "native" ]; then
arch=""
else
arch="$d"
fi
config "out.$d" "$arch" &
config "out.$d.static" "$arch" "--enable-static" &
config "out.$d.opt" "$arch" "--enable-optimization" &
done
wait

14
docs/cookbook/c/arches.do Normal file
View file

@ -0,0 +1,14 @@
IFS=:
echo native >$3
if [ -z "$NO_SLOW_TESTS" ]; then
for dir in $PATH; do
for d in "$dir"/*-cc "$dir"/*-gcc; do
base=${d##*/}
arch=${base%-*}
if [ -x "$d" ]; then echo "$arch"; fi
done
done >>$3
fi
redo-always
redo-stamp <$3

6
docs/cookbook/c/clean.od Normal file
View file

@ -0,0 +1,6 @@
# runs from the output directory
rm -f *~ .*~ *.rc *.log *.gch *.stamp \
*.[oa] *.deps \
*.so *.so.* *.ver \
*.exe *.list \
hello

3
docs/cookbook/c/configure vendored Executable file
View file

@ -0,0 +1,3 @@
#!/bin/sh
S="$(dirname "$0")"
. "$S/redoconf/configure.sh"

View file

@ -0,0 +1,17 @@
# Automatically generated by redoconf/_all.rc.od - do not edit
ARCH Architecture prefix for output (eg. i686-w64-mingw32-)
CC C compiler name (cc)
CPPFLAGS Extra C preprocessor flags (eg. -I... -D...)
CFLAGS Extra C compiler flags (eg. -O2 -g)
OPTFLAGS C/C++ compiler flag overrides (eg. -g0)
LINK Linker name (cc)
LDFLAGS Extra linker options (eg. -s -static)
LIBS Extra libraries to always link against (eg. -lsocket)
STATIC Link libraries and binaries statically
CXX C++ compiler name (c++)
CXXFLAGS Extra C++ compiler flags (eg. -O2 -g)
LIBQT4 Extra linker options for 'QtCore'
LIBGTK2 Extra linker options for 'gtk+-2.0 gio-2.0 gdk-2.0 gdk-pixbuf-2.0'
LIBM Extra linker options for 'libm'
LIBPNG Extra linker options for 'libpng'
PREFIX Change installation prefix (usually /usr/local)

View file

@ -0,0 +1,10 @@
# redo $2 in each of the registered output dirs.
# This way you can run commands or depend on targets like:
# redo clean.each.do
# redo all.each.do
# etc.
redo-ifchange allconfig
for dir in $(cat allconfig); do
echo "$dir/$2"
done | xargs redo-ifchange

View file

@ -0,0 +1,10 @@
#include "main.h"
#include <stdio.h>
#ifdef EXTRA_RC_INCLUDED
#error "rc/extra.rc should not be included when compiling flagtest.c"
#endif
void flag_test(void) {
printf("flagtest included\n");
}

View file

@ -0,0 +1,9 @@
# Demonstrate how to compile .o files using nonstandard
# compiler flags. You could also do this for a whole
# directory using default.o.od.
. ./redoconf.rc
rc_include all.rc
src="$S/${1%.o}.c"
redo-ifchange "_compile" "$src"
CC="$CC" CPPFLAGS="-DFLAGTEST_SET=42" ./_compile "$3" "$1.deps" "$src"

View file

@ -0,0 +1,30 @@
# This script is run from the output dir,
# which contains a src/ symlink to the source dir.
. ./redoconf.rc
rc_include all.rc
(
cd "$S"
echo "main.c"
echo "monotime.c"
echo "when.c" # auto-generated source
echo "flagtest.c" # source with different compiler flags
if [ -n "$CXX" ]; then
echo "slow.cc"
fi
# This is unnecessarily fancy.
# We're just using it as an example of
# how to dynamically generate a .list
# file.
for d in lib*/*.list lib*/*.list.od; do
[ -e "$d" ] && echo "${d%%.*}.so"
done | uniq
printf '%s\n' "$LIBGTK2" "$LIBQT4"
) >$3
redo-always
redo-stamp <$3

View file

@ -0,0 +1,6 @@
#include "hello.h"
#include <stdio.h>
void hello(void) {
printf("Hello, world!\n");
}

View file

@ -0,0 +1,6 @@
#ifndef __HELLO_H
#define __HELLO_H
void hello(void);
#endif /* __HELLO_H */

View file

@ -0,0 +1 @@
hello.c

View file

@ -0,0 +1 @@
1.2.5

22
docs/cookbook/c/main.c Normal file
View file

@ -0,0 +1,22 @@
#include "main.h"
#include "libhello/hello.h"
#include "monotime.h"
#include "redoconf.h"
#include <stdio.h>
#if EXTRA_RC_INCLUDED != 1
#error "EXTRA_RC was not included!"
#endif
int main() {
hello();
printf("Timestamp: %s\n", stamp_time());
printf("Monotime: %lld\n", monotime());
#ifdef CXX
printf("Length of 'hello world': %d\n", cpp_test());
#else
printf("No C++ compiler found.\n");
#endif
flag_test();
return 0;
}

19
docs/cookbook/c/main.h Normal file
View file

@ -0,0 +1,19 @@
#ifndef __MAIN_H
#define __MAIN_H
#ifdef __cplusplus
#define CDEF extern "C"
#else
#define CDEF
#endif
/* when.c */
CDEF const char *stamp_time(void);
/* slow.cc */
CDEF int cpp_test(void);
/* flagtest.c */
CDEF void flag_test(void);
#endif /* __MAIN_H */

View file

@ -0,0 +1,58 @@
#define __GNU_SOURCE
/*
* Returns the kernel monotonic timestamp in microseconds.
* This function never returns the value 0; it returns 1 instead, so that
* 0 can be used as a magic value.
*/
#include "monotime.h"
#include "redoconf.h"
#if HAVE_CLOCK_GETTIME
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
long long monotime(void) {
struct timespec ts;
if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0) {
perror("clock_gettime");
exit(98); /* really should never happen, so don't try to recover */
}
long long result = ts.tv_sec * 1000000LL + ts.tv_nsec / 1000;
return !result ? 1 : result;
}
#elif HAVE_MACH_TIME_H
#include <mach/mach.h>
#include <mach/mach_time.h>
long long monotime(void) {
static mach_timebase_info_data_t timebase;
if (!timebase.denom) mach_timebase_info(&timebase);
long long result = (mach_absolute_time() * timebase.numer /
timebase.denom / 1000);
return !result ? 1 : result;
}
#elif HAVE_WINDOWS_H
#include <windows.h>
/* WARNING: Not carefully tested. It might wrap around unexpectedly.
* Based on suggestions from:
* https://stackoverflow.com/questions/211257/windows-monotonic-clock
*/
long long monotime(void) {
LARGE_INTEGER tps, t;
QueryPerformanceFrequency(&tps);
QueryPerformanceCounter(&t);
return t.QuadPart * 1000000LL / tps.QuadPart;
}
#else
#error "No monotonic time function is available"
#endif

View file

@ -0,0 +1,6 @@
#ifndef __MONOTIME_H
#define __MONOTIME_H
long long monotime(void);
#endif /* __MONOTIME_H */

View file

@ -0,0 +1,5 @@
. ./redoconf.rc
rc_include
rc_appendln CPPFLAGS "-DEXTRA_RC_INCLUDED=1"
rc_save

View file

@ -0,0 +1,16 @@
. ./redoconf.rc
rc_include rc/CC.rc
prog='
#include <time.h>
struct timespec x;
'
x=
if ! rc_compile cc link "$prog"; then
x="-D_XOPEN_SOURCE=500"
rc_appendln CPPFLAGS "$x"
rc_compile cc link "$prog"
fi
rc_save

View file

@ -0,0 +1,16 @@
. ./redoconf.rc
rc_include rc/CC.rc rc/windows.h.rc rc/Wall.rc
appendln CPPFLAGS "-Werror" # abort if any warnings
prog='
#include <stdio.h>
void test() { printf("%lld", (long long)1); }
'
x=
if ! rc_compile cc link "$prog"; then
x="-D__USE_MINGW_ANSI_STDIO=1"
rc_appendln CPPFLAGS "$x"
rc_compile cc link "$prog"
fi
rc_save

1
docs/cookbook/c/redoconf Symbolic link
View file

@ -0,0 +1 @@
../../../redoconf

10
docs/cookbook/c/slow.cc Normal file
View file

@ -0,0 +1,10 @@
#include <algorithm>
#include <string>
#include <list>
#include <map>
#include "main.h"
int cpp_test() {
std::string a = "hello ", b = "world";
return a.length() + b.length();
}

1
docs/cookbook/c/test.do Normal file
View file

@ -0,0 +1 @@
redo-ifchange test.each

16
docs/cookbook/c/test.od Normal file
View file

@ -0,0 +1,16 @@
exec >&2
redo-always
redo-ifchange all run
if [ -x ./run ]; then
echo "Running: ./run ./hello\\ world"
./run './hello world' >"$1.out" 2>&1
cat "$1.out"
if grep -F -q 'Hello, world!' "$1.out"; then
echo "-- Test successful."
else
echo "-- Test failed."
exit 1
fi
else
echo "Non-native platform: test skipped."
fi

View file

@ -0,0 +1,7 @@
cat >$3 <<-EOF
const char *stamp_time(void) {
return "$(date +%Y-%m-%d)";
}
EOF
redo-always
redo-stamp <$3

51
redoconf/_all.rc.od Normal file
View file

@ -0,0 +1,51 @@
. ./redoconf.rc
# replace the placeholder for this function with
# one that will save help messages for later.
HELP="configure.help.new"
rm -f "$HELP"
echo '# Automatically generated by redoconf/_all.rc.od - do not edit' >"$HELP"
helpmsg() {
printf '%-11s %s\n' "$1" "$2" >>"$HELP"
rc_hook "$1"
}
# Remember initial set of keys provided by ./configure
orig_keys=""
rc_hook() {
contains_line "$orig_keys" "$1" || orig_keys="$orig_keys$NL$1"
}
# Include the rest of the necessary .rc files
keys=""
rc_hook() {
contains_line "$new_keys" "$1" || new_keys="$new_keys$NL$1"
}
allrc=
if [ -e "$S/all.rc.od" ]; then
allrc=all.rc
else
allrc=
redo-ifcreate "$S/all.rc.od"
fi
rc_include rc/_init.rc rc/CC.rc rc/zdefs.rc $allrc
rc_helpmsg PREFIX "Change installation prefix (usually /usr/local)"
IFS="$NL"
unused=
for d in $orig_keys; do
if ! contains_line "$new_keys" "$d"; then
unused=1
xecho "Error: '$d' was given to ./configure but not used." >&2
fi
done
[ -z "$unused" ] || exit 1
rc_save
# Now that all the rc files have run, update $S/configure.help with the
# newly-generated help text, so it's available to new users.
# Even if multiple output dirs are building at once, this replaces the
# file atomically, so it should be safe.
mv "$HELP" "$S/configure.help"

14
redoconf/_compile.od Normal file
View file

@ -0,0 +1,14 @@
# See compile.od for more explanation.
cat >$3 <<-EOF
#!/bin/sh -e
# Run the C/C++ compiler.
# Assumes config variables (CFLAGS, etc) are already set.
t="\$1" d="\$2" i="\$3"
IFS="$NL"
set -f
\$CC -o "\$t" -c "\$i" \\
-MMD -MF "\$d" \\
\$CPPFLAGS \$CFLAGS \$CXXFLAGS \$FLAGS_PCH \$xCFLAGS \$OPTFLAGS
EOF
chmod a+x "$3"
redo-stamp <$3

51
redoconf/compile.od Normal file
View file

@ -0,0 +1,51 @@
. ./redoconf.rc
rc_include _all.rc
redo-ifchange _compile
# Subtle:
# - un-backslashed $ expansions ($foo, $(cmd)) are
# done *now*, while writing the script contents.
# - backslashed $ expansions (\$foo) are written
# verbatim into the script, to be interpreted at
# the time the script is run.
#
# We want to insert the variable contents into the
# script near the top, making sure they are not
# split or interpreted at that point (hence the
# $(shquote)).
#
# Further down, we want to disable wildcard expansion
# (set -f) and split on $NL (so we change $IFS),
# so we use backslash escapes but *not* quoting.
cat >$3 <<-EOF
#!/bin/sh -e
# Run the C/++ compiler.
t="\$1" d="\$2" i="\$3"
CPPFLAGS=$(shquote "$CPPFLAGS")
OPTFLAGS=$(shquote "$OPTFLAGS")
case \$i in
*.c|*.h)
CC=$(shquote "$CC")
CFLAGS=$(shquote "$CFLAGS")
CXXFLAGS=
PCH1=$(shquote "$CFLAGS_PCH")
PCH2=$(shquote "$CFLAGS_PCH_FPIC")
;;
*)
CC=$(shquote "$CXX")
[ -n "\$CC" ] || (echo "No C++ compiler available." >&2; exit 1)
CFLAGS=
CXXFLAGS=$(shquote "$CXXFLAGS")
PCH1=$(shquote "$CXXFLAGS_PCH")
PCH2=$(shquote "$CXXFLAGS_PCH_FPIC")
;;
esac
case \$PCH in
1) FLAGS_PCH=\$PCH1 ;;
2) FLAGS_PCH=\$PCH2 ;;
esac
. ./_compile
EOF
chmod a+x "$3"
redo-stamp <$3

200
redoconf/configure.sh Normal file
View file

@ -0,0 +1,200 @@
#!/bin/sh -e
if [ -z "$S" ]; then
exec >&2
echo "configure.sh: must include this from a 'configure' script"
exit 99
fi
if [ -e "configure" ] || [ -e "rc.sh" ]; then
exec >&2
echo "$0: run this script from an empty output directory."
echo " For example:"
echo " (mkdir out && cd out && ../configure && redo -j10)"
exit 99
fi
S="$(dirname "$0")"
rm -f src Makefile
echo "$S" >src
# Don't regenerate these files unless they're missing. Otherwise redo
# will treat it as a changed dependency and rebuild a bunch of files
# unnecessarily.
[ -e redoconf.rc ] || cat >redoconf.rc <<-EOF
# Automatically generated by $0
read -r S <src
. "\$S/redoconf/rc.sh"
EOF
# Instead of a symlink, we could make this a script like redoconf.rc,
# except then we would have to redi-ifchange default.do.sh, which would
# greatly increase the number of calls to redo-ifchange in the fast path.
# That turns out to slow things down quite a bit. So let redo follow
# the symlink and note the dependency internally, which is faster.
[ -e default.do ] || ln -sf "$S/redoconf/default.do.sh" default.do
cat >Makefile <<'EOF'
# A wrapper for people who like to type 'make' instead of 'redo'
all $(filter-out all,$(MAKECMDGOALS)):
+redo "$@"
.PHONY: $(MAKECMDGOALS) all
EOF
# Don't include rc.sh here, because that might call redo-ifchange,
# and we're not ready for that yet.
. "$S/redoconf/utils.sh"
usage() {
exec >&2
printf 'Usage: %s %s' "$0" \
'[options...] [KEY=value] [--with-key=value]
--prefix= Change installation prefix (usually /usr/local)
--host= Architecture prefix for output (eg. i686-w64-mingw32-)
--enable-static Link libraries and binaries statically
--{dis,en}able-optimization Disable/enable optimization for C/C++
--{dis,en}able-debug Disable/enable debugging flags for C/C++
-h, --help This help message
'
if [ -e "$S/configure.help" ]; then
printf '\nProject-specific flags:\n'
sort "$S/configure.help" |
while read k msg; do
# skip blanks and comments
[ -n "${k%%\#*}" ] || continue
# skip options with syntax already explained above
[ "$k" != "ARCH" ] || continue
[ "$k" != "PREFIX" ] || continue
[ "$k" != "STATIC" ] || continue
printf ' %-15s %s\n' "$k=..." "$msg"
done
else
printf '\nNo extra help yet: configure.help is missing.\n'
fi
exit 1
}
upper() {
xecho "$1" | tr 'a-z' 'A-Z' | sed 's/[^A-Z0-9]/_/g'
}
lower() {
xecho "$1" | tr 'A-Z' 'a-z' | sed 's/[^a-z0-9]/-/g'
}
emit() {
xecho "replaceln" "$1" "$(shquote "$(rc_splitwords "$2")")"
}
emit_append() {
xecho "appendln" "$1" "$(shquote "$(rc_splitwords "$2")")"
}
for k in ARCH CC CXX CPPFLAGS CFLAGS CXXFLAGS \
OPTFLAGS LINK LDFLAGS LIBS xCFLAGS; do
eval v=\$$k
if [ -n "$v" ]; then
echo "$0: Fatal: '$k' environment variable is set." >&2
echo " This can cause inconsistent builds." >&2
echo " Pass variables as arguments to $0 instead." >&2
echo " Example: $0 $k=$(shquote "$v")" >&2
exit 4
fi
done
rm -f _flags.tmp
echo "# Auto-generated by $0" >_flags.tmp
for d in "$@"; do
case $d in
--help|-h|-\?)
usage
;;
--host=*)
v="${d#*=}"
emit "ARCH" "$v"
;;
--enable-static)
emit "STATIC" "1"
;;
--enable-optimization)
emit_append OPTFLAGS "-O2"
;;
--disable-optimization)
emit_append OPTFLAGS "-O0"
;;
--enable-debug)
emit_append OPTFLAGS "-g"
;;
--disable-debug)
emit_append OPTFLAGS "-g0"
;;
--prefix=*)
v="${d#*=}"
emit "PREFIX" "$v"
;;
--with-[A-Za-z]*)
if [ "$d" = "${d%%=*}" ]; then
xecho "$0: in $(shquote "$d"):" \
"must supply '--with-<key>=<value>'" >&2
exit 3
fi
k="${d%%=*}"
k="${k#--with-}"
v="${d#*=}"
kl=$(lower "$k")
if [ "$k" != "$kl" ]; then
xecho "$0: in $(shquote "--with-$k=..."):" \
"must be all lowercase and dashes" >&2
exit 3
fi
ku=$(upper "$k")
emit "$ku" "$v"
;;
--without-[A-Za-z]*)
if [ "$d" != "${d%%=*}" ]; then
xecho "$0: in $(shquote "$d"):" \
"do not supply '=...' with --without" >&2
exit 3
fi
k="${d#--without-}"
kl=$(lower "$k")
if [ "$k" != "$kl" ]; then
xecho "$0: in $(shquote "$d"):" \
"must be all lowercase and dashes" >&2
exit 3
fi
ku=$(upper "$k")
# This causes tests to try to link using an extra
# option "NONE", which will fail, thus making the
# package appear missing.
emit "$ku" "NONE"
;;
[A-Za-z_]*=*)
k="${d%%=*}"
v="${d#*=}"
ku=$(upper "$k")
if [ "$k" != "$ku" ]; then
xecho "$0: in $(shquote "$d"):" \
"invalid KEY=value;" \
"must be all caps and underscores" >&2
exit 3
fi
emit "$ku" "$v"
;;
*)
xecho "$0: in $(shquote "$d"):" \
"invalid option; use --help for help." >&2
exit 2
;;
esac
done >>_flags.tmp
# Avoid replacing the file if it's identical, so we don't trigger
# unnecessary rebuilds. redo-stamp can do this for a redo target,
# but _flags is being generated by this configure script, which is
# not a redo target.
if [ -e _flags ] && cmp _flags.tmp _flags >/dev/null; then
rm -f _flags.tmp
else
rm -f _flags
mv _flags.tmp _flags
fi

181
redoconf/default.do.sh Normal file
View file

@ -0,0 +1,181 @@
# This script starts with $PWD=output dir, $S=input dir.
read -r S <src
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
NL="
"
_mkdir_of() {
local dir="${1%/*}"
[ "$dir" = "$1" ] ||
[ -z "$dir" ] ||
[ -d "$dir" ] ||
mkdir -p "$dir"
}
# Delegate to .od files specifically for this target, if any.
_base1=${1##*/}
_dir1=${1%"$_base1"}
_missing=""
for d in "$S/$1.od" \
"$S/${_dir1}default.${_base1#*.}.od" \
"$REDOCONF/$1.od" \
"$REDOCONF/${_dir1}default.${_base1#*.}.od"; do
if [ -e "$d" ]; then
redo-ifchange "$d"
_mkdir_of "$3"
( PS4="$PS4[$d] "; . "$d" )
exit
else
missing="$missing$NL$d"
fi
done
_add_missing() {
[ -n "$missing" ] && (IFS="$NL"; set -f; redo-ifcreate $missing)
missing=
}
_pick_src() {
# Look for the source file corresponding to a given .o file.
# If the source file is missing, we can also build it from
# eg. a .c.do script.
#
# Returns the matching source file in $src, the compiler
# mode in $lang, and appends any redo-ifcreate targets to
# $missing.
lang=cc
for src in "$1.c"; do
[ -e "$src" ] && return
[ -e "$src.do" ] && return
missing="$missing$NL$src"
done
lang=cxx
for src in "$1.cc" "$1.cpp" "$1.cxx" "$1.C" "$1.c++"; do
[ -e "$src" ] && return
[ -e "$src.do" ] && return
missing="$missing$NL$src"
done
echo "default.do.sh: _pick_src: no source file found for '$1.*'" >&2
return 1
}
_objlist() {
local suffix="$1" list="$2" base="${2##*/}"
local dir="${2%"$base"}"
sed -Ee 's/\.(c|cc|cpp|cxx|C|c\+\+)$/'"$suffix/" <"$2" |
while read -r d; do
[ "$d" = "${d#-}" ] || continue
echo "$dir$d"
done
}
_flaglist() {
while read -r d; do
[ "$d" != "${d#-}" ] || continue
echo "$d"
done <"$1"
}
compile() {
redo-ifchange compile "$src" $dep
rm -f "$1.deps"
_mkdir_of "$3"
xCFLAGS="$xCFLAGS" PCH="$PCH" ./compile "$3" "$1.deps" "$src"
# TODO: make work with dependency filenames containing whitespace.
# gcc writes whitespace-containing filenames with spaces
# prefixed by backslash. read (without -r) will remove the
# backslashes but still use spaces for word splitting, so
# it loses the distinction. rc_splitwords() is the right
# function, but currently has a max word limit.
read deps <"$2.deps"
redo-ifchange ${deps#*:}
}
case $1 in
*.fpic.o)
_pick_src "$S/${1%.fpic.o}"
_add_missing
xCFLAGS="-fPIC" PCH="2" dep="$lang-fpic.precompile" compile "$@"
exit # fast path: exit as early as possible
;;
*.o)
_pick_src "$S/${1%.o}"
_add_missing
xCFLAGS="" PCH="1" dep="$lang.precompile" compile "$@"
exit # fast path: exit as early as possible
;;
*.h.fpic.gch|*.hpp.fpic.gch)
src="$S/${1%.fpic.gch}"
xCFLAGS="-fPIC" PCH="" dep="" compile "$@"
# precompiled header is "unchanged" if its component
# headers are unchanged.
cat ${deps#*:} | tee $1.stamp | redo-stamp
;;
*.h.gch|*.hpp.gch)
src="$S/${1%.gch}"
xCFLAGS="" PCH="" dep="" compile "$@"
# precompiled header is "unchanged" if its component
# headers are unchanged.
cat ${deps#*:} | tee $1.stamp | redo-stamp
;;
*.a)
listf="${1%.a}.list"
redo-ifchange "$listf"
files=$(_objlist .o "$listf")
IFS="$NL"
redo-ifchange $files
ar q "$3" $files
;;
*.so)
verf="${1%.so}.ver"
listf="${1%.so}.list"
redo-ifchange link-shlib "$verf" "$listf"
read ver <"$verf"
files=$(_objlist .fpic.o "$listf")
xLIBS=$(_flaglist "$listf")
IFS="$NL"
redo-ifchange $files
xLIBS="$xLIBS" ./link-shlib "$1.$ver" $files
ln -s "$(basename "$1.$ver")" "$3"
ln -sf "$1.$ver" .
;;
*.list|*.ver)
if [ -e "$S/$1" ]; then
redo-ifchange "$S/$1"
_mkdir_of "$3"
cp "$S/$1" "$3"
else
echo "default.do.sh: no rule to build '$1'" >&2
exit 99
fi
;;
*)
# In unix, binary names typically don't have any special filename
# pattern, so we have to handle them in a catch-all here.
# We detect that it's intended as a binary, and not just a typo,
# by the existence of a .list or .list.od file with the same name,
# defining the list of input files.
#
# Some people like to name their binaries *.exe, eg. on Windows,
# even though that doesn't matter anymore, even on Windows.
# So use the same rules for generating a .exe as a non-.exe.
bin="${1%.exe}"
if [ -e "$S/$bin.list" -o -e "$S/$bin.list.od" ]; then
# a final program binary
redo-ifchange link "$bin.list"
files=$(_objlist .o "$bin.list")
xLIBS=$(_flaglist "$bin.list")
IFS="$NL"
redo-ifchange $files
xLIBS="$xLIBS" ./link "$3" $files
else
echo "default.do.sh: no rule to build '$1' or '$1.list'" >&2
exit 99
fi
;;
esac

View file

@ -0,0 +1,31 @@
# Run the extra steps necessary before compiling
# C/C++ programs of the specified type.
#
# Notably, we have to precompile any precompiled
# headers. We also generate redoconf.h in case
# programs want to include it.
. ./redoconf.rc
rc_include _all.rc
case ${1%.precompile} in
cc) pch_files="$PRE_CC_TARGETS" ;;
cc-fpic) pch_files="$PRE_CC_TARGETS_FPIC" ;;
cxx) pch_files="$PRE_CXX_TARGETS" ;;
cxx-fpic) pch_files="$PRE_CXX_TARGETS_FPIC" ;;
*) exit 42 ;;
esac
IFS="$NL"
set -f
redo-ifchange "redoconf.h" $pch_files
# Subtle:
# Don't consider this target to have changed unless
# the precompiled header's stamp has changed.
# We generate redoconf.h, in case
# a C program wants to include it, but we
# don't care if it has changed, because the C program
# will have its own dependency on that file.
for d in $t; do
cat "$d.stamp"
done | redo-stamp

36
redoconf/link-shlib.od Normal file
View file

@ -0,0 +1,36 @@
. ./redoconf.rc
rc_include _all.rc rc/shlib.rc
# Tricky quoting: see _compile.od for details.
if [ "$HAVE_SHLIB" = 1 ]; then
cat >$3 <<-EOF
#!/bin/sh -e
LINK=$(shquote "$LINK")
LDFLAGS=$(shquote "$LDFLAGS")
OPTFLAGS=$(shquote "$OPTFLAGS")
LIBS=$(shquote "$LIBS")
o="\$1"
ob="\${o#*/}"
shift
IFS="$NL"
set -f
\$LINK -shared -o "\$o" \\
-Wl,-soname,"\$ob" \\
\$LDFLAGS \$OPTFLAGS \\
"\$@" \\
\$LIBS
EOF
else
# If no shared library support and we try to build one,
# compensate by building a static library instead in the
# same place.
cat >$3 <<-EOF
#!/bin/sh -e
o="\$1"
shift
rm -f "\$o"
ar q "\$o" "\$@"
EOF
fi
chmod a+x "$3"
redo-stamp <$3

22
redoconf/link.od Normal file
View file

@ -0,0 +1,22 @@
. ./redoconf.rc
rc_include _all.rc
# Tricky quoting: see _compile.od for details.
cat >$3 <<-EOF
#!/bin/sh -e
LINK=$(shquote "$LINK")
LDFLAGS=$(shquote "$LDFLAGS")
OPTFLAGS=$(shquote "$OPTFLAGS")
LIBS=$(shquote "$LIBS")
o="\$1"
shift
IFS="$NL"
set -f
\$LINK -o "\$o" \\
-Wl,-rpath,'\$ORIGIN' \\
\$LDFLAGS \$OPTFLAGS \\
"\$@" \\
\$LIBS
EOF
chmod a+x "$3"
redo-stamp <$3

240
redoconf/rc.sh Normal file
View file

@ -0,0 +1,240 @@
# 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
}

68
redoconf/rc/CC.rc.od Normal file
View file

@ -0,0 +1,68 @@
. ./redoconf.rc
rc_include
rc_helpmsg ARCH "Architecture prefix for output (eg. i686-w64-mingw32-)"
rc_helpmsg CC "C compiler name (cc)"
rc_helpmsg CPPFLAGS "Extra C preprocessor flags (eg. -I... -D...)"
rc_helpmsg CFLAGS "Extra C compiler flags (eg. -O2 -g)"
rc_helpmsg OPTFLAGS "C/C++ compiler flag overrides (eg. -g0)"
rc_helpmsg LINK "Linker name (cc)"
rc_helpmsg LDFLAGS "Extra linker options (eg. -s -static)"
rc_helpmsg LIBS "Extra libraries to always link against (eg. -lsocket)"
rc_helpmsg STATIC "Link libraries and binaries statically"
if [ -n "$CC" ]; then
set -- "$CC"
else
if [ -n "$ARCH" ] && [ "$ARCH" = "${ARCH%-}" ]; then
# Make sure arch name includes trailing dash if nonempty
ARCH="$ARCH-"
fi
set -- \
"${ARCH}cc" "${ARCH}gcc" \
"${ARCH}clang" "/usr/bin/${ARCH}clang"-[0-9]* \
"${ARCH}c++" "${ARCH}g++" \
"${ARCH}clang++" "/usr/bin/${ARCH}clang++"-[0-9]*
fi
rc_appendln CPPFLAGS "-I."
[ -n "$STATIC" ] && rc_appendln LDFLAGS "-static"
for d in "$@"; do
echo "Trying C compiler: '$d'" >&2
if CC="$d" CXX="" LINK="$d" rc_compile cc link 'extern int i;'; then
rc_replaceln CC "$d"
rc_replaceln LINK "$d"
# mingw32 (not mingw64) generates binaries that need
# a dynamic libgcc at runtime for certain operations
# (like 64-bit integer division), which is non-obvious.
# Include it statically if possible, but only on Windows,
# which we'll detect by whether windows.h exists.
#
# If adding the option fails, maybe it's some
# compiler other than gcc, so that's fine.
x="-static-libgcc$NL-static-libstdc++"
appendln LDFLAGS "$x"
prog='#include <windows.h>'
if CC="$d" LINK="$d" rc_compile cc link "$prog"; then
rc_appendln LDFLAGS "$x"
fi
# Set ARCH= to the right compiler prefix, either empty
# (use native compiler) or something like "i686-w64-mingw32-"
# (terminating dash), based on the compiler name.
x="-${CC##*/}"
x="${x%-*}"
x="${x#-}"
[ -n "$x" ] && x="$x-"
rc_replaceln ARCH "$x"
rc_save
exit 0
fi
done
echo "Can't find a working C compiler." >&2
rc_undo
exit 1

35
redoconf/rc/CXX.rc.od Normal file
View file

@ -0,0 +1,35 @@
. ./redoconf.rc
rc_include rc/CC.rc
rc_helpmsg CXX "C++ compiler name (c++)"
rc_helpmsg CXXFLAGS "Extra C++ compiler flags (eg. -O2 -g)"
if [ -n "$CXX" ]; then
set -- "$CXX"
else
# Note: $ARCH has already been set correctly by CC.rc
set -- \
"${ARCH}c++" "${ARCH}g++" \
"${ARCH}clang++" "/usr/bin/${ARCH}clang++"-[0-9]*
fi
for d in "$@"; do
[ -n "$d" ] || continue
echo "Trying C++ compiler: '$d'" >&2
if CC="" CXX="$d" LINK="$d" rc_compile cxx link 'class A {};'; then
rc_replaceln CXX "$d"
# If the project activates CXX.rc, then we
# replace the C linker with C++. This causes
# it to include -lstdc++, etc.
# A future .rc could override this again.
rc_replaceln LINK "$d"
rc_save
exit 0
fi
done
echo "Warning: Can't find a working C++ compiler." >&2
rc_undo
rc_replaceln CXX ""
rc_save
exit 0

9
redoconf/rc/Wall.rc.od Normal file
View file

@ -0,0 +1,9 @@
. ./redoconf.rc
rc_include rc/CC.rc
rc_appendln CPPFLAGS "-Wall"
if rc_compile cc nolink; then
rc_save
else
rc_undo
fi

9
redoconf/rc/Wextra.rc.od Normal file
View file

@ -0,0 +1,9 @@
. ./redoconf.rc
rc_include rc/CC.rc
rc_appendln CPPFLAGS "-Wextra"
if rc_compile cc nolink; then
rc_save
else
rc_undo
fi

8
redoconf/rc/_init.rc.od Normal file
View file

@ -0,0 +1,8 @@
if [ -e _flags ]; then
redo-ifchange _flags
cat _flags >$3
redo-stamp <_flags
else
redo-ifcreate _flags
redo-stamp </dev/null
fi

View file

@ -0,0 +1,18 @@
. ./redoconf.rc
rc_include rc/CC.rc
base="${1#*/}"
lib="${base%.autolib.rc}"
LIB=$(echo "$lib" | tr 'a-z.' 'A-Z_')
x="-l$lib"
rc_appendln "LIB$LIB" "$x"
appendln LIBS "$x"
if rc_compile cc link ""; then
rc_replaceln "HAVE_$LIB" 1
else
rc_undo
rc_replaceln "HAVE_$LIB" ""
rc_replaceln "LIB$LIB" ""
fi
rc_save

View file

@ -0,0 +1,13 @@
. ./redoconf.rc
rc_include rc/CC.rc
base="${1#*/}"
f="${base%.func.rc}"
F=$(echo "$f" | tr 'a-z.' 'A-Z_')
if rc_compile cc link "void $f(); void test() { $f(); }"; then
rc_replaceln "HAVE_$F" 1
else
rc_replaceln "HAVE_$F" ""
fi
rc_save

View file

@ -0,0 +1,23 @@
. ./redoconf.rc
rc_include rc/CC.rc
base="${1#rc/}"
src="${base%.h.precompiled.rc}"
# The existence of the specific gcc warning about
# precompiled headers is a pretty good indicator
# that they are supported in the way we expect.
rc_appendln CFLAGS "-Winvalid-pch"
if rc_compile cc nolink; then
rc_appendln CFLAGS "$x"
rc_appendln CFLAGS_PCH "-include$NL$src.h"
rc_appendln CFLAGS_PCH_FPIC "-include$NL$src.h.fpic"
rc_appendln PRE_CC_TARGETS "$src.h.gch"
rc_appendln PRE_CC_TARGETS_FPIC "$src.h.fpic.gch"
rc_save
else
echo "Precompiled C headers not supported." >&2
rc_undo
fi

View file

@ -0,0 +1,13 @@
. ./redoconf.rc
rc_include rc/CC.rc
base="${1#*/}"
h="${base%.rc}"
H=$(echo "$h" | tr 'a-z.' 'A-Z_')
if rc_compile cc nolink "#include <$h>"; then
rc_replaceln "HAVE_$H" 1
else
rc_replaceln "HAVE_$H" ""
fi
rc_save

View file

@ -0,0 +1,23 @@
. ./redoconf.rc
rc_include rc/CXX.rc
base="${1#rc/}"
src="${base%.hpp.precompiled.rc}"
# The existence of the specific gcc warning about
# precompiled headers is a pretty good indicator
# that they are supported in the way we expect.
appendln CXXFLAGS "-Winvalid-pch"
if rc_compile cxx nolink; then
rc_appendln CXXFLAGS "$x"
rc_appendln CXXFLAGS_PCH "-include$NL$src.hpp"
rc_appendln CXXFLAGS_PCH_FPIC "-include$NL$src.hpp.fpic"
rc_appendln PRE_CXX_TARGETS "$src.hpp.gch"
rc_appendln PRE_CXX_TARGETS_FPIC "$src.hpp.fpic.gch"
rc_save
else
echo "Precompiled C++ headers not supported." >&2
rc_undo
fi

12
redoconf/rc/libgl.rc.od Normal file
View file

@ -0,0 +1,12 @@
. ./redoconf.rc
rc_include rc/CC.rc rc/pkg-config.rc
prog='
#include <GL/gl.h>
int x = GL_VERSION_1_1;
void f() { glPopMatrix(); }
'
rc_pkg_detect LIBGL "gl" \
rc_compile cc link "$prog"
rc_save

12
redoconf/rc/libgtk2.rc.od Normal file
View file

@ -0,0 +1,12 @@
. ./redoconf.rc
rc_include rc/CC.rc rc/pkg-config.rc
prog='
#include <gtk/gtk.h>
int x = GTK_MAJOR_VERSION;
void f() { gtk_widget_child_focus(0, 0); }
'
rc_pkg_detect LIBGTK2 "gtk+-2.0 gio-2.0 gdk-2.0 gdk-pixbuf-2.0" \
rc_compile cc link "$prog"
rc_save

25
redoconf/rc/libm.rc.od Normal file
View file

@ -0,0 +1,25 @@
. ./redoconf.rc
rc_include rc/CC.rc rc/pkg-config.rc
prog='
#include <math.h>
int x = FP_NORMAL;
volatile float y;
void f() { y = sin(y); }
'
rc_pkg_detect LIBM libm \
rc_compile cc link "$prog"
if [ -z "$HAVE_LIBM" ]; then
rc_undo
rc_replaceln HAVE_LIBM 1
rc_appendln LIBM "-lm"
appendln LIBS "$LIBM"
if ! rc_compile cc link "$prog"; then
rc_undo
rc_replaceln HAVE_LIBM ""
rc_replaceln LIBM ""
fi
fi
rc_save

12
redoconf/rc/libpng.rc.od Normal file
View file

@ -0,0 +1,12 @@
. ./redoconf.rc
rc_include rc/CC.rc rc/pkg-config.rc
prog='
#include <png.h>
const char *x = PNG_LIBPNG_VER_STRING;
void f() { png_access_version_number(); }
'
rc_pkg_detect LIBPNG libpng \
rc_compile cc link "$prog"
rc_save

10
redoconf/rc/libqt4.rc.od Normal file
View file

@ -0,0 +1,10 @@
. ./redoconf.rc
rc_include rc/CXX.rc rc/pkg-config.rc
prog='
#include <QVector>
'
rc_pkg_detect LIBQT4 QtCore \
rc_compile cxx link "$prog"
rc_save

12
redoconf/rc/libsdl.rc.od Normal file
View file

@ -0,0 +1,12 @@
. ./redoconf.rc
rc_include rc/CC.rc rc/pkg-config.rc
prog='
#include <SDL.h>
int x = SDL_INIT_TIMER;
void f() { SDL_Init(0); }
'
rc_pkg_detect LIBSDL "sdl" \
rc_compile cc link "$prog"
rc_save

12
redoconf/rc/libx11.rc.od Normal file
View file

@ -0,0 +1,12 @@
. ./redoconf.rc
rc_include rc/CC.rc rc/pkg-config.rc
prog='
#include <X11/Xutil.h>
int x = XYBitmap;
void f() { XCreateRegion(); }
'
rc_pkg_detect LIBX11 "x11" \
rc_compile cc link "$prog"
rc_save

View file

@ -0,0 +1,17 @@
. ./redoconf.rc
rc_include
for d in "$PKG_CONFIG" pkg-config; do
rc_undo
if "$d" --version >/dev/null 2>&1; then
rc_replaceln PKG_CONFIG "$d"
rc_replaceln HAVE_PKG_CONFIG 1
rc_save
exit 0
fi
done
# Failed
rc_replaceln HAVE_PKG_CONFIG ""
rc_replaceln PKG_CONFIG ""
rc_save

27
redoconf/rc/run.rc.od Normal file
View file

@ -0,0 +1,27 @@
. ./redoconf.rc
rc_include rc/CC.rc rc/windows.h.rc
consider() {
echo "Considering RUN=$(shquote "$1")" >&2
if [ -z "$1" ] || type "$1" >/dev/null 2>&1; then
rc_undo
rc_replaceln RUN "$1"
if RUN="$RUN" rc_compile cc run ""; then
rc_replaceln CAN_RUN 1
rc_save
exit 0
fi
fi
}
consider ""
if [ -n "$HAVE_WINDOWS_H" ]; then
consider "wine64"
consider "wine"
consider "wine32"
fi
rc_undo
rc_replaceln RUN ""
rc_replaceln CAN_RUN ""
rc_save

21
redoconf/rc/shlib.rc.od Normal file
View file

@ -0,0 +1,21 @@
. ./redoconf.rc
rc_include rc/CC.rc
appendln CFLAGS "-fPIC"
appendln LDFLAGS "-shared"
prog='
#include <stdlib.h>
void f() { atoi(""); }
'
if [ -n "$STATIC" ]; then
echo "--enable-static specified; not building shared libraries." >&2
rc_replaceln HAVE_SHLIB ""
elif RCC_NO_MAIN=1 rc_compile cc link "$prog"; then
rc_replaceln HAVE_SHLIB 1
else
echo "Not building shared libraries on this platform." >&2
rc_replaceln HAVE_SHLIB ""
fi
rc_save

11
redoconf/rc/zdefs.rc.od Normal file
View file

@ -0,0 +1,11 @@
. ./redoconf.rc
rc_include rc/CC.rc
x="-Wl,-z,defs"
rc_appendln LDFLAGS "$x"
if rc_compile cc link; then
rc_save
else
echo "'$x' doesn't work on this platform; skipped." >&2
rc_undo
fi

25
redoconf/rc_vars.od Normal file
View file

@ -0,0 +1,25 @@
. ./redoconf.rc
# this is called by each call to replaceln() and appendln().
RC_KEYS="RC_KEYS"
rc_hook() {
contains_line "$RC_KEYS" "$1" || RC_KEYS="$RC_KEYS$NL$1"
}
rc_include _all.rc
# Escape double-quote and backslash so they can
# be included as a C-style double-quoted string.
cquote() {
local v="$(xecho "$1" | sed -e 's,[\"\\],\\&,g' -e 's,$, \\,')"
printf '"%s"' "${v% \\}"
}
(
echo "# Automatically generated by autovars.od"
for k in $(echo "$RC_KEYS" | sort); do
[ "$k" != "RC_INCLUDES" ] || continue
eval v=\$$k
echo "$k=$(shquote "$v")"
done
) >$3
redo-stamp <$3

34
redoconf/redoconf.h.od Normal file
View file

@ -0,0 +1,34 @@
redo-ifchange rc_vars
. ./rc_vars
NL="
"
# Escape double-quote and backslash so they can
# be included as a C-style double-quoted string.
cquote() {
local v="$(printf '%s' "$1" | sed -e 's,[\"\\],\\&,g' -e 's,$, \\,')"
printf '"%s"' "${v% \\}"
}
is_number() {
expr "$1" + 1 >/dev/null 2>&1
}
(
echo "/* Automatically generated by redoconf.h.od */"
IFS="$NL"
for k in $RC_KEYS; do
[ "$k" != "RC_KEYS" ] || continue
[ "$k" != "RC_INCLUDES" ] || continue
eval v=\$$k
if [ -z "$v" ]; then
echo "#undef $k"
elif is_number "$v"; then
echo "#define $k $v"
else
echo "#define $k $(cquote "$v")"
fi
done
) >$3
redo-stamp <$3

16
redoconf/run.od Normal file
View file

@ -0,0 +1,16 @@
. ./redoconf.rc
rc_include rc/run.rc
if [ -n "$CAN_RUN" ]; then
cat >$3 <<-EOF
#!/bin/sh -e
# Run the given program, possibly under an emulator.
[ -n "\$1" ]
unset DISPLAY
exec $RUN "\$@"
EOF
chmod a+x "$3"
redo-stamp <$3
else
echo "Cannot run programs; not creating run script." >&2
fi

71
redoconf/trycompile Normal file
View file

@ -0,0 +1,71 @@
#!/bin/sh -e
die() {
echo "$0: trycompile: $*" >&2
exit 99
}
ctype=$1
linktype=$2
code=$3
case $ctype in
cc)
[ -n "$CC" ] || die 'must set $CC first.'
useCC="$CC"
useCF="$CFLAGS"
useExt=".c"
;;
cxx)
[ -n "$CXX" ] || die 'must set $CXX first.'
useCC="$CXX"
useCF="$CXXFLAGS"
useExt=".cc"
;;
*)
die "unknown compile type '$ctype'"
;;
esac
case $linktype in
link|run)
[ -n "$LINK" ] || die 'must set $LINK first.'
;;
nolink)
;;
*)
die "unknown link type '$linktype'"
;;
esac
base="try.$$.tmp"
out="$base.o"
out2="$base.exe"
src="$base$useExt"
rm -f "$src" "$out" "$out2"
set -x
: "[trycompile]" "$@"
main=
[ -n "$RCC_NO_MAIN" ] || main="int main() { return 0; }"
printf '%s' "
$code
$main
" >"$src"
NL="
"
IFS="$NL"
set +e
set -f
# We intentionally want to split the variables here,
# splitting on $NL, so we don't quote them.
# 'set -f' prevents interpreting wildcards, which
# we don't want to treat as special.
(
$useCC $CPPFLAGS $useCF -o "$out" -c "$src" || exit
if [ "$linktype" = "link" -o "$linktype" = "run" ]; then
$LINK $LDFLAGS -o "$out2" "$out" $LIBS || exit
fi
if [ "$linktype" = "run" ]; then
$RUN "./$out2" || exit
fi
)
rv=$?
rm -f "$src" "$out" "$out2"
exit "$rv"

58
redoconf/utils.sh Normal file
View file

@ -0,0 +1,58 @@
NL="
"
# Like 'echo', but never processes backslash escapes.
# (Some shells' builtin echo do, and some don't, so this
# is safer.)
xecho() {
printf '%s\n' "$*"
}
# Returns true if string $1 contains the line $2.
# Lines are delimited by $NL.
contains_line() {
case "$NL$1$NL" in
*"$NL$2$NL"*) return 0 ;;
*) return 1 ;;
esac
}
# Split the first (up to) 20 words from $1,
# returning a string where the words are separated
# by $NL instead.
#
# To allow words including whitespace, you can backslash
# escape the whitespace (eg. hello\ world). Backslashes
# will be removed from the output string.
#
# We can use this to read pkg-config output, among other
# things.
#
# TODO: find a POSIX sh way to eliminate the word limit.
# I couldn't find an easy way to split on non-backslashed
# whitespace without a fork-exec, which is too slow.
# If we resorted to bashisms, we could use 'read -a',
# but that's not portable.
rc_splitwords() {
xecho "$1" | (
read v0 v1 v2 v3 v4 v5 v6 v7 v8 v9 \
v10 v11 v12 v13 v14 v15 v16 v17 v18 v19 \
x
if [ -n "$x" ]; then
echo "rc_splitwords: too many words" >&2
exit 97
fi
for d in "$v0" "$v1" "$v2" "$v3" "$v4" \
"$v5" "$v6" "$v7" "$v8" "$v9" \
"$v10" "$v11" "$v12" "$v13" "$v14" \
"$v15" "$v16" "$v17" "$v18" "$v19"; do
[ -z "$d" ] || xecho "$d"
done
)
}
# Escape single-quote characters so they can
# be included as a sh-style single-quoted string.
shquote() {
printf "'%s'" "$(xecho "$1" | sed -e "s,','\\\\'',g")"
}