C++项目 | 集群聊天服务器 | CMake

CMake简介

使用简单方便,可以跨平台,构建项目编译环境。尤其比直接写Makefile简单(在构建大型工程编译时,需要写大量的文件依赖关系),可以通过简单的CMake生成负责的Makefile文件。

CMake安装

ubuntu上直接执行 sudo apt install cmake 安装完成,可以通过cmake -version查看其版本:

1
2
3
4
darling@darling:~/桌面/chat_sever$ cmake --version
cmake version 3.28.3

CMake suite maintained and supported by Kitware (kitware.com/cmake).

CMake使用介绍

1.与项目相关的部分

就是使用一下CMake,编译一下我们的测试文件muduo_server.cpp

0.类比g++

1
g++ -o server -g muduo_server.cpp xxx.cpp -I/usr/include -L/usr/lib -lmuduo_net -lmuduo_base -lpthread

主要涉及这么几部分

1.server 或者是 *.a 或者 *.so 这部分是可执行文件的名称

2.-g 或者 -o之类的参数

3.xxx.cpp 要编译的文件

4.-I 头文件路径

5.-L 库文件的路径

6.-l 要连接的库

cmake命令会执行目录下的CMakeLists.txt配置文件里面的配置项,一个基本的CMakeLists.txt的配置文件内容如下:

1
2
3
4
5
6
7
8
9
cmake_minimum_required (VERSION 2 .8) #要求cmake最低的版本号
project (demo) # 定义当前工程名字
set(CMAKE_BUILD_TYPE "Debug")#设置debug模式,如果没有这一行将不能调试设断点
set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} -g)
add_executable(main main.c)

#进入子目录下执行 CMakeLists.txt文件 这里的lib和tests里面都有可编译的代码文件
add_subdirectory(lib)
add_subdirectory(tests)

举例:以muduo_server.cpp为例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 项目所需cmake的最低版本
cmake_minimum_required(VERSION 3.0)
#项目的名称
project(main)

#配置编译选项 对应第2项
set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} -g)

#配置头文件搜索路径 对应第4项
#include_directories()
#配置库文件的搜索路径 对应第5项
#link_directories()

#设置需要编译的源文件列表
set(SRC_LIST muduo_server.cpp)

#把指定路径下的所有源文件名字放入变量名SRC_LIST李米娜
#aux_source_directory(. SRC_LIST)

#生成可执行文件server 由SRC_LIST变量所定义的源文件编译而来 生成库是add_lib.. 对应第一项
add_executable(server ${SRC_LIST})

#表示server这个目标程序需要链接这三个库文件
target_link_libraries(server muduo_net muduo_base pthread)

image-20250117180737482

1.cmake .

根据CMakeList.txt生成Makefile文件

2.make

执行Makefile文件,生成可执行程序

1.一个正式的工程构建

0.正常的工程都由这些目录组成

1.-bin 生成的最终的可执行文件

2.-lib 生成的中间库文件

3.-include 头文件

​ –sum.h

​ –minor.h

4.-src 源代码文件

​ –sum.cpp

​ –minor.cpp

5.-build 编译过程中产生的临时文件

6.-example 示例代码测试文件

7.-thridparty 第三方库的源码文件 直接把第三方库的源码拿了过来集成到了自己的项目中

8.-CMakeLists.txt

9.-autobuild.sh 一键编译

1.创建一个build目录

image-20250117182806753

2.设置可执行文件最终存储的路径 bin

1
2
#设置可执行文件最终存储的路径 PROJECT_SOURCE_DIR是工程的根目录
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)

PROJECT_SOURCE_DIR是工程的根目录

.a和.so文件由对应的变量,可以去常用预定义变量处查看

3.完整的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 项目所需cmake的最低版本
cmake_minimum_required(VERSION 3.0)
#项目的名称
project(main)

#配置编译选项 对应第2项
set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} -g)

#配置头文件搜索路径 对应第4项
#include_directories()
#配置库文件的搜索路径 对应第5项
#link_directories()

#设置需要编译的源文件列表
set(SRC_LIST muduo_server.cpp)

#设置可执行文件最终存储的路径 PROJECT_SOURCE_DIR是工程的根目录
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)

#把指定路径下的所有源文件名字放入变量名SRC_LIST李米娜
#aux_source_directory(. SRC_LIST)

#生成可执行文件server 由SRC_LIST变量所定义的源文件编译而来 生成库是add_lib.. 对应第一项
add_executable(server ${SRC_LIST})

#表示server这个目标程序需要链接这三个库文件
target_link_libraries(server muduo_net muduo_base pthread)

image-20250117184043475

image-20250117184057295

可见,编译的中间文件都在build,可执行文件都在server中。

2.其他示例一

生成一个main.cpp源文件,输出”hello world”,然后在同级目录创建一个CMakeLists.txt文件,内容

如下:

1
2
3
4
cmake_minimum_required (VERSION 2 .8) #要求cmake最低的版本号
project (demo) # 定义当前工程名字
set(CMAKE_BUILD_TYPE "Debug")#设置debug模式,如果没有这一行将不能调试设断点
add_executable(main main.cpp)

保存退出,执行cmake .命令,输出如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
tony@tony-virtual-machine:~/code/cmake/rpc$ cmake.
-- The C compiler identification is GNU 7 .4.
-- The CXX compiler identification is GNU 7 .4.
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/tony/code/cmake/rpc

ls查看目录,发现除了CMake生成的一些中间文件,还生成好了Makefile文件

1
2
tony@tony-virtual-machine:~/code/cmake/rpc$ ls
CMakeCache.txt CMakeFiles cmake_install.cmake CMakeLists.txt main.cpp Makefile

make开始编译,最终生成可执行文件main

1
2
3
4
5
tony@tony-virtual-machine:~/code/cmake/rpc$ make
Scanning dependencies of target main
[ 50 %] Building CXX object CMakeFiles/main.dir/main.cpp.o
[100%] Linking CXX executable main
[100%] Built target main

查看生成的可执行文件:

1
2
tony@tony-virtual-machine:~/code/cmake/rpc$ ls
CMakeCache.txt CMakeFiles cmake_install.cmake CMakeLists.txt main main.cpp Makefile

上面生成的Makefile里面实现了clean,所以make clean可以清除生成的文件,然后重新编译源码。

3.其他示例二

如果需要编译的有多个源文件,可以都添加到add_executable(main main.cpp test.cpp)列表当中,

但是如果源文件太多,一个个添加到add_executable的源文件列表中,就太麻烦了,此时可以用

aux_source_directory(dir var) 来定义源文件列表,使用如下:

1
2
3
4
cmake_minimum_required (VERSION 2 .8)
project (demo)
aux_source_directory(. SRC_LIST) # 定义变量,存储当前目录下的所有源文件
add_executable(main ${SRC_LIST})

aux_source_directory()也存在弊端,它会把指定目录下的所有源文件都加进来,可能会加入一些我们

不需要的文件,此时我们可以使用set命令去新建变量来存放需要的源文件,如下

1
2
3
4
5
6
cmake_minimum_required (VERSION 2 .8)
project (demo)
set( SRC_LIST
./main.cpp
./test.cpp)
add_executable(main ${SRC_LIST})

4.其他示例三 - 一个正式的工程构建

一个正式的源码工程应该有这几个目录:

1.-bin 存放最终的可执行文件

2.-build 存放编译中间文件

3.-include 头文件

​ –sum.h

​ –minor.h

4.-src 源代码文件

​ –sum.cpp

​ –minor.cpp

5.main.cpp

6.-CMakeLists.txt

CMakeLists.txt如下:

1
2
3
4
5
6
7
8
cmake_minimum_required (VERSION 2 .8)
project (math)
# 设置cmake的全局变量
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
#添加头文件路径,相当于makefile里面的-I
include_directories(${PROJECT_SOURCE_DIR}/include)
aux_source_directory (src SRC_LIST)
add_executable (main main.cpp ${SRC_LIST})

然后在build目录里面执行cmake .. 命令,这样所有的编译中间文件都会在build目录下,最终的可执行

文件会在bin目录里面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
tony@tony-virtual-machine:~/code/cmake/rpc/build$ cmake ..
-- The C compiler identification is GNU 7 .4.
-- The CXX compiler identification is GNU 7 .4.
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/tony/code/cmake/rpc/build
tony@tony-virtual-machine:~/code/cmake/rpc/build$ make
Scanning dependencies of target main
[ 25 %] Building CXX object CMakeFiles/main.dir/main.cpp.o
[ 50 %] Building CXX object CMakeFiles/main.dir/src/minor.cpp.o
[ 75 %] Building CXX object CMakeFiles/main.dir/src/sum.cpp.o
[100%] Linking CXX executable ../bin/main
[100%] Built target main
tony@tony-virtual-machine:~/code/cmake/rpc$ cd bin/
tony@tony-virtual-machine:~/code/cmake/rpc/bin$ ls
main

5.静态库和动态库的编译控制

把上面的sum和minor源文件直接生成静态库或者动态库,让外部程序进行链接使用,代码结构如下:

1.-bin 存放最终的可执行文件

2.-build 存放编译中间文件

3.-lib 存放编译生成的库文件

4.-include 头文件

​ –sum.h

​ –minor.h

5.-src 源代码文件

​ –sum.cpp

​ –minor.cpp

6.–CMakeLists.txt

7.-test 测试代码

8.–main.cpp

9.–CMakeLists.txt

-CMakeLists.txt

最外层的CMakeLists.txt是总控制编译,内容如下:

1
2
3
4
cmake_minimum_required (VERSION 2 .8)
project (math)
add_subdirectory (test)
add_subdirectory (src)

src里面的源代码要生成静态库或动态库,CMakeLists.txt内容如下:

1
2
3
4
5
6
7
set (LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
# 生成库,动态库是SHARED,静态库是STATIC
add_library (sum SHARED sum.cpp)
add_library (minor SHARED minor.cpp)
# 修改库的名字
#set_target_properties (sum PROPERTIES OUTPUT_NAME "libsum")
#set_target_properties (minor PROPERTIES OUTPUT_NAME "libminor")

test里面的CMakeLists.txt内容如下:

1
2
3
4
5
set (EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
include_directories (../include) # 头文件搜索路径
link_directories (${PROJECT_SOURCE_DIR}/lib) # 库文件搜索路径
add_executable (main main.cpp) # 指定生成的可执行文件
target_link_libraries (main sum minor) # 执行可执行文件需要依赖的库

在build目录下执行cmake ..命令,然后执行make,如下

1
2
3
4
5
6
7
8
9
10
11
tony@tony-virtual-machine:~/code/cmake/rpc02/build$ make
[ 16 %] Building CXX object src/CMakeFiles/minor.dir/minor.cpp.o
[ 33 %] Linking CXX shared library ../../lib/libminor.so
[ 33 %] Built target minor
[ 50 %] Building CXX object src/CMakeFiles/sum.dir/sum.cpp.o
[ 66 %] Linking CXX shared library ../../lib/libsum.so
[ 66 %] Built target sum
Scanning dependencies of target main
[ 83 %] Building CXX object test/CMakeFiles/main.dir/main.cpp.o
[100%] Linking CXX executable ../../bin/main
[100%] Built target main

查看生成的可执行文件,检验其链接的库有哪些

1
2
3
4
5
6
7
8
9
10
11
12
13
tony@tony-virtual-machine:~/code/cmake/rpc02/bin$ ls
main
tony@tony-virtual-machine:~/code/cmake/rpc02/bin$ ./main
20 + 10 = 30
20 - 10 = 10
tony@tony-virtual-machine:~/code/cmake/rpc02/bin$ readelf -d ./main

Dynamic section at offset 0x1d48 contains 31 entries:
标记 类型 名称/值
0x0000000000000001 (NEEDED) 共享库:[libsum.so]
0x0000000000000001 (NEEDED) 共享库:[libminor.so]
0x0000000000000001 (NEEDED) 共享库:[libstdc++.so.6]
0x0000000000000001 (NEEDED) 共享库:[libc.so.6]

6.CMake常用的预定义变量

PROJECT_NAME : 通过 project() 指定项目名称

PROJECT_SOURCE_DIR : 工程的根目录

PROJECT_BINARY_DIR : 执行 cmake 命令的目录

CMAKE_CURRENT_SOURCE_DIR : 当前 CMakeList.txt 文件所在的目录

CMAKE_CURRENT_BINARY_DIR : 编译目录,可使用 add subdirectory 来修改

EXECUTABLE_OUTPUT_PATH : 二进制可执行文件输出位置

LIBRARY_OUTPUT_PATH : 库文件输出位置

BUILD_SHARED_LIBS : 默认的库编译方式 ( shared 或 static ) ,默认为 static

CMAKE_C_FLAGS : 设置 C 编译选项

CMAKE_CXX_FLAGS : 设置 C++ 编译选项

CMAKE_CXX_FLAGS_DEBUG : 设置编译类型 Debug 时的编译选项

CMAKE_CXX_FLAGS_RELEASE : 设置编译类型 Release 时的编译选项

CMAKE_GENERATOR : 编译器名称

CMAKE_COMMAND : CMake 可执行文件本身的全路径

CMAKE_BUILD_TYPE : 工程编译生成的版本, Debug / Release