Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I'm trying to write a shell script that will transform positional arguments that are passed to it as follows.

The shell script passes these arguments to a binary executable (ffigen) which is derived from the gcc 4.0 frontend, so the command line arguments that follow are also gcc arguments, and mean the same thing as they do for gcc.

Consider the following command line

./htest.sh -I/usr/include/gtk-2.0 -I /usr/include/glib-2.0 -include \
/usr/include/gtk-2.0/gtk/gtk.h -include/usr/include/bar.h /usr/include/myheader.h

There are essentially two different options here, the -I option, which passes a directory to gcc to search for include files, and -include, which processes a header file as if #include "file" appeared as the first line of the primary source file.

The two different versions given here differ in whether there is a space between the option flag and the argument. It seems both are permissible.

Currently the only versions that appear in upstream are

-I/usr/include/gtk-2.0 and -include /usr/include/gtk-2.0/gtk/gtk.h

but it doesn't seem a bad idea to attempt to future-proof this code, in case someone adds these other forms later.

What upstream's script does is, for the most part, add the arguments to CFLAGS, and then pass the final argument to ffigen. What ffigen does is not important here - my question is about the return value of CFLAGS. Here is upstream's script.

while [ $# -gt 1 ]
do
    echo $CFLAGS
    case ${1} in
      -pthread*)
        CFLAGS="${CFLAGS} -D_REENTRANT"
        shift
        ;;
      -x)
        shift
        shift
        ;;
      *)
        CFLAGS="${CFLAGS} ${1}"
        shift
        ;;
   esac
done

echo $CFLAGS

What I want to do, is where header filenames are specified in the above line, is to transform them to relative path names, relative to the include directory. So, what I would like to see as the returned value to CFLAGS, is

-I/usr/include/gtk-2.0 -I /usr/include/glib-2.0 -include gtk-2.0/gtk/gtk.h -includebar.h

What upstream produces is just the arguments unchanged.

-I/usr/include/gtk-2.0 -I /usr/include/glib-2.0 -include \
/usr/include/gtk-2.0/gtk/gtk.h -include/usr/include/bar.h

Rationale: Since Debian has switched to multilib, header files are no longer in their traditional locations. Now, ffigen, like its parent gcc, can search include paths for header files, provided that only the relative paths are specified. If absolute paths are specified, then of course it will not.

Ideally one would want to use a tool which was designed to work with multiarch, but a quick and dirty workaround is to simply strip the header file absolulute paths down to relative path names, and provided ffigen is provided the correct include paths, it will search for and discover the header files.

The version I currently have is below. It produces

-I/usr/include/gtk-2.0 -I glib-2.0 -include gtk-2.0/gtk/gtk.h bar.h

which is terrible, but does handle correctly the two cases that actually occur in upstream. A version that handles all four cases correctly would be most welcome. Yes, I'm lazy. I also suck at shell scripting.

while [ $# -gt 1 ]
do
    echo $CFLAGS
    case ${1} in
      -pthread*)
        CFLAGS="${CFLAGS} -D_REENTRANT"
        shift
        ;;
      -x)
        shift
        shift
        ;;
      -I*)
        CFLAGS="${CFLAGS} ${1}"
        shift
        ;;
      *)
        RELPATH_AFTER_INCLUDE="${1##*include/}"
        CFLAGS="${CFLAGS} ${RELPATH_AFTER_INCLUDE}"
        shift
        ;;
   esac
done

echo $CFLAGS

Here is a little script that I am using to test this

#!/bin/sh

PROGRAM=htest.sh
#PROGRAM=upstream.sh

./${PROGRAM} -I/usr/include/gtk-2.0 -I /usr/include/glib-2.0 -include \     
/usr/include/gtk-2.0/gtk/gtk.h -include/usr/include/bar.h /usr/include/myheader.h

A final note about portability. The scripts above run under dash, and look like they are portable to me. So a version that is portable would be nice, but is not essential. I don't see this code going upstream, so... If not portable, then bash would be Ok.

###################################################################

For anyone who is interested in the background of this, this is in the context of packaging Clozure Common Lisp for Debian.

See the Debian packaging and in particular the README.source file.

See upstream patches http://svn.clozure.com/publicsvn/ffigen4/trunk/ffigen4/source/h-to-ffi-common and http://svn.clozure.com/publicsvn/ffigen4/trunk/ffigen4/source/linuxx8664-gcc-4.0.0-h-to-ffi.sh , which combine to make the upstream script h-to-ffi.sh, of which I discuss a part here.

For the specific issue I'm trying to handle here, see the thread problems building interface databases on Debian multiarch.

A discussion of my rationale above is in the third message in that thread, from me, towards the bottom.

share|improve this question

2 Answers 2

Because you sometimes have a space after -I or -include and sometimes not, you can't count on $1 holding the option and the value.

I'd stick with the original version, with case choices "-pthread*", "-x" and "*". Then, after you have accumulated the CFLAGS variable, call out to sed to remove part of the path for -include option only:

CFLAGS=$(echo "$CFLAGS" | sed 's#\(-include \?\)/usr/include/#\1#g')
share|improve this answer
up vote 1 down vote accepted

The following works, but it is kind of verbose. Suggested improvements welcome.

while [ $# -gt 1 ]
do
    echo $CFLAGS
    case ${1} in
      -pthread*)
        CFLAGS="${CFLAGS} -D_REENTRANT"
        shift
        ;;
      -x)
        shift
        shift
        ;;
      -I)
        CFLAGS="${CFLAGS} ${1}"
        shift
        CFLAGS="${CFLAGS} ${1}"
        shift
        ;;
      -include)
        CFLAGS="${CFLAGS} ${1}"
        shift
        RELPATH_AFTER_INCLUDE="${1##*include/}"
        CFLAGS="${CFLAGS} ${RELPATH_AFTER_INCLUDE}"
        shift
        ;;
      -I*)
        CFLAGS="${CFLAGS} ${1}"
        shift
        ;;
      -include*)
        # strip off leading -include                                                                                                                              
        REMOVE_LEADING_INCLUDE="${1##-include}"
        echo "REMOVE-LEADING-INCLUDE" ${REMOVE_LEADING_INCLUDE}
        RELPATH_AFTER_INCLUDE="${REMOVE_LEADING_INCLUDE##*include/}"
        CFLAGS="${CFLAGS} -include${RELPATH_AFTER_INCLUDE}"
        shift
        ;;
      *)
       CFLAGS="${CFLAGS} ${1}"
        shift
        ;;
   esac
done

echo $CFLAGS
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.