## # @brief a ${SEMANTIC_VERSION} based on Git tags (or a `VERSION` file) à la # `v..` is provided # # Versions have the format "v.." in accordance with # Semantic Versioning. If a version is provided by a file named `VERSION`, the # full version must be given. If a version is provided by Git tags, only part of # the version must be given, the format is then: "v.". The # version is automatically deduced from the distance in commits to the latest # Git tag. The version set in the file overrides the tags in the Git repository. # # based upon Semantic Versioning: https://semver.org/ # # @attention The version-part is a bit flaky; it gives the distance in # commits to the . version. Due to the non-linear # nature of the commit history (because of branches), it is ambigous # (i. e. multiple commits can be found at the same distance). On top # of that, it is your job to make sure, that patches only fix bugs # and do not change the API (no new functionalty; or old removed). # # One option to address both issues is: Stay in one branch for # releases — for example named 'release' — and cherry-pick new # versions into the branch (alphas, betas, candidates and releases). # Optional: squash cherry-picked commits, so that the part of # the version is only incremented by one. # @warning To update the version you have to run CMake again. # # @todo escape/quote paths # @todo test cases for CMake file components? # @todo make variables local (see: unset()) and put them in a "namespace" # @author Christian Burger find_package(Git) function(get_semantic_version) if(EXISTS "${CMAKE_SOURCE_DIR}/VERSION") message(STATUS "using file `VERSION` to determine version") file(READ "VERSION" REPOSITORY_VERSION) elseif(GIT_FOUND) message(STATUS "using Git to determine project version") execute_process(COMMAND ${GIT_EXECUTABLE} -C ${CMAKE_SOURCE_DIR} describe --always --match v[0-9]*.[0-9]* OUTPUT_VARIABLE REPOSITORY_VERSION) else() message(WARNING "We need Git or a file \"VERSION\" to determine a version (e. g. \"v1.0\").") endif() # test cases #set(REPOSITORY_VERSION "v0.1") # = 0.1.0 #set(REPOSITORY_VERSION "v0.1-1-g12345678") # = 0.1.1 #set(REPOSITORY_VERSION "v0.1") # = 0.0.0 #set(REPOSITORY_VERSION "1234abcd") # = 0.0.0 #set(REPOSITORY_VERSION "") # = 0.1.0 string(REGEX MATCH "^v([0-9]+)\.([0-9]+)([-\\.]([0-9]+))?" SEMANTIC_VERSION_PREPROCESSED "${REPOSITORY_VERSION}") if("${SEMANTIC_VERSION_PREPROCESSED}" STREQUAL "") message(STATUS "Found no version tag (e. g. \"v1.0\").") endif() set(SEMANTIC_VERSION_MAJOR ${CMAKE_MATCH_1}) set(SEMANTIC_VERSION_MINOR ${CMAKE_MATCH_2}) set(SEMANTIC_VERSION_PATCH ${CMAKE_MATCH_4}) if("${SEMANTIC_VERSION_MAJOR}" STREQUAL "") set(SEMANTIC_VERSION "0.0.0") else() if("${SEMANTIC_VERSION_PATCH}" STREQUAL "") set(SEMANTIC_VERSION_PATCH "0") endif() set(SEMANTIC_VERSION "${SEMANTIC_VERSION_MAJOR}.${SEMANTIC_VERSION_MINOR}.${SEMANTIC_VERSION_PATCH}") endif() message(STATUS "Project version is: ${SEMANTIC_VERSION}") set(SEMANTIC_VERSION "${SEMANTIC_VERSION}" PARENT_SCOPE) endfunction() get_semantic_version()