[CMake] Putting the git commit hash in a cmake variable

Elvis Stansvik elvis.stansvik at orexplore.com
Thu Oct 11 13:38:23 EDT 2018


Den tors 11 okt. 2018 kl 18:28 skrev Matt Schulte <schultetwin1 at gmail.com>:
>
> Thanks Isaiah and Michael.
>
> Both solutions work great if you just want to generate a header file
> that contains the git commit hash. I have seen these solutions before.
> I'd like to go a little farther and have the current commit hash
> available in a CMake variable. This means CMake must re-configure if
> the commit hash changes. That's what I have setup in my example above,
> but it only works with cmake 3.12 and ninja 1.8.2. I was curious if
> anyone else has tried to store the results of an command line tool in
> a variable and make sure its always up to date by forcing cmake to
> reconfigure if the output of the command no longer matches what is
> stored in that variable.

We have something like this in MyAppVersion.cmake:

find_package(Git QUIET REQUIRED)

execute_process(
    COMMAND "${GIT_EXECUTABLE}" describe --always HEAD
    WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
    RESULT_VARIABLE res
    OUTPUT_VARIABLE MYAPP_VERSION
    ERROR_QUIET
    OUTPUT_STRIP_TRAILING_WHITESPACE)

set_property(GLOBAL APPEND
    PROPERTY CMAKE_CONFIGURE_DEPENDS
    "${CMAKE_SOURCE_DIR}/.git/index")

string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+).*$"
"\\1;\\2;\\3" _ver_parts "${MYAPP_VERSION}")
list(GET _ver_parts 0 MYAPP_VERSION_MAJOR)
list(GET _ver_parts 1 MYAPP_VERSION_MINOR)
list(GET _ver_parts 2 MYAPP_VERSION_PATCH)

if("${MYAPP_VERSION}" MATCHES "^.*-(.*)-g.*$")
    string(REGEX REPLACE "^.*-(.*)-g.*$" "\\1" MYAPP_VERSION_MICRO
"${MYAPP_VERSION}")
else()
    set(MYAPP_VERSION_MICRO "0")
endif()

and include it in our CMakeLists.txt.

That makes the output of `git describe` availabe in the MYAPP_VERSION
CMake variable.

The trick to make it always current is to append to
CMAKE_CONFIGURE_DEPENDS the .git/index file, so that the project is
re-configured if the Git index file is touched.

Can't remember where I found this trick, but it has worked fine for us
ever since.

Elvis

> On Thu, Oct 11, 2018 at 5:55 AM Michael Jackson
> <mike.jackson at bluequartz.net> wrote:
> >
> > You could use a custom_target() instead, where that target is a simple shell/batch file that runs the needed git command and creates a simple header file. Then your main executable/library targets are dependent on that custom_target() so that it is run every time. We do something similar in our project. I added some "smarts" to it to at least compare the new output with the old output and only over write if they are different.
> >
> > --
> > Michael Jackson | Owner, President
> >       BlueQuartz Software
> > [e] mike.jackson at bluequartz.net
> > [w] www.bluequartz.net <http://www.bluequartz.net>
> >
> > On 10/10/18, 5:05 PM, "CMake on behalf of Matt Schulte" <cmake-bounces at cmake.org on behalf of schultetwin1 at gmail.com> wrote:
> >
> >     Hi all,
> >
> >     I'd like to set a CMake variable to the current git commit short hash.
> >     This variable will be used as part of the version string for my
> >     project (ex: "1.0.1+git.${SHORT_HASH}"). I can get at this short hash
> >     by using execute_process and setting the resulting output to a
> >     variable.
> >
> >     ```cmake
> >     execute_process(
> >         COMMAND
> >             git rev-parse --short HEAD
> >         RESULT_VARIABLE
> >             SHORT_HASH_RESULT
> >         OUTPUT_VARIABLE
> >             SHORT_HASH)
> >     ```
> >
> >     My issue is that cmake will only run execute_process once, during the
> >     configure step. I need cmake to run this execute_process on every
> >     build and, if the output has changed, reconfigure to make sure
> >     SHORT_HASH is up to date.
> >
> >     I came up with one solution to this issue: During the configure step,
> >     I can write the current short hash to a file named short_hash.txt. On
> >     every build, I'll re-compute the short hash and verify that the
> >     computed short hash is the same as what is in short_hash.txt. If its
> >     not, I'll write the new short hash to short_hash.txt. I then make
> >     short_hash.txt an input to configure_file. This will cause cmake to
> >     validate SHORT_HASH is properly set, and re-configure if its not.
> >
> >     ```cmake
> >     execute_process(
> >         COMMAND
> >             git rev-parse --short HEAD
> >         RESULT_VARIABLE
> >             SHORT_HASH_RESULT
> >         OUTPUT_VARIABLE
> >             SHORT_HASH)
> >
> >     # If running in script mode (this runs on every build)
> >     if (CMAKE_SCRIPT_MODE_FILE)
> >         if (EXISTS "${SHORT_HASH_FILE}")
> >             file(READ ${SHORT_HASH_FILE} READ_IN_SHORT_HASH)
> >         else()
> >             set(READ_IN_SHORT_HASH "")
> >         endif()
> >
> >         if (NOT ("${READ_IN_SHORT_HASH}" STREQUAL "${SHORT_HASH}"))
> >             message(STATUS "Short hash is out of date")
> >             # This will update short_hash.txt, causing cmake to reconfigure
> >             file(WRITE ${SHORT_HASH_FILE} ${SHORT_HASH})
> >         endif()
> >
> >     # Else running as part of cmake configure
> >     else()
> >         set(SHORT_HASH_FILE ${CMAKE_CURRENT_BINARY_DIR}/short_hash.txt)
> >         file(WRITE ${SHORT_HASH_FILE} ${SHORT_HASH})
> >
> >         # The trick here is to make sure short_hash.txt is listed as a byproduct
> >         add_custom_target(
> >             git_short_hash
> >             BYPRODUCTS
> >                 ${SHORT_HASH_FILE}
> >             COMMAND
> >                 ${CMAKE_COMMAND}
> >                 "-DSHORT_HASH_FILE=${SHORT_HASH_FILE}"
> >                 "-P" "${CMAKE_CURRENT_LIST_FILE}"
> >             COMMENT
> >                 "Re-checking short hash..."
> >             VERBATIM
> >             USES_TERMINAL)
> >
> >         # This configure_file makes cmake reconfigure dependent on short_hash.txt
> >         configure_file(${SHORT_HASH_FILE} ${SHORT_HASH_FILE}.junk COPYONLY)
> >
> >         message(STATUS "Short Hash: ${SHORT_HASH}")
> >     endif()
> >     ```
> >
> >     This works great with cmake 3.12 and ninja 1.8.2! (I was really happy
> >     with how well it worked. I tip my hat to the cmake developers for
> >     this). However, it doesn't work with Makefiles, and causes ninja 1.7.2
> >     to get stuck in an infinite loop. On CMake 3.10 this will cause ninja
> >     1.8.2 to generate a warning about a loop.
> >
> >     Has anyone run into this issue before and have a better solution? Or
> >     is trying to execute a command before cmake checks if it should
> >     reconfigure a hack that should never be done?
> >
> >     Thanks for the help!
> >     Matt
> >     --
> >
> >     Powered by www.kitware.com
> >
> >     Please keep messages on-topic and check the CMake FAQ at: http://www.cmake.org/Wiki/CMake_FAQ
> >
> >     Kitware offers various services to support the CMake community. For more information on each offering, please visit:
> >
> >     CMake Support: http://cmake.org/cmake/help/support.html
> >     CMake Consulting: http://cmake.org/cmake/help/consulting.html
> >     CMake Training Courses: http://cmake.org/cmake/help/training.html
> >
> >     Visit other Kitware open-source projects at http://www.kitware.com/opensource/opensource.html
> >
> >     Follow this link to subscribe/unsubscribe:
> >     https://cmake.org/mailman/listinfo/cmake
> >
> >
> >
> --
>
> Powered by www.kitware.com
>
> Please keep messages on-topic and check the CMake FAQ at: http://www.cmake.org/Wiki/CMake_FAQ
>
> Kitware offers various services to support the CMake community. For more information on each offering, please visit:
>
> CMake Support: http://cmake.org/cmake/help/support.html
> CMake Consulting: http://cmake.org/cmake/help/consulting.html
> CMake Training Courses: http://cmake.org/cmake/help/training.html
>
> Visit other Kitware open-source projects at http://www.kitware.com/opensource/opensource.html
>
> Follow this link to subscribe/unsubscribe:
> https://cmake.org/mailman/listinfo/cmake


More information about the CMake mailing list