12

In order to run the unit tests in one of my projects, I have a custom command which copies the executable, libraries, and other related files to somewhere else so that they can be run with a specific setup rather than running them where they're built. On Linux, this is quite straightforward. But on Windows, I've hit a bit of a snag due to the fact that cmake appends the configuration name to the ouput directories (which I like in general, but it screws up what I'm doing in this case). It makes it hard to determine the paths to the generated libraries or executables. For instance, if I had a custom command which just copied the executable to another directory

set(EXE_PATH "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/exeName${CMAKE_EXECUTABLE_SUFFIX}")
set(NEW_EXE_PATH "${RUN_UNITTESTS_DIR}/exeName${CMAKE_EXECUTABLE_SUFFIX}")

add_custom_command(TARGET unitTests POST_BUILD
                   COMMAND ${CMAKE_COMMAND} ARGS -E copy "${EXE_PATH}" "${NEW_EXE_PATH}")

it's going to choke on Windows, because the executable isn't really in CMAKE_RUNTIME_OUTPUT_DIRECTORY. Depending on the configuration type, it's in either ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/Release or ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/Debug. On Linux, that could be trivially fixed by using CMAKE_BUILD_TYPE and adding it to the path, but that doesn't work with Windows, because on Windows, cmake generates multiple configurations rather than just one. So, what I'd like to be able to do is something like

add_custom_command(TARGET unitTests POST_BUILD
                   debug
                   COMMAND ${CMAKE_COMMAND} ARGS -E copy "${DEBUG_EXE_PATH}" "${DEBUG_NEW_EXE}")

add_custom_command(TARGET unitTests POST_BUILD
                   release
                   COMMAND ${CMAKE_COMMAND} ARGS -E copy "${RELEASE_EXE_PATH}" "${RELEASE_NEW_EXE}")

and some cmake commands see to be able to do that (e.g. target_link_libraries), but as far as I can tell, add_custom_target doesn't provide that capability. So, the question is how I would do that? How can I make a custom command be configuration-specific on Windows?

Jonathan M Davis
  • 37,181
  • 17
  • 72
  • 102
  • 1
    Please keep in mind that this isn't a matter of "Windows vs. Linux", but a matter of single- vs. multi-configuration generators. There are single-configuration generators on Windows also (Ninja, NMake) and there are multi-configuration generators on at least macOS (XCode). For your question, you probably don't need to care, but if you do, look at whether CMAKE_CONFIGURATION_TYPES is empty or not, and not what platform you are on. – Matthew Jun 13 '18 at 17:38

2 Answers2

12

It could be solved with the help of the next "generator expressions" (CMake 2.8.10):

  • $<0:...> = empty string (ignores "...")
  • $<1:...> = content of "..."
  • $<CONFIG:cfg> = '1' if config is "cfg", else '0'

You can combine them to reach behaviour that you need (pseudocode):

if debug then ${DEBUG_EXE_PATH} elseif release then ${RELEASE_EXE_PATH}

which translates to:

$<$<CONFIG:debug>:${DEBUG_EXE_PATH}>$<$<CONFIG:release>:${RELEASE_EXE_PATH}>

So your string will look like:

add_custom_command(TARGET unitTests POST_BUILD
                       COMMAND ${CMAKE_COMMAND} ARGS -E copy "$<$<CONFIG:debug>:${DEBUG_EXE_PATH}>$<$<CONFIG:release>:${RELEASE_EXE_PATH}>" "$<$<CONFIG:debug>:${DEBUG_NEW_EXE}>$<$<CONFIG:release>:${RELEASE_NEW_EXE}>")

Details: CMake:add_custom_command

malat
  • 12,152
  • 13
  • 89
  • 158
Vladimir Ivanov
  • 147
  • 1
  • 4
  • 2
    You should *almost never* need to use this approach. Unless your build is doing something truly exotic, using `$` is much, much better. – Matthew Jun 13 '18 at 17:35
  • Does not work for solution generated project (as it's multiconfiguration), but plain $ does work. Open folder with ninja is anyway works normally faster than visual studio project, but there are still some glitches with folder open (at 9.1.2020), someone might prefer solution. – TarmoPikaro Jan 09 '20 at 06:59
7

This is a case for the generator expressions provided for use with add_custom_command.

In your case you want the full path to your compiled exe, and also its filename to append to your destination directory. These are $<TARGET_FILE:unitTests> and $<TARGET_FILE_NAME:unitTests> respectively.

Your full command would be:

add_custom_command(TARGET unitTests POST_BUILD
                   COMMAND ${CMAKE_COMMAND} -E
                       copy $<TARGET_FILE:unitTests>
                            ${RUN_UNITTESTS_DIR}/$<TARGET_FILE_NAME:unitTests>)
ceztko
  • 14,736
  • 5
  • 58
  • 73
Fraser
  • 74,704
  • 20
  • 238
  • 215