好久没有写东西了,上次写还是在去年10月,都快半年了。这期间,除了工作比较忙外,也开始参与一些开源项目,比如 pika
刚好今年pika做了一些改动,比如编译方式从MakeFile
改成了 cmake
。我有幸参与了一些工作,正好趁此机会,又深入学习了一下cmake
的知识,这周末有时间,来记录一下
以前我也写过两篇和cmake相关的文章,这次又有了新的收获,就想着再写一篇记录一下
我那就从pika中用到的cmake的一些知识,记录一下吧。我没有找到好的中文cmake文档,遇到问题最好还是去官网查,文中的相关知识,很多都是从官网学到的。cmake官网 传送门
find_program
主要功能是检查当前系统上是否有对应的程序
比如在pika中,用来检查系统中是否有autoconf
这个程序
这个函数的第一参数是一个变量,用来接收检查结果
第二个参数是要检查的程序的名字
后面是一些可选参数,在cmake中,可选参数要加上参数的名比如在这里,要给出检查的路径,参数名是PATHS
1find_program(AUTOCONF
2 autoconf
3 PATHS /usr/bin /usr/local/bin)
4
5if (${AUTOCONF} MATCHES AUTOCONF-NOTFOUND)
6 message(FATAL_ERROR "not find autoconf on localhost")
7endif()
后面通过比较 AUTOCONF
是不是 AUTOCONF-NOTFOUND
来确定系统中是否存在 autoconf
这个程序
这知识最基本的用法,find_program
还有很多参数,可以实现更多的功能,可以去官网直接学习传送门
execute_process
主要功能是执行一个命令,一般常用于执行一个本机的程序或者脚本
这个函数常用的有下面这些参数
COMMAND 要执行的命令,后面可以加参数,可以支持执行多个命令
WORKING_DIRECTORY 这个命令执行时的工作目录
TIMEOUT 执行这个命令的超时时间
RESULT_VARIABLE 这是一个变量,保存命令执行的结果
OUTPUT_VARIABLE 调用结果的返回值
ERROR_VARIABLE 如果出错了,error的返回值
在pika中的应用
1execute_process(COMMAND sh ${CMAKE_UTILS_DIR}/Get_OS_Version.sh
2 OUTPUT_VARIABLE OS_VERSION)
这段命令的作用是执行一个shell脚本,得到这个脚本的返回值
需要注意的是,execute_process不是使用shell去执行命令,所以要指定执行的程序。
比如这里,要执行shell脚本,必须指定用sh程序去执行这个脚本。
cmake_host_system_information
获取本机的一些信息
这个函数使用比较简单,只有两个参数,
RESULT 获取的结果
QUERY 要获取的信息
QUERY
常用的值,这些值在不同版本的cmake上支持程度也是不一样的,使用前还是去官网看一下,传送门
NUMBER_OF_LOGICAL_CORES
:逻辑核心数量。NUMBER_OF_PHYSICAL_CORES
:物理核心数量。HOSTNAME
:主机名称。TOTAL_VIRTUAL_MEMORY
:总虚拟内存,单位是M
。AVAILABLE_VIRTUAL_MEMORY
:可用虚拟内存,单位是M
。TOTAL_PHYSICAL_MEMORY
:总物理内存,单位是M
。AVAILABLE_PHYSICAL_MEMORY
:可用物理内存,单位是M
。IS_64BIT
:如果处理器是64
位,查询结果为1
。HAS_FPU
:如果处理器拥有浮点处理单元,查询结果为1
。HAS_MMX
:如果处理器支持MMX
指令集,查询结果为1
。HAS_MMX_PLUS
:如果处理器支持Ext. MMX
指令集,查询结果为1
。HAS_SSE
:如果处理器支持SSE
指令集,查询结果为1
。HAS_SSE2
:如果处理器支持SSE2
指令集,查询结果为1
。HAS_SSE_FP
:如果处理器支持SSE FP
指令集,查询结果为1
。HAS_SSE_MMX
:如果处理器支持SSE MMX
指令集,查询结果为1
。HAS_AMD_3DNOW
:如果处理器支持3DNow
指令集,查询结果为1
。HAS_AMD_3DNOW_PLUS
:如果处理器支持3DNow+
指令集,查询结果为1
。HAS_IA64
:如果IA64
处理器可以模拟X86
,查询结果为1
。HAS_SERIAL_NUMBER
:如果处理器有序列号,查询结果为1
。PROCESSOR_SERIAL_NUMBER
:处理器序列号。PROCESSOR_NAME
:可读的处理器全称。OS_NAME
:操作系统名称,也就是uname -s
的输出,三大操作系统对应的名称是Linux
、Windows
和Darwin
(masOS
),也可以通过CMAKE_HOST_SYSTEM_NAME
变量获取。OS_RELEASE
:操作系统子类型,例如Windows Professional
。OS_VERSION
:操作系统构建ID
。OS_PLATFORM
:处理器架构,Windows
下可以通过PROCESSOR_ARCHITECTURE
变量获取,Unix/Linux/macOS
等平台可以通过uname -m
或uname -p
获取。也可以通过CMAKE_HOST_SYSTEM_PROCESSOR
变量获取。
示例:
1cmake_host_system_information(RESULT CPU_CORE QUERY NUMBER_OF_LOGICAL_CORES)
2message(STATUS "Cpu core ${CPU_CORE}")
获取当前电脑的逻辑核数
ExternalProject_Add
这个就比较复杂了,主要功能是引入一个外部项目。比如在一些项目中,需要别的库,可以使用这个函数来实现。只需要一些简单的配置就能完成依赖的编译和安装,极大的简化了C++的依赖管理。
使用这个函数前,需要先导入
1include(ExternalProject)
这个函数的常用参数有
Directory Options:
这部分是目录相关的配置
PREFIX
:外部项目的根目录TMP_DIR
:外部项目的临时目录LOG_DIR
:外部项目在执行时,产生的log目录STAMP_DIR
:外部项目在执行时,每一步的时间戳都会记录在这里,如果没有设置LOG_DIR
log文件默认也会在这里DOWNLOAD_DIR
: 外部项目下载文件的存放目录SOURCE_DIR
: 外部项目的源码存放目录BINARY_DIR
: 外部项目编译产生的二进制文件的存放目录INSTALL_DIR
: 外部项目编译产生的动态库和静态库的存放目录
如果设置了PREFIX
属性,那么默认的目录结构是这样的
1TMP_DIR = <prefix>/tmp
2STAMP_DIR = <prefix>/src/<name>-stamp
3DOWNLOAD_DIR = <prefix>/src
4SOURCE_DIR = <prefix>/src/<name>
5BINARY_DIR = <prefix>/src/<name>-build
6INSTALL_DIR = <prefix>
7LOG_DIR = <STAMP_DIR>
如果设置了EP_BASE
,目录结构是这样的
1TMP_DIR = <base>/tmp/<name>
2STAMP_DIR = <base>/Stamp/<name>
3DOWNLOAD_DIR = <base>/Download/<name>
4SOURCE_DIR = <base>/Source/<name>
5BINARY_DIR = <base>/Build/<name>
6INSTALL_DIR = <base>/Install/<name>
7LOG_DIR = <STAMP_DIR>
在pika中,是这样设置的
1set(EP_BASE_SUFFIX "buildtrees")
2set_property(DIRECTORY PROPERTY EP_BASE ${CMAKE_CURRENT_SOURCE_DIR}/${EP_BASE_SUFFIX})
最后的目录结构是这样的
1buildtrees/
2├── Build
3├── Download
4├── Install
5├── Source
6├── Stamp
7└── tmp
Download Step Options:
这部分是依赖下载相关的,下载又可以分url下载,git、svn、等等版本管理工具的下载
URL
:没什么好说的,就是下载依赖包我网址URL_HASH
:对下载的文件做校验,必须要指定校验的算法,比如SHA1=0cf3c3d176a2134dec9702c64abb13da593aea0c
URL_MD5
:使用md5对文件校验DOWNLOAD_NAME
:下载的文件名TIMEOUT
:下载的超时时间GIT_REPOSITORY
:拉取代码的git仓库的url地址GIT_TAG
:拉取代码的tag
还有一些就不一一列举了
Configure Step Options:
编译前的准备步骤,比如生成MakeFile
CONFIGURE_COMMAND
: 通过autoconf
生成MakeFile文件CMAKE_COMMAND
:使用CMakeList.txt
生成MakeFile文件CMAKE_ARGS
:cmake的参数
Build Step Options:
编译相关的参数
BUILD_COMMAND
:编译命令BUILD_IN_SOURCE
:是否在源代码目录进行编译,一般来说这个不用指定即可BUILD_ALWAYS
:启用此选项将强制始终运行构建步骤。这可能是最简单的方法,可以可靠地确保评估外部项目自己的构建依赖关系,而不是依赖于默认的基于成功时间戳的方法。一般也不用设置
后面的一些不太常用,就不列举了。有需要还是看官方文档
在pika中的使用
1ExternalProject_Add(gflags
2 URL
3 https://github.com/gflags/gflags/archive/refs/tags/v2.2.2.tar.gz
4 URL_HASH
5 MD5=1a865b93bacfa963201af3f75b7bd64c
6 DOWNLOAD_NO_PROGRESS
7 1
8 UPDATE_COMMAND
9 ""
10 LOG_CONFIGURE
11 1
12 LOG_BUILD
13 1
14 LOG_INSTALL
15 1
16 BUILD_ALWAYS
17 1
18 CMAKE_ARGS
19 -DCMAKE_INSTALL_PREFIX=${STAGED_INSTALL_PREFIX}
20 -DCMAKE_BUILD_TYPE=${LIB_BUILD_TYPE}
21 -DGFLAGS_NAMESPACE=gflags
22 -DBUILD_STATIC_LIBS=ON
23 -DBUILD_SHARED_LIBS=OFF
24 BUILD_COMMAND
25 make -j${CPU_CORE}
26)
就是一些很常规的使用
总结
cmake功能还是非常强大,但是同样的,使用也是想对复杂的,而且也没有相对好的中文文档,要用好还是有一定学习门槛的。但是用好cmake 可以很大程度上减轻一些工作量的。
pika在改用cmake的过程中我也提交过几个简单的PR,也是一次很好的学习过程,先记录这些吧,后面想起来再补充。