It’s honestly surprising how frustrating it is to setup a clean C++ development environment on Mac. VSCode is the standard tool “IDE” of choice these days and C++ is no exception to the list of languages that is used within the VSCode editor.
There have been various Mac OS updates over the years that break things such as Catalina swapping C++ header and library directories from /usr/include
and /usr/lib
into XCode stuff. Let’s fix things up and get a debugger, Intellisense and compilation working on Mac.
Note: Not every step might be required for you, but there is a lot of cross-device issues with C++ configuration on Mac depending on the architecture and OS version, so the idea is to include as many debug options as possible. For reference, I’m using an M1 Mac (ARM).
Starting fresh with VSCode (optional)
I start with a clean slate if I’m having issues. Be sure to perform necessary backups:
rm -rf ~/.vscode
rm -rf $HOME/Library/Application Support/Code
Also, drag Visual Studio Code
from your /Applications
dir to the trash.
Reinstall using Homebrew or download from Microsoft here. Personally, I download from Microsoft directly.
brew install --cask visual-studio-code
Or if you already have it then:
brew reinstall visual-studio-code
Open it by running code
in your terminal to ensure it was installed properly.
Compiler
clang
is the c++ compiler Apple provides via the XCode tools. You can check if you have it via:
clang --version
If you have errors, then just run:
xcode-select --install
To prevent any issues with missing libraries, you can install XCode from Apple from the App Store if you don’t have it.
With Homebrew, you can get clang & clang++ (For C and C++ respectively) installed via LLVM
.
brew update
brew install llvm
Verify path is correct for both:
❯ which clang
/opt/homebrew/opt/llvm/bin/clang
❯ which clang++
/opt/homebrew/opt/llvm/bin/clang++
Mac will install C++ header files and libraries in some random directory. You can find the directory via xcrun
by running:
xcrun --show-sdk-path
This path is important because we need to tell the compiler to look at this root via the CPLUS_INCLUDE_PATH
environment variable. Additionally, we should include the LLVM
path as a higher precedent search location.
export CPLUS_INCLUDE_PATH=/opt/homebrew/opt/llvm/include/c++/v1:/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include
I’ve added this into my ~/.zshrc
file to export the PATH by default for all future terminal sessions.
If you run into any linker errors, then tell the linker to look for libraries in the following path via the LIBRARY_PATH
env variable.
export LIBRARY_PATH=$LIBRARY_PATH:/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib
Again, I export this in ~/.zshrc
.
Extensions
VSCode is not a traditional C/C++ IDE, but Microsoft has extensions that keep you thinking it is. Install the following extensions:
Name: C/C++
Id: ms-vscode.cpptools
Description: C/C++ IntelliSense, debugging, and code browsing.
Version: 1.19.9
Publisher: Microsoft
VS Marketplace Link: https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools
If you create a new folder with a new C++ file in it like:
mkdir cpp_fun
touch main.cpp
There are two approaches you can use for configuration. Default and per-project. I’ll explain both.
Default Configuration
Verify the paths match your system
Hit <cmd> + <shift> + P
you should see:
Preferences: Open User Settings (JSON)
{
...
"C_Cpp.default.defines": [],
"C_Cpp.default.macFrameworkPath": [],
"C_Cpp.default.compilerPath": "/opt/homebrew/opt/llvm/bin/clang",
"C_Cpp.default.cStandard": "c23",
"C_Cpp.default.cppStandard": "c++23",
"C_Cpp.default.intelliSenseMode": "macos-clang-arm64",
"C_Cpp.intelliSenseEngineFallback": "enabled",
"C_Cpp.default.includePath": [
"${workspaceFolder}/**",
"/opt/homebrew/opt/llvm/include/c++/v1",
"/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include"
]
}
You can trigger a new c_cpp_properties.json
file by cmd + shift + p
and selecting Edit Configurations
. It should auto gen the file with the following:
{
"configurations": [
{
"name": "Mac",
"includePath": [
"${default}"
]
}
],
"version": 4
}
And inherit the defaults we defined above.
Per Project Configuration
Hit <cmd> + <shift> + P
you should see:
C/C++ Edit Configurations (JSON)
This will auto generate a c_cpp_properties.json
file underneath .vscode
at the root.
I have it configured with the following:
{
"configurations": [
{
"name": "Mac",
"includePath": [
"${workspaceFolder}/**",
"/opt/homebrew/opt/llvm/include/c++/v1",
"/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include"
],
"defines": [],
"macFrameworkPath": [],
"compilerPath": "/opt/homebrew/opt/llvm/bin/clang",
"cStandard": "c23",
"cppStandard": "c++23",
"intelliSenseMode": "macos-clang-arm64"
}
],
"version": 4
}
I explicitly removed all macFrameworkPath
values as it made my Intellisense ugly.
Debugging
For debugging, I’m using CodeLLDB
as shown above. If you select the Debug
button in the sidebar, it’ll ask you to create a new configuration. Select the second option:
The configuration in .vscode/tasks.json
might look like:
{
"tasks": [
{
"type": "cppbuild",
"label": "C/C++: clang++ build active file",
"command": "/opt/homebrew/opt/llvm/bin/clang++",
"args": [
"-fcolor-diagnostics",
"-fansi-escape-codes",
"-g",
"${file}",
"-o",
"${fileDirname}/${fileBasenameNoExtension}"
],
"options": {
"cwd": "${fileDirname}"
},
"problemMatcher": [
"$gcc"
],
"group": {
"kind": "build",
"isDefault": true
},
"detail": "Task generated by Debugger."
}
],
"version": "2.0.0"
}
Active debugger
Example stepping through the program
Source code
Very complicate program code example /s.
#include <iostream>
#include <string>
int main()
{
std::string greeting = "hello";
std::cout << greeting << std::endl;
}
Yay 👏, we did it. Clearly, this is a lot more fun to configure than more modern day languages like Go and Javascript! If you have updates or suggestions, post them down below.
Note: If you have any issues with the extension, double check paths. Double