Choosing the right Continuous Integration (CI) compiler is crucial because it impacts code quality, deployment reliability, and development velocity. A consistent compiler version and configuration ensure that the same source code always produces identical binaries and avoid the “works on my machine” scenarios. A wisely chosen compiler is future-proofing and assuring that it can always be obtainable, which can avoid spending extra time revisiting the CI when adding a new feature in the future.
We wanted to set up CI for an embedded ARM processor project. The project doesn’t use an operating system (OS), so it’s just a “bare metal” project. In case you’re not familiar with this concept, the compiler and its libraries need to know about your underlying system. If the system is capable of providing certain services, such as memory management, the program’s requests are passed down to the underlying system. Otherwise, it’s up to the compiler and libraries to provide those services.
The GCC development tools are built for a specific combination of host (aka, your laptop) OS, host processor, target OS, and target processor. There are many supported target operating systems and processors. For the build environment, we are using an x86_64-based Debian Linux server. So, the setup is an ARM-based platform with no OS, and it will build on an x86_64-based Debian Linux server. While searching for a GCC cross-compiler to use for this project, we found several options with pros and cons.
Installation Options
apt-get Command
For each Ubuntu/Debian version, there is a corresponding GCC version associated with it. If you use the apt-get package installation tool, you will get the corresponding version. Check out the table below for a few of these combinations:
| Debian Release | Debian Version | Debian Release Date | gcc-arm-none-eabi Package Version | GCC Version Release Date |
| Bullseye | 11 (Current oldoldstable release) | 2021-08-14 | 8.3.1 (15:8-2019-q3-1+b1 ) | 2019-07-03 |
| Bookworm | 12 (Current oldstable release) | 2023-06-10 | 12.2.1 (15:12.2.rel1-1) | 2022-12-05 |
| Trixie | 13 (Current stable release) | 2025-08-09 | 14.2.1 (15:14.2.rel1-1) | 2024-11-19 |
The advantage here is that this is the easiest, standard, and most straightforward way to install the GCC compiler and tools. The downside is that you may end up with a version of the GCC compiler and tools that is several major versions behind the current version. Although the distribution is updated regularly, it does not include updates to GCC with a different major version number.
When the blog post was first written, we had to choose Debian Buster due to compatibility issues with other components in the project. Therefore, we were stuck with GCC version 7.3. Since the Debian distribution has an end-of-life (EOL) date, a compiler tied to a specific distribution might not be available in the future. For example, as of 2025, Debian Buster is no longer supported. A CI pipeline that always sets up the build environment by installing the compiler might start failing after five years because it is no longer able to install the compiler. We can create a Docker image to capture a snapshot of it, but when the chosen Debian distribution becomes obsolete, we will not be able to rebuild the Docker image.
ARM Website
A second option is to download the compiler and tools from the ARM website. They provide an official release of the complete set of GNU tools. Several flavors of this are available for multiple host OS platforms, including Linux, Windows, and macOS. This is a good option because it’s the ARM official release. The releases have become more frequent in the past few years. As a side note, ARM also offers a proprietary compiler/toolset that’s licensed for a fee. We didn’t consider that as an option for this project.
xPack Binary Distribution
While searching around for more options, we came across a binary distribution of GNU ARM Embedded GCC. This distribution is based on the same GNU code base as the official ARM releases and the Debian releases. It’s not tied to any specific distribution version, like Debian. The frequency of releases is a few weeks or a few months later than the official ARM website, and the installation is a bit non-standard. It installs into a folder local to the user who runs the installation script. This results in a non-standard location with an obscure path, which required two things:
(a) The build scripts and path variables must be updated to point to the non-standard installation path. The executables will wind up in ~/.local/xPacks/@xpack-dev-tools/ instead of the typical /usr/local/.
(b) In Debian, the default user is root. If the installation is performed as root, the installation directory will be located in the user’s root home directory, /root/. For security reasons, /root/ does not provide read access to non-root users. That prevents non-root users from accessing the installation directory and, therefore, using the tools. We addressed this by creating the container and installing most of the necessary components as root, then creating a non-root user and switching to that user before performing the tool installation. For security and sanity reasons, we set up CI environments to perform all the CI work as a non-root user.
Build From Source
Yet another option is to pull in the GCC source code and build your compiler, libraries, and tools from scratch. The GCC tools have been involved in a mature project with numerous features, and this means there’s an enormous amount of code to be built. The compressed source code is about 118 MB! And that is just the compiler, not the libraries or the tools. This can be time-consuming even on a fast system. Considering that you can get a recent, pre-built copy, this doesn’t provide much benefit. The value here would be if a custom configuration were needed, but for our project, that wasn’t the case. If you need the absolutely latest version (15.2 at the time of this writing), this is your only option.
Comparisons
We experimented with each one of these approaches by building the EmbedOps STM32 Quickstart. Although the latest GCC version on the ARM website is 14.3, we decided to use GCC version 14.2.1 for this test because both Trixie Debian and the xPack distributions have GCC version 14.2.1. We found out that the xPack distribution resulted in a smaller binary compared to other approaches. The following table is a comparison of the binary built by the GCC compiler from different approaches:
| Compiler | GCC Version | Text | Data | BSS | Total Bytes |
| Trixie Debian Release | 14.2.1 | 28812 | 128 | 2192 | 31,132 |
| ARM GCC release | 14.2.1 | 25220 | 128 | 2192 | 27,540 |
| xPack Release | 14.2.1 | 25144 | 128 | 2192 | 27,464 |
In each case, the same compiler settings were used:
--specs=nano.specs
Tells the GCC/linker to use the Newlib-nano C library instead of the standard Newlib library
-std=gnu11
Set the language standard to gnu11
-mcpu=cortex-m4
Generate code for an ARM Cortex-M4
-mthumb
Generate code that can call between ARM and Thumb instructions
-mfloat-abi=hard
Select ‘hard’ as the floating-point ABI allows the generation of floating-point instructions and uses FPU-specific calling conventions
-mfpu=fpv4-sp-d16
Select ‘fpv4-sp-d16’ as the floating-point hardware
-g3
Include extra debugging information
-fdata-sections
Place each data item into its own section in the output file
-ffunction-sections
Place each function into its own section in the output file
-O0
Reduce compilation time and make debugging produce the expected results
Conclusion
When building an embedded project, we want to use a fixed compiler version because different compiler versions might result in different behavior or different binary size. If we choose the Debian release approach and we pin a compiler version, when the selected Debian distribution is no longer supported, we are forced to revisit our build system. It leaves us with the official ARM release and the xPack distribution. Both approaches have equal complexity in installing the compiler. You will get the latest release faster on the ARM website, but you can get a smaller binary with the xPack distribution. For this project, we proceed with the xPack approach for a smaller footprint.
Let’s Work Together
If you want all of the benefits listed here and none of the hassle of setting them up or maintaining them, consider reaching out! At Dojo Five, we have an elite team of engineers ready to help you with all aspects of your embedded journey. We enjoy working on interesting problems that need solving. You can schedule a call with our team at any time or check out EmbedOps, our embedded DevOps platform.


