'Auto increment build number using PlatformIO

I have several micro controller projects for home automation. Each of my nodes have a version number which is manually set in the code. This version number is reported during the startup of the node to inform me which code is running.

Sometimes changing the version number is forgotten after making some changes in the code. So an automatic solution has to be found.

I have some idea about the solution:

  1. create a file (version.h): #define BUILDNO xxx
  2. include it in the relevant c codes
  3. auto increment xxx before every build

Can it be implemented? Or are there any other solutions with similar result?



Solution 1:[1]

I have made some research based on answers to my question. PlatformIO can run custom scripts before compile. Here is the process to generate a build number and include it into your project code:

  1. Create a Python script into the project folder: buildscript_versioning.py
FILENAME_BUILDNO = 'versioning'
FILENAME_VERSION_H = 'include/version.h'
version = 'v0.1.'

import datetime

build_no = 0
try:
    with open(FILENAME_BUILDNO) as f:
        build_no = int(f.readline()) + 1
except:
    print('Starting build number from 1..')
    build_no = 1
with open(FILENAME_BUILDNO, 'w+') as f:
    f.write(str(build_no))
    print('Build number: {}'.format(build_no))

hf = """
#ifndef BUILD_NUMBER
  #define BUILD_NUMBER "{}"
#endif
#ifndef VERSION
  #define VERSION "{} - {}"
#endif
#ifndef VERSION_SHORT
  #define VERSION_SHORT "{}"
#endif
""".format(build_no, version+str(build_no), datetime.datetime.now(), version+str(build_no))
with open(FILENAME_VERSION_H, 'w+') as f:
    f.write(hf)
  1. Add a line to platformio.ini:
    extra_scripts = 
        pre:buildscript_versioning.py

Building your project will run the script. 2 files will be created:

  • versioning: a simple text file to store the last build number

  • include/version.h: header file to be included

Now you can add this line to your C code:

#include <version.h>

I started a gitlab repository with some documentation here: https://gitlab.com/pvojnisek/buildnumber-for-platformio/tree/master Further ideas are welcome!

Solution 2:[2]

I solved this problem with git describe and the PlatformIO's advanced scripting.

First off, The project that I used this on relies heavily on git tags for version control. In my opinion, manually tracking version numbers in multiple places is a pain, it should all be based on the git tags. This makes CI and such really easy, Since I never forget to update the version in some file somewhere, It just need to refer to the git tags (regex protecting tags on github/gitlab helps too).

Using git describe, we can inject a commit description into the PIO build.

Here is an example:

platformio.ini

[env:my_env]
platform = teensy
board = teensy40
framework = arduino
extra_scripts = 
    pre:auto_firmware_version.py

auto_firmware_version.py

import subprocess

Import("env")

def get_firmware_specifier_build_flag():    
    ret = subprocess.run(["git", "describe"], stdout=subprocess.PIPE, text=True) #Uses only annotated tags
    #ret = subprocess.run(["git", "describe", "--tags"], stdout=subprocess.PIPE, text=True) #Uses any tags
    build_version = ret.stdout.strip()
    build_flag = "-D AUTO_VERSION=\\\"" + build_version + "\\\""
    print ("Firmware Revision: " + build_version)
    return (build_flag)

env.Append(
    BUILD_FLAGS=[get_firmware_specifier_build_flag()]
)

main.cpp

#include <Arduino.h>

void setup(){
    serial.begin(115200);
    serial.print("Firmware Version: ");
    serial.println(AUTO_VERSION);   // Use the preprocessor directive

   // OR //

   char firmware_char_array[] = AUTO_VERSION;
   serial.println(firmware_char_array, sizeof(firmware_char_array));
}

void loop(){
    // Loop
}

With this configuration, you get the firmware version as a string literal. You can use it however you want since it is dealt with in the preprocessor and not compiler.


This, for example, will print the tag that the commit is aligned with:

v1.2.3

or, if there isnt a tag at the commit, the relation to the latest tag:

v1.2.3-13-gabc1234
?????? ??  ???????? Short commit Hash (not the g)
     ?  ?? Distance from tag
     ?? Latest Tag in Git

You can customize this string however you like in the python script, For example:

build_version = "My_Project_Firmware-" + ret.stdout.strip() + "-" + env['PIOENV'].upper()

would produce:

My_Project_Firmware-v1.2.3-13-gabc1234-MY_ENV

I use the env['PIOENV'] to distinguish between different build environments, Useful if you have regular builds and debug builds.


Mostly a copy of a platformIO forum post:

https://community.platformio.org/t/how-to-build-got-revision-into-binary-for-version-output/15380/5?u=awbmilne

Solution 3:[3]

You'll have to depend on pre-build programs when using C or C++ (Arduino). You have to add a pre-build program which updates a file with a simple:

#define VERSION "1.0.0"

Your automatic increment program needs to store the current version somewhere (preferably inside the version.h so it won't get out of sync) and read, increment and store it upon compilation.

You can use a solution like this one from vurdalakov or this one on cplusadd.blogspot.com which uses Makefiles.

Solution 4:[4]

For my use-case I did NOT necessarily need an incremental number that is always increased by one, but any kind of ascending numbers is fine, therefore using timeticks was for me just fine.

In your platformio.ini:

[common]
firmware_version = '"0.1.0+${UNIX_TIME}"'

[env:release]
build_flags =
    -D FIRMWARE_VERSION=${common.firmware_version}

This shall give you a macro definition in the following format:

#define FIRMWARE_VERSION "0.1.0+1615469592"

Solution 5:[5]

I like your solution for the problem. But wouldn't a version number based on the source code be more useful ? PlatformIO has a section on dynamic build variables with an example that pulls a hash from the git source version https://docs.platformio.org/en/latest/projectconf/section_env_build.html#id4 (scroll down to the Dynamic Build Variables section)

Solution 6:[6]

autoinc-semver is also a good solution for it.

You just have to include the version.h file in your code which looks as followed.

#define _VERSION_MAJOR 0 
#define _VERSION_MINOR 0
#define _VERSION_PATCH 1 
#define _VERSION_BUILD 41 
#define _VERSION_DATE 04-07-2020 
#define _VERSION_TIME 14:40:18 
#define _VERSION_ONLY 0.0.1 
#define _VERSION_NOBUILD 0.0.1 (04-07-2020) 
#define _VERSION 2.0.3+41 (04-07-2020)

And then you just have to add the semver-build.bat ./version.h or semver-build.sh ./version.h command as pre-built option to your compiler environment.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Awbmilne
Solution 3 Tarick Welling
Solution 4
Solution 5 tavis
Solution 6 Michael Uray