gcc_select   [plain text]


#!/bin/sh

# gcc_select [-n] [-force] [2 | 3 | 3.x] [-h | --help] [-v | --version]
#            [-l | --list] [-root]
#            
#
# Switch the default gcc to either 2.x or 3.x.
#
#  2             Select gcc 2.95.2 as the default compiler.
#  3             Select gcc 3.1 as the default compiler.
#  3.x           Select gcc 3.x as the default compiler.
#  -force        Ensure the links are correct for the specified version.
#                even if the one specified is the current version.
#  -h            Display this help info.
#  --help        Same as -h.
#  -n            Show commands to do selection but do not execute them.
#  -root         Skip 'root' check and assume you have root access.
#  -v            Display gcc_select version number.
#  --version     Same as -v.
#  -l		 Display available compiler versions.
#  --list	 Same as -l.
#           
# This script will switch the current compiler installation from 2.x
# to 3.x or vice versa (or echo the commands to do it if -n is used).
# The current version is displayed if no arguments are specified.
#
# Note, for documentation completeness, there is one additional option.
#
#  -dstroot dir  Install the sym links in a /usr directory within the
#		 specified dir or the dir itself if it specifies a usr
#                directory (e.g., /foo/bar/usr).  -dstroot /usr is
#                allowed, in which case, the sytem is changed to the
#                requested compiler, i.e., the same effect as not
#                specifying it at all. 
#
# This is a special option NOT for general use.  It is deliberately 
# omitted from the --help info.  It is intended for internal build
# procedures to install the sym links in the specified dir instead
# of the system.
#
# Copyright Apple Computer, Inc. 2002, 2003

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

GCC_SELECT_VERSION="2.5"

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

# List of headers in & underneath /usr/include that need to be symlinked
# to a compiler-specific version.

symlink_hdrs0="assert.h		\
	float.h			\
	inttypes.h		\
	stdarg.h  	 	\
	stdbool.h 	 	\
	stdint.h  	 	\
	varargs.h 	 	\
	va-ppc.h"
symlink_hdrs1="machine/limits.h"

# Note, in the above list va-ppc.h is only for gcc2.

# List of libraries in /usr/lib and /usr/local/lib that need to be 
# symlinked to a compiler-specific version.

symlink_libs="libcc_dynamic.a	\
	libcc_kext.a		\
	libgcc.a		\
	libcc.a			\
	libstdc++.a		\
	libsupc++.a"
symlink_local_libs="libcc_noc++.a"

# List of directories (underneath /usr) containing a 'default' 
# symlink that must be adjusted to correspond to the default compiler.

default_dirs="include/gcc/darwin	\
	libexec/gcc/darwin/i386		\
	libexec/gcc/darwin/ppc		\
	lib/gcc/darwin"


#######################################################################
#
## gcc_select's main control function
#
switch_it()
{
    local f who status argc=$# cwd="`/bin/pwd`"
    local n="\n`echo "usage: ${0##*/}" | sed -e 's/./ /g'`"
    local usage="${0##*/} [-n] [-force] [2 | 3 | 3.x] [-h | --help] [-v | --version]$n [-l | --list] [-root]"
    
    #
    # Collect the arguments...
    #
    dashn=
    switchto_cc=0
    switchto_cc_driver=0
    forced=
    list_versions=
    usr="/usr"
    installing=
    show_help=
    root=
    
    while [ $# -gt 0 ] ; do 
	case $1 in
	    2* | -2* | 3* | -3*)
		if [[ "$switchto_cc" != "0" && "$switchto_cc" != "$1" ]]; then
		    echo -e "usage: $usage"
		    echo    "       Inconsistent compiler versions specified."
		    exit 1
		fi
		switchto_cc=$1
		shift
		;;
	    -force | --force)
		forced=1
		shift
		;;
	    -l | -list | --list)
		list_versions=1
		shift
		;;
	    -h | --help | -help | -\?)
	    	show_help=1
	    	shift
		;;
	    -n)
		dashn="echo -e ""\040"
		shift
		;;
	    -dstroot | --dstroot)
		shift
		usr="`echo "$1" | sed -e 's,/$,,'`"
		if [ "${usr##*/}" != "usr" ]; then
		    usr="$usr/usr"
		fi
		installing=1
		shift
		;;
	    -root)
	    	root=1
	    	shift
	    	;;
	    -v | -version | --version)
	    	echo "gcc_select v$GCC_SELECT_VERSION"
	    	exit 0
	    	;;
	    *)
		echo -e "usage: $usage"
		echo    "       Invalid argument ($1)."
		exit 1
		;;
	esac
    done
    
    if [ "$show_help" ]; then
	echo
	echo -e "usage: $usage"
	echo
	echo "2          Select gcc 2.95.2 as the default compiler."
	echo "3          Select gcc 3.1 as the default compiler."
	echo "3.x        Select gcc 3.x as the default compiler."
	echo "-force     Ensure the links are correct for the specified version"
	echo "           even if it maches the current default version."
	echo "-h         Display this help info."
	echo "--help     Same as -h."
	echo "-l         List available compiler versions."
	echo "--list     Same as -l."	
	echo "-n         Show commands to do selection but do not execute them."
	echo "-root      Skip 'root' check and assume you have root access."
	echo "-v         Display gcc_select version number."
	echo "--version  Same as -v."
	echo
	exit 0
    fi

    if [ "$list_versions" ]; then
      echo "Available compiler versions:"
      cd /usr/include/gcc/darwin
      command ls -d [23]*
      exit 1
    fi
    #
    # We determine which compiler is currently installed by looking at
    # the version number it displays when we do a cc -v...
    #
    actual_ver="`cc -v 2>&1  | grep -i 'gcc version'`"
    current_cc="`echo \"$actual_ver\" | sed -e 's/.*gcc version \([^ ]*\).*/\1/'`"
    if [[ ! "$current_cc" == 2* && ! "$current_cc" == 3* ]]; then
        echo "Error trying to determine current cc version (got $current_cc)"
        exit 1
    fi
    # No arguments means display only the current compiler version...
    #   
    if [ $argc -eq 0 ] || [ $argc -eq 1 -a "$show_ver" ]; then
	echo "Current default compiler:"
        echo "$actual_ver"
        exit 0
    fi  
    # if -force is specified without a compiler version, simply refresh the
    # currently installed compiler
    if [ "$forced" ] && [ "$switchto_cc" == "0" ]; then
      switchto_cc=$current_cc
    fi    

    # From here on we must know what we're switching to...
    # Handle 2.95.2 and 3.1 compiler driver names specially, 
    # for compatibility with Jaguar systems.  Other version numbers 
    # should simply have a dash prepended to them, yielding driver
    # names such as "gcc-3.3", "g++-3.4", etc.
    #
    case "$switchto_cc" in
      0)
    	echo -e "usage: $usage"
    	echo    "       You did not specify a compiler version."
    	exit 1
	;;
      2 | 2\.95\.2)
  	switchto_cc_driver="2"
	switchto_cc="2.95.2"
	;;
      3 | 3\.1)
	switchto_cc_driver="3"
	switchto_cc="3.1"
	;;
      *)
	switchto_cc_driver="-$switchto_cc"
	;;
    esac  

    #
    # If actually modifying the system do additional checks...
    #
    if [ ! "$installing" ]; then
	#
	# If what we want to switch to is the same as the current installation
	# we have nothing more to do (unless we're "forced" to do it)...
	#
	if [ ! "$forced" -a "$switchto_cc" == "$current_cc" ]; then
	    echo "You are already using gcc version $current_cc as the default compiler."
	    exit 0
	fi

	#
	# The gcc_select script operates by setting various symlinks to point to
  	# compiler-version-specific files.  The following checks that we do not
	# accidentally clobber non-symlink files while creating the symlinks. 
	#
        list=
        for driver in cc c++ gcc g++; do
	  if [ -e /usr/bin/$driver -a ! -L /usr/bin/$driver ]; then
	    list="$list /usr/bin/$driver"
	  fi
	done
        for header in $symlink_hdrs0 $symlink_hdrs1; do
          if [ -e /usr/include/$header -a ! -L /usr/include/$header ]; then
            list="$list /usr/include/$header"
          fi
	done
        for lib in $symlink_libs; do
          if [ -e /usr/lib/$lib -a ! -L /usr/lib/$lib ]; then
            list="$list /usr/lib/$lib"
          fi
	done
        for lib in $symlink_local_libs; do
          if [ -e /usr/local/lib/$lib -a ! -L /usr/local/lib/$lib ]; then
            list="$list /usr/local/lib/$lib"
          fi
	done
	if [ "$list" != "" ]; then
	    show_list "The following file(s) exist on your system but are NOT symlinks." \
			"Delete them before using gcc_select." $list
	    exit 1
	fi
	
	#
	# We must be running as "root" to be able to do the switching,
	# unless a `-root' has been specified.
	#
	if [ "$dashn" = "" -a ! "$root" ]; then
	    who=$UID
	    if [ $who != 0 ]; then
		echo '*******************************************'
		echo '*** THE gcc_select SCRIPT MUST BE RUN   ***'
		echo '*** AS root (OR WITH THE -root OPTION). ***'
		echo '*** NO CHANGES WERE MADE TO YOUR SETUP. ***'
		echo '*******************************************'
		exit 1
	    fi
	fi
    fi # usr
    
	
    #
    # Make sure that the compiler we are switching to has all the indispensable
    # drivers, headers and libraries.  Here we check only files common to all
    # compiler releases.
    #
    missing=
    #echo "switchto_cc=$switchto_cc   switchto_cc_driver=$switchto_cc_driver"
    for file_or_dir in \
	/usr/bin/gcc$switchto_cc_driver 		\
	/usr/bin/g++$switchto_cc_driver 		\
	/usr/include/gcc/darwin/$switchto_cc/assert.h	\
        /usr/include/gcc/darwin/$switchto_cc/float.h	\
        /usr/include/gcc/darwin/$switchto_cc/inttypes.h	\
        /usr/include/gcc/darwin/$switchto_cc/stdarg.h	\
        /usr/include/gcc/darwin/$switchto_cc/stdbool.h	\
        /usr/include/gcc/darwin/$switchto_cc/stdint.h	\
        /usr/include/gcc/darwin/$switchto_cc/varargs.h	\
	/usr/libexec/gcc/darwin/ppc/$switchto_cc/ 	\
	/usr/lib/gcc/darwin/$switchto_cc/ 		\
	/usr/include/gcc/darwin/$switchto_cc/ ; do
      if [ ! -e $file_or_dir ]; then
	missing="$missing $file_or_dir"
      fi
    done
    if [ "$missing" != "" ]; then
        show_list "The following is missing from your gcc $switchto_cc compiler installation." \
                  "Reinstall the $switchto_cc compiler, or use another release." $missing
        exit 1
    fi

    if [ "$dashn" != "" ]; then
      echo "Commands that would be executed if \"-n\" were not specified:"
    fi
    #
    # Set the sym links to point to the specified compiler tools.
    #
    $dashn mkdir -p $usr/bin
    $dashn rm -f $usr/bin/cc $usr/bin/c++
    $dashn ln -sf gcc$switchto_cc_driver $usr/bin/cc
    $dashn ln -sf g++$switchto_cc_driver $usr/bin/c++
    for driver in gcc g++ gcov; do
      $dashn rm -f $usr/bin/$driver
      if [ -x /usr/bin/$driver$switchto_cc_driver ]; then
	$dashn ln -sf $driver$switchto_cc_driver $usr/bin/$driver
      fi
    done
    # we shall use the cxxfilt in the binutils package for _all_ compiler releases
    $dashn rm -f $usr/bin/c++filt
    $dashn ln -sf ../libexec/binutils/cxxfilt $usr/bin/c++filt

    # Change the various "default" sym links for
    # directories.
    for dir in $default_dirs; do
      if [ -d /usr/$dir ]; then
	$dashn mkdir -p $usr/$dir
	$dashn rm -f $usr/$dir/default
	$dashn ln -sf $switchto_cc $usr/$dir/default
      fi
    done

    # Set up the library symlinks...
    $dashn mkdir -p $usr/lib
    for lib in $symlink_libs; do
      $dashn rm -f $usr/lib/$lib
      if [ -e /usr/lib/gcc/darwin/default/$lib ]; then
        $dashn ln -sf gcc/darwin/default/$lib $usr/lib/$lib
      fi
    done
    $dashn mkdir -p $usr/local/lib
    for lib in $symlink_local_libs; do            
      $dashn rm -f $usr/local/lib/$lib
      if [ -e /usr/lib/gcc/darwin/default/$lib ]; then            
        $dashn ln -sf ../gcc/darwin/default/$lib $usr/local/lib/$lib                                   
      fi
    done
    # The functionality in what used to be libcc_dynamic.a in 2.95.2 now
    # resides in libgcc.a.
    if [ ! -e $usr/lib/libcc_dynamic.a ]; then
      $dashn ln -sf gcc/darwin/default/libgcc.a $usr/lib/libcc_dynamic.a
    fi

    # Set up header symlinks...
    $dashn mkdir -p $usr/include
    for header in $symlink_hdrs0; do
      $dashn rm -f $usr/include/$header
      if [ -e /usr/include/gcc/darwin/default/$header ]; then
        $dashn ln -sf gcc/darwin/default/$header $usr/include/$header
      fi
    done
    for header in $symlink_hdrs1; do    
      $dashn rm -f $usr/include/$header   
      if [ -e /usr/include/gcc/darwin/default/$header ]; then
        $dashn mkdir -p $usr/include/$(dirname $header)   
        $dashn ln -sf ../gcc/darwin/default/$header $usr/include/$header   
      fi
    done
    # Set up man page symlinks...
    # NB: The crufty /usr/bin/cpp script utilizes the
    # /usr/libexec/gcc/darwin/{i386,ppc}/default/cpp binary to do its work.
    # Hence, it makes sense to symlink the cpp man page to the current compiler
    # default.
    $dashn mkdir -p $usr/share/man/man1
    for manpage in c++ g++ gcc gcov cpp; do
      $dashn rm -f $usr/share/man/man1/$manpage.1
      if [ -e /usr/share/man/man1/$manpage$switchto_cc_driver.1 ]; then
        $dashn ln -sf $manpage$switchto_cc_driver.1 $usr/share/man/man1/$manpage.1
      fi
    done
    #
    # We determine which compiler is currently installed by looking at
    # the version number it displays when we do a cc -v...
    #
    if [ ! "$installing" ]; then
      if [ "$dashn" == "" ]; then
        actual_ver="`cc -v 2>&1  | grep -i 'gcc version'`"
        current_cc="`echo \"$actual_ver\" | sed -e 's/.*gcc version \([^ ]*\).*/\1/'`"
        if [[ ! "$current_cc" == 2* && ! "$current_cc" == 3* ]]; then
      	    echo "Error trying to determine current cc version (got $current_cc)"
     	    exit 1
        fi
        echo "Default compiler has been set to:"
        echo "$actual_ver"
      fi
    else
      echo "$(basename $0): SYMLINKS UNDER '$usr'"
      echo "$(basename $0): NOW POINT AT gcc$switchto_cc_driver ($switchto_cc)"
    fi
    exit 0
}

#---------------------------------------------------------------------#
#
## show_list info1 info2 item1 ... - display a list of items
##
## This outputs an error message about a list of "bad" items.  The
## format is:
##
## info1
## info2
##   item1
##   - - -
##
## The info2 line is not output if it is null.
#
show_list()
{
    local info1="$1"
    local info2="$2"
    
    shift 2
    
    if [ ${#@} -gt 0 ]; then
	echo "$info1"
	
	if [ "$info2" != "" ]; then
	    echo "$info2"
	fi
	
	for f; do
	    echo "  $f"
	done
    fi
}

#######################################################################
### main() ############################################################
#######################################################################

os_major="`uname -r | sed -e 's/\([^\.]*\).*/\1/'`"
if [ $os_major -lt 6 ]; then
  echo "You must be using MacOS X 10.2 (Jaguar) or later."
  exit 1
fi
switch_it "$@"