Giving Your Firmware Build a Version

Updated: 2019-03-02

Every software product needs a version number. Once bug reports start rolling in, a version number is an absolute requirement for debugging and addressing issues.

In this guide, I describe the details I like to store in each build, how I capture these details, and how I make them available to the firmware.

Table of Contents:

  1. What to Capture
    1. Timestamp
    2. Build Machine
    3. Version
    4. Modifications to Support Windows
  2. Getting Version Into Source
  3. How do I generate my version automatically?
  4. Is there a simpler approach without git tags?

What to Capture

Here are the details I like to log for each build:

  • Time/date of build
  • Machine that built it
  • Version (git tag number)
  • Number of commits away from the last tag
  • Commit hash
  • Dirty or clean build (dirty = local changes that are not committed)

With this set of information, I get a full picture of the build's history and can answer questions such as:

  • Was the build created by the server, or locally by Joe Smith?
  • Is it a clean build, or are there changes that weren't committed?
  • Is it a tagged build, or are there extra commits since the last build?


The easiest way to get the timestamp is to run the date shell command:

$ date
Thu Oct 27 10:27:40 PDT 2016

To include the string in a Makefile:

-DVERSION_BUILD_DATE=\""$(shell date)"\"

Build Machine

For build machine details, I use the whoami and hostname commands:

$ whoami

$ hostname

I take these two values and combine them into a single string:


To include the string in a Makefile:

-DVERSION_BUILD_MACHINE=\""$(shell echo `whoami`@`hostname`)"\"


Conveniently, you can get the version information directly from git:

$ git describe --long --dirty --tags

Above, 0.1.64 is the latest version tag, 2 indicates that I am two commits ahead of the last versioned build, and the commit hash is at the end.

The simplest approach is to pass the entire git describe output into your version string, but I think it makes your version more confusing to customers, vendors, and CMs. The git describe output is also too verbose if you all you want is to display a marketing version, such as "1.2.64".

I store the following values separately:

  • BUILD_TAG (0.1.64)
  • COMMIT (gb27efef)
  • DIRTY_FLAG (+ if dirty, empty if clean)

Here's some example Makefile code to make these values available:

version := $(subst -, ,$(shell git describe --long --dirty --tags))
COMMIT := $(strip $(word 3, $(version)))
COMMITS_PAST := $(strip $(word 2, $(version)))
DIRTY := $(strip $(word 4, $(version)))
ifneq ($(COMMITS_PAST), 0)
ifneq ($(DIRTY),)

export BUILD_TAG :=$(strip $(word 1, $(version)))

This will result in:

Build version: 0.1.64
Build info: gb27efef.2

If the build was dirty, you'd see this instead:

Build info: gb27efef.2+

Modifications to Support Windows

If you're using Windows, you will need to use different commands to get the BUILD_DATE and BUILD_MACHINE variables.

BUILD_DATE := $(shell python -c "from datetime import datetime; print(datetime.utcnow().strftime('%d/%m/%Y, %H:%M'))"
BUILD_MACHINE := $(shell echo %username%)@$(shell hostname)

You can support both Windows and OSX/Linux using shell detection:

ifeq ($(SHELL), cmd.exe)
BUILD_DATE := $(shell python -c "from datetime import datetime; print(datetime.utcnow().strftime('%d/%m/%Y, %H:%M'))"
BUILD_MACHINE := $(shell echo %username%)@$(shell hostname)
BUILD_MACHINE := $(shell whoami)@$(shell hostname)
BUILD_DATE := $(shell date -u +"%d/%m/%Y, %H:%M")

Getting Version into Source

Now that we have all the information we want to include for each build available in MAKE variables, we can pass this information to the compiler using compiler definitions.

In your makefile, create a set of definitions for your version library and add them to your CFLAGS:

CFLAGS += -DVERSION_BUILD_DATE=\""$(shell date)"\" \
          -DVERSION_BUILD_MACHINE=\""$(shell echo `whoami`@`hostname`)"\" \
          -DVERSION_TAG=\"$(BUILD_TAG\" \

In your source code, refer to these definitions:

void version_print()

And voilĂ ! you have a version available.

How do I generate my version automatically?

If you're at the stage where you need version builds, you should also be using a CI server such as Jenkins to build your software. These automated build tools will give each build a unique version. You can use the version in your build process.

I create two parametric values for my builds:


When combined with the Jenkins BUILD_NUMBER variable, you can form your typical version triple by combing them:


Resulting in a string like:


The versioning strategy outlined in the article relies on git tags. We'll use temporary tags during the build process to create a clean version number.

Before running the build, I make a temporary local tag:

make rtos

By creating an annotated tag before the build, git describe will work as expected and return a clean "0.1.64" instead of "0.1.63-8". After the build succeeds, push the tag to the host and so it will be available for the next build:

git tag -a -f -m "Successful nightly build ${MAJOR_VERSION}.${MINOR_VERSION}.${BUILD_NUMBER}" ${MAJOR_VERSION}.${MINOR_VERSION}.${BUILD_NUMBER}

Is there a simpler approach without git tags?

You can pass the ${MAJOR_VERSION}.${MINOR_VERSION}.${BUILD_NUMBER} variables into your make command and utilize those definitions directly:




However, I always recommend pushing a tag for successful builds. Having each build tagged allows for easier debugging and investigation.

Change Log

  • 20190302:
    • Improved grammar
    • Corrected misspellings
    • Added table of contents
    • Added notes on supporting Windows

Related Articles