cmake学习总结

一、cmake相关概念

cmake的出现背景—独立于特定平台编译工具需求,KDE4项目的推动

cmake是kitware公司以及一些开源开发者在开发几个工具套件(VTK)的过程中所产生的衍生品,经过发展最终形成体系,在2001年成为一个独立的开放源代码项目。随着cmake 在KDE4项目中的成功,越来越多的项目正在使用cmake作为其构建工具。makefile通常依赖于你当前的编译平台,而且编写makefile的工作量比较大,解决依赖关系时也容易出错。对于大多数(编译相对复杂,有可能跨平台)项目,应当考虑使用更自动化的cmake或者autotools来生成makefile,而不是手动编写。

cmake的定义—一个开源 跨平台 的 软件编译构建 工具

cmake:cross platform make,是由 Kitware 创建一个开源、跨平台的针对软件构建、测试和打包工具系列。CMake 使用一种简单的(跨平台、不依赖特定编译器)的 配置文件来控制编译过程,并生成你所在编译器环境中特定的编译构建文件,如Unix平台的makefile文件。当前如NetFlix、Inria、The HDF Group、KDE等项目 都使用的cmake进行构建。

cmake的推荐使用场景—c/c++/java语言开发阶段较多文件(特别多平台)项目

如果使用c/c++/java之外的语言,不建议使用cmake。如果项目管理文件很少几个文件,就没必要使用cmake,直接编写makefile即可。如果本身项目编译构建管理已经完备的维护项目,没有必要迁移到cmake上。特别是多平台编译的项目,用cmake可以自动适配平台。

二、cmake的安装使用

cmake的下载安装

一般情况linux发行版都已经自带有cmake,如果需要下载 或者 更新版本参考下面流程:
登陆官网进行版本下载:https://cmake.org/download/,我选择的最新3.21.2版本下载。阅读readme安装。

# 编译命令
./bootstrap && make && sudo make install
# 查看命名版本
oot@ubuntu:~/cmake_test/cmake/cmake-3.21.2/bin# ./cmake --version
cmake version 3.21.2
CMake suite maintained and supported by Kitware (kitware.com/cmake).

最基础的helloword用例

主要过程,编写源文件和CMakeLists.txt脚本,执行cmake. . 进行编译脚本构建,make进行编译。

//main.c内容
#include <stdio.h>
int main(void) {
	printf("Hello World\n");
	return 0;
}
//CMakeLists.txt内容 
cmake_minimum_required (VERSION 2.8) #指定最低cmake版本
project (demo) #指定项目名称
add_executable(main main.c) #指定编译可执行文件为main 源文件main.c
root@ubuntu:~/cmake_test/cmake/test# ls #查看编译前文件
CMakeLists.txt  main.c
root@ubuntu:~/cmake_test/cmake/test# cmake . #执行cmake编译当前目录
-- The C compiler identification is GNU 4.8.4
-- The CXX compiler identification is GNU 4.8.4
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /root/cmake_test/cmake/test
root@ubuntu:~/cmake_test/cmake/test# ls #查看编译后内容
CMakeCache.txt  cmake_install.cmake  main.c
CMakeFiles      CMakeLists.txt       Makefile
root@ubuntu:~/cmake_test/cmake/test# make #执行make进行编译
[ 50%] Building C object CMakeFiles/main.dir/main.c.o
[100%] Linking C executable main
[100%] Built target main
root@ubuntu:~/cmake_test/cmake/test# ls #编译后文件
CMakeCache.txt  cmake_install.cmake  main    Makefile
CMakeFiles      CMakeLists.txt       main.c
root@ubuntu:~/cmake_test/cmake/test# ./main #执行编译后文件
Hello World

三、cmake详细编译语法规则

CMakeLists.txt就是cmake的编译构建脚本文件,里面记录了编译规则。
CMakeLists.txt文件项目主目标一个,主目录中可指定包含的子目录。

注释—使用#注释行

cmake的注释通过#对行进行注释,类似makefile。

变量—定义set与引用${}、内部变量、环境变量

定义:使用set指令显式定义及赋值,变量名区分大小写;后续的set指令会清理变量原来的值;

set (CTEST_PROJECT_NAME "Tutorial")
set (EXTRA_LIBS ${EXTRA_LIBS} MathFunctions) # 设置EXTRA_LIBS为原来的EXTRA_LIBS值加上MathFunctions

引用:在非if语句中,使用${}引用,if中直接使用变量名引用
内部变量:编译过程会自动使用的一些变量,可以进行设置,比如编译附加参数等。
在CMakeLists.txt中使用set指定,cmake命令中使用,如cmake -DBUILD_SHARED_LIBS=OFF

CMAKE_C_COMPILER:指定C编译器
CMAKE_CXX_FLAGS:c++编译参数
CMAKE_SYSTEM_NAME:编译系统的类型,如设置linux)
CMAKE_SYSTEM_PROCESSOR:编译系统处理器类型,可设置如arm
CMAKE_C_FLAGS:编译C文件时的选项,如-g;也可以通过add_definitions添加编译选项
EXECUTABLE_OUTPUT_PATH:可执行文件的输出路径
LIBRARY_OUTPUT_PATH:库文件编译输出路径
CMAKE_BUILD_TYPE::build 类型(Debug, Release, ...),CMAKE_BUILD_TYPE=Debug
BUILD_SHARED_LIBS:是否按动态链接库

https://gitlab.kitware.com/cmake/community/-/wikis/doc/cmake/Useful-Variables // 常用变量查询
环境变量:通过在变量前追加关键字 $ENV {name} 可以对系统的环境变量进行引用,或者set设置。

表达式—判真假/逻辑与或/检测/正则匹配/比大小

if(var):如果变量不是空、O、N、NO、OFF、FALSE、NOTT-OUND、_NOTFOUND时为真
if(NOT var):
if(var1 AND var2):当两个变量都为真时为真
if(var1 OR var2):当两个变量其中一个变量为真时为真
if(COMMAND cmd):当给定的cmd为命令,并可以调用时为真
if(EXISTS dir) 或 IF(EXISTS file):当目录或文件存在时为真
if(file1 IS_NEWER_THAN file2):当file1比file2新,或者file1/file2其中一个不存在时为真
if(IS_DIRECTORY dirname):当dirname为目录时为真
if(variable MATCHES regex):给定的变量能够匹配正则表达式时为真
if(string MATCHES regex):给定的字符串能够匹配正则表达式时为真
if(variable LESS/GREATER/EQUAL number):数字比较
if(string STRLESS/STRGREATER/STREQUAL string):按照字母序的排列进行比较
if(DEFINED variable):如果变量被定义,则为真
if (variable LESS number):LESS 小于
if (variable GREATER number):GREATER 大于
if (variable EQUAL number):EQUAL 等于

条件语句if()、循环语句foreach()/while()

条件语句:if(var) / else() / elseif() / endif(var)

if(CMAKE_BOOTSTRAP)
  set(CMAKE_BOOTSTRAP 1)
  unset(CMAKE_BOOTSTRAP CACHE)
endif()

循环语句:foreach(f ${VAR}) / endforeach(f) 、 while() / endwhile()

 foreach(util ${UTILITIES} KWIML)
    if(CMAKE_USE_SYSTEM_${util})
      message(STATUS "Using system-installed ${util}")
    endif()
  endforeach()

基础cmd指令介绍—版本/指定编译文件/编译参/编译项/消息

指令方式cmd (args …) ,参数使用空格分隔,使用双引号引起参数中空格,命令不分大小写

#制定cmake编译最小版本
cmake_minimum_required(VERSION 3.4.1)
#指定项目名称
project (<project_name>)
project (HELLO)   #指定项目名称为HELLO;后续使用${HELLO_SOURCE_DIR}表示项目根目录
#获取源文件
aux_source_directory(<dir> <variable>)
aux_source_directory(. SRC_LIST) #查找当前目录下所有源文件并保存至SRC_LIST变量中
#指定头文件搜索目录
include_directories (${HELLO_SOURCE_DIR}/Hello)  #增加Hello为include目录
#包含子目录(子目录中也需要包含有CMakeLists.txt)
add_subdirectory(<dir>)
#添加可执行生成文件
add_executable(<name> ${SRC_LIST})
add_executable(main main.c b.c) # 生成main文件,编译来源main.c b.c文件
#指定目标文件依赖关系
add_dependencies(target-name depend-target1 depend-target2 ...)
#添加编译参数
add_definitions(<variable>)
add_compile_options(<variable>) 
add_definitions(-DDEBUG)
add_compile_options(-std=c++11 -Wall) 
# 编译项
option(CMake_RUN_CLANG_TIDY "Run clang-tidy with the compiler." OFF)
# 编译过程消息
message("build with debug mode")
message(FATAL_ERROR "error ${CMAKE_CXX_COMPILER} version is: ${_GXX_VERSION}")
message(STATUS "info ${CMAKE_CXX_COMPILER} version is: ${_GXX_VERSION}")
message(WARNING "this is warnning message")
# 设置作用域命名属性
set_property(<GLOBAL | 
            DIRECTORY [dir] | 
            TARGET [target ...] | 
            SOURCE [src1 ...] | 
            TEST [test1 ...] | 
            CACHE [entry1 ...]>
             [APPEND] 
             PROPERTY <name> [value ...])

【指令】库链接编译相关

#添加链接库,类似gcc的-lxxx命令
target_link_libraries(<name> lib1 lib2 lib3)
#添加库编译命令 
## 用源文件编译库并添加:static为静态库.a SHARED为动态库.so source1 source2 ... sourceN用来指定源文
add_library(<name> [STATIC | SHARED | MODULE] [EXCLUDE_FROM_ALL] source1 source2 ... sourceN)
## 添加已有库
add_library(<name> [STATIC | SHARED | MODULE | UNKNOWN] IMPORTED)
add_library(test SHARED IMPORTED) # 导入一个现有的同态库libtest.so
set_target_properties(  test #指定目标库名称
                        PROPERTIES IMPORTED_LOCATION #指明要设置的参数
                        libs/src/${ANDROID_ABI}/libtest.so #设定导入库的路径)
set_target_properties (test PROPERTIES OUTPUT_NAME "testFunc") #指定库输出名称

【指令】文件目录操作指令

# 文件及目录操作
file(WRITE <filename> "message") # 写文件 覆盖文件原有内容
file(APPEND <filename> "message") # 写文件 追加文件末尾
file(RENAME <oldname> <newname>) # 重命名
file(REMOVE [file1 ...]) # 删除文件,类似rm
file(REMOVE_RECURSE [file1 ...]) # 递归删除文件,类似rm -r
file(MAKE_DIRECTORY [dir1 dir2 ...]) # 创建目录

【指令】 安装指令install

如果安装非目标文件的可执行程序安装,如脚本,则把FILES改成PROGRAMS,目标文件安装替换为TARGETS

INSTALL(FILES files... # 安装可执行程序替换为PROGRAMS programs... 安装目标文件替换为TARGETS targets...
	DESRINATION <dir>
	[PERMISSIONS permissions ...]
	[CONFIGURATIONS [Debug | Release | ...]]
	[COMPONENT <component>]
	[RENAME <name>]
	[OPTIONAL]
)
# 如下,把myrun安装到bin文件夹、动态库mylib库安装到lib文件夹、静态库mystaticlib安装到libstatic文件夹
INSTALL(TARGETS myrun mylib mystaticlib
	RUNTIME DESRINATION bin
	LIBRARY DESRINATION lib
	ARCHIVE DESRINATION libstatic
)
# 把头文件MathFunctions.h安装到include目录下
install(FILES MathFunctions.h DESTINATION include) 

【指令】 查找指令find

FIND_FILE(<VAR> name1 path1 path2 ...) #在路径path中寻找文件name1,文件全路径(包含名)存VAR中
FIND_LIBRARY(<VAR> name1 path1 path2 ...) #在路径path中寻找库name1,库全路径(包含名)存VAR
FIND_PATH(<VAR> name1 path1 path2 ...) #在路径path中寻找文件name1,把文件路径储在变量存VAR
FIND_PROGRAM(<VAR> name1 path1 path2 ...) #在路径path中寻找程序name1,程序全路径(包含名)存VAR中

四、cmake编译技巧

多个目录多源文件的匹配

多个源文件编译过程,建议使用如aux_source_directory()命令对文件夹中源文件进行遍历提取;
如果涉及多个目录,一种方法是通过add_subdirectory()添加子目录,然后在其子目录中写CMakeLists.txt

file(GLOB SRC_LIST "protocol/*.cpp") #protocol目录的cpp文件
file(GLOB SRC_LIST "*.cpp") #当前目录的cpp文件
file(GLOB SRC_LIST "*.cpp" "protocol/*.cpp")
file(GLOB_RECURSE SRC_LIST "*.cpp") #递归当前目录搜索cpp文件
file(GLOB SRC_LIST RELATIVE "protocol" "*.cpp") # 相对protocol目录下搜索
aux_source_directory(. SRC_LIST) #当前目录的源文件

外部构建方式、防止中间文件影响

默认情况cmake在构建脚本过程会产生大量中间文件,无法自动删除。另一种更合理的方式就是out-of-source的编译方式,约定过程出现的build目录。典型用法:在根目录创建一个build目录,进入build中执行cmake … 对上一层也就是根目录生成编译中间文件到build目录中,然后执行make进行编译。

指定编译目标输出目录

通过set命令修改内部变量来调:

EXECUTABLE_OUTPUT_PATH:可执行文件的输出路径
LIBRARY_OUTPUT_PATH:库文件编译输出路径

动态库版本号设置

常见动态库规则都包含版本号,一般情况是多级软连接对接最后的动态库:
libhello.so.1.2, libhello.so.1—>libhello.so.1.2, libhello.so—>libhello.so.1,
通过设置共享库的属性版本号可以在编译的时候自动生成软连接和目标动态库。

set_target_properties (hello PROPERTIES VERSION 1.2 SOVERSION 1) 

五、cmake编译实战举例(待更新)

参考

cmake官网:https://cmake.org/
cmake官方教程:https://cmake.org/cmake/help/latest/index.html
https://blog.csdn.net/fengzhongluoleidehua/article/details/79809756
https://blog.csdn.net/whahu1989/article/details/82078563
https://blog.csdn.net/appleml/article/details/52829412
https://www.cnblogs.com/lidabo/p/7359422.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值