How to use add_custom_command to copy dlls using g

2019-05-22 14:56发布

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.

标签: cmake
1条回答
狗以群分
2楼-- · 2019-05-22 15:26

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 sufffixes 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.

查看更多
登录 后发表回答