On this page
The first article explained why this site uses CMake and VSCode as the default STM32 workflow. This article installs the tools that make that workflow possible on macOS.
The goal is not to create a project yet. The goal is to make sure the compiler, build tools, flashing tool, and editor support are available before the next article builds a minimal STM32 firmware project.
The target board for this series is the STM32 Nucleo L433RC-P. The toolchain setup is useful for many STM32 boards, but later articles will use that board when commands or hardware details need to be specific.
What We Are Installing
An STM32 CMake workflow needs several pieces:
- CMake to configure the build.
- Ninja to run the build quickly and predictably.
- The Arm GNU toolchain to compile and link firmware for Cortex-M targets.
- OpenOCD to flash and debug through ST-LINK.
- VSCode extensions for editing, CMake integration, and debugger launch support.
These tools have separate jobs. CMake is not the compiler. VSCode is not the build system. OpenOCD is not the firmware. Keeping those roles separate makes the workflow easier to understand and easier to fix when something breaks.
Install Homebrew
This article uses Homebrew as the macOS package manager.
If Homebrew is already installed, update it first:
brew update
If Homebrew is not installed, install it from the official Homebrew instructions at https://brew.sh/.
After installation, make sure brew is available in your shell:
brew --version
On Apple Silicon Macs, Homebrew is commonly installed under /opt/homebrew. On Intel Macs, it is commonly installed under /usr/local. You usually do not need to care about the exact path as long as your shell can find brew.
Install CMake and Ninja
Install CMake and Ninja with Homebrew:
brew install cmake ninja
Then verify both commands are available:
cmake --version
ninja --version
CMake will generate build files for the project. Ninja will execute the actual build steps. You can use other CMake generators, but Ninja is a good default for embedded firmware because it is fast, small, and works well from terminals, scripts, and VSCode.
Install the Arm GNU Toolchain
STM32 microcontrollers do not run macOS binaries. They need firmware built for Arm Cortex-M targets. For this series, the compiler toolchain is Arm GNU.
Use the official Arm GNU Toolchain installer from Arm's download page:
https://gitlab.arm.com/tooling/gnu-toolchains-for-arm
On an Apple Silicon Mac, download the package whose name ends with:
darwin-arm64-arm-none-eabi.pkg
For example:
arm-gnu-toolchain-15.3.rel1-darwin-arm64-arm-none-eabi.pkg
The darwin-arm64 part means the toolchain runs on Apple Silicon macOS. The arm-none-eabi part means it builds bare-metal Arm embedded firmware, which is what STM32 Cortex-M projects need.
Do not choose the aarch64-none-elf package for this series. That target is for bare-metal 64-bit Arm systems, not STM32 Cortex-M microcontrollers.
After installing the package, the toolchain is commonly installed under:
/Applications/ArmGNUToolchain/15.3.rel1/arm-none-eabi
Add that toolchain to your current terminal session:
export ARM_GNU_TOOLCHAIN_ROOT="/Applications/ArmGNUToolchain/15.3.rel1/arm-none-eabi"
export PATH="$ARM_GNU_TOOLCHAIN_ROOT/bin:$PATH"
This changes only the current terminal. It does not modify your global shell profile. That is useful if you also use other embedded SDKs, such as nRF Connect SDK, that manage their own compiler environment.
Verify the compiler path and version:
which arm-none-eabi-gcc
arm-none-eabi-gcc --version
You should also have the related binutils commands available:
arm-none-eabi-objcopy --version
arm-none-eabi-size --version
Those tools matter because STM32 projects commonly build an ELF file first, then produce additional outputs such as .bin or .hex files for flashing or inspection. arm-none-eabi-size is also useful for checking how much flash and RAM a firmware image uses.
You may see Homebrew packages or online instructions that install arm-none-eabi-gcc with Homebrew. Be careful with that path. At the time this article was updated, the Homebrew formula available on the test machine installed the compiler driver but did not provide the full target C library headers expected by later examples. That caused a compile error when a simple project included stdint.h:
fatal error: stdint.h: No such file or directory
Using the official Arm GNU Toolchain avoids that issue because the compiler, binutils, and target headers are installed together.
Install OpenOCD
OpenOCD connects your development machine to the microcontroller through a debug probe. The STM32 Nucleo L433RC-P includes onboard ST-LINK hardware, so you do not need a separate probe for the basic workflow.
Install OpenOCD:
brew install open-ocd
Homebrew installs the command as openocd:
openocd --version
OpenOCD will be used later to flash firmware and start a debug server. VSCode can then connect a debugger to that server.
Install VSCode Extensions
Install Visual Studio Code if you do not already have it.
Then install these extensions:
- C/C++ from Microsoft.
- CMake Tools from Microsoft.
- Cortex-Debug from marus25.
The C/C++ extension provides language support. CMake Tools helps VSCode discover and run CMake configure/build tasks. Cortex-Debug provides embedded debug launch support for Cortex-M targets.
These extensions improve the editor workflow, but they should not become the source of truth for the project. The source of truth will be the files in the repository: CMakeLists.txt, the toolchain file, source files, startup code, linker script, and debug configuration.
Check That the Tools Are Available
At this point, these commands should work from the terminal where you added the Arm GNU Toolchain to PATH:
brew --version
cmake --version
ninja --version
which arm-none-eabi-gcc
arm-none-eabi-gcc --version
arm-none-eabi-gcc -print-file-name=include
arm-none-eabi-objcopy --version
arm-none-eabi-size --version
openocd --version
The which command should show the official Arm toolchain path, not Homebrew's shim path. On the test machine, the expected path was:
/Applications/ArmGNUToolchain/15.3.rel1/arm-none-eabi/bin/arm-none-eabi-gcc
At the time of writing, this article was checked with these exact tool versions:
- Homebrew 6.0.18
- CMake 4.4.2
- Ninja 1.13.2
- Arm GNU Toolchain 15.3.rel1
- arm-none-eabi-gcc 15.3.1
- GNU Binutils 2.45.0 for
arm-none-eabi-objcopyandarm-none-eabi-size - OpenOCD 0.12.0
Newer versions will usually work, but recording the versions makes the setup easier to compare if a command behaves differently later.
If those commands work in the terminal, VSCode can usually use them too. If VSCode cannot find a command that works in your terminal, launch VSCode from the terminal with:
code .
That can help VSCode inherit the same environment as your shell. If the code command is not available, open the VSCode command palette and run Shell Command: Install 'code' command in PATH.
Connect the Nucleo Board
Connect the STM32 Nucleo L433RC-P over USB.
The board should power up, and the onboard ST-LINK interface should appear to macOS as a USB device. You do not need to flash anything yet. This article only verifies that the host-side tooling is installed.
If you want a quick OpenOCD connection check, you can try:
openocd -f interface/stlink.cfg -f target/stm32l4x.cfg
If OpenOCD connects, it will keep running and wait for debugger connections. Stop it with Ctrl+C.
Do not worry if this command needs adjustment on your machine. OpenOCD configuration can vary by OpenOCD version, board, and ST-LINK firmware. The important installation check for this article is that the openocd command exists. Later articles will use a project-specific debug setup.
Common macOS Issues
The most common setup issues are path problems, missing tools, and USB/debug probe connection problems.
If a command works in Terminal but not in VSCode, the editor may not have the same PATH. Launching VSCode from the terminal often fixes that. Open the project from the same terminal where you added the Arm GNU Toolchain to PATH:
code .
If which arm-none-eabi-gcc prints a path under /opt/homebrew/bin, your shell is using Homebrew's compiler instead of the official Arm toolchain. Put the Arm toolchain path earlier in PATH for the current terminal session:
export ARM_GNU_TOOLCHAIN_ROOT="/Applications/ArmGNUToolchain/15.3.rel1/arm-none-eabi"
export PATH="$ARM_GNU_TOOLCHAIN_ROOT/bin:$PATH"
If arm-none-eabi-gcc is missing after installation, check the installed toolchain path and confirm that its bin directory is in PATH for the current terminal.
If a later compile fails with fatal error: stdint.h: No such file or directory, the active compiler is probably missing the target C library headers. Confirm which compiler is being used:
which arm-none-eabi-gcc
arm-none-eabi-gcc -print-file-name=include
Use the official Arm GNU Toolchain package and make sure its bin directory appears before Homebrew's directories in PATH.
If you also use nRF Connect SDK, avoid permanently putting the STM32 Arm toolchain at the front of your global shell profile. nRF Connect SDK manages its own Zephyr toolchain environment, and keeping the STM32 path local to STM32 terminal sessions avoids unnecessary conflicts.
If OpenOCD cannot connect to the board, check the USB cable first. Some USB cables provide power but no data. Also make sure no other program is already connected to ST-LINK.
If macOS shows permission or security prompts around downloaded tools, allow the tool only if you intentionally installed it from a trusted source. Prefer Homebrew or official vendor downloads over random binaries.
What This Setup Does Not Include Yet
This article does not create an STM32 project.
It also does not explain linker scripts, startup code, CMake toolchain files, flashing targets, or VSCode debug configurations in detail. Those pieces are easier to understand once the required commands are installed and available.
For now, the success condition is simple: your machine can run the build and debug tools needed by the next article.
Next Steps
The next article creates a minimal STM32 CMake project.
That project will turn this installed toolchain into something concrete: source files, a CMake build, target compiler flags, linker settings, and a firmware artifact that can eventually run on the Nucleo board.