2

I have to copy dll's in my solution at the compile time , so i am using add_custom_command for that as follows:

function(dll_dependencies TEST_CASE )
foreach(depencency ${ARGN})        
    add_custom_command(TARGET ${TEST_CASE} POST_BUILD
        COMMAND ${CMAKE_COMMAND} -E copy
        $<DLL_FILE_NAME:${depencency}>)
endforeach()
endfunction()

and in the cmakelist.txt i am calling the above function as:

dll_dependencies(performance QT5::Core QT5::FileSystem)

but i am getting error as :

Error evaluating generator expression:
$<DLL_FILE_NAME:QT5::Core>

Error evaluating generator expression:
$<DLL_FILE_NAME:QT5::FileSystem>

So, i am not getting wether its not able to reach to the location of where qt related stuff is installed or am i doing mistake in the add_custom_command generator expression? I want the function to be so generic that whatever dll i want to get in the solution is placed inside the target during compilation.

einpoklum
  • 118,144
  • 57
  • 340
  • 684
Learner
  • 453
  • 13
  • 29
  • I would take `copy $ $`. Or can you give a reference for `DLL_FILE_NAME` being a valid generator expression? And I can't see where `_file` is coming from. – Florian May 02 '16 at 12:08
  • @Florian i did as you said but error is still the same and _file is not there that was by mistake added and i have not defined DLL_FILE_NAME anywhere i am using it directly in function. New to generator expresion. Do i need to define it before using it? – Learner May 02 '16 at 12:18
  • You can't just define your own generator expressions. They are a given. And I'm wondering how the error message could be the same, when I removed `DLL_FILE_NAME` reference in my suggested code snippet above? – Florian May 02 '16 at 12:26
  • @Florian i mean error while using your snippet is:Error evaluating generator expression: $ No, target"QT5::core" – Learner May 02 '16 at 12:37
  • Ok, then this is another error and those targets may really not exist? Because what we're discussing here is sort of the "official recommended way" of doing it. See Brad Kings's comment in [0009974: CMake should support custom commands that can vary by configuration.](https://public.kitware.com/Bug/view.php?id=9974#c29033) – Florian May 02 '16 at 12:45
  • Possible duplicate of [How to copy the dll's using cmake?](http://stackoverflow.com/questions/36931454/how-to-copy-the-dlls-using-cmake) You should update the old question and not spam SO with repeated, almost identical questions. – usr1234567 May 02 '16 at 12:54
  • Specifically for Qt5 dll's, your custom step should be running the `windeployqt.exe` executable. – Nicolas Holthaus May 02 '16 at 14:29

1 Answers1

4

You're not going to be able to manage all dependencies in a generic way, because it's not a generic problem, and I'll contend that it's not even a good idea to try.

Take Qt for example (which are the only dependencies mentioned in your question). On windows, the right way to copy Qt dll dependencies is to run windeployqt.exe. You need to do more than just copy dll's to get Qt apps to run, there are directory structures/dependencies that have to be mirrored exactly in your binary directory. windeployqt does all that for you. Managing the Qt dll dependencies any other way is a Bad Idea. There is no reason to fight the tool or reinvent the wheel.

Here's my custom build step for running windeployqt:

# Windows specific build steps
if(WIN32)
    # Run winddeployqt if it can be found
    find_program(WINDEPLOYQT_EXECUTABLE NAMES windeployqt HINTS ${QTDIR} ENV QTDIR PATH_SUFFIXES bin)
    add_custom_command(TARGET ${TARGET_NAME} POST_BUILD
    COMMAND ${WINDEPLOYQT_EXECUTABLE} $<TARGET_FILE:${TARGET_NAME}>)
endif()

It finds it, then runs it as a post-build step. Simple.

It assumes:

  • Your Qt root path is set in the QTDIR cmake or environment variable.
  • your executable target name is stored in the variable TARGET_NAME

More generically (for windows, cmake < 3.5):

For non-qt dll's (in this case qwt), I have a step like this:

# on windows, to copy, we need to replace the pesky forward slashes with back slashes
STRING(REGEX REPLACE "/" "\\\\" copySource \"${QWT_DIR}/lib/*.dll\")
STRING(REGEX REPLACE "/" "\\\\" copyDest \"${CMAKE_CURRENT_BINARY_DIR}/$<CONFIGURATION>\")

# Copy
ADD_CUSTOM_COMMAND( TARGET ${TARGET_NAME}
    POST_BUILD
    COMMAND COMMAND copy ${copySource} ${copyDest}
    COMMENT "Copying Qwt dll's...\n"
)

Usually that works, but it also probably copies a lot of garbage. Notice, you still need to know where to find the Dll's (specified in ${QWT_DIR}). Environment variables could be your friend,

You can probably also use ${CMAKE_COMMAND} -E copy if you like, or turn it into a function that takes in the dll lib location.

Even more generically (cmake >= 3.5):

@Florian made some very helpful suggestions for making the shared-library copy step cross-platform. Replacing the regexs with file(TO_NATIVE_PATH), the suffixes with cmake variables, and the copy command with the cmake command-line copy, you get:

file(TO_NATIVE_PATH "${QWT_DIR}/lib/*.${CMAKE_SHARED_LIBRARY_SUFFIX}" copy_source)
set(copy_dest "$<TARGET_FILE_DIR:${TARGET_NAME}>")

# perform copy
ADD_CUSTOM_COMMAND(TARGET ${TARGET_NAME}
    POST_BUILD
    COMMAND ${CMAKE_COMMAND} -E copy ${copySource} ${copyDest}
)

As mentioned in the comments, using wildcard(*) in cmake -E copy was not working until version 3.5. For older versions, a variable containing the platform-specific copy command could be used.

Community
  • 1
  • 1
Nicolas Holthaus
  • 7,763
  • 4
  • 42
  • 97
  • 1
    Just a few hints for cross-platform compatibility of your last code snippet: for changing the CMake paths to platform native paths there is `file(TO_NATIVE_PATH "" )`. For the destination path you should use the generator expression `$`, because for makefiles it won't be `${CMAKE_CURRENT_BINARY_DIR}/$`. And using `${CMAKE_COMMAND} -E copy` would be preferable, but unfortunately [0015703: "cmake -E copy" support for multiple files](https://cmake.org/Bug/view.php?id=15703) was only recently added with CMake 3.5. – Florian May 03 '16 at 12:06
  • will `file(TO_NATIVE_PATH)` work on the when the path contains a wildcard? – Nicolas Holthaus May 03 '16 at 12:59
  • Yes, it does. Just tested `file(TO_NATIVE_PATH "some/dir/lib/*.dll" _native_path)` | `message("${_native_path}")` and I got `some\dir\lib\*.dll`. – Florian May 03 '16 at 13:18
  • @Florian. Cool! This is really helpful. I'm looking into switching my projects method for yours (upgrading cmake isn't a big deal for us). I think you should post it as a separate answer. – Nicolas Holthaus May 03 '16 at 13:21
  • You're welcome. And I like your answer. The use of `windeployqt` is very handy. If you update the last part of your answer, mine will not have so much to add. I just wanted to take a look at why the imported targets are not working (see my comments at the question). If I have some time looking into it and find a solution there, I will add it as an answer with another possible approach. – Florian May 03 '16 at 13:39
  • One can make canonical paths with `$` – diapir Mar 05 '18 at 21:14