概述
首先来看看,什么是CMake,它是做什么用的
众所周知,我们编写的 .c 和 .cpp 等源代码文件无法直接运行,需要经过 预处理 → 编译 → 汇编 → 链接 四个阶段,最终生成可执行文件。这时我们可以通过编写 Makefile 文件,在使用make工具自动化执行上述构建过程,从而生成可执行文件。但是 Makefile 的语法相对比较复杂,有时候没注意写错一点就可能导致构建失败,在大型项目中维护成本就比较高了。
为了解决 Makefile 在跨平台项目中的维护问题, CMake 就被推广使用了,传统的 Makefile通常依赖于当前平台,若要在不同平台上构建,需要分别编写对应的构建脚本。而使用 CMake 只需编写一次 CMakeList.txt 文件,CMake 会根据目标平台自动生成相应的构建文件,很大程度上简化了跨平台开发流程。
准备工作
这里我使用的是Linux环境进行演示
首先在Linux中下载CMake
下面是几种常见版本的下载指令
Debain/Ubuntu
sudo apt update
sudo apt install cmake -y
CentOS 7:
sudo yum install cmake -y
RHEL 8 / CentOS 8 / Rocky / AlmaLinux:
sudo dnf install cmake -y
检查是否下载完成
camke --version
若显示版本号就说明已经安装了
编写CMakeLists
准备工作做完后,就先写几个.c文件
首先写一个加法函数
减法函数
写个头文件声明一下
最后写个main函数调用
随后编写CMakeLists.txt文件(文件名千万不能错)
规则
"cmake_minimum_required":指定使用cmake的最低版本(由于编译时是需要本地的cmake进行编译的,所以要写本地camke的版本)
''project":指定工程的名字
"add_executable":添加一个可执行程序,第一个参数是程序的名字,后面的参数都是工程中的源文件名称
注释:
使用#进行单行注释
使用#[[ ]]进行块注释
执行cmake
将CMakeLists 文件编辑好后就可以开始执行cmake
cmake 后跟一个参数(CMakeLists.txt所在的路径 )
这里CMakeLists 就处于当前路径,就可以直接camke .
构建成功后会发现生成了几个文件,其中就包含一个makefile文件
再输入 make 构建一下就生成了之前写的test可执行文件
然后就可以运行了
文件存放
cmake生成的文件和代码源文件放在一块比较乱,就可以创建一个特定的文件夹来存放cmake后的文件,可以起名为bulid
关于CMakeLists
set
1.定义变量
CMakeLists中"add_executable"的第二参数在一个工程中会有很多,直接一个个手写难免出错,于是就可以使用set关键字
set可以将冗杂的文件名全都存储到一个string变量里面
第一个参数VAR就是变量名,中间的VALUE就是所有要存放的文件名,最后一个参数基本用不到不做了解
这里就是后面的文件名都存放在了SRC变量里面 set中的第二个参数可以用空格或者分号隔开
关于参数的写法,必须写成 ${XXX}这样
2.指定使用C++的标准
在CMakeLists中使用
set(CMAKE_CXX_STANDARD C++标准)
就可以设置构建时使用的C++的标准
除了这样,也可以在cmake时指定
cmake CMakeLists.txt路径 -DCMAKE_CXX_STANDARD=C++标准
3.指定输出的路径
还可以使用set指定可执行程序输出的路径
先将路径存放在一个变量里
set(HOME /home/2025code/test_7_24)
然后使用set指定,这里的EXECUTABLE_OUTPUT PATH是个宏,如果通过set指定了这个宏的值之后,生成的可执行程序就会保存到这个宏指定的路径,如果没找到就会默认生成
set(EXECUTABLE_OUTPUT PATH ${HOME}/bin)
至于后面的\bin则是约定俗成的存放可执行文件的文件夹,也可以不加或者自定义
搜索文件
假如一个项目里的源文件很多,在编写CMakeLists 时也不可能一个个罗列出来,所以就有了搜索文件的指令,只需要搜索相关目录下的源文件就行了
1.aux_source_directory
aux_source_directory(< dir >, < variable >)
该命令共有两个参数,第一个参数dir 就是要搜索的相关目录,第二个参数variable 就是一个变量,这个命令就会将从dir 中搜索到的源文件列表存储到该变量中
2.file
file(GLOB/GLOB_RECURSE 变量名 要搜索的文件类型和文件名)
这个命令参数不少
首先是第一个参数 GLOB是在指定目录下搜索,而GLOB_RECURSE是在指定目录下递归搜索,意思就是指定目录的所有子目录也搜索
第二个参数就是自定义一个变量,存放所有搜索到的文件列表
第三个参数其实也已是固定的 要么是 PORJECT_SOURCE_DIR (是camke后面跟的路径)要么是 CMAKE_CURRENT_SOURCE_DIR(是CMAKELists文件所处的路径)其实两个宏的意思都一样,写哪个都行
这是具体的例子
搜索当前目录下的src目录的所有.cpp文件
file(GLOB MAIN_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp
这个则是搜素头文件
file(GLOB MAIN_HEAD ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h
通过CMake制作库文件
有时候编写的源代码并不需要编译生成可执行程序,而是生成一些静态库或动态库提供给第三方使用
制作静态库
制作静态库需要使用的命令是:
add_library(库名称 STATIC 源文件1 源文件2...)
在LInux中,静态库的名字分为三部分:lib+库名字+.a,此处只需给出库名就可以了,另外两部分在生成该文件时会自动填充。
Windows中库名可能不同,但也只需给出名字即可
cmake_minimum_required(VERSION 2.8)
project(CALC)
file(GLOB SRC "${CMAKE_CURRENT_SOURCE_DIR}/*.c")
add_library(calc STATIC ${SRC})
这就是生成的静态库
制作动态库
制作动态库需要使用的命令是:
add_library(库名称 SHARED 源文件1 源文件2...)
其实就换了个参数,其他都一样
这是生成的动态库,或许你可以发现,生成的动态库的颜色也是绿色的,这说明它也是个可执行程序,在Linux下
指定输出的路径
适用于动态库
对于生成的库文件来说和可执行程序一样都可以指定输出路径 由于在LInux下生成的动态库默认是有执行权想的
可执行程序的方式去指定它生成的目录
假如说没有lib目录,那么就会自动生成一个
cmake_minimum_required(VERSION 2.8)
project(CALC)
file(GLOB SRC "${CMAKE_CURRENT_SOURCE_DIR}/*.c")
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
add_library(calc STATIC ${SRC})
都适用
由于在Linux下生成的静态库默认不具有可执行权限,所以在指定静态库生成的路径的时候就不能使用EXECUTABLE_OUTPUT_PATH宏了,而应该使用LIBRARY_OUTPUT_PATH,这个宏对应静态库文件和动态库文件都适用。
cmake_minimum_required(VERSION 2.8)
project(CALC)
file(GLOB SRC "${CMAKE_CURRENT_SOURCE_DIR}/*.c")
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib2)
#生成静态库
add_library(calc STATIC ${SRC})
#生成动态库
add_library(calc SHARED ${SRC})
包含库文件
链接静态库
在使用制作出的库文件之前,需要先将库文件发布出去,要发布出去首先需要库文件,可以是静态库也可以是动态库。
然后就是指定头文件(头文件有库文件里定义的一系列函数的声明,因为库文件本身也是由源文件打包的,必须要有头文件才能知道这个库里面有什么东西才能调用库里面的函数,去实现相对应的功能 )
指定头文件
然后下面就是指定静态库的路径,上面制作出的静态库被放在了lib1
在cmake中,链接静态库的命令如下:
link_libraries(<static lib> <static lib>...)
只有一种参数,可以是全名 libxxx.a 也可以是掐头去尾之后的名字 xxx
这个指令的意思就是告诉cmake,除了要链接.c文件外还要链接一个静态库文件
最后因为这个静态库是我们自定义的,系统并不知道它的存在,所有这里还要通过link_directories手动指定这个静态库的地址。
cmake_minimum_required(VERSION 2.8)
project(CALC)
#查找源文件
file(GLOB SRC "${CMAKE_CURRENT_SOURCE_DIR}/*.c")
#包含头文件路径
include_directories(${PROJECT_SOURCE_DIR}/include)
# 链接静态库
link_libraries(calc)
#指定静态库的地址
link_directories(${CMAKE_CURRENT_SOURCE_DIR}/lib1)
#指定生成的可执行文件叫app
add_executable(app ${SRC}
然后就生成了可执行程序app
链接动态库
在 cmake中链接动态库的命令如下:
target_link_libraries(
<target>
<PRIVATE|PUBLIC|INTERFACE> <item>...
<PRIVATE|PUBLIC|INTERFACE> <item>......)
参数:
target:指定要加载的库的文件的名字也就是:谁要链接动态库
该文件可能是个源文件,也可能是个动态库/静态库文件还可能是个可执行程序文件
PRIVATE|PUBLIC|INTERFACE:动态库的访问权限,默认为 PUBLIC
- 如果各个动态库之间没有依赖关系,无需做任何设置,三者没有区别,一般无需指定,使用默认的PUBLIC即可。
- 动态库的链接具有传递性,如果动态库A链接了动态库B,C动态库D链接了动态库A,此时动态库D,也相当于链接了动态库B,C,并可以使用动态库B,C中定义的方法。
- PUBLIC:如果动态库A链接了动态库B,C动态库链接了动态库A,此时动态库C,也相当于链接了动态库B,并可以使用动态库B中定义的方法。
- PRIVATE:如果动态库A链接了动态库B,B前面加上PRIVATE,C动态库链接了动态库A,此时动态库C,并不能使用动态库B中定义的方法。(这时的传递性只传到A)
- INTERFANCE:假如动态库A链接B和C时,B和C的前面的权限是INTERFANCE,此时动态库A也可以使用B和C里面的函数,但是并不知道具体用的哪个库里面的
而且动态库的链接通常放在CMakeLIsts文件的最后
#查找源文件
file(GLOB SRC "${CMAKE_CURRENT_SOURCE_DIR}/*.c")
#包含头文件路径
include_directories(${PROJECT_SOURCE_DIR}/include)
#指定静态库的地址
link_directories(${CMAKE_CURRENT_SOURCE_DIR}/lib2)
#指定生成的可执行文件叫app
add_executable(app ${SRC})
#链接动态库
target_link_libraries(app calc)
这样就链接成功了