#!/bin/sh # See the manpage for the documented options. # # This script will switch the current compiler installation around # among 2.x, 3.x, and 4.x (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 are three additional options. # # -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. # --wrap SCRIPT Forces all possible mechanisms for invoking the # compiler to be diverted from /usr/bin/NAME to # /usr/bin/NAME.real, and /usr/bin/NAME will become # a symbolic link to SCRIPT. Will fail if binaries # are already diverted. # --unwrap Restores diversions created by --wrap. # # These are special options NOT for general use. They are deliberately # omitted from the --help info. They are intended for internal build # procedures. # # Copyright Apple Computer, Inc. 2002, 2003, 2004, 2005 ####################################################################### GCC_SELECT_VERSION="2.19.1" ####################################################################### # List of headers in & underneath /usr/include that need to be symlinked # to a compiler-specific version. symlink_hdrs0="stdint.h" # 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 should explicitly set a PATH # This is good for security as well as isolating the script from # users mucking around with the system. PATH="/bin:/sbin:/usr/bin:/usr/sbin" ####################################################################### # ## 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. | 4. ] [-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= wrapper= unwrap=0 while [ $# -gt 0 ] ; do case $1 in 2* | -2* | 3* | -3* | 4.* | -4.* ) 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 ;; -wrap | --wrap) if [ $unwrap -ne 0 ]; then echo "Cannot specify both --wrap and --unwrap." exit 1 fi shift if [ -z "$1" ]; then cat < /dev/null; then newdest=$(readlink "$driver.real" | sed "s/\\.real\$//") $dashn ln -sf "$newdest" "$driver.real" || { echo "Couldn't relink $driver.real to $newdest" if [ -z "$forced" ]; then exit 1 ; fi } fi $dashn mv "$driver.real" "$driver" || { echo "Couldn't move $driver.real to $driver" if [ -z "$forced" ]; then exit 1 ; fi } fi else echo "Sorry, $driver does not appear to be wrapped." if [ -z "$forced" ] ; then exit 1 ; fi fi ;; esac done # Re-select current version so that symlinks *don't* go to .real forced=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" -a "$switchto_cc" != "0" ]; 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++ cc.real c++.real gcc.real g++.real; do if [ -e /usr/bin/$driver -a ! -L /usr/bin/$driver ]; then list="$list /usr/bin/$driver" fi done for header in $symlink_hdrs0 ; do if [ -e /usr/include/$header -a ! -L /usr/include/$header ]; then list="$list /usr/include/$header" 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/stdint.h \ /usr/include/gcc/darwin/$switchto_cc/ ; do if [ ! -e $file_or_dir ]; then missing="$missing $file_or_dir" fi done # The following headers are compiler-provided on systems prior to Tiger, # and on Tiger systems with compilers prior to gcc 4.0. if [ $switchto_cc \< 4.0 ]; then for file_or_dir in \ /usr/include/gcc/darwin/$switchto_cc/float.h \ /usr/include/gcc/darwin/$switchto_cc/stdarg.h \ /usr/include/gcc/darwin/$switchto_cc/stdbool.h \ /usr/include/gcc/darwin/$switchto_cc/varargs.h \ /usr/include/gcc/darwin/$switchto_cc/ ; do if [ ! -e $file_or_dir ]; then missing="$missing $file_or_dir" fi done fi 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 [ $switchto_cc \< 3.5 ]; then if [ ! -e /usr/libexec/gcc/darwin/ppc/$switchto_cc ]; then "Files in /usr/libexec/gcc/darwin/ppc/$switchto_cc are missing from your gcc "compiler installation. Reinstall the $switchto_cc compiler, or use another release." exit 1 fi if [ ! -e /usr/lib/gcc/darwin/ppc/$switchto_cc ]; then "Files in /usr/lib/gcc/darwin/ppc/$switchto_cc are missing from your gcc "compiler installation. Reinstall the $switchto_cc compiler, or use another release." exit 1 fi # 3.5 and above # We ought to have an else clause here that checks that # /usr/libexec/gcc/ppc-apple-darwin/4.0.0 exists here. 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++ # Handle wrappers if [ -L $usr/bin/gcc$switchto_cc_driver -o -n "$wrapper" ]; then $dashn ln -sf gcc$switchto_cc_driver.real $usr/bin/cc.real $dashn ln -sf g++$switchto_cc_driver.real $usr/bin/c++.real for driver in gcc g++ gcov; do $dashn ln -sf $driver$switchto_cc_driver.real $usr/bin/$driver.real done else $dashn rm -f $usr/bin/cc.real $dashn rm -f $usr/bin/c++.real for driver in gcc g++ gcov; do $dashn rm -f $usr/bin/$driver.real done fi $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 # 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 [ ! -f $usr/lib/$lib -o -L $usr/lib/$lib ] || continue $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 [ ! -f $usr/local/lib/$lib -o -L $usr/local/lib/$lib ] || continue $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 # For gcc 3.5 and above, some of the symlinks we just set up point to nowhere. # Get rid of them. if [ $switchto_cc \> 3.3 ]; then $dashn rm -f $usr/lib/gcc/darwin/default 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 # 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 $dashn rm -rf $usr/share/man/man1/cc.1 if [ -e /usr/share/man/man1/gcc.1 ]; then $dashn ln -sf gcc.1 $usr/share/man/man1/cc.1 fi # # 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* && ! "$current_cc" == 4* ]]; 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_version=$(sw_vers -productVersion | awk -F. '{ printf "%02d%02d", $1, $2 }') if [ $os_version -ne 1004 ]; then echo "This version of gcc_select can be used only on MacOS X 10.4." exit 1 fi switch_it "$@"