# Copyright (C) 2016 Daniel C. Dillon
#
# r-stripper is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# r-stripper is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with r-stripper.  If not, see <http://www.gnu.org/licenses/>.

# Takes an executable that IS A linker and determines if it knows
# how to tell it to strip a shared library.  Returns 0 if we don't
# support the linker, 1 if -S is the appropriate option to pass the
# linker.
check_supported_linker()
{
    if [ "$1" != "" ]; then
        OUTPUT=`$1 --version 2>&1`
        case "$OUTPUT" in
            GNU?ld*) return 1 ;;
            GNU?gold*) return 1 ;;
            *Solaris?Link?Editors*) return 3 ;;
        esac
    fi

    return 0
}

# Takes an executable that MIGHT BE a linker (or might be something
# like gcc, clang, etc.) and determines whether it knows how to
# discover the actual linker or whether it is a linker.  Returns 0
# if we don't support the linker, 1 if passing -Wl,-S will work,
# 2 if just -S will work.
check_supported_linker_command()
{
    if [ "$1" != "" ]; then
        # First see if it's a linker in its own right
        check_supported_linker $1
        RETURN_CODE=$?

        if [ $RETURN_CODE -eq 1 ]; then
            return 2
        elif [ $RETURN_CODE -eq 3 ]; then
            return 4
        elif [ $RETURN_CODE -eq 0 ]; then
            OUTPUT=`$1 --version`
            LD_PROG=""
        
            case "$OUTPUT" in
                *gcc*) LD_PROG=`$1 --print-prog-name=ld` ;;
                *g++*) LD_PROG=`$1 --print-prog-name=ld` ;;
                *clang*) LD_PROG=`$1 --print-prog-name=ld` ;;
            esac

            if [ "$LD_PROG" != "" ]; then
                check_supported_linker $LD_PROG
                RETURN_CODE=$?
                return $RETURN_CODE
            fi
        fi
    fi

    return 0
}

# Takes the R config variable that describes this linker as the only
# argument (e.g. SHLIB_LD).  Returns 0 if we don't support the linker,
# 1 if we should pass -Wl,-S and 2 if we should pass -S.
check_linker()
{
    if [ -z $R_HOME ]; then
        R_HOME=`R RHOME`
    fi

    if [ "$R_HOME" = "" ]; then
        echo "Could not find R_HOME."
        exit 0
    fi

    LINK_COMMAND=`$R_HOME/bin/R CMD config $1`

    if [ "$LINK_COMMAND" != "" ]; then
        check_supported_linker_command $LINK_COMMAND
        RETURN_CODE=$?
        return $RETURN_CODE
    fi

    return 0
}

# Takes an argument with the name of the Makevars file to modify (assuming it
# is in src/ and modifies it accordingly.  If the second parameter is set to
# "create-if-missing" it will create the file if it is not already there.
modify_makevars()
{
    MAKEVARS_FILE="$1"
    CREATE_IF_MISSING="$2"
    
    # Clean up any temporary files we created previously
    rm -f src/$MAKEVARS_FILE.R_STRIP_LINKER_OPTION
    
    # If there is a pre-existing Makevars file, go through it and remove the
    # line where we assign to R_STRIP_LINKER_OPTION.  Also, if there is already
    # a line that assigns to PKG_LIBS, make sure it adds
    # $(R_STRIP_LINKER_OPTION) to the end.  Store the results in
    # src/Makevars.R_STRIP_LINKER_OPTION
    
    FOUND_PKG_LIBS_ASSIGNMENT=0
    
    if [ -f "src/$MAKEVARS_FILE" ]; then
        while read -r LINE || [ -n "$LINE" ]; do
            case "$LINE" in
                *PKG_LIBS*=*)
                    case "$LINE" in
                        *R_STRIP_LINKER_OPTION*)
                            echo $LINE >> src/$MAKEVARS_FILE.R_STRIP_LINKER_OPTION ;;
                        *)
                            echo "$LINE \$(R_STRIP_LINKER_OPTION)" \
                                >> src/$MAKEVARS_FILE.R_STRIP_LINKER_OPTION ;;
                    esac
                    FOUND_PKG_LIBS_ASSIGNMENT=1
                    ;;
                *R_STRIP_LINKER_OPTION*) ;;
                *) echo $LINE >> src/$MAKEVARS_FILE.R_STRIP_LINKER_OPTION ;;
            esac
        done <src/$MAKEVARS_FILE
        
        # Place the proper value in R_STRIP_LINKER_OPTION
        echo "R_STRIP_LINKER_OPTION=\"$ARGUMENT\"" \
            >> src/$MAKEVARS_FILE.R_STRIP_LINKER_OPTION
        
        # If there wasn't an assignment to PKG_LIBS already in place, then we need
        # to add one to get our R_STRIP_LINKER_OPTION in there.
        
        if [ $FOUND_PKG_LIBS_ASSIGNMENT -eq 0 ]; then
            echo "PKG_LIBS = \$(R_STRIP_LINKER_OPTION)" \
                >> src/$MAKEVARS_FILE.R_STRIP_LINKER_OPTION
        fi
    elif [ "$CREATE_IF_MISSING" = "create-if-missing" ]; then
        # Place the proper value in R_STRIP_LINKER_OPTION
        echo "R_STRIP_LINKER_OPTION=\"$ARGUMENT\"" \
            >> src/$MAKEVARS_FILE.R_STRIP_LINKER_OPTION
        echo "PKG_LIBS = \$(R_STRIP_LINKER_OPTION)" \
            >> src/$MAKEVARS_FILE.R_STRIP_LINKER_OPTION
    fi
    
    if [ -f src/$MAKEVARS_FILE.R_STRIP_LINKER_OPTION ]; then
        if [ -f src/$MAKEVARS_FILE ]; then
            mv src/$MAKEVARS_FILE src/$MAKEVARS_FILE.R_STRIP_LINKER_OPTION.bak
        fi
        
        mv src/$MAKEVARS_FILE.R_STRIP_LINKER_OPTION src/$MAKEVARS_FILE
    fi
    
    # This function always suceeds (even if it does nothing)
    return 0
}

# Set the string we want to pass
ARGUMENT=""

if [ "$1" = "fail" ]; then
   check_linker "F77"
   SHLIB_LD_RESULT=$?
   SHLIB_CXXLD_RESULT=$?
else
    check_linker "SHLIB_LD"
    SHLIB_LD_RESULT=$?

    check_linker "SHLIB_CXXLD"
    SHLIB_CXXLD_RESULT=$?
fi

if [ $SHLIB_LD_RESULT -eq $SHLIB_CXXLD_RESULT ]; then
    if [ $SHLIB_LD_RESULT -eq 1 ]; then
        ARGUMENT="-Wl,-S"
    elif [ $SHLIB_LD_RESULT -eq 2 ]; then
        ARGUMENT="-S"
    elif [ $SHLIB_LD_RESULT -eq 3 ]; then
        ARGUMENT="-Wl,-zstrip-class=debug"
    elif [ $SHLIB_LD_RESULT -eq 4 ]; then
        ARGUMENT="-zstrip-class=debug"
    fi
fi

# If both SHLIB_LD and SHLIB_CXXLD are gcc compatible, then we can use the
# -Wl,-S option to strip debugging symbols out of the shared library we create.
# If not, then we'll need set R_STRIP_LINKER_OPTION to an empty string.
if [ "$ARGUMENT" != "" ]; then
    echo "Compatible linkers found."
    
    modify_makevars Makevars create-if-missing
    modify_makevars Makevars.win
fi