Home How to install an Alternative C/C++ Compiler (GCC/Clang) on macOS (Apple Silicon Mac M1/M2/M3)
Post
Cancel

How to install an Alternative C/C++ Compiler (GCC/Clang) on macOS (Apple Silicon Mac M1/M2/M3)

When installing the XCode command line tools, they also install the Apple Clang compiler for C/C++ and set several symbolic links which shadow the gcc and clang commands and thereby redirecting every call of those commands to the Apple Clang compiler. If you only work with XCode to build Swift or Objective-C applications, the Apple Clang compiler is the definitive best choice, however, if you mainly work with C++ and 3rd party libraries, you might run into issues with the default compiler. Additionally, if you want to use the most recent additions to C++ you often need to install a newer version of GCC or Clang. In this article, you will learn how to install alternative compilers on macOS and how to switch between different compiler versions on the fly.

Installing GCC on macOS

As with most command line tools on macOS, GCC can be installed using Homebrew. If you are already familiar with Homebrew and have it installed, you can skip the following two steps.

To install Homebrew run the following command in your terminal:

1
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

When brew is installed, it needs to be set up for your terminal environment in the ~/.zshrc file. Open or create the ~/.zshrc file (~ means it is in your home directory) and add the following lines:

1
2
3
# homebrew
export PATH="/opt/homebrew/bin:${PATH}"
eval "$(/opt/homebrew/bin/brew shellenv)"

This makes sure brew is set up every time you open a new terminal window. Now close and reopen your terminal to be able to use brew or enter source ~/.zshrc such that the changes to your ~/.zshrc take effect.

Next up, check which versions of GCC can be installed using brew with the following command:

1
brew search gcc

This will show a list of all tools which are associated with the search term gcc most of them are actually versions of GCC (also called Formulae using Homebrew’s semantic):

1
2
3
4
5
6
7
8
==> Formulae
aarch64-elf-gcc     gcc@11              gcc@7               libgccjit           grc
arm-none-eabi-gcc   gcc@12              gcc@8               riscv64-elf-gcc     scc
gcc                 gcc@5               gcc@9               x86_64-elf-gcc      tcc
gcc@10              gcc@6               i686-elf-gcc        ghc                 ncc

==> Casks
gcc-aarch64-embedded      gcc-arm-embedded          gcs                       icc

The standard GCC compilers for Desktop use are the Formulae staring with gcc. For example, to install GCC version 12, execute the following command in your terminal:

1
brew install gcc@12

Afterward, text similar to the text below will appear which shows the installation progress:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
==> Downloading https://formulae.brew.sh/api/formula.jws.json
############################################################################################### 100.0%
==> Downloading https://formulae.brew.sh/api/cask.jws.json
############################################################################################### 100.0%
==> Downloading https://ghcr.io/v2/homebrew/core/gcc/12/manifests/12.3.0-1
############################################################################################### 100.0%
==> Fetching gcc@12
==> Downloading https://ghcr.io/v2/homebrew/core/gcc/12/blobs/sha256:5484357fe4f2083bf035868e0449fede5
############################################################################################### 100.0%
==> Pouring [email protected]_sonoma.bottle.1.tar.gz
🍺  /opt/homebrew/Cellar/gcc@12/12.3.0: 1,437 files, 358.7MB
==> Running `brew cleanup gcc@12`...
Disable this behaviour by setting HOMEBREW_NO_INSTALL_CLEANUP.
Hide these hints with HOMEBREW_NO_ENV_HINTS (see `man brew`).

The text above tells you in which directory the GCC compiler was installed. Lets have a look into this directory with:

1
ls /opt/homebrew/Cellar/gcc@12/12.3.0

This will show several files and directories. However, the most important one is the bin directory because it contains the gcc-12 and g++-12 executables. Both are now accessible from the following locations:

1
2
/opt/homebrew/Cellar/gcc@12/12.3.0/bin/gcc-12
/opt/homebrew/Cellar/gcc@12/12.3.0/bin/g++-12

Since the paths Homebrew creates are sometimes difficult to remember, you can use a shortcut to get the paths using the brew --prefix command:

1
2
$(brew --prefix gcc@12)/bin/gcc-12
$(brew --prefix gcc@12)/bin/g++-12

Installing Clang on macOS

Clang can also be installed using Homebrew. However, executing

1
brew search clang

gives only an incomplete list of available Clang versions for macOS. Therefore, it makes sense to install Clang versions directly from the official GitHub releases of the llvm-project: https://github.com/llvm/llvm-project/releases where you can find pre-built executables for each major and minor version of Clang.

For example, to install version 17.0.6 of Clang on an Apple Silicon Mac download the TAR bundle with the name:

1
clang+llvm-17.0.6-arm64-apple-darwin22.0.tar.xz

It is important that you download a bundle that starts with clang+llvm, otherwise you won’t get a Clang compiler, and also make sure that the name includes arm64-apple-darwin which specifies that it was built for computer architecture of Apple Silicon Macs. If you are using an Intel-based Mac you have to download a TAR bundle with includes x86_64-apple-darwin in its name.

After the download has finished, navigate into your download directory using the Terminal and unpack the TAR bundle with the following command:

1
tar -xvf clang+llvm-17.0.6-arm64-apple-darwin22.0.tar.xz

Now there is a directory with the same name as the TAR bundle in your download directory. You can use the Clang compiler from this directory, however, the recommended way to install software packages under Unix operating systems is to put them into the /opt directory which is reserved for all software packages that are not part of the default operating system installation (as you have seen earlier Homebrew complies with this rule as it is also installed under /opt).

Create a directory /opt/clang+llvm and then move the unpacked TAR bundle into it with:

1
2
sudo mkdir /opt/clang+llvm
sudo mv clang+llvm-17.0.6-arm64-apple-darwin22.0 /opt/clang+llvm/clang+llvm-17.0.6

Because /opt is only writable with root privileges, you have to use sudo and enter your user password to create a directory and move files into it. After successfully moving all files of the Clang compiler into /opt/clang+llvm/clang+llvm-17.0.6 you can use clang and clang++ from the following locations:

1
2
/opt/clang+llvm/clang+llvm-17.0.6/bin/clang
/opt/clang+llvm/clang+llvm-17.0.6/bin/clang++

How to set a Default Compiler on macOS

After you have learned to how to install your desired version of GCC or Clang on macOS, it is time to actually use them. On Unix systems such as macOS or Linux, there are two environment variables which determine the C and C++ compiler that should be used by default. Those variables are $CC and $CXX which point to the executable of the respective compilers ($CC for C and $CXX for C++). However, by default those two environment variables aren’t set, which you can check by typing:

1
2
echo $CC
echo $CXX

Both will return an empty result indicating that they aren’t set. To set GCC 12 as a default compiler for C and C++ add the following two lines to your ~/.zshrc:

1
2
export CC="$(brew --prefix gcc@12)/bin/gcc-12"
export CXX="$(brew --prefix gcc@12)/bin/g++-12"

Enter source ~/.zshrc such that the changes take effect. To validate that $CC and $CXX are set correctly enter both into the terminal (without echo):

1
2
$CC
$CXX

This will run the gcc/g++ compiler. Both compilers will return an error message that there is no input data to compile which validates the environment variables $CC and $CXX are set up correctly.

To set an installed version of Clang, a default compiler works almost the same way. However, as clang wasn’t installed using Homebrew $CC and $CXX have to be set to an absolute path rather than using the brew --prefix command by adding the following two lines to your ~/.zshrc:

1
2
export CC="/opt/clang+llvm/clang+llvm-17.0.6/bin/clang"
export CXX="/opt/clang+llvm/clang+llvm-17.0.6/bin/clang++"

However, you can’t have both compilers enabled at the same time. The last export command for a given environment variable overrides all previous export commands.

Fix C/C++ Compiler Error on macOS when using GCC or Clang

Now that GCC and/or Clang are installed on your computer, it is time to use them. Since the gcc, g++, clang, and clang++ commands are still redirected to Apple’s Clang you have to use the environment variables $CC and $CXX when compiling on the command line. Tools such as cmake will respect the $CC and $CXX variables and won’t use Apple Clang.

The following presents basic Hello World implementations in C and C++ together with the commands to compile them:

Hello World C

1
2
3
4
5
6
#include <stdio.h>

int main() {
    printf("Hello World!\n");
    return 0;
}
1
$CC -o hello hello.c

Hello World C++

1
2
3
4
5
6
7
8
#include <iostream>

using namespace std;

int main() {
    cout << "Hello World!" << endl;
    return 0;
}
1
$CXX -o hello hello.cc

When compiling either of those Hello World programs, you will see an error that libraries and includes can’t be found. This is because macOS places the C/C++ standard libraries and includes into a non-standard location. You can fix this by adding the following options to your compile command (xcrun -show-sdk-path returns the path to the macOS SDK.):

1
-isysroot $(xcrun -show-sdk-path) -L$(xcrun -show-sdk-path)/usr/lib

Resulting in the following commands for compiling the Hello World programs:

1
$CC -o hello hello.c -isysroot $(xcrun -show-sdk-path) -L$(xcrun -show-sdk-path)/usr/lib
1
$CXX -o hello hello.cc -isysroot $(xcrun -show-sdk-path) -L$(xcrun -show-sdk-path)/usr/lib

To make sure that compilation tools such as cmake or Makefiles also include the paths in their compilation process, add the following lines to your ~/.zshrc:

1
2
3
export CFLAGS="-isysroot $(xcrun -show-sdk-path) ${CFLAGS}"
export CXXFLAGS="-isysroot $(xcrun -show-sdk-path) ${CXXFLAGS}"
export LDFLAGS="-L$(xcrun -show-sdk-path)/usr/lib ${LDFLAGS}"

The first two lines extend the environment variables $CFLAGS, and $CXXPFLAGS by adding the include directory for the standard library to them. The third line extends the environment variable $LDFLAGS with the path to the libraries the compiler should link against. All three environment variables are the standard variables incorporated by standard C/C++ compilation tools.

Addendum

Even after adding the CFLAGS, CXXPFLAGS, and LDFLAGS Clang might complain. A possible solution is to export SDKROOT in your ~/.zshrc:

1
export SDKROOT=$(xcrun -show-sdk-path)

How to Switch Between Different Compilers on macOS

If you must switch between different compilers, it is tedious to always edit and source your ~/.zshrc file. Therefore, it is helpful to define custom zsh-functions which let you set your desired compiler by changing the value of the $CC and $CXX variables on the fly. The custom zsh-functions set_gcc_12 and set_clang_17 contain the export commands for $CC and $CXX and set those variables when the functions are called. Remove the previous definition of $CC and $CXX and replace them with the following two zsh-functions:

1
2
3
4
5
6
7
8
9
set_gcc_12() {
    export CC="$(brew --prefix gcc@12)/bin/gcc-12"
    export CXX="$(brew --prefix gcc@12)/bin/g++-12"
}

set_clang_17() {
    export CC="/opt/clang+llvm/clang+llvm-17.0.6/bin/clang"
    export CXX="/opt/clang+llvm/clang+llvm-17.0.6/bin/clang++"
}

After sourcing your ~/.zshrc file, you can now enter the command set_gcc_12 or set_clang_17 to set your desired compiler. If you got even more compilers, you can copy the function, change the name and paths, and you are good to go. If you would like to set one of the compilers as default when starting a new terminal, add a function call under the function definitions in your ~/.zshrc file. This function will be called every time you open a new terminal.

This concludes this article, you learned how to install a custom GCC or Clang C/C++ compiler on macOS, how to fix compilation errors when using those compilers and how to switch between different compilers on the fly.

This post is licensed under CC BY 4.0 by the author.