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 gcc@12--12.3.0.arm64_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.