Installing and using newer GCC

Jurjen Bokma

Last modified: "2021-09-15 21:34:36 jurjen"

Abstract

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


Table of Contents

Rationale

Multiple packaged versions of GCC can coexist on Debian/Ubuntu. But it will often involve mixing distributions (e.g. 'stable' and 'testing' and perhaps 'unstable') on Debian. Or on Ubuntu it will involve using PPAs on Ubuntu. (Likely this one.)

That has drawbacks in and of itself. For me, it is not desirable to maintain a mixed system.

Moreover, building GCC for yourself really isn't that hard. It's well documented too. So what I show here is just how I do it, with some settings I usually use.

The way shown here can be repeated to install multiple versions on the same system.

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

  2. At the time of this writing, 10.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-10.2.0/gcc-10.2.0.tar.xz
    user@host:~src/gcc$ tar xf gcc-10.2.0.tar.xz
    user@host:~src/gcc$ cd gcc-10.2.0/   

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

    As of this writing, compiling from git is not attractive, because gcc 11 is not in pre-release but in experimental phase. So this old example of compiling (by now released) version 10 from git is only kept as a reference. 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 released 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-10.2.0./contrib/download_prerequisites

Procedure 3.  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-10.2.0mkdir ../build-10.2.0
    user@host:~/src/gcc/gcc-10.2.0$ cd ../build-10.2.0
      

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

  2. We're going to install the compiler into (subdirs of) /usr/local, and (if you choose to) in such a way that we can later on remove it and replace it by yet a newer version. That may go wrong if we e.g. need 10.1 to remove 10.2. To avoid that, let's ensure that nothing in /usr/local is used to compile the compiler.

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

    Use env|grep /usr/local to see if any other environment variables mention /usr/local. If necessary, remove or unset these variables.

  3. Before doing the actual build, you should take a look at the prerequisites section of the build docs. But a quick and dirty way on Ubuntu to ensure GCC will most likely build is to install the build-dependencies for the gcc present on the system.

    sudo apt-get build-dep gcc

    . This does assume that deb-src lines are enabled in your /etc/apt/sources.list.

    If that is too hard, try sudo apt-get install build-essential flex. I recently heard it goes a long way.

  4. user@host:~/src/gcc/build-10.2.0$ ../gcc-10.2.0/configure --enable-languages=c,c++ --enable-libquadmath --enable-lto --with-isl --prefix=/usr/local/gcc-10.2.0 --enable-version-specific-runtime-libs --disable-multilib

    The location of the configure script determines which version we will compile here.

    The --prefix prepares the compiler to be installed in that dir. If you are happy with overwriting older versions (if any) with newer (and possibly leave some confusing cruft), you can put it in /usr/local. In that case, keeping multiple versions of the compiler(s)/linker(s) side by side is harder, and you won't be able to easily remove a version. But using the compiler (and the linker, and the manpages, and the libraries...) will be easier.

    The --enable-version-specific-runtime-libs enables you to use multiple versions of compiler and libraries on a system. If you don't care, leave the option out.

    There are more compilers in the collection. If you are so inclined, consider omitting --enable-languages to get all of them at the cost of more time and more disk space.

    Using --disable-multilib is ok if you are only going to compile for the system you're on.

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

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

Procedure 4.  Installing
Procedure 5.  Uninstalling

If you used --prefix=/usr/local/gcc-10.2.0 (as opposed to: --prefix=/usr/local), you can skip this section, because to uninstall this version of compiler and libraries, you can simply remove /usr/local/gcc-10.2.0. If you didn't, read on.

Unfortunately, we cannot simply do make uninstall. We can, however, figure out during installation what files are written. To uninstall later on, we remove precisely those files.

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

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

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

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

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

  5. user@host:~/src/gcc/build-10.2.0$ 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 6.  Using

If you installed in to /usr/local, and if /usr/local/bin comes before /usr/bin in your PATH, you're good to go now. Read on if you opted to accomodate multiple versions of compiler/libraries.

You could create symlinks from the binaries in /usr/local/gcc-10.2.0/bin/ to /usr/local/bin/. But that wouldn't help for e.g. manpages. And it wouldn't uses the old libraries.

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
            puts stderr "\tlibbase = $libbase"
    
    	set includebase  $basedir/lib/gcc/x86_64-pc-linux-gnu/$version/include
            puts stderr "\tincludebase = $includebase"
    
            # to find the compiler and other utils
            prepend-path  PATH  $basedir/bin
    
    	# Just for documentation
    	setenv GCC_VERSION $version
    
    	# This works for Make, even inside Emacs,
    	# as opposed to PATH, which Emacs does not copy from the environment.
    	setenv CXX $basedir/bin/g++
    
    	# 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/lib64:$libbase/$version
    	#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:$libbase/$version
    	#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 from a shell 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."
    
    }
    
    module-whatis   "Sets up GCC 10"
    
    if {[info procs GccFromBase] eq ""} {
        source $::env(HOME)/privatemodules/gcc
    }
    
    GccFromBase /usr/local/gcc-10.2.0 10.2.0
    	  

  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