Best way to form target executable path at configure time?

I realize this is fraught with peril due to multi-config generators, but we need to be able to write metadata about the build to a json file that is consumed by one of our executables. In particular, this means collecting all executables, their paths on disk after build, along with a couple of target properties. This is what I have so far, which is inside of a function that takes the name of the target as an argument and determines the executable path to the best of its ability.

    # because this is all happening at configure time instead of generation time, we have to
    # fabricate the exe path ourselves instead of using $<TARGET_FILE>
    get_target_property(RUNTIME_OUTPUT_DIRECTORY ${EXECUTABLE_TARGET} RUNTIME_OUTPUT_DIRECTORY)
    if (NOT RUNTIME_OUTPUT_DIRECTORY)
        get_target_property(RUNTIME_OUTPUT_DIRECTORY ${EXECUTABLE_TARGET} BINARY_DIR)
    endif()
    set(TARGET_EXECUTABLE_PATH "${RUNTIME_OUTPUT_DIRECTORY}/${CMAKE_CFG_INTDIR}/${EXECUTABLE_TARGET}")
    if(WIN32)
        set(TARGET_EXECUTABLE_PATH "${TARGET_EXECUTABLE_PATH}.exe")
    endif()

    string(REGEX REPLACE "/\\." "" TARGET_EXECUTABLE_PATH "${TARGET_EXECUTABLE_PATH}")

What are all the ways this can go wrong?

Things that come to mind:

  • Other platforms can have other extensions, so you’ll want ${CMAKE_EXECUTABLE_SUFFIX} there.
  • Properties could be updated after this code.
  • If someone is building in a hidden directory, your regex is inaccurate (~/.foo/bar.exe becomes ~foo/bar.exe which is…very different :slight_smile: ).
  • The name of the executable could be different based on OUTPUT_NAME or SUFFIX or a few other properties.
  • This doesn’t actually work with multi-config generators (assuming TARGET_EXECUTABLE_PATH is meant to work as a path during configure) as $(Configuration) on Visual Studio is…not very handy.

There are probably other cases, but if that doesn’t scare you, I’m sure other things could be found :wink: .

Thanks. The last one is believe it or not, not an issue because the tool that consumes this understands $(Configuration).

Taking a step back though, is there a different solution to my problem? The context here is that we have our own internal testing infrastructure (we don’t use ctest). There are various reasons for this, which I can go into if you’re interested, but the TL;DR is that we have something similar to ctest, but not ctest.

It needs to know about every executable which we designate as a test executable, so we auto-generate this manifest file that the tool then consumes when it’s time to run the test. The basic algorithm is simple. It just iterates every executable which was designated as a test executable, and for each one writes a json blob to a single global file that contains information the program needs in order to run all of the tests.

In this case, I would use configure_file to put the “information the program needs in order to run” except the locations of the binaries. Then file(GENERATE) the configured file into the final location. That will let you use genexes to get the right information and it will be available after CMake runs.

Interesting, I was not aware of file(GENERATE). So essentially I could do almost exactly what I’m doing now to generate the json, but instead of the code I originally posted to try to compute an exe path, just hardcode $<TARGET_FILE:${tgt}> into the json as the value of every "exe_path" key in the json, then have a single file(GENERATE) step which would then fill out all of them with the appropriate values?

Yep, that sounds about right.