modern cmake的概念剖析

1 篇文章 0 订阅

modern cmake的概念剖析

这篇文章主要介绍一些相关的概念,具体的语法建议去github上看相关教程。

从c++的依赖关系说起

如果要学习modern cmake,最基本的是要搞清楚文件之间以及项目之间的依赖关系。在c++中,文件之间的依赖关系主要来自两个方面:

  1. include头文件;
  2. 函数声明和实现的分离,声明依赖于实现才能正确链接;

按照依赖关系,把一个项目中所有文件都连上箭头,我们可以得到一个有向图。(通常是带环的)
如果对这个项目进行编译链接,所有的cpp文件会编译链接成一个整体,它们的依赖关系也会合并。同时,由于cpp中的include在编译过程中已经展开了,所以cpp文件中由(1. include头文件)引入的依赖关系会消失,而(2. 函数声明和实现的分离,声明依赖于实现才能正确链接)只会剩下.h文件对.cpp文件的依赖,.cpp文件对.h文件的依赖往往不存在(正常来说没有人会在一个cpp文件声明某个函数并且在另一个h文件中实现它)。于是,生成的lib文件不会依赖于任何文件(动态链接是另一种情况,暂且不提),不过,h文件引入的依赖关系依旧会保存。这时,整个项目只剩下不同h文件之间的依赖关系和h文件对lib的依赖。

cmake中文件的interface和private

如果这个项目是一个库,我们往往会把库中的一些h文件提供给外部使用,另一部分则隐藏起来。把提供给外部使用的头文件设置为interface,相应的,这些提供给外部使用的头文件所依赖的其它头文件也应当设置为interface(显然,interface必须要随着依赖关系传递,如果A依赖于B并且A是interface的,那么B也是interface的)(一般的做法是把这些头文件单独放到一个目录并且通过target_include_libraries(interface)来设置,事实上,由于include是通过目录来寻找.h文件,我们不能单独把某个头文件设置为interface,只能把某个目录设置为interface)。如果一个头文件不需要提供给外面,也不会被其它提供给外部的头文件引用,只是自己编译链接才需要,则设置为private,private可以理解为在编译链接后就不复存在的东西,只是编译链接时使用。既是interface又是private的头文件则设置为public。绝大多数情况下,由于h文件往往依赖于lib,所以lib也会提供给外部使用,默认为interface,不需要显示指出(实际上也没法指出来,在cmake的相关教程中并未说明编译后的lib是interface还是private,但从逻辑上看,把编译后的lib理解为interface是非常合理的)。同时,由于cpp文件无法对外提供(一般不会去include一个cpp文件),所以cpp文件默认为private。(某些特殊情况下,有些cpp不会被编译,可以把这些cpp文件指定为interface)。
简单的理解,interface是提供给外部使用的,private是自己编译链接项目所使用。另外,还有一个名为public的关键字,如果既是interface也是private,则设置为public。

cmake中项目(target)的interface和private

在上一节已经提到,对于一个项目来说,设置为interface的文件是提供给外部使用的。于是,如果一个项目要使用另一个项目,这个项目也就只能拿到另一个项目中设置为interface的文件而接触不到设置为private的文件。事实上,对于这个项目来说,另一个项目就是另一个项目中所有设置为interface的文件的合集。于是,可以仿照上一节中,把文件区分为interface或private,项目也可以被区分为为interface或private。另外,在正式使用中,不止文件和项目,其它属性也可以区分为interface或者private。

interface libraries

在cmake中还有一个相当有趣的东西,interface libraries。一个不编译链接的项目是一个interface libraries(通过add_libraries(interface)指明)。由于自己不编译链接,所以不存在private的东西,全是interface。这个指令主要用来处理:

  1. 只有头文件的库;
  2. 没有指明extern “C"或者__declspec (dllexport)或者def文件的库(比如OpenGL的glad库);
  3. 属性表

以上这些情况下,对应的库都不能编译链接,因此只能作为interface libraries来管理。

动态链接

在上面几节中,还只提到了静态的编译链接问题,对于动态链接与加载该如何处理则没有提到。事实上,cmake和编译器并不能很好的解决第三方dll的动态加载问题,动态链接本身也不是编译器的任务(在path里设置dll的路径是无效的,只有在系统环境变量添加path或者dll和exe在同一个目录时才能找到正确的dll)。因此,对于dll的管理,需要自己手动写add_custom_target和add_custom_command去管理。除此之外,在c++的代码中还可能动态的加载一些文件或者图片,这些同样超出了cmake的职责,只能自己写add_custom_target和add_custom_command去管理。

项目是modern cmake的核心

在cmake 3.0之前,cmake一直是全局变量漫天飞,成不成功全看运气。为了解决这一问题,从3.0开始,cmake要求尽量少的使用全局变量,而是用项目去管理所有的东西。
从上面这几节可以发现,一个项目,既可以是一个文件、也可以是一个大型的库、也可以是一个属性表、还可以是一个命令或者一组命令,具有非常高的灵活性。同时,又通过interface和private来区分项目对其它事物的使用,极大的简化了编译链接中的逻辑,使得编译链接的管理变得愈发清晰,这也正是modern cmake所推崇的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值