#!/bin/sh # # POSIX shell script to detect target system properties required by Oil. # Distributed with the source tarball. # # The only library Oil needs is readline. # # External utilities used: expr, cc # # TODO: Should be able to run this from another directory. # # Other settings: LTO, PGO? Consider moving prefix, LTO, PGO to build and # install steps. setglobal TMP = $(TMP:-/tmp) # Assume that any system has $TMP set or /tmp exists readonly TMP # POSIX sh supports 'readonly' proc log { echo "$0: $ifsjoin(Argv)" !1 > !2 } proc die { echo "$0 ERROR: $ifsjoin(Argv)" !1 > !2 exit 1 } proc show_help { cat << """ Usage: ./configure [OPTION] Detects system settings before a build of Oil. --prefix Prefix for the bin/ directory [/usr/local] --with-readline Fail unless readline is available. --without-readline Don't compile with readline, even if it's available. The shell won't have any interactive features. """ } # Default installation is /usr/local/bin/oil, but this can be changed with # --prefix. We roughly follow GNU: # https://www.gnu.org/prep/standards/html_node/Directory-Variables.html setglobal FLAG_prefix = ''/usr/local'' setglobal FLAG_with_readline = '''' # Fail if it's not available. setglobal FLAG_without_readline = '''' # Don't even check if it's available> while true { match $1 { with '' break with --help # TODO: Fill out help show_help exit 0 with --with-readline setglobal FLAG_with_readline = '1' with --without-readline setglobal FLAG_without_readline = '1' # TODO: Maybe prefix only needs to be part of the install step? I'm not # sure if we need it for building anything. with --prefix=* setglobal FLAG_prefix = $[expr $1 : '--prefix=\(.*\)] with --prefix if test $Argc -eq 1 { die "--prefix requires an argument" } shift setglobal FLAG_prefix = $1 with * die "Invalid argument '$1'" } shift } # No output file, no logging, no stderr. # TODO: Maybe send stdout/stderr to config.log? proc cc_quiet { cc @Argv -o /dev/null >/dev/null !2 > !1 } proc cc_or_die { if ! cc @Argv >$TMP/cc.log !2 > !1 { log "Error running 'cc $ifsjoin(Argv)':" cat $TMP/cc.log die "Fatal compile error running feature test" } } # Check if a given program compiles proc cc_statement { local pp_var="$1" local prog="$2" cat >$TMP/cc_statement.c << """ int main() { $prog } """ # Return exit code of compiler if cc_quiet $TMP/cc_statement.c { echo "#define $pp_var 1" return 0 } else { return 1 } } # Write a shell script to standard out with variables, or fail. proc detect_readline { if cc_quiet build/detect-readline.c -l readline { echo 'HAVE_READLINE=1' } else { if test $FLAG_with_readline = 1 { die 'readline was not detected on the system (--with-readline passed).' } echo 'HAVE_READLINE=' } } proc detect_and_echo_vars { if test $FLAG_without_readline = 1 { echo 'HAVE_READLINE=' } else { detect_readline } echo "PREFIX=$FLAG_prefix" } # c.m4 AC_LANG_INT_SAVE proc cc_print_expr { local c_expr="$1" cat >$TMP/print_expr.c << """ #include #include /* size_t, pid_t */ int main() { printf("%lu", $c_expr); } """ cc_or_die -o $TMP/print_expr $TMP/print_expr.c $TMP/print_expr > $TMP/print_expr.out } # Shell note: # - local is not POSIX, but most shells have it. # C note: # - autoconf uses ac_fn_compute_int (in sh) aka AC_COMPUTE_INT (in m4). # - it uses different tests when cross compiling. # - cross-compiling does binary search? # - other one does AC_LANG_INT_SAVE # - generates a C program that outputs to conftest.val! # - well why not use exit code? # - QEMU configure doesn't do any tests # Hm, don't bother with cross compiling case for now. # Check if the size of a type is greater than a certain integer. proc check_sizeof { local pp_var="$1" local c_type="$2" local min_bytes="$3" cc_print_expr "sizeof($c_type)" local actual_bytes setglobal actual_bytes = $[cat $TMP/print_expr.out] if test -n $min_bytes && test $actual_bytes -lt $min_bytes { die "sizeof($c_type) should be at least $min_bytes; got $actual_bytes" } # Echo to stdout! echo "#define $pp_var $actual_bytes" } proc detect_c_language { # This is the equivalent of AC_CHECK_SIZEOF(int, 4) check_sizeof SIZEOF_INT 'int' 4 check_sizeof SIZEOF_LONG 'long' 4 check_sizeof SIZEOF_VOID_P 'void *' 4 check_sizeof SIZEOF_SHORT 'short' 2 check_sizeof SIZEOF_FLOAT 'float' 4 check_sizeof SIZEOF_DOUBLE 'double' 8 check_sizeof SIZEOF_SIZE_T 'size_t' 4 # NOTE: This might only be relevant for large file support, which we don't # have. check_sizeof SIZEOF_FPOS_T 'fpos_t' 4 check_sizeof SIZEOF_PID_T 'pid_t' 4 check_sizeof SIZEOF_OFF_T 'off_t' '' # autoconf checks if we have time.h, but the check isn't used. We just # assume it's there. check_sizeof SIZEOF_TIME_T 'time_t' '' if cc_statement HAVE_LONG_LONG 'long long x; x = (long long)0;' { check_sizeof SIZEOF_LONG_LONG 'long long' 8 } if cc_statement HAVE_LONG_DOUBLE 'long double x; x = (long double)0;' { check_sizeof SIZEOF_LONG_DOUBLE 'long double' 8 } if cc_statement HAVE_C99_BOOL '_Bool x; x = (_Bool)0;' { # NOTE: this is mainly used in ctypes.h, which we might not need. check_sizeof SIZEOF__BOOL '_Bool' 1 } # NOTE: Python also has a check for C99 uintptr_t. Just assume we don't # have it? #if cc_statement HAVE_C99_BOOL 'wchar_t x; x = (wchar_t)0;' #then # check_sizeof SIZEOF_WCHAR_T 'wchar_t' 4 #fi # TODO: Detect header and size. echo '#define HAVE_WCHAR_H 1' echo '#define SIZEOF_WCHAR_T 4' cat >$TMP/detect_va_list.c << """ #include /* C89 */ int main() { va_list list1, list2; list1 = list2; } """ if cc_quiet $TMP/detect_va_list.c { echo '' # not an array } else { echo '#define VA_LIST_IS_ARRAY 1' } } # Another way of working: set detected-config.mk ? # And set the default target as oil_readline, oil_no_readline, oil_lto, # oil_pgo, etc.? proc main { if ! cc_quiet build/detect-cc.c { die "Couldn't compile a basic C program (cc not installed?)" } # The shell build actions will 'source _build/detected-config.sh'. And then # adjust flags to compiler (-D, -l, etc.) mkdir -p _build local sh_out=_build/detected-config.sh local c_out=_build/detected-config.h detect_and_echo_vars > $sh_out detect_c_language > $c_out log "Wrote $sh_out and $c_out" } proc unittest { cc_print_expr 'sizeof(int)' local actual setglobal actual = $[cat $TMP/print_expr.out] test $actual = 4 || die "Expected 4, got $actual" check_sizeof SIZEOF_INT 'int' 4 || die "FAILED" # failing test #check_sizeof SIZEOF_INT 'int' 8 cc_statement HAVE_INT 'int x = (int)0;' || die "FAILED" cc_statement HAVE_FOO 'foo x = (foo)0;' && die "Expected to fail" #detect_c_language } main @Argv #unittest "$@"