Git version in cmake

Posted by Marcus Folkesson on Tuesday, November 14, 2023

Git version in CMake

All applications have versions. The version should somehow be exposed in the application to make it possible to determine which application we are actually running.

I've seen a plenty of variants on how this is achieved, some are good and some are really bad. Since it's such a common thing, I thought I'd show how I usually do it.

I use to let CMake determine the version based on git describe and tags, the benefit's that it is part of the build process (i.e no extra step on the build server - I've seen it all...), and you will get right version information also for local builds.

git describe also gives you useful information which allows you to derive the build to the actual commit. Incredible useful.

The version information will be stored as a define in a header file which should be included into the project.

Hands on

The version file we intend to include in the project is generated from Version.h.in:

1#pragma once
2#define SOFTWARE_VERSION "@FOO_VERSION@"

@FOO_VERSION@ is a placeholder that will be replaced by configure_file() later on.

The "core" functionality is in GenerateVersion.cmake:

 1find_package(Git)
 2
 3if(GIT_EXECUTABLE)
 4  get_filename_component(WORKING_DIR ${SRC} DIRECTORY)
 5  execute_process(
 6    COMMAND ${GIT_EXECUTABLE} describe --tags --dirty
 7    WORKING_DIRECTORY ${WORKING_DIR}
 8    OUTPUT_VARIABLE FOO_VERSION
 9    RESULT_VARIABLE ERROR_CODE
10    OUTPUT_STRIP_TRAILING_WHITESPACE
11    )
12endif()
13
14if(FOO_VERSION STREQUAL "")
15  set(FOO_VERSION 0.0.0-unknown)
16  message(WARNING "Failed to determine version from Git tags. Using default version \"${FOO_VERSION}\".")
17endif()
18
19configure_file(${SRC} ${DST} @ONLY)

It will look for the git package, receive the tag information with git describe and store the version in the FOO_VERSION variable.

configure_file() is then used to create a version.h out of version.h.in where the FOO_VERSION placeholder is replaced by the actual version.

To use GenerateVersion.cmake, we have to create a custom target in the CMakeLists.txt file and provide the source and destination path for the version file:

1add_custom_target(version
2  ${CMAKE_COMMAND} -D SRC=${CMAKE_SOURCE_DIR}/Version.h.in
3                   -D DST=${CMAKE_SOURCE_DIR}/Version.h
4                   -P ${CMAKE_SOURCE_DIR}/GenerateVersion.cmake
5  )

Finally, make sure that the target (${PROJECT_NAME} in this case) depends on the version target:

1add_dependencies(${PROJECT_NAME} version)

That's basically it.