Installing and using newer GCC

Jurjen Bokma

Last modified: "2020-02-25 22:59:33 jurjen"

Abstract

How to install and then use (multiple versions of) GCC compiled from source, newer than the packages on a Debian system.


When we want a version of GCC's gcc or g++ that is not available through the package management system, installing from source is a good idea. Depending on access rights, skills and stability needs, installing from source should even be preferred over installing a package.

The way we do it here can be repeated to install multiple versions on the same system.

Procedure 1.  Fetching sources, external software
  1. user@host:~$ mkdir ~/src/gcc
    cd ~/src/gcc

  2. At the time of this writing, 9.2 is a released version. We can fetch and the compressed tar archive of the release and unpack it:

    user@host:~src/gcc$ wget ftp://ftp.nluug.nl/mirror/languages/gcc/releases/gcc-9.2.0/gcc-9.2.0.tar.xz
    user@host:~src/gcc$ tar xf gcc-9.2.0.tar.xz
    user@host:~src/gcc$ cd gcc-9.2.0   

    The result is the source directory:~/src/gcc/gcc-9.2.0

    Alternatively, we may want gcc 10, which is not yet released. But we can clone GCC's git repository and check out a revision we know will build.

    user@host:~/src/gcc$ git clone git://gcc.gnu.org/git/gcc.git gcc.git
    user@host:~/src/gcc$ cd gcc.git
    user@host:~/src/gcc$ git checkout 787c79e559f5   

    This also results in a source directory: ~/src/gcc/gcc.git

    Whether the source dir is a cloned git repo or an unpacked release archive does not matter. From here on, the commands work on the cloned version, simply because they refer to its directory. So do not copy commands mindlessly. Check that the paths are right.

  3. From inside the source directory:

    user@host:~/src/gcc/gcc.git./contrib/download_prerequisites

Procedure 2.  Configuring and building
  1. GCC doesn't even support building in the source directory. So we create a separate one.

    user@host:~/src/gcc/gcc.gitmkdir ../build-10
    user@host:~/src/gcc/gcc.git$ cd ../build-10
      

    Our build directory has a version, too. IMHO this reduces confusion.

  2. We're going to install into (subdirs of) /usr/local. But if we let anything under that path influence installation, we may end up with multiple version of the compiler that can only be removed in a particular order. That's why we use the system's default compiler (in /usr/bin, from a package) to configure and build the newer compilers from source.

    user@host:~/src/gcc/build-10$ export PATH="/usr/bin:/bin"

    Use env|grep /usr/local to see if any other environment variables mention /usr/local. If necessary, remove them. Or unset the entire variable.

  3. user@host:~/src/gcc/build-10$ ../gcc.git/configure --enable-languages=c,c++,fortran --enable-libquadmath --enable-lto --with-isl --prefix=/usr/local/gcc-10 --enable-version-specific-runtime-libs

    Note that the location of the configure script determines which version we will compile here. The last two options are to enable multiple distinct versions. If we have only one non-package GCC on the system, the last option could be omitted, and prefix be /usr/local

  4. I build using all my four CPUs to speed up the process:

    user@host:~/src/gcc/build-10$ make -j4

Procedure 3.  Installing
Procedure 4.  Uninstalling

Unfortunately, we cannot simply do make uninstall. Since we used a fairly specific prefix of /usr/local/gcc-10, chances are good that uninstalling effectively amounts to removing that directory.

However, the method described here also works if we had used a prefix of /usr/local, which would have mixed the installed files with others that need to remain after uninstall.

  1. user@host:~/src/gcc/build-10$ mkdir /tmp/gcc-local-10

  2. user@host:~/src/gcc/build-10$ sudo mount --bind /tmp/gcc-local-10

  3. user@host:~/src/gcc/build-10$ make install

    (No sudo here. We already owned the target directory.

  4. user@host:~/src/gcc/build-10$ sudo umount /tmp/gcc-local-10

  5. user@host:~/src/gcc/build-10$ pushd /tmp/gcc-local-10
    user@host:/tmp/gcc-local-10$ find -type f > found-files.txt
    user@host:/tmp/gcc-local-10$ find -type l > found-links.txt
    user@host:/tmp/gcc-local-10$ find -type d > found-dirs.txt
    user@host:/tmp/gcc-local-10$ mkdir ~/src/gcc/installed-10
    user@host:/tmp/gcc-local-10$ mv found-*.txt ~/src/gcc/installed-10
    user@host:/tmp/gcc-local-10$ popd

  6. If you want to clean up, the build directory and the source directory can now be removed, provided that the found-*.txt files are saved somewhere else.

  7. If ever you want to remove the version of GCC that you just installed from the system, simply go to the prefix dir (/usr/local in our example) and remove the listed files and symlinks:

    user@host:~$cd /usr/local
    user@host:/usr/local$   cat ~/src/gcc/installed-10/found-{files,links}.txt |sudo xargs rm
    user@host:/usr/local$   cat ~/src/gcc/installed-10/found-dirs.txt |sudo xargs rmdir

    (It may be that some dirs that are not empty need removing as well. The last command will list those, but only the first time you run it.)

Procedure 5.  Using

Switching from one compiler to another should be easy. We use environment modules to set the relevant environment variables.

  1. user@host:~$ sudo apt-get install environment-modules
      

  2. Add these lines to ~/.bashrc:

    MODULES_INIT_FILE=/usr/share/modules/init/bash
    [ -r "${MODULES_INIT_FILE}" ] && . "${MODULES_INIT_FILE}"
    	

  3. I'm a beginner with this, so there's probably a better way. That said, a function sets some environment variables in ~/privatemodules/gcc:

    #%Module1.0#####################################################################
    ##
    ## gcc modulefile
    ##
    ## privatemodules/gcc.
    ##
    proc ModulesHelp { } {
            global gcc
    
            puts stderr "\tDoes nothing."
    	puts stderr "\n\tOffers function to set GCC up using basedir and version."
            puts stderr "\n\tVersion $dotversion\n"
    }
    
    proc GccFromBase {basedir version} {
    
    	set libbase  $basedir/lib/gcc/x86_64-pc-linux-gnu/$version
    	set includebase  $basedir/include/c++/$version
    
            # to find the compiler and other utils
            prepend-path  PATH  $basedir/bin
    
    	# Just for documentation
    	setenv GCC_VERSION $version
    
    	# Where to find the compiler itself. GCC uses this, but PATH above makes it superfluous
    	prepend-path COMPILER_PATH $basedir/bin
    
    	prepend-path CPATH                $includebase
    	#setenv C_INCLUDE_PATH
    	#setenv CPLUS_INCLUDE_PATH
    	#setenv OBJC_INCLUDE_PATH
    
    
    	# This is for linking during build: prefer newer libraries
    	prepend-path LIBRARY_PATH $libbase
    	#setenv LIBRARY_PATH="$(prefix_path LIBRARY_PATH /usr/local/lib64)"
    
    	# Paths mentioned here are built into the executable during the linking phase.
    	# So they persist. As newer gcc is likely not standard on this system, this is
    	# useful to enable the executable to actually run.
    	prepend-path LD_RUN_PATH $libbase
    	#export LD_RUN_PATH="$(prefix_path LD_RUN_PATH /usr/local/lib64)"
    
    	# LD_LIBRARY_PATH is not set here. It is a directive for the linker during
    	# run-time. LD_RUN_PATH should be used instead to pick the right libraries,
    	# as the executable shoud use the libraries that correspond with the includes
    	# it was built with. Using LD_LIBRARY_PATH for that creates a veritable mess.
    
    	# Man should also find the newer manpages
    	prepend-path MANPATH $basedir/share/man
    }
    
    module-whatis   "Include this in other modules to set up a gcc version"
    
    # for Tcl script use only
    set     dotversion      3.2.10	    
    	  

    (If compiling not works, but compiling from under Emacs still gives you trouble, that is because Emacs starts a shell of its own, and doesn't copy the PATH from the shell that started Emacs. To work around, set CXX to $basedir/bin/g++ (and CC to $basedir/bin/gcc, etc). Then the makefile will find the compiler anyway.)

    Using this function, we only need a short ~/privatemodules/gcc-10:

    #%Module1.0#####################################################################
    ##
    ## gcc-10 modulefile
    ##
    ## privatemodules/gcc-10.
    ##
    proc ModulesHelp { } {
            global gcc
    
            puts stderr "\tSets up GCC version 10.0.1."
    
    }
    
    module-whatis   "Sets up GCC 10.0.1"
    
    if {[info procs GccFromBase] eq ""} {
        source /home/jurjen/privatemodules/gcc
    }
    
    GccFromBase /usr/local/gcc-10 10.0.1
    	  

    There is also ~/privatemodules/gcc-9, but it is identical except for the numbers, so I'm leaving it out here.

  4. user@host:~/dev/c++/tests/concepts$ module load use.own
    user@host:~/dev/c++/tests/concepts$  module load gcc-10
    user@host:~/dev/c++/tests/concepts$  make clean && make # works
    user@host:~/dev/c++/tests/concepts$  module unload gcc-10
    user@host:~/dev/c++/tests/concepts$  module load gcc-9
    user@host:~/dev/c++/tests/concepts$  make clean && make # fails: no concepts in this version