]> git.xonotic.org Git - xonotic/gmqcc.git/blob - configure
configure: if the compiler has no known way to generate deps: use the preprocessor...
[xonotic/gmqcc.git] / configure
1 #!/bin/sh
2 # vim: ts=2 sts=2 sw=2 et:
3
4 progname="$0"
5 cf_log="config.log"
6
7 usage() {
8   cat <<EOF
9 ${progname} [options]
10 options:
11   Target directories:
12     --prefix=PREFIX      change the install prefix [/usr/local]
13     --bindir=BINDIR      target of executables [PREFIX/bin]
14     --datadir=DATADIR    target of additional data [PREFIX/share]
15     --mandir=MANDIR      target of manpages [DATADIR/man]
16     --man1dir=MAN1DIR    manual section 1 [MANDIR/man1]
17   Environment variables:
18     CC, CPP, CFLAGS, CPPFLAGS
19 EOF
20   exit 1
21 }
22
23 parse_cmdline() {
24   while [ $# -ge 1 ]; do
25     case "$1" in
26       --prefix=*)  cf_prefix="${1#--prefix=}" ;;
27       --bindir=*)  cf_bindir="${1#--bindir=}" ;;
28       --datadir=*) cf_datadir="${1#--datadir=}" ;;
29       --mandir=*)  cf_mandir="${1#--mandir=}"   ;;
30       --man1dir=*) cf_man1dir="${1#--man1dir=}" ;;
31       -h|--help) usage ;;
32       *)
33         echo "Unknown parameter: $1"
34         usage
35         ;;
36     esac
37     shift
38   done
39 }
40
41 # TODO: colors
42 die() {
43   local mesg="$1"; shift
44   printf "fatal: ${mesg}\n" "$@" >> "${cf_log}"
45   printf "fatal: ${mesg}\n" "$@"
46   exit 1
47 }
48
49 msg() {
50   local mesg="$1"; shift
51   printf "configure: ${mesg}\n" "$@" >> "${cf_log}"
52   printf "configure: ${mesg}\n" "$@"
53 }
54
55 logprint() {
56   local mesg="$1"; shift
57   printf "${mesg}\n" "$@" >> "${cf_log}"
58 }
59
60 log() {
61   echo "$@" >> "${cf_log}"
62   "$@"
63 }
64
65 #
66 # Some library functions
67 #
68 need_cmd() {
69   if which $1 >/dev/null 2>&1
70   then msg "found $1"
71   else die "need $1"
72   fi
73 }
74
75 # so we don't have to repeat the >/dev/null all the time
76 # also TODO:
77 #    strip parameters (ie, 'need_cmd $CC' with CC="gcc -m32" should work)
78 has_cmd() {
79   which $1 >/dev/null 2>&1
80 }
81
82 #
83 # Check environment
84 # Well we can expect those to exist, no?
85 #
86 need_cmd uname
87 need_cmd awk
88 need_cmd tr
89 need_cmd readlink
90
91 #
92 # default host specific values:
93 #
94 host="$(uname -s | tr A-Z a-z)"
95 case "${host}" in
96   linux|*bsd*)
97     cf_prefix="${cf_prefix:-/usr/local}"
98     cf_bindir="${cf_bindir:-${cf_prefix}/bin}"
99     cf_datadir="${cf_datadir:-${cf_prefix}/share}"
100     cf_mandir="${cf_mandir:-${cf_datadir}/man}"
101     cf_man1dir="${cf_man1dir:-${cf_mandir}/man1}"
102     cf_exesuffix=""
103     ;;
104   *)
105     cf_prefix="${cf_prefix:-}"
106     cf_bindir="${cf_bindir:-}"
107     cf_datadir="${cf_datadir:-}"
108     cf_mandir="${cf_mandir:-}"
109     cf_man1dir="${cf_man1dir:-}"
110     cf_exesuffix=".exe"
111     ;;
112 esac
113
114 # will be set to one if the compiler can generate .d files
115 cf_dynamic_depends=0
116
117 # for the default-supported compilers:
118 cf_cflags_gcc="-Wall -Wextra -Werror -Wstrict-aliasing -Wno-attributes"
119 cf_ldflags_gcc=""
120 cf_libs_gcc="-lm"
121
122 cflags_gcc() {
123   cf_cflags_gcc="${cf_cflags_gcc} $@"
124 }
125 ldflags_gcc() {
126   cf_ldflags_gcc="${cf_ldflags_gcc} $@"
127 }
128 libs_gcc() {
129   cf_libs_gcc="${cf_libs_gcc} $@"
130 }
131
132 # Let's figure out where we are...
133 cf_wd="${PWD}"
134 cf_log="${cf_wd}/config.log"
135 cf_dir="$(readlink -f "${progname}")"
136 # or should we use the hopefully more reliable basename command?
137 cf_dir="${cf_dir%/*}"
138
139 if [ "x${cf_dir}" = "x${cf_wd}" ]; then
140   echo "Please run this script in a different directory \
141 to not overwrite the git working tree."
142   exit 1
143 fi
144
145 # execute a command inside $cf_dir
146 indir() {
147   # do it in a subshell so we don't change directory ourselves
148   ( cd "${cf_dir}" && "$@" ) || false
149 }
150
151 #
152 # Find a compiler...
153 #
154 msg "looking for C compiler..."
155 CC=${CC:-clang}
156 has_cmd "${CC}" || CC=clang
157 has_cmd "${CC}" || CC=gcc
158 has_cmd "${CC}" || CC=cc
159 has_cmd "${CC}" || CC=tcc
160 has_cmd "${CC}" || die "no compiler found"
161 msg 'using CC = %s' "${CC}"
162
163 # We might add support for different compilers with a different CLI
164 cf_cctype="gcc"
165
166 if [ "x${CC}" != "xclang" -a "x${CC}" != "gcc" -a "x${CC}" != "g++" ]; then
167   msg "checking compiler type"
168   cf_ccver="$(${CC} -v 2>&1)"
169   [ $? -eq 0 ] || die "Failed to retrieve compiler version info"
170   if (echo "${cf_ccver}" | grep -q '\<clang\|gcc\>'); then
171     msg "found compatible compiler"
172   else
173     die "don't know how to use this compiler..."
174   fi
175 fi
176
177 # Find a preprocessor too
178 msg "looking for a C preprocessor..."
179 CPP=${CPP:-clang-cpp}
180 has_cmd "${CPP}" || CPP=cpp
181 if ! has_cmd "${CPP}"; then
182   msg "no C preprocessor found, trying -E"
183   logprint 'executing the following program with %s' "$CC -E"
184   prog="#define SHOW(X) :X:\nSHOW(THIS WORKS)\n"
185   logprint '%s' "${prog}"
186   if echo "${prog}" | $CC -E - 2>&1 \
187      | awk '/^:THIS WORKS:$/{ exit(0); } END {exit(1);}'; then
188     msg 'using preprocessor: %s' "$CC -E"
189     CPP="${CC} -E"
190   else
191     fatal "cannot find a working C preprocessor"
192   fi
193 else
194   msg 'using CPP = %s' "${CPP}"
195 fi
196
197 # Git information - that is, if git is available
198 cf_gitinfo=0
199 if has_cmd git; then
200   # And provided we're in a git repo:
201   if [ -d "${cf_dir}/.git" ]; then
202     cf_gitinfo=1
203     msg "reading git info"
204     cf_gitinfo_text="$(indir git describe --always)"
205   fi
206 fi
207
208 # valgrind?
209 cf_valgrind=0
210 has_cmd valgrind && cf_valgrind=1
211
212 # compiler specific flags:
213 [ "x${CC}" != "xg++" ] && \
214   cflags_gcc -Wmissing-prototypes -Wstrict-prototypes
215
216 if [ "x${CC}" = "xclang" ]; then
217   cflags_gcc -Weverything
218   cflags_gcc -Wno-padded
219   cflags_gcc -Wno-format-nonliteral
220   cflags_gcc -Wno-disabled-macro-expansion
221   cflags_gcc -Wno-conversion
222   cflags_gcc -Wno-float-equal
223   cflags_gcc -Wno-unknown-warning-option
224   cflags_gcc -Wno-cast-align
225 fi
226
227 if [ "x${CC}" != "xtcc" ]; then
228   cflags_gcc -pedantic-errors
229 else
230   cflags_gcc -Wno-pointer-sign -fno-common
231 fi
232
233 parse_cmdline
234
235 if [ ${cf_gitinfo} -ne 0 ]; then
236   cflags_gcc '-DGMQCC_GITINFO="${cf_gitinfo_text}"'
237 fi
238
239 if [ ${cf_valgrind} -eq 0 ]; then
240   cflags_gcc -DNVALGRIND
241 fi
242
243 #
244 # Put the cflags/ldflags/libs we use into cf_cflags/ldflags/libs
245 #
246 case "${cf_cctype}" in
247   gcc|clang)
248     cf_cflags="${cf_cflags_gcc}"
249     cf_ldflags="${cf_ldflags_gcc}"
250     cf_libs="${cf_libs_gcc}"
251     ;;
252   *)
253     die "compiler type '%s' not handled here!" "${cf_cctype}"
254 esac
255
256 #
257 # Dependency generation
258 #
259 depgrep() {
260   (echo "${cf_dir}"; $CPP "$@") | awk \
261   'BEGIN {
262     getline cf_dir;
263     cf_len=length(cf_dir);
264   }
265   /^#/{
266     gsub("\"","",$3);
267     if (substr($3,1,cf_len) == cf_dir)
268       print $3;
269   }' \
270     | sort \
271     | uniq
272 }
273
274 #
275 # Makefile generation routines
276 #
277
278 # executables is an array of variable names used in the makefile to
279 # name an executable; the list of objects is assumed to be
280 # in ${var}_OBJ
281 executables="GMQCC QCVM TESTSUITE PAK"
282 all_c_obj=""
283 add_c_obj() {
284   all_c_obj="${all_c_obj} $@"
285 }
286
287 print_all_rule() {
288   printf 'all:'
289   for i in ${executables}; do
290     printf ' $(%s)' "${i}"
291   done
292   echo
293 }
294
295 # create all the object variables:
296 print_objects() {
297   common="ansi.o util.o hash.o stat.o fs.o opts.o conout.o"
298   add_c_obj ${common}
299
300   gmqcc="main.o utf8.o lexer.o parser.o ftepp.o fold.o"
301   gmqcc="${gmqcc} intrin.o correct.o ast.o ir.o code.o"
302   add_c_obj ${gmqcc}
303
304   qcvm=exec.o
305   add_c_obj ${qcvm}
306
307   testsuite=test.o
308   add_c_obj ${testsuite}
309
310   pak=pak.o
311   add_c_obj ${pak}
312
313   cat <<EOF
314 GMQCC     = gmqcc${cf_exesuffix}
315 QCVM      = qcvm${cf_exesuffix}
316 TESTSUITE = testsuite${cf_exesuffix}
317 PAK       = pak${cf_exesuffix}
318
319 QCVM_OBJ      := ${common} ${qcvm}
320 GMQCC_OBJ     := ${common} ${gmqcc}
321 TESTSUITE_OBJ := ${common} ${testsuite}
322 PAK_OBJ       := ${common} ${pak}
323
324 EOF
325   printf 'ALL_PROGRAMS ='
326   for i in ${executables}; do
327     printf ' $(%s)' "${i}"
328   done
329   echo
330 }
331
332 # generate the commands used to build objects and executables
333 # in a way that works with both BSD make and gmake by not relying
334 # on special vars like - also generate the .d files
335 print_targets() {
336   # generate object rules to get the right path: $cf_dir
337   for obj in ${all_c_obj}; do
338     local c_src="${cf_dir}/${obj%.o}.c"
339     local d_inc="${obj}.d"
340     echo "${obj}: ${c_src}"
341     printf '\t$(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ "%s"' "${c_src}"
342     if [ "x$cf_cctype" == "xgcc" ]; then
343       cf_dynamic_depends=1
344       printf ' -MMD -MF "%s" -MT $@\n' "${d_inc}"
345     else
346       echo
347     fi
348   done
349
350   for exe in ${executables}; do
351     echo "\$(${exe}): \$(${exe}_OBJ)"
352     printf '\t$(CC) $(LDFLAGS) -o $(%s) $(%s_OBJ) $(LIBS)\n' "${exe}" "${exe}"
353   done
354 }
355
356 #
357 # Now generate our output file
358 #
359 echo "Generating Makefile"
360 ( cd "${cf_dir}"
361
362   # First: cflags and directories
363
364   cat <<EOF
365 CC      = ${CC}
366
367 CFLAGS  = ${CFLAGS}  ${cf_cflags}
368 LDFLAGS = ${LDFLAGS} ${cf_ldflags}
369 LIBS    = ${LIBS}    ${cf_libs}
370
371 SRCDIR = "${cf_dir}"
372 CFGDIR = "${cf_wd}"
373
374 PREFIX  = ${cf_prefix}
375 BINDIR  = ${cf_bindir}
376 DATADIR = ${cf_datadir}
377 MANDIR  = ${cf_mandir}
378 MAN1DIR = ${cf_man1dir}
379 EOF
380   echo
381
382   # now all object variables
383   print_objects
384   echo
385
386   # the all rule to include all executables
387   print_all_rule
388
389   # Now the Makefile.in
390   echo "# Makefile.in contents:"
391   echo
392   cat Makefile.in
393   echo
394
395   # all the targets and how to build them
396   print_targets
397
398   # include dependency files too
399   echo
400   echo '# Dependency rules'
401   if [ ${cf_dynamic_depends} -ne 0 ]; then
402     echo "-include *.o.d"
403   else
404     for obj in ${all_c_obj}; do
405       src="${obj%.o}.c"
406       logprint 'generating dependencies for: %s' "${src}"
407       deps=$(depgrep "${cf_dir}/${src}" | tr "\n" " ")
408       logprint 'found: %s' "${deps}"
409       printf '%s: %s\n' "${obj}" "${cf_dir}/${src} ${deps}"
410     done
411   fi
412 ) > "${cf_wd}/Makefile"