1 #!/bin/sh
2 #
3 # POSIX shell script to detect target system properties required by Oil.
4 # Distributed with the source tarball.
5 #
6 # The only library Oil needs is readline.
7 #
8 # External utilities used: cc
9 #
10 # TODO: Should be able to run this from another directory.
11 #
12 # Other settings: LTO, PGO? Consider moving prefix, LTO, PGO to build and
13 # install steps.
14
15 TMP=${TMPDIR:-/tmp} # Assume that any system has $TMPDIR set or /tmp exists
16 readonly TMP # POSIX sh supports 'readonly'
17
18 log() {
19 echo "$0: $@" 1>&2
20 }
21
22 info() {
23 echo "$0 INFO: $@" 1>&2
24 }
25
26 die() {
27 echo "$0 ERROR: $@" 1>&2
28 exit 1
29 }
30
31 show_help() {
32 cat <<'EOF'
33 Usage: ./configure [OPTION...]
34
35 Detects system settings before a build of Oil.
36
37 Installation directories:
38 --prefix=PREFIX Prefix for the bin/ directory [/usr/local]
39 --datarootdir=DATAROOTDIR Prefix for data files, including man page [PREFIX/share]
40
41 Optional features:
42 --with-readline Fail unless readline is available.
43 --without-readline Don't compile with readline, even if it's available.
44 The shell won't have any interactive features.
45 --readline=DIR An alternative readline installation to link against
46 EOF
47 }
48
49 # This script roughly follows the GNU Standards
50 # https://www.gnu.org/prep/standards/html_node/Configuration.html
51 # https://www.gnu.org/prep/standards/html_node/Directory-Variables.html
52 #
53 # Default installation is /usr/local/bin/oil, but this can be changed with
54 # --prefix.
55 #
56 # While this script only uses a handful of the standard directory variables
57 # listed on the above documents, it accepts most of them in the --arg=value
58 # form as noops. This helps automated build-systems passing preconfigured
59 # sets of arguments to configure oil.
60 FLAG_prefix='/usr/local'
61 FLAG_datarootdir='' # default initialized after processing flags
62 FLAG_with_readline='' # Fail if it's not available.
63 FLAG_without_readline='' # Don't even check if it's available
64 FLAG_readline=''
65
66 # These variables are set by detect_readline and used by echo_cpp and
67 # echo_shell_vars
68 detected_readline=''
69 have_readline=''
70 readline_dir=''
71
72 parse_flags() {
73 while true; do
74 case "$1" in
75 '')
76 break
77 ;;
78 --help)
79 # TODO: Fill out help
80 show_help
81 exit 0
82 ;;
83
84 --with-readline)
85 FLAG_with_readline=1
86 ;;
87
88 --without-readline)
89 FLAG_without_readline=1
90 ;;
91
92 --readline=*)
93 FLAG_readline="${1#*=}"
94 ;;
95 --readline)
96 if test $# -eq 1; then
97 die "--readline requires an argument"
98 fi
99 shift
100 FLAG_readline=$1
101 ;;
102
103 # TODO: Maybe prefix only needs to be part of the install step? I'm not
104 # sure if we need it for building anything.
105 --prefix=*)
106 FLAG_prefix="${1#*=}"
107 ;;
108 --prefix)
109 if test $# -eq 1; then
110 die "--prefix requires an argument"
111 fi
112 shift
113 FLAG_prefix=$1
114 ;;
115
116 # Following autoconf's spelling of --mandir
117 --datarootdir=*)
118 FLAG_datarootdir="${1#*=}"
119 ;;
120 --datarootdir)
121 if test $# -eq 1; then
122 die "--datarootdir requires an argument"
123 fi
124 shift
125 FLAG_datarootdir=$1
126 ;;
127
128 --with-*|--enable-*)
129 info "Argument '$1' not used by this configure script"
130 ;;
131
132 --build=*|--host=*)
133 info "Argument '$1' not used by this configure script"
134 ;;
135
136 --exec-prefix=*|--bindir=*|--sbindir=*|--libexecdir=*|--sysconfdir=*)
137 info "Argument '$1' not used by this configure script"
138 ;;
139 --sharedstatedir=*|--localstatedir=*|--runstatedir=*)
140 info "Argument '$1' not used by this configure script"
141 ;;
142 --libdir=*|--includedir=*|--oldincludedir=*)
143 info "Argument '$1' not used by this configure script"
144 ;;
145 --datadir=*|--infodir=*|--localedir=*|--mandir=*|--docdir=*)
146 info "Argument '$1' not used by this configure script"
147 ;;
148 --htmldir=*|--dvidir=*|--pdfdir=*|--psdir=*)
149 info "Argument '$1' not used by this configure script"
150 ;;
151
152 *)
153 die "Invalid argument '$1'"
154 ;;
155 esac
156 shift
157 done
158
159 # If not set, fallback to --prefix
160 FLAG_datarootdir=${FLAG_datarootdir:-$FLAG_prefix/share}
161 }
162
163 # No output file, no logging, no stderr.
164 # TODO: Maybe send stdout/stderr to config.log?
165 cc_quiet() {
166 cc "$@" -o /dev/null >/dev/null 2>&1
167 }
168
169 cc_or_die() {
170 if ! cc "$@" >$TMP/cc.log 2>&1; then
171 log "Error running 'cc $@':"
172 cat $TMP/cc.log
173 die "Fatal compile error running feature test"
174 fi
175 }
176
177 # Check if a given program compiles
178 cc_statement() {
179 local pp_var="$1"
180 local prog="$2"
181 local includes="$3"
182
183 cat >$TMP/cc_statement.c <<EOF
184 $includes
185 int main() {
186 $prog
187 }
188 EOF
189 # Return exit code of compiler
190 if cc_quiet $TMP/cc_statement.c; then
191 echo "#define $pp_var 1"
192 return 0
193 else
194 return 1
195 fi
196 }
197
198 # Check if a given library is installed via compilation
199 cc_header_file() {
200 local pp_var="$1"
201 local c_lib="$2"
202
203 cc_statement "$pp_var" 'return 0;' "#include <$c_lib>"
204 }
205
206 detect_readline() {
207 detected_readline=1 # for assertions in echo_shell_vars and echo_cpp
208
209 # User disabled readline
210 if test -n "$FLAG_without_readline"; then
211 # have_readline remains false
212 return
213 fi
214
215 # User requested specific location
216 if test -n "$FLAG_readline"; then
217 if cc_quiet build/detect-readline.c \
218 -L "$FLAG_readline/lib" \
219 -I "$FLAG_readline/include" \
220 -l readline; then
221
222 readline_dir="$FLAG_readline"
223 have_readline=1
224 fi
225 return
226 fi
227
228 # Detect in default location
229 if cc_quiet build/detect-readline.c -l readline; then
230 have_readline=1
231 return
232 fi
233
234 # User requested that it be found
235 if test "$FLAG_with_readline" = 1 && test "$have_readline" != 1; then
236 die 'readline was not detected on the system (--with-readline passed).'
237 fi
238 }
239
240 echo_shell_vars() {
241 if test "$detected_readline" != 1; then
242 die 'called echo_shell_vars before detecting readline.'
243 fi
244 if test "$have_readline" = 1; then
245 echo 'HAVE_READLINE=1'
246 echo "READLINE_DIR=$readline_dir"
247 else
248 echo 'HAVE_READLINE='
249 # Present a consistent interface to build/ninja-rules-cpp.sh
250 echo 'READLINE_DIR='
251 fi
252 echo "PREFIX=$FLAG_prefix"
253 echo "DATAROOTDIR=$FLAG_datarootdir"
254 if cc_quiet build/detect-cc.c -Wl,--gc-sections; then
255 echo 'STRIP_FLAGS=--gc-sections'
256 elif cc_quiet build/detect-cc.c -Wl,-dead_strip; then
257 echo 'STRIP_FLAGS=-dead_strip'
258 fi
259 }
260
261 # c.m4 AC_LANG_INT_SAVE
262 cc_print_expr() {
263 local c_expr="$1"
264 cat >$TMP/print_expr.c <<EOF
265 #include <stdio.h>
266 #include <sys/types.h> /* size_t, pid_t */
267
268 int main() {
269 printf("%lu", $c_expr);
270 }
271 EOF
272 cc_or_die -o $TMP/print_expr $TMP/print_expr.c
273 $TMP/print_expr > $TMP/print_expr.out
274 }
275
276 # Shell note:
277 # - local is not POSIX, but most shells have it.
278 # C note:
279 # - autoconf uses ac_fn_compute_int (in sh) aka AC_COMPUTE_INT (in m4).
280 # - it uses different tests when cross compiling.
281 # - cross-compiling does binary search?
282 # - other one does AC_LANG_INT_SAVE
283 # - generates a C program that outputs to conftest.val!
284 # - well why not use exit code?
285 # - QEMU configure doesn't do any tests
286
287 # Hm, don't bother with cross compiling case for now.
288
289 # Check if the size of a type is greater than a certain integer.
290 check_sizeof() {
291 local pp_var="$1"
292 local c_type="$2"
293 local min_bytes="$3"
294
295 cc_print_expr "sizeof($c_type)"
296
297 local actual_bytes
298 actual_bytes=$(cat $TMP/print_expr.out)
299
300 if test -n "$min_bytes" && test "$actual_bytes" -lt "$min_bytes"; then
301 die "sizeof($c_type) should be at least $min_bytes; got $actual_bytes"
302 fi
303
304 # Echo to stdout!
305 echo "#define $pp_var $actual_bytes"
306 }
307
308 detect_c_language() {
309 # This is the equivalent of AC_CHECK_SIZEOF(int, 4)
310 check_sizeof SIZEOF_INT 'int' 4
311 check_sizeof SIZEOF_LONG 'long' 4
312 check_sizeof SIZEOF_VOID_P 'void *' 4
313 check_sizeof SIZEOF_SHORT 'short' 2
314 check_sizeof SIZEOF_FLOAT 'float' 4
315 check_sizeof SIZEOF_DOUBLE 'double' 8
316
317 check_sizeof SIZEOF_SIZE_T 'size_t' 4
318
319 # NOTE: This might only be relevant for large file support, which we don't
320 # have.
321 check_sizeof SIZEOF_FPOS_T 'fpos_t' 4
322 check_sizeof SIZEOF_PID_T 'pid_t' 4
323
324 check_sizeof SIZEOF_OFF_T 'off_t' ''
325 # autoconf checks if we have time.h, but the check isn't used. We just
326 # assume it's there.
327 check_sizeof SIZEOF_TIME_T 'time_t' ''
328
329 if cc_statement HAVE_LONG_LONG 'long long x; x = (long long)0;'
330 then
331 check_sizeof SIZEOF_LONG_LONG 'long long' 8
332 fi
333 if cc_statement HAVE_LONG_DOUBLE 'long double x; x = (long double)0;'
334 then
335 check_sizeof SIZEOF_LONG_DOUBLE 'long double' 8
336 fi
337
338 if cc_statement HAVE_C99_BOOL '_Bool x; x = (_Bool)0;'
339 then
340 # NOTE: this is mainly used in ctypes.h, which we might not need.
341 check_sizeof SIZEOF__BOOL '_Bool' 1
342 fi
343 # NOTE: Python also has a check for C99 uintptr_t. Just assume we don't
344 # have it?
345
346 #if cc_statement HAVE_C99_BOOL 'wchar_t x; x = (wchar_t)0;'
347 #then
348 # check_sizeof SIZEOF_WCHAR_T 'wchar_t' 4
349 #fi
350
351 # TODO: Detect header and size.
352 echo '#define HAVE_WCHAR_H 1'
353 echo '#define SIZEOF_WCHAR_T 4'
354
355 cat >$TMP/detect_va_list.c <<EOF
356 #include <stdarg.h> /* C89 */
357 int main() {
358 va_list list1, list2;
359 list1 = list2;
360 }
361 EOF
362 if cc_quiet $TMP/detect_va_list.c; then
363 echo '' # not an array
364 else
365 echo '#define VA_LIST_IS_ARRAY 1'
366 fi
367
368 # TODO: are these feature checks really necessary, or can we
369 # strip these out of posixmodule.c entirely?
370 cc_header_file HAVE_PTY_H 'pty.h'
371 cc_header_file HAVE_LIBUTIL_H 'libutil.h'
372 cc_header_file HAVE_UTIL_H 'util.h'
373
374 # TODO: are these feature checks really necessary?
375 cc_statement HAVE_STAT_TV_NSEC \
376 'struct stat st; st.st_mtim.tv_nsec = 1; return 0;' \
377 '#include <sys/stat.h>'
378 cc_statement HAVE_STAT_TV_NSEC2 \
379 'struct stat st; st.st_mtimespec.tv_nsec = 1; return 0;' \
380 '#include <sys/stat.h>'
381 }
382
383 echo_cpp() {
384 if test "$detected_readline" != 1; then
385 die 'called echo_cpp before detecting readline.'
386 fi
387 # Dev builds can use non-portable clock_gettime()
388 if test -n "$_OIL_DEV"; then
389 echo '#define GC_TIMING 1'
390 log 'Turned on -D GC_TIMING because $_OIL_DEV is set'
391 fi
392
393 if test "$have_readline" = 1; then
394 echo '#define HAVE_READLINE 1'
395 else
396 echo '/* #undef HAVE_READLINE */'
397 fi
398 }
399
400 # Another way of working: set detected-config.mk ?
401 # And set the default target as oil_readline, oil_no_readline, oil_lto,
402 # oil_pgo, etc.?
403 main() {
404 parse_flags "$@" # sets FLAG_*
405
406 mkdir -p _build
407
408 if ! cc_quiet build/detect-cc.c; then
409 die "Couldn't compile a basic C program (cc not installed?)"
410 fi
411
412 # Sets globals $have_readline and $readline_dir
413 detect_readline
414
415 # Generate configuration for oil-native
416 local cpp_out=_build/detected-cpp-config.h
417 echo_cpp > $cpp_out
418 log "Wrote $cpp_out"
419
420 # Legacy OVM build: shell build actions will 'source
421 # _build/detected-config.sh'. And then adjust flags to compiler (-D, -l,
422 # etc.)
423 local sh_out=_build/detected-config.sh
424
425 echo_shell_vars > $sh_out
426 log "Wrote $sh_out"
427
428 # Fast mode
429 if test -n "$_OIL_DEV"; then
430 return
431 fi
432
433 local c_out=_build/detected-config.h
434 detect_c_language > $c_out
435 log "Wrote $c_out"
436 }
437
438 if test -z "$_OIL_CONFIGURE_TEST"; then
439 main "$@"
440 fi