2

Is there a way to have CMake build and run a C++ program at configure time (i.e. when you run cmake .) but only if it hasn't been built already?

For example suppose I have a program (written in C++) that generates the version information for my main program, and I want to use that version information in the main program's CMakeLists.txt.

I believe you may be able to do this by having the main CMakeLists.txt execute cmake as a sub-process but that doesn't seem very clean. Is there a better 'official' way?

Edit: This is basically the same question and it seems like there isn't really a good way to do this.

Community
  • 1
  • 1
Timmmm
  • 88,195
  • 71
  • 364
  • 509
  • 1
    Sound's fishy to me. Is it a x-y problem? For extraction a version information, I expect you don't have to compile and execute a file. – usr1234567 Jan 26 '17 at 14:58
  • Possible duplicate of [How to instruct CMake to use the build architecture compiler?](http://stackoverflow.com/questions/36173840/how-to-instruct-cmake-to-use-the-build-architecture-compiler) – Florian Jan 26 '17 at 14:59
  • Well I could have done a lot of sketchy CMake stuff but it was easier to write a C++ program and link it with libgit2. It actually works great when I want the version information to go into a header because I can use `add_custom_command` and have that depend on my version-extraction-program. But it doesn't work if I want it in a CMake variable. – Timmmm Jan 26 '17 at 15:00
  • @Florian: That's the effect I want, but I don't like their solution of 'just write a bash script to do it manually'. Of course I can do that - I want to know if there is a way for CMake to do it for me. (Especially because I support Windows too.) – Timmmm Jan 26 '17 at 15:02
  • Is calling Git with costum_command an option? Compiling software to access a Git repository seems to be over the top. – usr1234567 Jan 26 '17 at 15:23
  • 1
    It's an option, but there's some string processing afterwards that is a bit of a pain in CMake. It may be slightly over the top but it seems cleaner (if I can solve this problem). – Timmmm Jan 26 '17 at 15:35
  • 1
    Possible duplicate of [Building a tool immediately so it can be used later in same CMake run](http://stackoverflow.com/questions/36084785/building-a-tool-immediately-so-it-can-be-used-later-in-same-cmake-run) – usr1234567 Jan 26 '17 at 15:46

1 Answers1

2

I agree with the comments that in this case it may be easier to solve your underlying problem than to answer your question directly. Here's how I get branch/version information from git and put it into my programs:

I use this method on a code-base which supports Windows and several linux variants.

Create a CMake macro to extract information from git:

# Get the current Git commit hash
macro(git_information BRANCH_VAR COMMIT_VAR COMMIT_VAR_SHORT)

    # Get the branch name
    execute_process(
    COMMAND git rev-parse --abbrev-ref HEAD
    WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
    OUTPUT_VARIABLE ${BRANCH_VAR}
    OUTPUT_STRIP_TRAILING_WHITESPACE
    )
    MESSAGE(STATUS "GIT: Current branch -> ${${BRANCH_VAR}}")

    # Retrigger configuration process each time config file changes
    get_filename_component(_cfg_name "${CMAKE_SOURCE_DIR}/.git/refs/heads/${BRANCH}" NAME)
    configure_file("${CMAKE_SOURCE_DIR}/.git/refs/heads/${BRANCH}" "${_cfg_name}.tmp")
    set_source_files_properties(${_cfg_name}.tmp PROPERTIES GENERATED true)

    # Get the head commit
    execute_process(
    COMMAND git rev-parse HEAD
    WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
    OUTPUT_VARIABLE ${COMMIT_VAR}
    OUTPUT_STRIP_TRAILING_WHITESPACE
    )
    MESSAGE(STATUS "GIT: Current commit -> ${${COMMIT_VAR}}")

    # Get the short version of the head commit
    execute_process(
    COMMAND git rev-parse --short HEAD
    WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
    OUTPUT_VARIABLE ${COMMIT_VAR_SHORT}
    OUTPUT_STRIP_TRAILING_WHITESPACE
    )

endmacro()

Then call the macro like so in your top-level CMakeLists.txt:

find_package(Git)
if(GIT_FOUND)
    git_information(BRANCH GIT_COMMIT GIT_COMMIT_SHORT)
ELSE()
    # as a fallback, use the directory name as the branch name
    GET_FILENAME_COMPONENT(BRANCH ${CMAKE_CURRENT_SOURCE_DIR} NAME)
ENDIF()

To actually get the information into the program, I use configure_file (see the documentation if you're not familiar):

configure_file(include/version.h.in version.h @ONLY NEWLINE_STYLE LF)

and that file looks something like this:

#include <string>

namespace myProg
{
    static const std::string BRANCH("@BRANCH@");
    static const std::string GIT_COMMIT("@GIT_COMMIT@");
    static const std::string GIT_COMMIT_SHORT("@GIT_COMMIT_SHORT@");
}

You can imagine that the macro could be used to pull tag or descriptive information from git as well, by adding additional execute_process blocks with the appropriate git commands.

Nicolas Holthaus
  • 7,763
  • 4
  • 42
  • 97
  • I like the answer, maybe the question must be adjusted instead of closing it as a duplicate. – usr1234567 Jan 26 '17 at 21:27
  • 1
    @usr1234567 That's what I'm wondering too, but I think we'd need some feedback first from the OP that editing the question to fit the answer wouldn't undermine his original intent, and in fact helped him. – Nicolas Holthaus Jan 26 '17 at 21:40