当目标位于子目录中时替代 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_command
和add_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_<TYPE>_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_NAME
和 SUFFIX
属性。 “如果你给 $mytarget_filename 任何值,这似乎有效” - 为 mytarget_filename
变量指定正确的值,如果目标文件存在并且原始可执行文件未更改,则不会执行 COMMAND
。 mytarget_filename
变量 COMMAND 的值不正确每次都会执行,同样可以通过在 add_custom_target
中直接指定 COMMAND 来实现。以上是关于当目标位于子目录中时替代 CMake POST_BUILD 命令的主要内容,如果未能解决你的问题,请参考以下文章
当设备位于我的用户目录中时,为啥 Android 模拟器会报告“未知虚拟设备”?
CMake 更改强制 DESTINATION 目录名称(安装目标)