如何为CMake导出目标的接口包含目录添加BEFORE属性?
好问题!你遇到的这个场景确实是CMake新旧写法过渡时的常见困惑——旧式写法可以通过BEFORE手动调整优先级,但现代的导出目标链接方式似乎没地方加这个参数。不过别担心,CMake提供了几种优雅的解决方案,不用退回到旧式写法:
方法1:在定义导出目标时直接设置BEFORE优先级
这是最推荐的方式,从根源上解决问题。不要用set_target_properties来设置INTERFACE_INCLUDE_DIRECTORIES,而是改用target_include_directories命令,直接给导出目标的INTERFACE包含目录加上BEFORE参数:
# 假设你正在构建库A时设置其INTERFACE属性 target_include_directories(A INTERFACE BEFORE # 关键!让这些目录在后续链接时优先于其他目录(包括系统目录) $<BUILD_INTERFACE:${A_INCLUDE_DIRS}> # 构建时使用的路径 $<INSTALL_INTERFACE:include> # 安装/导出后使用的路径(根据你的实际安装路径调整) ) # 然后正常导出目标 install(TARGETS A EXPORT ATargets) install(EXPORT ATargets FILE ATargets.cmake NAMESPACE A::)
当其他项目通过target_link_libraries(some_target PRIVATE A::A)链接时,A::A的INTERFACE包含目录会自动以BEFORE的优先级被添加到some_target的包含目录列表中,自然就会优先于系统目录。
方法2:如果无法修改A的构建脚本,手动调整目标的包含目录
如果你没办法修改库A的CMake配置(比如用的是预编译的导出目标),可以在链接A::A之后,手动提取它的INTERFACE包含目录并以BEFORE添加到你的目标中:
target_link_libraries(some_target PRIVATE A::A) # 提取A::A的INTERFACE包含目录,并用BEFORE优先级添加到some_target target_include_directories(some_target BEFORE PRIVATE $<TARGET_PROPERTY:A::A,INTERFACE_INCLUDE_DIRECTORIES> )
这种方式兼顾了现代链接写法和优先级需求,虽然有点冗余,但能快速解决问题。
方法3:全局设置项目包含目录优先于系统目录
如果你希望整个项目的所有本地包含目录都优先于系统目录,可以设置全局CMake变量:
set(CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE ON)
这个变量会让CMake将所有项目内的包含目录(包括导出目标的INTERFACE目录)都放在系统目录之前。不过这是全局生效的,适合你希望统一优先级规则的场景,注意不要影响其他依赖的预期行为。
总结一下,最推荐的是方法1,从导出目标本身定义优先级,最符合现代CMake的设计理念。如果没法修改目标定义,方法2是灵活的折中方案,方法3则适合全局统一配置的场景。
内容的提问来源于stack exchange,提问作者Wang

