目录
前言
在我们开发项目的时候,经常会碰到项目很大,而且可能不止要输出一个可执行文件的情况。这时候就可以利用CMake对项目进行统一的管理与编译。
CMake是一个跨平台的构建系统工具,用于管理软件项目的构建过程。CMake的主要目标是提供一个简单、高效和可扩展的方式来生成各种不同平台和编译器下的构建脚本。CMake支持嵌套结构,我们可以在一个CMakeLists.txt文件中使用add_subdirectory()
函数来引入其他目录中的CMakeLists.txt文件。这样可以形成CMake项目的嵌套结构。
本文对CMake 保姆级教程【C/C++】_哔哩哔哩_bilibili视频中的教程进行总结,并应用到其他项目中。
CMake的基本操作可以参考我的另一篇博客:利用cmake配置VScode的C++运行与调试环境
项目介绍
该项目是一个用线程池实现的包含客户端与服务器端的简单网络编程实例。项目主要的需求是:
- 编译得到一个Client.exe文件
- 线程池相关文件打包成一个静态库,供服务器端代码进行简单的调用
- 利用上面的静态库编译得到一个Server.exe文件
项目文件结构如下:
client文件夹存放客户端代码,ThreadPool文件夹存放线程池实现的文件代码,server文件夹存放服务器端代码
实现思路:在文件夹同级中编写一个CMakeLists.txt文件,用于管理整个大项目,在其中使用add_subdirectory()
函数引入client、server
和ThreadPool
目录中的CMakeLists.txt文件。三个子文件夹中的CMakeLists.txt文件负责各个文件夹的不同操作。
cmake配置
CMakeLists.txt文件创建位置展示如下:
首先编辑最外层的CMakeLists.txt文件1,也就是最外层的CMakeLists.txt文件。在这里我们要指定一些子项目中可能用到的一些路径变量,并引入对应的子项目。CMakeLists.txt内容如下:
cmake_minimum_required(VERSION 3.22.1) #指定CMake最低版本
project(NetSample) #指定项目名称
set(LIB_PATH ${CMAKE_CURRENT_SOURCE_DIR}/lib) #设置静态库输出与调用位置
set(APP_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/output) #设置生成的可执行文件的输出位置
set(INCLUDE_SERVER_PATH ${CMAKE_CURRENT_SOURCE_DIR}/ThreadPool) #设置线程池头文件位置,方便server中的文件引入代码
add_subdirectory(client) #导入client文件夹,在这个文件夹中的CMakeLists文件生成一个客户端可执行文件
add_subdirectory(ThreadPool) #导入线程池文件夹,在这个文件夹中的CMakeLists文件制作出一个可供调用的静态库
add_subdirectory(server) #导入server文件夹,在这个文件夹中的CMakeLists文件生成一个服务器可执行文件
下一步编辑client文件夹中的CMakeLists.txt文件2,在这里生成client.exe到指定文件夹
cmake_minimum_required(VERSION 3.22.1) #指定最低版本
project(Client) #子项目项目名称
aux_source_directory(${PROJECT_SOURCE_DIR} source) #在本文件夹中找到TCPClient.cpp源文件,并将其存入source变量中
set(EXECUTABLE_OUTPUT_PATH ${APP_OUTPUT_PATH}) #设置可执行文件输出位置,APP_OUTPUT_PATH在最外层CMakeLists定义
add_executable(client ${source}) #生成client.exe
target_link_libraries(client ws2_32) #给client.exe链接一些需要的动态库
接下来编辑ThreadPool中的CMakeLists.txt文件3,制作静态库libMyThreadPoolLib.a,输出到指定文件夹
cmake_minimum_required(VERSION 3.22.1) #指定最低版本
project(ThreadPoolLib) #子项目项目名称
aux_source_directory(${PROJECT_SOURCE_DIR} source) #找到制作静态库所需的源文件,存入source变量中
include_directories(ThreadPool) #指定头文件所在的文件夹
set(LIBRARY_OUTPUT_PATH ${LIB_PATH}) #指定静态库生成后存储的位置
message(${LIBRARY_OUTPUT_PATH}) #打印该位置
link_libraries(pthread) #向该静态库中再链接一个要用到的库
find_package(Threads REQUIRED) #寻找系统安装的pthread库
add_library(MyThreadPoolLib STATIC ${source}) #生成静态库
target_link_libraries(MyThreadPoolLib Threads::Threads ws2_32) #链接要用的动态库到静态库中
最后编辑server文件夹下的CMakeLists.txt文件4,利用刚刚生成的静态库得到一个server.exe可执行文件,存入指定目录。
cmake_minimum_required(VERSION 3.22.1) #指定最低版本
project(server) #子项目名称
include_directories(${INCLUDE_SERVER_PATH}) #指定头文件所在位置,以防在TcpServerThreadPool.cpp文件中找不到头文件
link_directories(${LIB_PATH}) #指定静态库所在位置
link_libraries(MyThreadPoolLib) #链接静态库
aux_source_directory(${PROJECT_SOURCE_DIR} source) #找到TcpServerThreadPool.cpp文件,并将其存入source
set(EXECUTABLE_OUTPUT_PATH ${APP_OUTPUT_PATH}) #设置可执行文件输出位置
add_executable(server ${source}) #生成可执行文件
下一步就是配置CMake,首先在VScode中按下组合键shift+Ctrl+p,弹出搜索框。输入CMake:configure。
选择一个自己环境中的编译器,比如我这选的GCC8.1.0
等待配置完毕,配置完毕后会生成一个build文件夹,并输出以下内容:
下一步我们要进入build文件夹,利用cmake命令生成Makefile文件。所以我们需要新建一个终端,在终端中输入cd build (进入build文件夹)与cmake ..(利用刚刚写好的CMakeLists.txt生成Makefile文件,如果输入cmake报错,可以尝试输入:cmake .. -G "MinGW Makefiles")
执行cmake ..
的效果是在build
文件夹中生成构建系统所需的构建文件,成功运行完这两条命令后输出done,就可以进行下一步build了
build与其结果
在终端中输入命令:mingw32-make按照之前的CMakeLists.txt编译整个项目。(注:可以将编译器中的mingw32-make.exe复制一份,重命名为make.exe,这样以后就可以输入make命令就可以直接编译,方便一点)
编译完成后,检查文件结构,发现多了几个文件夹,这几个都是我们刚刚在CMakeLists文件中指定的,而且文件夹中都有我们需要的结果,说明编译成功。
运行可执行文件
生成的服务器是用线程池的多线程机制实现的,所以我们可以打开多个客户端和一个服务器端同时交互。