cmake构建C++项目避免编译时对第三方库头文件进行依赖检查

  • cmake
  • 2022-07-15 16:44:53

最近有同事反应一个使用cmake构建的C++项目编译很慢,具体表现是随便修改一个很简单的cpp源文件后,重新编译生成可执行文件的时间需要用3分钟左右。统计了一下整个增量编译过程各阶段耗时,发现时间主要消耗在了cmake生成依赖规则和make检查依赖规则上。生成的依赖规则文件depend.make有200M左右,包含依赖规则条目有100W行以上。所以生成依赖规则和检查依赖都会消耗大量时间。进一步分析,查看这些依赖规则90%以上都是对三方库头文件的依赖。这些依赖显然是不必要的,因为三方库头文件是不可能轻易被修改的。那么如何消除这些不必要的依赖呢?
哪个搜索引擎也没有帮我找到满意的答案,不得已只能从cmake源码入手。由于项目使用cmake版本为3.5.1,所以一开始从3.5.1版本cmake源码入手。发现cmake使用cmLocalGenerator::GetIncludeDirectories接口获得当前target所包含的路径,这个接口主要有两处调用:一处用来生成flags.make中的CXX_INCLUDES编译参数;另一处用来生成DependInfo.cmake中的CMAKE_CXX_TARGET_INCLUDE_PATH参数,而编译规则文件就是根据这个参数生成的。不幸的是,两处调用对从该接口获得的头文件目录列表都没有做任何处理,也就是说编译参数中指定的头文件目录和依赖检查目录一定是完全相同的。如果使用一定的手段(如CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES选项)去除了依赖检查目录中的某个目录,也一定会导致在编译参数中该目录被去除,这样就通不过编译了。虽然这条路被堵死了,但还是有一些可做的工作,那就是在cmake读取DependInfo.cmake文件生成depend.make时做些手脚,在这个帖子有提到用include_regular_expression("^([^b]|b[^o]|bo[^o]|boo[^s]|boos[^t]|boost[^/]).*$") 在生成依赖规则时过滤掉对boost的依赖,但是显然局限性很大,因为包含boost头文件时都会加上boost目录名,所以很容易过滤,但并不是所有三方库都是这样的。
就在我计划着写一个超长的正则表达式时,我又打开了查看了另一个项目的代码,这个项目编译速度不错。惊奇的发现这个项目的某一个target的flags.make和DependInfo.cmake中的头文件目录竟然不同,所以又编译了一份最新的3.12.4的cmake进行调试查看源码,果不其然,在这个版本中,生成DependInfo.cmake中头文件目录的代码变成了这样:

     this->GetIncludeDirectories(includes, target, implicitLang.first, config);
     std::string binaryDir = this->GetState()->GetBinaryDirectory();
     if (this->Makefile->IsOn("CMAKE_DEPENDS_IN_PROJECT_ONLY")) {
       const char* sourceDir = this->GetState()->GetSourceDirectory();
       cmEraseIf(includes, ::NotInProjectDir(sourceDir, binaryDir));
     }
     for (std::string const& include : includes) {
       cmakefileStream << "  \""
                       << this->MaybeConvertToRelativePath(binaryDir, include)
                       << "\"\n";
     }
     cmakefileStream << "  )\n";

它使用了CMAKE_DEPENDS_IN_PROJECT_ONLY这个选项对依赖检查目录进行了过滤,如果这个选项被打开,依赖检查目录列表就会忽略外部的第三方库头文件,这样就不会导致产生一个超大的depend.make文件了。
所以直接set(CMAKE_DEPENDS_IN_PROJECT_ONLY ON)应该是解决这个编译慢问题的最快捷方法了。然而不幸的是这个选项是在cmake 3.6.0版本才引入的,当前环境的3.5.1版本cmake用不了,我也没权限升级软件,所以最后还是写了一个超长的正则表达式,效果也还行。

猜你喜欢