CMake笔记

CMake三大原则

  • Decalare a target;
  • Decalare traits of the target;
  • It is all about target.

PUBLIC PRIVATE INTERFACE关键字

前言

这三个关键字出现在target_include_directoriestarget_link_libraries两个命令中,如

1
2
3
4
5
6
7
8
9
10
11
add_library(fruit STATIC fruit.cpp)
target_include_directories(fruit PUBLIC include/fruit_h/)

add_library(apple STATIC apple.cpp)
target_include_directories(apple PUBLIC include/apple_h/)
target_link_libraries(apple INTERFACE fruit)


add_library(eat_apple STATIC apple.cpp)
target_include_directories(eat_apple PUBLIC include/eat_apple_h/)
target_link_libraries(eat_apple PRIVATE apple)

在头文件命令中的作用

首先,借助两个预定义变量,或者说target默认拥有的两个traitsINCLUDE_DIRECTORIESINTERFACE_INCLUDE_DIRECTORIES来说明PRIVATE等关键字在target_include_directories命令中的作用。

  • INCLUDE_DIRECTORIES中存放的是当前target的头文件搜索路径
  • INTERFACE_INCLUDE_DIRECTORIES用于存放的是另外一组头文件搜索路径,在编译本target(记作A)时不生效。当target_link_libraries(B PUBLIC A)命令执行时,会将A的INTERFACE_INCLUDE_DIRECTORIES中的内容append到B的INCLUDE_DIRECTORIES中。

然后,在target_include_directories命令中的PRIVATE等关键字通过影响这两个traits来产生效果:

  • PRIVATE会将其后的路径添加到当前target的INCLUDE_DIRECTORIES中;
  • INTERFACE会将其后的路径添加到当前target的INTERFACE_INCLUDE_DIRECTORIES中;
  • PUBLIC则同时添加;

在链接命令中的作用

链接命令中虽然没有(尚待确认)类似INCLUDE_DIRECTORIESINTERFACE_INCLUDE_DIRECTORIES这两个显式的traits,但是最终效果是一样的,可以按照存在LINKED_LIBRARIESINTERFACE_LINKED_LIBRARIES这么两个traits,然后结合上述机制来理解。

然后,分析target_link_libraries(apple INTERFACE fruit)的情况。此时apple并未真正的链接fruit库,也就是说如果apple中调用了一个实现在fruit中的函数size(),那么编译是会报错的,但是eat_apple中size()却是可以的。

最后分析target_link_libraries(apple PRIVATE fruit)的情况。此时apple中可以调用size(),但是eat_apple中却不行了,因为此时fruit被添加到了apple的LINKED_LIBRARIES中,但是没有被添加到apple的INTERFACE_LINKED_LIBRARIES中,自然也就不会被append到eat_apple的LINKED_LIBRARIES中。

结论

  • PRIVATE只关心本target
  • INTERFACE只关心依赖本target的target
  • PUBLIC同时关心

参考链接

  1. https://leimao.github.io/blog/CMake-Public-Private-Interface/#:~:text=PRIVATE%20only%20cares%20about%20himself,about%20everyone%20and%20allows%20inheritance.
  2. https://kubasejdak.com/modern-cmake-is-like-inheritance