Choosing a Continuous Integration (CI) compiler

A bike chain close up

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 ReleaseDebian VersionDebian Release Dategcc-arm-none-eabi Package VersionGCC Version Release Date
Bullseye11 (Current oldoldstable release)2021-08-148.3.1 (15:8-2019-q3-1+b1 )2019-07-03
Bookworm12 (Current oldstable release)2023-06-1012.2.1 (15:12.2.rel1-1)2022-12-05
Trixie13 (Current stable release)2025-08-0914.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:

CompilerGCC VersionTextDataBSSTotal Bytes
Trixie Debian Release14.2.1 28812128219231,132
ARM GCC release14.2.125220128219227,540
xPack Release14.2.125144128219227,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.

Discover why Dojo Five EmbedOps is the embedded enterprise choice for build tool and test management.

Sign up to receive a free account to the EmbedOps platform and start building with confidence..

  • Connect a repo
  • Use Dev Containers with your Continuous Integration (CI) provider
  • Analyze memory usage
  • Integrate and visualize static analysis results
  • Perform Hardware-in-the-Loop (HIL) tests
  • Install the Command Line Interface for a developer-friendly experience

Subscribe to our Monthly Newsletter

Subscribe to our monthly newsletter for development insights delivered straight to your inbox.

Interested in learning more?

Best-in-class embedded firmware content, resources and best practices

Laptop with some code on screen

I want to write my first embedded program. Where do I start?

The boom in the Internet of Things (IoT) commercial devices and hobbyist platforms like the Raspberry Pi and Arduino have created a lot of options, offering inexpensive platforms with easy to use development tools for creating embedded projects. You have a lot of options to choose from. An embedded development platform is typically a microcontroller chip mounted on a circuit board designed to show off its features. There are typically two types out there: there are inexpensive versions, sometimes called

Read More »
Medical device monitoring vitals

IEC-62304 Medical Device Software – Software Life Cycle Processes Primer – Part 1

IEC-62304 Software Lifecycle requires a lot of self-reflection to scrutinize and document your development processes. There is an endless pursuit of perfection when it comes to heavily regulated industries. How can you guarantee something will have zero defects? That’s a pretty hefty task. The regulatory approach for the medical device industry is process control. The concept essentially states that if you document how every step must be completed, and provide checks to show every step has been completed properly, you

Read More »
Operating room filled with medical devices

IEC-62304 Medical Device Software – Software Life Cycle Processes Primer – Part II

Part I provides some background to IEC-62304. Part II provides a slightly more in-depth look at some of the specifics. The IEC 62304 Medical Device Software – Software Lifecycle Processes looks into your development processes for creating and maintaining your software. The standard is available for purchase here. So what activities does the standard look at? Here are some of the major topics. For any given topic, there will be a lot more specifics. This will look at a few

Read More »