这篇文章上次修改于 695 天前,可能其部分内容已经发生变化,如有疑问可询问作者。

cmake提供了针对于系统或编译器的检测函数,使用这些函数可以完成如autotools等同类工具中对程序依赖接口进行判断的功能,进而实现根据系统或编译器属性来选择启用或禁用所构建的程序中某些特性。

cmake中定义的函数主要有:

check_struct_has_member

check_compiler_flag
check_c_source_compiles
check_cxx_source_compiles
check_cxx_symbol_exists
check_source_runs
check_linker_flag
check_library_exists

全部列表可参见cmake文档

下面简单介绍 check_source_compiles 的使用方法,其他命令使用大同小异,具体参考对应doc。

1. 需求

某一基础模块提供了如下接口供其他程序使用:

int func(char *param1, char* parm2);

但在 项目A 中为了实现特点需求修改了该接口为:

int func(char *param1, char* parm2, int parm3);

即增加了一个参数。
使用 cmake 管理的 模块M 使用了func接口,但在 项目A 中并未使用到func提供的功能。
我们想要在 模块M 构建时,自动检查func接口类型,但发现是修改后的(3个参数版)时,自动取消某些调用了 func 接口的源文件。

2. 实现

使用 check_cxx_source_compiles 可以完成该功能,示例实现如下:

unset(NEW_FUNC_API CACHE)
set(CMAKE_REQUIRED_INCLUDES "<PRJ_HOME_SRC>/include")  #使用到的头文件目录
if(WIN32)
    set(CMAKE_REQUIRED_DEFINITIONS "-D_IMPORT")  #编译所需的预定义宏
    set(CMAKE_REQUIRED_LINK_OPTIONS "/LIBPATH:\"<PRJ_HOME>/lib\"")  #windwos系统依赖库文件所在目录
else()
    set(CMAKE_REQUIRED_LINK_OPTIONS "-L/<PRJ_HOME>/bin")  #非windwos系统依赖库文件所在目录
endif(WIN32)
set(CMAKE_REQUIRED_LIBRARIES "func")  #libfunc.so or func.lib
include(CheckCXXSourceCompiles)
check_cxx_source_compiles(
"#include \"func.h\"
int main() {
    func("", "", 1);

    //old API
    //func("", "");
    return 0;
}"
NEW_FUNC_API)

if(NEW_FUNC_API)
    ...
else()
    ...
endif()

3. 说明

  • unset(NEW_FUNC_API CACHE):删除以前检查结果缓存实现cmake时均进行编译检查,否则只会在第一次cmake进行判断;
  • CMAKE_REQUIRED_INCLUDES:设置编译测试代码包含头文件的目录(多个时以“;”分隔,如“dir1;dir2;dir3”);
  • CMAKE_REQUIRED_DEFINITIONS:设置编译测试代码需要的宏(多个时以“;”分隔,如“-DAA;-DBB;-DCC”);
  • CMAKE_REQUIRED_LINK_OPTIONS:设置编译测试代码链接时的选项,这里根据系统指定了依赖库目录信息;
  • CMAKE_REQUIRED_LIBRARIES:设置编译测试代码链接时依赖的库;
  • include(CheckCXXSourceCompiles):加载cmake中check_cxx_source_compiles模块;
  • check_cxx_source_compiles()

    • 编译测试代码:简单一个main函数调用了两个参数的func函数,当编译环境是 项目A 时,明显会编译成功。
    • NEW_FUNC_API:接收 check_cxx_source_compiles 的结果。

检测完成后便可以根据 NEW_FUNC_API 设置项目属性信息了。

4. 错误查看

check_cxx_source_compiles 在cmake命令中只输出是否成功结果

-- Performing Test NEW_FUNC_API
-- Performing Test NEW_FUNC_API - Success

编译过程日志会输出到日志文件中,编译失败日志在 错误日志文件 中,调试时可以分别检查 CMakeFiles/CMakeOutput.logCMakeFiles/CMakeError.log 两个文件中的信息。
Windows: vc++

E:\workspace\xxx\CMakeFiles\CMakeTmp\src.cxx(8,48): error C2660: “func”: 函数不接受 3 个参数 [E:\workspace\xxx\build\CMakeFiles\CMakeTmp\cmTC_b790a.vcxproj]

E:\src-master\include\func.h(117,7): message : 参见“func”的声明 [E:\workspace\xxx\build\CMakeFiles\CMakeTmp\cmTC_b790a.vcxproj]

Linux: g++

/home/doufu/xxx/build/CMakeFiles/CMakeTmp/src.cxx: In function ‘int main()’:
/home/doufu/xxx/build/CMakeFiles/CMakeTmp/src.cxx:5:23: error: no matching function for call to ‘func(const char [1], const char [1], int)’
    5 |     func("","",0);
      |     ~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from /home/doufu/xxx/build/CMakeFiles/CMakeTmp/src.cxx:1:
/home/doufu/src/include/func.h:117:14: note: candidate: ‘int func(char*, char*, int)’
  117 |          int func(char *param1,char *parm2);
      |              ^~~~~~~~~~~~
/home/doufu/src/include/func.h:117:14: note:   candidate expects 2 arguments, 3 provided
gmake[1]: *** [CMakeFiles/cmTC_f072c.dir/build.make:78:CMakeFiles/cmTC_f072c.dir/src.cxx.o] 错误 1
gmake[1]: 离开目录“/home/doufu/xxx/build/CMakeFiles/CMakeTmp”
gmake: *** [Makefile:127:cmTC_f072c/fast] 错误 2