Cmake 如何使用add_custom_命令使用生成器表达式复制DLL?

Cmake 如何使用add_custom_命令使用生成器表达式复制DLL?,cmake,Cmake,我必须在编译时复制解决方案中的dll,因此我将使用add_custom_命令,如下所示: function(dll_dependencies TEST_CASE ) foreach(depencency ${ARGN}) add_custom_command(TARGET ${TEST_CASE} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy $<DLL_FILE_NAME:${depence

我必须在编译时复制解决方案中的dll,因此我将使用add_custom_命令,如下所示:

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()
但我得到的错误是:

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

Error evaluating generator expression:
$<DLL_FILE_NAME:QT5::FileSystem>
计算生成器表达式时出错: $ 计算生成器表达式时出错: $ 所以,我不知道它是否无法到达安装qt相关内容的位置,或者我在add_custom_命令生成器表达式中犯了错误?
我希望函数具有通用性,以便在编译过程中,无论我想在解决方案中获得什么dll,都会放在目标中。

您将无法以通用的方式管理所有依赖项,因为这不是一个通用问题,我认为这甚至不是一个好主意

以Qt为例(这是您问题中提到的唯一依赖项)。在windows上,复制Qt dll依赖项的正确方法是运行
windeployqt.exe
。要运行Qt应用程序,您需要做的不仅仅是复制dll,还需要在二进制目录中精确镜像目录结构/依赖项
windeployqt
为您完成所有这些。以任何其他方式管理Qt dll依赖项都不是一个好主意。没有理由与工具抗争或重新发明轮子

下面是我运行
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()
通常这是可行的,但它也可能复制了大量垃圾。注意,您仍然需要知道在哪里可以找到Dll(在
${QWT_DIR}
中指定)。环境变量可能是你的朋友

如果愿意,您也可以使用
${CMAKE_COMMAND}-E copy
,或者将其转换为接收dll库位置的函数

更一般地说(cmake>=3.5): @Florian提出了一些非常有用的建议,使共享库拷贝跨平台。将regexs替换为
文件(TO_NATIVE_PATH)
,将后缀替换为cmake变量,并将
copy
命令替换为cmake命令行副本,您将获得:

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}
)
文件(到本机路径“${QWT\u DIR}/lib/*.${CMAKE\u SHARED\u LIBRARY\u SUFFIX}”复制\u源)
设置(复制目的地“$”)
#复制
添加自定义命令(目标${TARGET\u NAME}
后期构建
命令${CMAKE_COMMAND}-E copy${copySource}${copyDest}
)

正如评论中提到的,在cmake-E副本中使用通配符(*)直到3.5版才起作用。对于旧版本,可以使用包含特定于平台的copy命令的变量。

我将采用
copy$$
。或者,您是否可以为
DLL\u FILE\u NAME
作为有效的生成器表达式提供参考?我看不出
\u文件
是从哪里来的。@Florian我按照你说的做了,但错误仍然是一样的,错误添加的文件不在那里,我没有在函数中直接使用它的任何地方定义DLL\u文件名。新的生成器表达式。我需要在使用它之前定义它吗?不能只定义自己的生成器表达式。他们是天生的。我想知道,当我在上面建议的代码段中删除了
DLL\u FILE\u NAME
引用时,错误消息怎么会是相同的?@Florian我的意思是,使用代码段时的错误是:错误评估生成器表达式:$No,目标“QT5::core”好的,那么这是另一个错误,这些目标可能真的不存在?因为我们在这里讨论的是一种“官方推荐的方式”。请参阅Brad Kings在上一个代码片段的跨平台兼容性提示中的评论:要将CMake路径更改为平台本机路径,有
文件(to_native_PATH“”)
。对于目标路径,您应该使用生成器表达式
$
,因为对于makefile,它不是
${CMAKE\u CURRENT\u BINARY\u DIR}/$
。使用
${CMAKE_COMMAND}-E copy
会更好,但不幸的是最近才添加了CMAKE 3.5。当路径包含通配符时,
文件(TO_NATIVE_PATH)
会在上工作吗?是的,会。刚刚测试了
文件(到\u NATIVE\u PATH“some/dir/lib/*.dll”\u NATIVE\u PATH)
消息(${u NATIVE\u PATH}”)
,我得到了
some\dir\lib\*.dll
@Florian。酷!这真的很有帮助。我正在考虑将我的项目方法转换为您的(升级cmake对我们来说不是什么大问题)。我想你应该把它作为一个单独的答案贴出来。不客气。我喜欢你的回答。
windeployqt
的使用非常方便。如果你更新了你答案的最后一部分,我的答案就没有那么多要补充的了。我只是想看看为什么导入的目标不起作用(请参阅我对问题的评论)。如果我有时间研究它并在那里找到一个解决方案,我会用另一种可能的方法将它作为一个答案添加进来。
# 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"
)
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}
)