2 # gast (Gcc Automatically -Save-Temps) : wrapper script for gcc to save temporary file.
4 # Copyright (C) 2009 Tadashi Koike
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 #----------------------------------------------------------------------
22 # (I am weak in English. Please modify English comment more suitable. :T.K)
24 # [ Explanation of environment variable ]
26 # When this environment variable is defined (however excludes its
27 # value is "no"), this script works to generate compiler's temporary
28 # file (.i file) automatically, and stores it.
29 # When this environment variable is not defined, this script behaves
30 # itself like just same as an real compiler.
33 # When this environment variable is defined and its value can be
34 # recognized a directory name, a genrerated temporary file (.i file)
35 # by compiler is moved to this directory.
36 # When that directory is not defined , this script attempt to create
40 # When this environment variable is defined and its value can be
41 # recognized a directory name, this script tries to get a relative
42 # path-name from a source file path-name by removing a directory
43 # name which this environment means. And that relative path-name
44 # applies under the GAST_SAVE_DIR directory to moved a temporary
46 # When this environment variable is not defined or the value does
47 # not match a header of source file path-name, this script get a
48 # relative path-name by removing "/" from a top of source file
52 # When this environment variable is not defined and this script
53 # is executed under a build process by 'rpmbuild' command, this
54 # script tries to define GAST_SOURCE_DIR automatically with a
55 # value of RPM_BUILD_DIR.
57 # - GAST_CONTENT_WITHOUT_H
58 # When this environment variable is defined (however excludes its
59 # value is "no"), created temporary file (.i file) is modified.
60 # Most of all the contents from header files are removed.
63 # Only a track of including header file is left.
65 # - GAST_CONTENT_UNIQUE
66 # When this environment variable is not defined, If C source file
67 # is compiled any number of times with different compile options,
68 # this script preserve each temporary files by adding number.
70 # When this environment variable is defined (however excludes its
71 # value is "no"), If compile options are different but content of
72 # temporary file is same as one of some previous stored files,
73 # this script does preserve current temporary file.
83 # If this script is executed under the rpmbuild command, GAST_SOURCE_DIR is
84 # forced to set value by RPM_BUILD_DIR environment variable's one.
85 [ -n "${RPM_BUILD_DIR}" ] && GAST_SOURCE_DIR="${RPM_BUILD_DIR}"
86 [ -n "${GAST_SOURCE_DIR}" ] && GAST_SOURCE_DIR=${GAST_SOURCE_DIR%/}
88 # When this script is executed as 'gast', this script creates symbolic link
89 # file name as compiler alias name and exit.
90 if [ "${SCRIPT_NAME}" = "${ORIGINAL_NAME}" ]; then
95 echo "${ORIGINAL_NAME} ${VERSION}"
100 usage : ${ORIGINAL_NAME} [-v|--version] [-h|--help]
102 -h, --help display this help and exit
103 -v, --version display version information and exit
105 When this script is executed as '${ORIGINAL_NAME}' with no option, this script
106 creates symbolic link toward itself which name is from compiler (e.g. gcc/cc).
108 When this script is executed as compiler, this script executes original
109 compiler with additional -save-temps option (If GAST_ENABLE is defined), and
110 stores temporary files.
112 [ environment variables to control storing temporary files ]
113 GAST_ENABLE : add '-save-temps' option automatically
114 in compiling when this variable is defined.
115 GAST_SAVE_DIR=<DIR> : directory name temporary file is stored in.
116 GAST_SOURCE_DIR=<DIR> : top directory of source tree.
117 GAST_CONTENT_WITHOUT_H : slim temporary files when this variable
123 echo "usage : ${ORIGINAL_NAME} [--version|-v] [--help|-h]"
132 if [ -e "${cmd}" ]; then
133 file "${cmd}" 2>/dev/null | egrep 'symbolic link' >/dev/null 2>&1
134 if [ "$?" -ne 0 ]; then
135 echo "${ORIGINAL_NAME} : '${cmd}' exists as normal file or directory. Could not create symbolic link." >&2
139 ln -sf "${ORIGINAL_NAME}" "${cmd}"
140 if [ "$?" -eq 0 ]; then
141 echo "${ORIGINAL_NAME} : Create symbolic link '${cmd}'" >&2
143 echo "${ORIGINAL_NAME} : Couldn't create symbolic link '${cmd}'" >&2
149 # get a directory path (full-path) of this script
150 case "${SCRIPT_PATH}" in
151 '.') SCRIPT_PATH="${CWD}" ;;
152 \./*) SCRIPT_PATH="${CWD}/${SCRIPT_PATH#*/}" ;;
153 '~') SCRIPT_PATH="${HOME}" ;;
154 \~/*) SCRIPT_PATH="${HOME}/${SCRIPT_PATH#*/}" ;;
156 *) SCRIPT_PATH="${CWD}/${SCRIPT_PATH}" ;;
159 # leaves out a script path name from PATH environment variable because of
160 # executing original compiler binary file in this script.
162 for dir in ${PATH//:/ }
164 [ "${dir}" = "${SCRIPT_PATH}" ] && continue
165 [ "${dir}" = '.' ] && dir="${CWD}"
167 if [ -z "${path_tmp}" ]; then
170 path_tmp="${path_tmp}:${dir}"
173 export PATH=${path_tmp}
176 # When GAST_ENABLE does't exist, execute an original compiler and exit.
177 if [ "${GAST_ENABLE=no}" = "no" ]; then
178 exec "${SCRIPT_NAME}" "$@"
181 # Check whether C source file(s) is/are specified.
182 # And create new commandline arguments.
189 new_args[$idx]="${arg}"
192 *.c) src_list[$idx_src]="${arg}"
195 '-S') asm_notdelete_flg="YES"
197 '-pipe') pipe_flg="YES"
198 new_args[$idx]="-save-temps"
205 if [ -z "${src_list[*]}" ]; then
206 # No C source file was specified. -save-temps option does not need.
207 exec "${SCRIPT_NAME}" "$@"
209 # C source file(s) is/are specified. -save-temp option is set to argument.
210 if [ -z "${pipe_flg}" ]; then
211 "${SCRIPT_NAME}" -save-temps "$@"
213 "${SCRIPT_NAME}" "${new_args[@]}"
219 # modify a value of GAST_SOURCE_DIR environment variable to full-path name.
220 # When GAST_SOURCE_DIR doesn't exist, define it and set null.
221 case "${GAST_SOURCE_DIR}" in
222 '') GAST_SOURCE_DIR="/" ;;
223 '.') GAST_SOURCE_DIR="${CWD}" ;;
224 \./*) GAST_SOURCE_DIR="${CWD}/${GAST_SOURCE_DIR#*/}" ;;
225 '~') GAST_SOURCE_DIR="${HOME}" ;;
226 \~/*) GAST_SOURCE_DIR="${HOME}/${GAST_SOURCE_DIR#*/}" ;;
228 *) GAST_SOURCE_DIR="${CWD}/${GAST_SOURCE_DIR}" ;;
230 GAST_SOURCE_DIR="${GAST_SOURCE_DIR%/}"
231 # NOTE : In above process, when user set GAST_SOURCE_DIR as "/" or
232 # GAST_SOURCE_DIR doesn't exists, value of GAST_SOURCE_DIR is set to "".
234 # When GAST_CONTENT_WITHOUT_H environment variable exist, define a filter
236 HAS_PERL=`which perl 2>/dev/null`
238 if [ "${GAST_CONTENT_WITHOUT_H=no}" != "no" ]; then
239 if [ -n "${HAS_PERL}" ]; then
240 function filter_tempfile_perl () {
242 #----------(start temporary perl script)-------------------------
248 if (/^#\s+(\d+)\s+"([^"]+)".*$/) {
252 if ($cfile !~ /\.h$/) {
254 $prnt_flg = $line_no if ($prnt_flg >= 0);
255 } elsif ($cfile =~ /\.h$/) {
256 print if ($line_no == 1);
258 } elsif ($cfile =~ /^</) {
260 $prnt_flg = 0 if ($cfile eq "<built-in>");
262 if ($prnt_flg == -1) {
264 } elsif ($line_no == 1) {
266 $prnt_flg = 0 if ($prnt_flg >= 0);
271 } elsif ($prnt_flg > 0) {
275 #----------(end temporary perl script)---------------------------
279 function delete_added_info_perl () {
281 #----------(start temporary perl script)-------------------------
286 if ($prnt_flg != 0) {
288 } elsif (/^#\s+(\d+)\s+"<compile-options>/) {
292 #----------(end temporary perl script)---------------------------
296 elif [ ${BASH_VERSION%%.*} -ge 3 ]; then
297 function filter_tempfile_bash () {
304 if [[ "${line}" =~ "^#[ \t]+([0-9]+)[ \t]+\"([^\"]+)\".*$" ]]; then
305 line_no=${BASH_REMATCH[1]}
306 cfile=${BASH_REMATCH[2]}
308 #echo "$line_no : $cfile"
309 if [[ ! "${cfile}" =~ "\.h$" ]]; then
311 [ "${prnt_flg}" -ge 0 ] && prnt_flg="${line_no}"
312 elif [[ "${cfile}" =~ "\.h$" ]]; then
313 [ "${line_no}" -eq 1 ] && echo "${line}"
315 elif [ "${cfile:0:1}" = "<" ]; then
317 [ "${cfile}" = "<built-in>" ] && prnt_flg=0
319 if [ "${prnt_flg}" -eq -1 ]; then
321 elif [ "${line_no}" -eq 1 ]; then
323 [ "${prnt_flg}" -ge 0 ] && prnt_flg=0
329 [ "${prnt_flg}" -gt 0 ] && echo "${line}"
335 function delete_added_info_bash () {
341 if [ "${prnt_flg}" -ne 0 ]; then
343 elif [[ "${line}" =~ "^#[ \t]+[0-9]+[ \t]+\"<compile-options" ]]; then
352 # processes the compiler's temporary file
353 for src_file in "${src_list[@]}"
355 # get full-path name of a C source file.
357 case "${src_file}" in
358 \/*) fullname="${src_file}" ;;
359 \~/*) fullname="${HOME}/${src_file#*/}" ;;
360 \./*) fullname="${CWD}/${src_file#*/}" ;;
361 *) fullname="${CWD}/${src_file}" ;;
364 # Get relative-path of C source file from GAST_SOURCE_DIR directory.
365 basename="${fullname##*/}" # C source filename
366 dirname="${fullname%/*}" # directory name of C source file (full-path)
367 target="${basename/%.c/.i}" # Compiler's temporary filename (.i file)
368 asmfile="${basename/%.c/.s}" # Compiler's temporary filename (.s file)
369 src_relative_dir= # directory name of C source file (relative-path from GAST_SOURCE_DIR)
371 case "${fullname}" in
372 ${GAST_SOURCE_DIR}/*) # contain GAST_SOURCE_DIR=""
373 if [ "${dirname}" = "${GAST_SOURCE_DIR}" ]; then
376 src_relative_dir="${dirname#${GAST_SOURCE_DIR}/}"
380 # source file is not under the GAST_SOURCE_DIR directory.
381 # Set the relative-path from '/' directory.
382 src_relative_dir="${dirname#/}"
385 [ "${src_relative_dir:0:1}" = '/' ] && src_relative_dir="${src_relative_dir#/}"
386 # Delete assembler temporary file (except that compiler is executed with -S option)
387 [ -e "${asmfile}" -a -z "${asm_notdelete_flg}" ] && rm -f "${asmfile}" > /dev/null 2>&1
389 if [ -e "${target}" ]; then
390 if [ "${target}" = "conftest.i" ]; then
391 # temporary file is created by 'configure' script as test.
396 # Add an information in the header of temporary file.
397 # When filtering temporary file was specified, try it.
399 until [ -n "${tmpfile_org}" -a ! -e "${tmpfile_org}" ]
401 tmpfile_org="${target}.${RANDOM}${RANDOM}${RANDOM}"
403 trap "rm -rf ${tmpfile_org} ${target} 2>/dev/null" HUP INT QUIT ABRT TERM XCPU
405 mv "${target}" "${tmpfile_org}"
406 if [ $? -eq 0 ]; then
407 echo "# 1 \"<compile-options> $@\"" > "${target}"
408 #if [ -z "${pipe_flg}" ]; then
409 # echo "# 1 \"<modified compile-options> -save-temps $@\"" >> "${target}"
411 # echo "# 1 \"<modified compile-options> ${new_args[@]}\"" >> "${target}"
414 if [ "${GAST_CONTENT_WITHOUT_H=no}" = "no" ]; then
415 cat "${tmpfile_org}" >> "${target}"
417 #echo "# 1 \"<GAST_CONTENT_WITHOUT_H>\"" >> "${target}"
418 if [ -n "${HAS_PERL}" ]; then
419 filter_tempfile_perl "${basename}" < "${tmpfile_org}" >> "${target}"
420 elif [ ${BASH_VERSION%%.*} -ge 3 ]; then
421 filter_tempfile_bash "${basename}" < "${tmpfile_org}" >> "${target}"
423 cat "${tmpfile_org}" >> "${target}"
428 # Check whether the GAST_SAVE_DIR has a value or not.
429 # When it has a value, that is recognized as a directory name.
430 # When such directory does not exist, try to create.
431 if [ -n "${GAST_SAVE_DIR}" ]; then
432 GAST_SAVE_DIR="${GAST_SAVE_DIR%/}"
433 if [ ! -d "${GAST_SAVE_DIR}" ]; then
434 if [ -e "${GAST_SAVE_DIR}" ]; then
435 echo "${ORIGINAL_NAME} : ${GAST_SAVE_DIR} is not a directory." >&2
438 mkdir -p "${GAST_SAVE_DIR}"
439 if [ $? -ne 0 ]; then
440 echo "${ORIGINAL_NAME} : Couldn't create ${GAST_SAVE_DIR} directory." >&2
447 # define a directory path-name of temporary file to resotre.
449 if [ -n "${GAST_SAVE_DIR}" ]; then
450 # resolve a directory path-name of a temporary file to move.
452 if [ "${src_relative_dir}" = "." ]; then
453 temp_dirname="${GAST_SAVE_DIR}"
455 temp_dirname="${GAST_SAVE_DIR}/${src_relative_dir}"
458 # Check a directory to move a temporary file.
459 if [ -e "${temp_dirname}" ]; then
460 if [ ! -d "${temp_dirname}" ]; then
461 echo "${ORIGINAL_NAME}(${SCRIPT_NAME}): ${temp_dirname} is not a directory. Couldn't move ${target} to ${temp_dirname} ." >&2
466 mkdir -p ${temp_dirname}
467 if [ $? -ne 0 ]; then
468 echo "${ORIGINAL_NAME}(${SCRIPT_NAME}): Couldn't create ${temp_dirname} directory, so couldn't move ${target} to ${temp_dirname} ." >&2
476 # set destination filename(full-path) to move a temporary file.
481 if [ -n "${temp_dirname}" ]; then
483 dest_file="${temp_dirname}/${dest_name}"
485 # When destination directory to store the temporary file doesn't
486 # exist, try to rename temporary file as '*****.0.i', because
487 # if some C source file is compiled over and over, old temporary
488 # file is overwrited by new one. We wish to avoid that.
489 dest_name="${target/%.i/.${loop_count}.i}"
490 dest_file="${dest_name}"
493 while [ -e "${dest_file}" ]
496 # software loop limit
497 if [ "${loop_count}" -ge 500 ]; then
498 echo "${ORIGINAL_NAME}(${SCRIPT_NAME}): loop abort. give up to move ${target} to ${temp_dirname} ." >&2
503 # destination file already exists. compare a content.
505 if [ "${GAST_CONTENT_UNIQUE=no}" != "no" ]; then
506 # comparison excluding "<compile-option>" line.
508 until [ -n "${tmpfile_prev}" -a ! -e "${tmpfile_prev}" ]
510 tmpfile_prev="${WORK_DIR}/${destname}.${RANDOM}${RANDOM}${RANDOM}"
511 tmpfile_cur="${WORK_DIR}/${target}.${RANDOM}${RANDOM}${RANDOM}"
512 trap "rm -rf ${tmpfile_prev} ${tmpfile_cur} 2>/dev/null" HUP INT QUIT ABRT TERM XCPU
515 if [ -n "${HAS_PERL}" ]; then
516 delete_added_info_perl < "${dest_file}" > "${tmpfile_prev}"
517 delete_added_info_perl < "${target}" > "${tmpfile_cur}"
518 elif [ ${BASH_VERSION%%.*} -ge 3 ]; then
519 delete_added_info_bash < "${dest_file}" > "${tmpfile_prev}"
520 delete_added_info_bash < "${target}" > "${tmpfile_cur}"
522 if [ -e "${tmpfile_prev}" -a -e "${tmpfile_cur}" ]; then
523 diff --brief "${tmpfile_prev}" "${tmpfile_cur}" > /dev/null 2>&1
529 rm -f "${tmpfile_prev}" "${tmpfile_cur}" > /dev/null 2>&1
530 trap - HUP INT QUIT ABRT TERM XCPU
532 # comparison including "<compile-option>" line.
533 diff --brief "${dest_file}" ${target} > /dev/null 2>&1
537 if [ "${cmp_result}" -eq 0 ]; then
538 # delete current temporary file.
539 inode_from=`ls -i "${target}" | sed -e 's/\([0-9][0-9]*\)[^0-9]*/\1/'`
540 inode_to=`ls -i "${dest_file}" | sed -e 's/\([0-9][0-9]*\)[^0-9]*/\1/'`
541 [ "${inode_from}" != "${inode_to}" ] && rm -f "${target}"
546 # same file already exists and contents is different.
547 # try to store this temporary file with another filename
548 # by using ${loop_count} number.
549 dest_name="${target/%.i/.${loop_count}.i}"
550 if [ -n "${temp_dirname}" ]; then
551 dest_file="${temp_dirname}/${dest_name}"
553 dest_file="${dest_name}"
558 # move a temporary file to a save-directory.
559 if [ -z "${skip_moving}" ]; then
560 mv -f "${target}" "${dest_file}" 2>/dev/null
561 [ $? -ne 0 ] && echo "${ORIGINAL_NAME}(${SCRIPT_NAME}): Couldn't move ${target} to ${dest_file} ." >&2
564 # Delete original temporary file.
565 rm -f "${tmpfile_org}"
566 trap - HUP INT QUIT ABRT TERM XCPU
568 fi #if [ -e "${target}" ]; then
571 # return with compiler's return code