TARGET_RUNTIME_DLLS produces a malformed post-build copy command

Let’s say I have two targets that I am working on Windows. Both of those targets include this CmakeLists.txt file as it is where the main executable is created for both of them. I use a visual studio generator for both of them.

cmake_minimum_required (VERSION 3.21)

project(min_proj CXX C)
add_executable(min_proj)

target_sources(min_proj
PRIVATE
  src/main.cpp

target_include_directories(min_proj
PRIVATE
  "src"
)
  
add_custom_command(TARGET min_proj POST_BUILD
  COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_RUNTIME_DLLS:min_proj> $<TARGET_FILE_DIR:min_proj> 
  COMMAND_EXPAND_LISTS
)

One target has a dependency on some other external my_awesome.dll library that is linked to the executable and is its runtime dependency. The other target is just standalone main.cpp

So what happens is that target with the external library dependency will build successfully while the other will build but will error out at a post-build step. The reason is that TARGET_RUNTIME_DLLS will evaluate to an empty string and CMake will try to use that empty string as an argument for a CMake post-build copy command and will fail since the command will look malformed.

The copy command will look like below which will miss the actual DLL file that is needed to be copied.
Since there should be no DLL, I would expect that this post-build command should not even show up in the first place.

Error out log looks like this

Error MSB3073 ...
"C:\Program Files\CMake\bin\cmake.exe" -E copy .../single_target/build_folder/Debug

On the other target with dependency library

"C:\Program Files\CMake\bin\cmake.exe" -E copy my_awesome.dll .../single_target/build_folder/Debug

I think TARGET_RUNTIME_DLLS behaviour could be a bit more accommodative.
Is this a bug or I am using this feature wrong?

I think that cmake -E copy could have better syntax here. There is the cp -t <dest> flag which, if supported, would allow cmake -E copy -t $<TARGET_FILE_DIR:exe> $<TARGET_RUNTIME_DLLS:exe> which would work much better if the DLLS genex expands to nothing.

1 Like

Note that the code example you have does not have any DLL dependencies, so it being empty is fine. I would first check that the imported targets you’re using actually know where their .dll file is (i.e., check the properties on the targets).

See also this issue:

https://gitlab.kitware.com/cmake/cmake/-/issues/22845

My ticket is just a simple example that I took from a big codebase.

What Robbert has reported is the same flavour of this error, and I agree with him is a bug.

Sure, it could be improved (mainly be CMake providing better imported target utility bits myself). I’m just saying that there are times you’ll also actually have no dependencies and -E copy will fail.

Is there any plan to add this functionality? It seems easy and would really help simplify custom commands (even beyond this). I just bumped in to this exact issue today and wound up “fixing” it with an if (WIN32) check.

I think an issue would be the first step. But a solution would be accepted. I think best would be to add a -t <target dir> flag to cmake -E copy so that an otherwise-empty list of arguments still makes sense. That signature can then be used to do this.

Thanks, I opened https://gitlab.kitware.com/cmake/cmake/-/issues/23543