当目标位于子目录中时替代 CMake POST_BUILD 命令

Posted

技术标签:

【中文标题】当目标位于子目录中时替代 CMake POST_BUILD 命令【英文标题】:Alternative to CMake POST_BUILD command when target is in subdirectory 【发布时间】:2016-10-27 21:02:25 【问题描述】:

通常需要确保 CMake 构建项目在编译后最终位于某个位置,而 add_custom_command(..POST_BUILD...) 命令是实现此目的的常用设计模式:

add_custom_command(
  TARGET mytarget
  POST_BUILD
  COMMAND $CMAKE_COMMAND -E copy $<TARGET_FILE:mytarget> $CMAKE_BINARY_DIR/final_destination
)

遗憾的是,当相关目标位于相对于包含 add_custom_command 调用的文件的子目录中时,它不起作用,该调用是通过 add_subdirectory() 命令递归编译的。尝试这样做会导致以下错误消息:

CMake Warning (dev) at CMakeLists.txt:4 (add_custom_command):
  Policy CMP0040 is not set: The target in the TARGET signature of
  add_custom_command() must exist.  Run "cmake --help-policy CMP0040" for
  policy details.  Use the cmake_policy command to set the policy and
  suppress this warning.

  TARGET 'mytarget' was not created in this directory.
This warning is for project developers.  Use -Wno-dev to suppress it.

在许多情况下,有一个简单的解决方法:只需确保add_custom_command() 调用发生在子目录的CMakeLists.txt 文件中,一切都会正常工作。

但是,这并不总是可能的!子目录可能是我们无法控制的外部依赖项的 CMake 项目。例如,将 CMake 递归编译与 Git 子模块相结合是很常见的,在这种情况下,无法永久存储对子项目构建系统的修改。

然后我的问题归结为以下几点:CMake 是否提供另一种机制来创建目标,该目标将在重建子项目的目标时自动触发,并可用于将最终的可执行文件或共享库复制到其他一些地点?

我的目标是自动发生这种情况,而无需使用另一个目标专门调用“make”/“ninja”。此外,副本仅应在实际需要时执行(根据 cmake 文档,一些 add_custom_* 命令不跟踪它们是否实际需要运行,并且保守地假设目标始终是陈旧的)。

【问题讨论】:

所有子目录CMakeLists.txt文件都是同一个/你的项目吗?我们是否谈论所有目标,例如可执行类型还是您只想将其应用于特定目标?在我的项目中,我为所有标准 CMake 调用创建了自己的 function(),因此我可以修改例如add_executable() 用于我的项目(例如自动为所有此类调用添加构建后步骤)。 子目录通常是共享库,作为从 github 克隆的 git 子模块分发(例如 pugixml)。这些项目有自己的 CMake 项目,我不能(也不想)改变。父项目可以生成可执行或共享库的混合——我并不认为它对这个问题特别重要。 【参考方案1】:

当第一个为第二个生成文件时,只需使用add_custom_commandadd_custom_target 的常见组合:

# Because OUTPUT option may not use generator expressions,
# extract name of file from target's properties.
get_target_property(mytarget_basename mytarget OUTPUT_NAME)
get_target_property(mytarget_suffix mytarget SUFFIX)
set(mytarget_filename $mytarget_basename$mytarget_suffix)
# make copied file be dependent from one which is build.
# Note, that DEPENDS here creates dependencies both from the target
# and from the file it creates.
add_custom_command(OUTPUT
        $CMAKE_BINARY_DIR/final_destination/$mytarget_filename
    COMMAND $CMAKE_COMMAND -E copy $<TARGET_FILE:mytarget>
        $CMAKE_BINARY_DIR/final_destination
    DEPENDS mytarget
)
# Create target which consume the command via DEPENDS.
add_custom_target(copy_files ALL
    DEPENDS $CMAKE_BINARY_DIR/final_destination/$mytarget_filename
)

与使用 POST_BUILD 相比,此代码使用了额外的目标。但是您别无选择:add_custom_command 不能附加到在其他目录中创建的目标。


通常,通过CMAKE_&lt;TYPE&gt;_OUTPUT_DIRECTORY 变量指定该目录,而不是将可执行文件/库复制到其他二进制目录中。

【讨论】:

这不太符合要求:我的目标是即使没有指定“make”或“ninja”的目标,副本也会默认运行。此外,副本仅应在实际需要时执行(根据 cmake 文档,一些 add_custom_* 命令不跟踪它们是否实际需要运行,并且保守地假设目标始终是陈旧的)。跨度> my goal is that the copy is run by default - 添加 ALL 选项后,将默认构建目标。我已经确定了答案。 copy should only be executed when it is actually necessary - 这就是 add_custom_command 所做的。它是 target (添加了add_custom_target),它被假定为总是过时的。但由于它不包含 COMMAND 选项,因此它什么也不做。 这是否假设您在原始目标中调用 set_target_properties() ?如果你给 $mytarget_filename 任何值,这似乎可行 "这是否假设您在原始目标中调用 set_target_properties()?" - 不,没有这样的假设。 CMake 自动填充 OUTPUT_NAMESUFFIX 属性。 “如果你给 $mytarget_filename 任何值,这似乎有效” - 为 mytarget_filename 变量指定正确的值,如果目标文件存在并且原始可执行文件未更改,则不会执行 COMMANDmytarget_filename 变量 COMMAND 的值不正确每次都会执行,同样可以通过在 add_custom_target 中直接指定 COMMAND 来实现。

以上是关于当目标位于子目录中时替代 CMake POST_BUILD 命令的主要内容,如果未能解决你的问题,请参考以下文章

使用 CMAKE 为 C++ 设置 Neovim

当设备位于我的用户目录中时,为啥 Android 模拟器会报告“未知虚拟设备”?

当 XAMPP 不在分区的根目录中时,为啥它不能工作?

CMake 更改强制 DESTINATION 目录名称(安装目标)

当用户的手指不再位于启动 panGesture 的视图中时,如何结束 UIPanGesture?

CMake / Ninja:当内容未知时递归“清理”输出目录......?