Git revision as compiler definition in build with CMake

This is how to auto-generate a version header file with git revision (SHA) and the exact build time as C defines. Include header in your source as convenient way to have access to the exact git version for the application version string or diagnostics output.

Header file with the git revision and build timestamp.

// gitversion.h
#pragma once

#define GIT_REVISION "f8d2aca"
#define BUILD_TIMESTAMP "2017-07-14T20:24:36"

CMake script generates the header in cmake build directory

# cmake/gitversion.cmake
cmake_minimum_required(VERSION 3.0.0)

message(STATUS "Resolving GIT Version")

set(_build_version "unknown")

find_package(Git)
if(GIT_FOUND)
  execute_process(
    COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD
    WORKING_DIRECTORY "${local_dir}"
    OUTPUT_VARIABLE _build_version
    ERROR_QUIET
    OUTPUT_STRIP_TRAILING_WHITESPACE
  )
  message( STATUS "GIT hash: ${_build_version}")
else()
  message(STATUS "GIT not found")
endif()

string(TIMESTAMP _time_stamp)

configure_file(${local_dir}/cmake/gitversion.h.in ${output_dir}/gitversion.h @ONLY)

It’s possible to run this script only once in configuration, but you could also use it in main CMakeLists.txt to execute it before every build.

# Example CMakeLists.txt
cmake_minimum_required(VERSION 3.7.0)
project(Main)

set(_target Main)
add_executable(${_target} src/main.c)

add_custom_command(TARGET ${_target}
  PRE_BUILD
  COMMAND ${CMAKE_COMMAND}
    -Dlocal_dir="${CMAKE_CURRENT_SOURCE_DIR}"
    -Doutput_dir="${CMAKE_CURRENT_BINARY_DIR}"
    -P "${CMAKE_CURRENT_SOURCE_DIR}/cmake/gitversion.cmake"
)

# for finding generated gitversion.h
target_include_directories(${_target} PRIVATE ${CMAKE_BINARY_DIR})

Caveats!
– Build steps are executed only if the build is really needed, if target is up to date no command is executed.
– All build steps are not supported when using the Unix Makefile generator.

Get full example from https://github.com/tikonen/blog/tree/master/cmake/git_version

update_directory command for CMake

CMake offers several platform independent commands for manipulating files that are used often in the custom build commands. (For more details see Command-Line Tool Mode.)

One of the more useful ones is the copy_directory that is commonly used in post-build step to copy configuration and support files (configs, DLLs, …) to the binary directory so program can be directly executed in a IDE or debugger.
Unfortunately this command always overwrites the contents of the destination directory so all changes in the target folder are lost, this is a problem when developer wants to keep local configuration changes during development.

Here is how you can implement a custom update_directory that works exactly like copy_directory but writes files to the destination folder only if they are missing or the file timestamp is older.

# cmake/update_directory.cmake
file(GLOB_RECURSE _file_list RELATIVE "${src_dir}" "${src_dir}/*")

foreach( each_file ${_file_list} )
  set(destinationfile "${dst_dir}/${each_file}")
  set(sourcefile "${src_dir}/${each_file}")
  if(NOT EXISTS ${destinationfile} OR ${sourcefile} IS_NEWER_THAN ${destinationfile})
    get_filename_component(destinationdir ${destinationfile} DIRECTORY)
    file(COPY ${sourcefile} DESTINATION ${destinationdir})
  endif()
endforeach(each_file)

This is how you might use it in CMakeLists.txt

# Example CMakeLists.txt
cmake_minimum_required(VERSION 3.7.0)
project(Dummy)

set(_target Dummy)
add_executable(${_target} src/dummy.c)

set(source_dir "${CMAKE_CURRENT_SOURCE_DIR}/configs")
set(dest_dir "$<TARGET_FILE_DIR:${_target}>")

# Copy changed files from config to the binary folder after
# a successful build
add_custom_command(TARGET ${_target}
  POST_BUILD
  COMMAND ${CMAKE_COMMAND}
    -Dsrc_dir="${source_dir}"
    -Ddst_dir="${dest_dir}"
    -P "${CMAKE_CURRENT_SOURCE_DIR}/cmake/update_directory.cmake"
)

Caveat! CMake post build steps are executed only if the build is really needed, if target is up to date no command is executed.

Get full example from https://github.com/tikonen/blog/tree/master/cmake/update_directory

C++11 Variadic Templates implementation of parameterized strings

C++11 introduces variadic templates that allow nice way to have generic functions with unknown arguments, especially when used in recursive way. This makes easy to implement functions that before were cumbersome to write or hard to use.

Nice use case are parametric formatted strings where part of the strings are replaced with run time data. For example:

"Hello {0}, how are you?"
"Hello {name}, you logged in last {date}".

This find of format is quite easy to implement with recursive variadic templates so that it supports both indexed  and named replacement.

inline std::string format_r(int /*pos*/, std::string format) { return format; }
inline std::string format(const std::string format) { return format; }

template <typename T, typename... ARGS>
std::string format_r(int pos, std::string format, const T&amp; value, ARGS... args);

// convenience short hand for building a format parameter
template <typename... ARGS>
std::pair<ARGS...> _p(ARGS... args) { return std::make_pair(std::forward<ARGS>(args)...); };

template <typename K, typename T, typename... ARGS>
std::string format_r(int pos, std::string format, const std::pair<K, T>& value, ARGS... args)
{
    std::ostringstream os;
    os << value.second;
    auto parameter = str("{", value.first, "}");
    replace_all(format, parameter, std::string(os.str()));
    return format_r(pos + 1, format, std::forward<ARGS>(args)...);
}

template <typename T, typename... ARGS>
std::string format_r(int pos, std::string format, const &T value, ARGS... args)
{
    return format_r(pos, format, std::make_pair(str(pos), value), std::forward<ARGS>(args)...);
}

template <typename T, typename... ARGS>
std::string format(const std::string format, const &T value, ARGS... args)
{
    return format_r(0, format, value, std::forward<ARGS>(args)...);
}

 

Format function wraps a recursive function that builds the string by replacing one parameter at a time and then processing it again for the next argument until it runs out of parameters.

String conversions are handled with std::ostringstream that supports overloaded ‘<<‘ operation for most common data types.

Format function can be called with index formatting:

using util::format;
format("Hello {0}!", "World"); // Hello World
format("{0} {1}!", "Hello", "World"); // Hello World
format("{0} + {1} = {2}", 1, 2, 3); // 1 + 2 = 3

Or alternatively with explicit parameter names:

using util;
format("Hello {what}!", _p("what", "World"));
format("{word} {other}!", _p("word", "World"), _p("other", "World"));
format("{a} + {b} = {c}", _p("a", 1), _p("b", 2), _p("c", 3));

Get full implementation ftom https://github.com/tikonen/blog/tree/master/variadicformat