1#!/bin/sh
2#############################################################################
3##
4##  This file is part of GAP, a system for computational discrete algebra.
5##
6##  Copyright of GAP belongs to its developers, whose names are too numerous
7##  to list here. Please refer to the COPYRIGHT file for details.
8##
9##  SPDX-License-Identifier: GPL-2.0-or-later
10##
11##
12#W  gac, the GAP compiler
13##
14##  gac [-d] [-c|-C] [-o <output>] {-f<option>} <input>...
15##
16##  'gac'  compiles the input files.   Input files  must be  GAP  source code
17##  (suffix '.g' or '.gap'),  C source code (suffix '.c'),  or compiled  code
18##  files (suffix '.o').
19##
20##  If '-d' is given then the code is compiled for dynamic loading
21##
22##  If  neither '-c' nor '-C'  is given,  then 'gac'  compiles the code completely,
23##  producing a  new kernel for static compilation or a dynamically loadable '.so'
24##  file for dynamic compilation.
25##
26##  If '-c' is given,  then 'gac' only compiles the input files to
27##  '.o' object files, which must be further linked to make a static kernel or
28##  dynamically loadable module
29##
30##  If '-C is given, then 'gac' only compiles the input files to C code, which
31##  will require compilation and linking to be usable.
32##
33##  If '-r' is given, then statically compiled files will be assumed to be given
34##  by pathnames relative to the GAP root, and will be compiled for automatic loading
35##  when files are sought relative to the GAP root.
36##
37##  The option '-o <output>' tells 'gac' to name the output file <output>.
38##
39##  The option '-save-temps' tells 'gac' to not delete any intermediate files
40##
41##  The option '-p <option>' tells 'gac' to pass the option  <option> to  the
42##  C compiler.
43##
44##  The option '-P <option>' tells 'gac' to pass the option  <option> to  the
45##  C linker.
46##
47##  The option '-L <option>' tells 'gac' to pass the option  <option>
48##  to the C linker when linking dynamic modules. Contrary to -P the
49##  option is appended at the end of the link command after the .o
50##  files to link.
51##
52##  other options:
53##   -k|--gap-compiler
54##
55
56SHELL="@SHELL@"
57
58# absolute path of the directory in which GAP was compiled
59abs_top_builddir="@abs_top_builddir@"
60
61# path of the directory the GAP sources contained in
62abs_top_srcdir="@abs_top_srcdir@"
63
64#
65libdir="@libdir@"
66
67# path to the GAP executable
68gap_compiler="${abs_top_builddir}/gap"
69
70libtool="$SHELL $abs_top_builddir/libtool"
71CC="@CC@"
72
73# These three should be filled in by the standard autoconf procedures
74c_compiler="$libtool --mode=compile $CC"
75c_linker="$libtool --mode=link $CC"
76
77# read sysinfo.gap, which should set GAP_CFLAGS, GAP_CPPFLAGS, GAP_LDFLAGS, GAP_LIBS
78. "${abs_top_builddir}/sysinfo.gap"
79
80# These will need special care
81c_dyn_linker="$libtool --mode=link $CC -module -avoid-version -rpath $libdir"
82c_addlibs=""
83
84GAPARCH=@GAPARCH@
85SYS_IS_CYGWIN32=@SYS_IS_CYGWIN32@
86if [ X"$SYS_IS_CYGWIN32" = X"yes" ] ; then
87    c_dyn_linker="$c_dyn_linker -no-undefined -version-info 0:0:0"
88    # FIXME: use correct DLL path (also after "make install")
89    c_dyn_linker="$c_dyn_linker -Wl,${abs_top_builddir}/bin/${GAPARCH}/gap.dll"
90fi
91
92
93#############################################################################
94##
95#F  gap_compile <output> <input> <module-name> <identifier>
96##
97gap_compile () {
98    echo ${gap_compiler} -C $1 $2 $3 $4
99    ${gap_compiler} -C "$1" "$2" "$3" "$4"
100}
101
102
103#############################################################################
104##
105#F  c_compile   <output> <input> <options>
106##
107c_compile () {
108    echo ${c_compiler} $3 -o $1 ${GAP_CPPFLAGS} -c $2
109    ${c_compiler} $3 -o $1 ${GAP_CPPFLAGS} -c $2 || exit 1
110}
111
112
113#############################################################################
114##
115#F  c_link_dyn <output> <input>
116##
117c_link_dyn () {
118    echo ${c_dyn_linker} ${GAP_LDFLAGS} -o $1 $2 ${c_addlibs}
119    output_la=${1%.so}.la
120    output_dir=$(dirname $1)
121    ${c_dyn_linker} ${GAP_LDFLAGS} -o "$output_la" $2 ${c_addlibs} || exit 1
122    if [ X"$SYS_IS_CYGWIN32" = X"yes" ] ; then
123        # GAP assumes shared libraries end in .so
124        for dllfile in `cd $output_dir/.libs; ls *.dll`; do
125            cp $output_dir/.libs/$dllfile $output_dir/${dllfile%.dll}.so
126        done
127    else
128        cp $output_dir/.libs/*.so $output_dir
129    fi;
130}
131
132
133#############################################################################
134##
135#F  c_link <output> <inputs_o>
136##
137c_link () {
138    echo ${c_linker} ${GAP_LDFLAGS} -o $1 $2 ${GAP_LIBS}
139    ${c_linker} ${GAP_LDFLAGS} -o $1 $2 ${GAP_LIBS} || exit 1
140}
141
142#############################################################################
143##
144#F process_o_file <basename> <filename>
145##
146## Compile according to comp_mode and comp_howfar
147##
148## This does everything except the final link phase in the static case
149## in that case it adds the basename and object file path of $names and $objects
150##
151
152process_o_file () {
153  name=$1
154  o_file=$2
155
156  # the GAP compiler replaces _ by __ in names, so adjust here, too
157  name="$(echo "$name" | sed 's/_/__/g')"
158
159  # just remember for the linking stage later
160  names="${names} ${name}"
161  objects="${objects} $o_file"
162}
163
164#############################################################################
165##
166#F process_c_file <basename> <filename>
167##
168## Compile according to comp_mode and comp_howfar
169##
170## This does everything except the final link phase in the static case
171## in that case it adds the basename and object file path of $names and $objects
172##
173
174process_c_file () {
175    name=$1
176    c_file=$2
177    extra_cflags=$3
178
179    if [ $comp_howfar != "object" ]; then
180       o_file=${gactmp}/$$_${name}.lo
181        temps_o="${temps_o} ${o_file}"
182    elif [ "X$output" != "X" ]; then
183       o_file=$output
184    else
185       o_file=${name}.lo
186    fi
187    c_compile $o_file $c_file "$GAP_CFLAGS $extra_cflags"
188    if [ $comp_howfar = "link" ]; then
189      process_o_file $name $o_file
190    fi
191}
192
193#############################################################################
194##
195#F process_gap_file <filename> <ext>
196##
197## Compile according to comp_mode and comp_howfar
198##
199## This does everything except the final link phase in the static case
200## in that case it adds the basename and object file path of $names and $objects
201##
202
203process_gap_file () {
204  name=$(basename $1 $2)
205
206  if [ $comp_howfar != "c_code" ]; then
207    c_file=${gactmp}/$$_${name}.c
208    temps_c="${temps_c} $c_file"
209  elif [ "X$output" = "X" ]; then
210     c_file=${name}.c
211  else
212     c_file=$output
213  fi
214  gap_compile_in=$input
215  gap_compile_name=$input
216  if [ $comp_mode = "comp_static" ]; then
217    gap_compile_id=Init_${name}
218    if [ $comp_static_root_relative = "yes" ]; then
219        gap_compile_in=${abs_top_srcdir}/$input;
220        gap_compile_name=GAPROOT/$input;
221    fi
222  else
223    gap_compile_id=Init_Dynamic
224  fi
225  gap_compile $c_file ${gap_compile_in} $gap_compile_id ${gap_compile_name}
226  if [ $comp_howfar != "c_code" ]; then
227    process_c_file $name $c_file
228    if [ "$savetemps" = "true" ]; then
229        echo "Leaving C file " $c_file
230    else
231        echo rm -f $c_file
232        rm -f $c_file
233    fi
234  fi
235}
236
237#############################################################################
238##
239#F  clean_up
240##
241clean_up () {
242     if [ "$savetemps" = "true" ]; then
243        echo "Leaving files on cleanup: " ${temps_c} ${temps_o}
244    else
245        echo rm -f ${temps_c} ${temps_o}
246        rm -f ${temps_c} ${temps_o}
247    fi
248}
249trap "clean_up" 2 3
250
251
252#############################################################################
253##
254##  parse the arguments
255##
256if [ $# = 0 ]; then
257    echo "usage: $0 [-d|-r] [-c|-C] [-o <output>] <input>..."
258    exit 1
259fi
260
261comp_mode="comp_static"
262comp_howfar="link"
263comp_static_root_relative="no"
264output=""
265inputs=""
266savetemps="false"
267
268while [ $# -gt 0 ]; do
269    case $1 in
270
271    -c|--compile)         comp_howfar="object";;
272
273    -d|--dynamic)         comp_mode="comp_dyna";;
274
275    -C|--create-c)        comp_howfar="c_code";;
276
277    -o|--output)          shift; output="$1";;
278
279    -r)                   comp_static_root_relative="yes";;
280
281    -save-temps)          savetemps="true";;
282
283    -f*)                  echo "$0: no such option '$1'"
284                          exit 1;;
285
286    -k|--gap-compiler)    shift; gap_compiler="$1";;
287
288    -p)                   shift; GAP_CFLAGS="${GAP_CFLAGS} $1";;
289
290    -P)                   shift; GAP_LDFLAGS="${GAP_LDFLAGS} $1";;
291
292    -L|--addlibs)         shift; c_addlibs="${c_addlibs} $1";;
293
294    *.g|*.gap|*.gd|*.gi|*.c|*.cc|*.cpp|*.cxx|*.s|*.o|*.lo)
295                          inputs="${inputs} $1";;
296
297    *)                    echo "$0: cannot handle this argument '$1'"
298                          exit 1;;
299
300    esac
301    shift
302done
303
304if [ "X${inputs}" = "X" ]; then
305    echo "$0: no input files given"
306    exit 1
307fi
308
309
310
311
312#############################################################################
313##
314#F  make_compstat
315##
316
317make_compstat () {
318    # make 'compstat.c' and compile it
319    temps_c="${temps_c} ${gactmp}/$$compstat.c"
320    (
321        echo     "/* made by 'gac', can be thrown away */"
322        echo     "#include \"compiled.h\""
323
324        echo     "#ifndef AVOID_PRECOMPILED"
325        echo     "extern StructInitInfo * Init__type1 ( void );"
326        echo     "extern StructInitInfo * Init__oper1( void );"
327        echo     "#endif"
328
329        for name in ${names}; do
330            echo "extern StructInitInfo * Init__${name} ( void );"
331        done
332
333        echo     "InitInfoFunc CompInitFuncs [] = {"
334        echo     "#ifndef AVOID_PRECOMPILED"
335        echo     "    Init__type1,"
336        echo     "    Init__oper1,"
337        echo     "#endif"
338
339        for name in ${names}; do
340            echo "    Init__${name},"
341        done
342        echo "    0"
343        echo "};"
344    ) > ${gactmp}/$$compstat.c
345
346    temps_o="${gactmp}/$$compstat.lo ${temps_o}"
347    c_compile ${gactmp}/$$compstat.o ${gactmp}/$$compstat.c "${GAP_CFLAGS}"
348    if [ "$savetemps" = "true" ]; then
349        echo "Leaving temp file " ${gactmp}/$$compstat.c
350    else
351        echo rm -f ${gactmp}/$$compstat.c
352        rm -f ${gactmp}/$$compstat.c
353    fi
354    objects="${gactmp}/$$compstat.lo ${objects}"
355}
356
357make_tmpdir () {
358    if command -v mktemp >/dev/null 2>&1 ; then
359        gactmp=$(mktemp -d -t "gacXXXXXXX")
360    else
361        basetmp=${TMPDIR:-/tmp}; #honor the TMPDIR environment variable.
362        gactmp="$basetmp/gac$$";
363        mkdir "$gactmp" || exit 1;
364    fi
365}
366
367#############################################################################
368##
369##  main loop
370##
371
372#Make temporary directory
373make_tmpdir;
374
375# loop over the input files
376for input in ${inputs}; do
377  case $input in
378
379        *.g) process_gap_file $input .g;;
380        *.gap) process_gap_file $input .gap;;
381        *.gd) process_gap_file $input .gd;;
382        *.gi) process_gap_file $input .gi;;
383
384        *.c) # compile '.c' source files
385            name=$(basename ${input} .c)
386            process_c_file $name $input;;
387
388        *.cc) # compile '.cc' source files (C++)
389            name=$(basename ${input} .cc)
390            process_c_file $name $input "-x c++";;
391
392        *.cpp) # compile '.cpp' source files (also C++)
393            name=$(basename ${input} .cpp)
394            process_c_file $name $input "-x c++";;
395
396        *.s) # compile '.s' source files (assembler)
397            name=$(basename ${input} .s)
398            process_c_file $name $input;; # HACK: just use the C compiler
399
400        *.o) # look over '.o' source files
401            name=$(basename ${input} .o)
402            process_o_file $name $input;;
403
404        *.lo) # look over '.lo' source files
405            name=$(basename ${input} .lo)
406            process_o_file $name $input;;
407        esac
408    done
409
410
411#  static link phase
412if [ $comp_howfar = "link" ]; then
413    if [ $comp_mode = "comp_static" ]; then
414        make_compstat
415
416        # link everything together
417        if [ "X${output}" = "X" ]; then output="gacout"; fi
418
419        for object in $GAP_OBJS;  do
420            if [ ${object} != "obj/src/compstat.lo" ]; then
421                objects="${abs_top_builddir}/${object} ${objects}"
422            fi
423        done
424
425        c_link ${output} "${objects}"
426
427    else
428        if [ "X${output}" = "X" ]; then output="${name}.la"; fi
429        c_link_dyn ${output} "${objects}"
430    fi
431
432    if [ "$savetemps" = "true" ]; then
433        echo "Leaving object files " ${temps_o}
434    else
435        echo rm -f ${temps_o}
436        rm -f ${temps_o}
437    fi
438fi
439
440# Remove temporary directory.
441# We may assume it is empty at this stage.
442if [ "$savetemps" = "false" ]; then
443    rm -rf "${gactmp}/.libs"
444    rmdir "${gactmp}"
445fi
446