【问题标题】:cmake does not copy file in custom commandcmake不会在自定义命令中复制文件
【发布时间】:2015-11-26 12:03:35
【问题描述】:

我想将 qml 文件从源目录复制到构建目录。以下脚本仅在第一次时才能正常工作。当我更改任何 *.qml 文件并运行 make 时,它​​们不会被复制到构建文件夹,它们不会被更新。我做错了什么?

file(GLOB_RECURSE SOURCES *.cpp)
file(GLOB_RECURSE QMLS *.qml)

add_library(MyLib SHARED ${SOURCES} ${QMLS})

foreach(QmlFile ${QMLS})
  add_custom_command(TARGET MyLib POST_BUILD
                     COMMAND ${CMAKE_COMMAND} -E copy_if_different
                     ${QmlFile} $<TARGET_FILE_DIR:MyLib>)
endforeach()

【问题讨论】:

  • 您能否添加您正在使用/定位的 CMake 版本和构建环境?一般来说,将非源文件添加到add_library() 是为了在生成的IDE 项目中列出这些文件。它不会向该文件添加依赖项。我认为您正在寻找的是目标属性LINK_DEPENDS:“指定此目标的链接规则所依赖的文件的完整路径的分号分隔列表。如果任何命名文件,目标二进制文件将被链接比它新。”
  • @Florian 建议设置 LINK_DEPENDS 属性将解决最小代码编辑问题,但使用 non-TARGET add_custom_command,正如 Angew 回答的那样,似乎是这里比较合适。如果库链接实际上不依赖于.qml,为什么每次更改这些文件时都要重新链接?如果复制的.qml文件不依赖于库,为什么只有在.cpp文件发生更改时才重新检查原始文件的内容?

标签: cmake copy dependencies


【解决方案1】:

虽然 Angew 的 answer 很好,但可以消除使用额外的目标。为此,add_library 调用应使用 已复制 .qml 文件(而不是 原始 文件,如问题帖子中的脚本):

# This part is same as in Angew's answer
set(copiedQmls "")
foreach(QmlFile ${QMLS})
  get_filename_component(nam ${QmlFile} NAME)
  add_custom_command(
    OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${nam}
    COMMAND ${CMAKE_COMMAND} -E copy_if_different ${QmlFile} ${CMAKE_CURRENT_BINARY_DIR}
    DEPENDS ${QmlFile}
    COMMENT "Copying ${QmlFile}"
    VERBATIM
  )
  list(APPEND copiedQmls ${CMAKE_CURRENT_BINARY_DIR}/${nam})
endforeach()

# But instead of creating new target, we reuse library one.
add_library(MyLib SHARED ${SOURCES} ${copiedQmls})

当库target被构建时,它会触发add_library调用中的non-sources文件被更新(如果有对应的add_custom_command调用),但是更新非源文件不会强制重建库文件。这就是为什么您的原始代码无法按预期工作的原因。

注意,由于 CMake 无法将 .qml 文件识别为源文件,因此您不需要为它们设置 GENERATED 属性,如 here 所述。

【讨论】:

  • 关于使用库目标驱动复制命令的要点; +1。
【解决方案2】:

您正在使用add_custom_commandTARGET 签名,这意味着这些命令是作为构建TARGET 的一部分执行的。在您的情况下,POST_BUILD,这意味着命令将在MyLib 的构建完成后运行。如果MyLib 是最新的并且不需要重新构建,则命令将不会运行。

您可能希望改用输出生成签名 (OUTPUT)。像这样的:

set(copiedQmls "")
foreach(QmlFile ${QMLS})
  get_filename_component(nam ${QmlFile} NAME)
  add_custom_command(
    OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${nam}
    COMMAND ${CMAKE_COMMAND} -E copy_if_different ${QmlFile} ${CMAKE_CURRENT_BINARY_DIR}
    DEPENDS ${QmlFile}
    COMMENT "Copying ${QmlFile}"
    VERBATIM
  )
  list(APPEND copiedQmls ${CMAKE_CURRENT_BINARY_DIR}/${nam})
endforeach()

add_custom_target(
  CopyQMLs ALL
  DEPENDS ${copiedQmls}
)

请注意,很遗憾,add_custom_commandOUTPUT 参数不支持生成器表达式,因此无法在此处使用 $&lt;TARGET_FILE_DIR&gt;。我在上面的示例中使用了${CMAKE_CURRENT_BINARY_DIR},您可能需要对其进行自定义以满足您的需求。 ${CMAKE_CFG_INTDIR} 可用于指定多配置生成器的每个配置目录。

请注意我上面的代码中存在额外的自定义目标。这是必要的,因为 CMake 只会在某些依赖于该输出的情况下执行生成输出的自定义命令。自定义命令通过在其DEPENDS 部分中列出输出来做到这一点。

【讨论】:

  • 您正在使用add_custom_target,它始终在构建中。我只想在 QML 文件被更改时复制它们。我仍然不明白为什么我的方法不起作用。如果我更改任何QML 文件,MyLib 应该已经过时,因为QML 文件作为依赖项add_library(MyLib SHARED ${SOURCES} ${QMLS}) 添加到MyLib
  • @beemaster 是的,自定义目标本身总是被认为是过时的。但请注意,“构建它”意味着 1) 检查其依赖项是否是最新的,以及 2) 执行其 COMMAND。它没有COMMAND,所以构建它只意味着检查它的依赖关系——正是你想要的。
  • @beemaster 我不认为将文件列为(非自定义)目标的源自动会在该文件更改时重新构建目标。如果该文件不是构建的一部分(未编译或链接),则它不带有依赖项。
  • 你是对的。您提出的解决方案对我有用。不幸的是,QML 文件如果添加到add_library,则不携带依赖项
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-09-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-18
  • 2019-12-18
  • 1970-01-01
相关资源
最近更新 更多