使用 CMake 管理 C/C++ 项目:从基础到第三方库集成
目前,我的几乎所有 C/C++ 项目都使用 CMake 进行管理。CMake 语法简洁、功能强大,且绝大多数主流 C/C++ 库都内建了对 CMake 的支持。在我的日常工作中,经常会用到以下具有代表性的库:
- OpenCV:由 Intel 维护的开源计算机视觉库,图像处理必备。
- Boost:C++ 语言最重要的扩展库,提供了对标准库的增强、尚未进入标准的新特性以及大量实用的语法糖。
- Qt:功能强大的跨平台 UI 框架。
- CUDA:NVIDIA 推出的并行计算平台,用于显卡硬件加速。
下面我将从一个最简单的 Hello CMake 程序开始,介绍 CMake 在实际开发中的使用技巧。
1. 极简 C/C++ 项目
最简单的 CMakeLists.txt 文件可以参考 hello-cmake。假设项目中只有一个源文件 main.cpp:
#include <iostream>
int main(int argc, char *argv[])
{
std::cout << "Hello CMake!" << std::endl;
return 0;
}
在项目根目录下创建一个 CMakeLists.txt 文件,内容如下:
cmake_minimum_required(VERSION 3.5)
# 设置项目名称
project (hello_cmake)
# 添加可执行文件
add_executable(hello_cmake main.cpp)
在终端执行以下命令即可完成编译:
mkdir build && cd build
cmake ..
make
运行编译出的程序:
./hello_cmake
输出 Hello CMake,说明编译成功。
2. 在 CMake 中使用 OpenCV
如果想在项目中调用 OpenCV,该如何配置?首先编写一个简单的程序 cv-test.cpp,实现读取并显示图像:
#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/highgui.hpp>
int main(int argc, char** argv )
{
if ( argc != 2 )
{
std::cout<<"usage: DisplayImage.out <Image_Path>"<<std::endl;
return -1;
}
cv::Mat image;
image = cv::imread( argv[1] );
if ( image.empty() )
{
std::cout<<"No image data"<<std::endl;
return -1;
}
cv::imshow("Display Image", image);
cv::waitKey(0);
return 0;
}
对应的 CMakeLists.txt 配置如下:
cmake_minimum_required(VERSION 2.8)
project( DisplayImage )
find_package( OpenCV REQUIRED )
include_directories( ${OpenCV_INCLUDE_DIRS} )
add_executable( cv-test cv-test.cpp )
target_link_libraries( cv-test ${OpenCV_LIBS} )
其中核心指令是这三行:
find_package:查找系统中安装的 OpenCV。include_directories:添加头文件路径。target_link_libraries:将 OpenCV 库链接到可执行文件。
注意:前提是 OpenCV 已正确安装且环境变量 OpenCV_DIR 已设置。如果你想指定特定版本的 OpenCV(例如我自己编译的带 CUDA 11 支持的 OpenCV 4.5.2),可以手动设置路径:
set(OpenCV_DIR D:/WORK/opencv-github/opencv452/build-cuda11/install)
3. 在 CMake 中使用 Boost
配置 Boost 相对简单,主要涉及 Boost_INCLUDE_DIR 和 Boost_LIBRARY_DIRS 两个变量。
为了方便分发,我通常开启 Boost_USE_STATIC_LIBS 以支持静态链接。以下是常用的配置模板:
set(Boost_USE_STATIC_LIBS ON)
set(Boost_USE_MULTITHREADED ON)
# 清除旧的缓存(可选)
unset(Boost_LIBRARIES)
unset(Boost_INCLUDE_DIR CACHE)
unset(Boost_LIBRARY_DIRS CACHE)
# 指定 Boost 路径
set(Boost_INCLUDE_DIR D:/Lib/boost_1_76_0)
set(Boost_LIBRARY_DIRS D:/Lib/boost_1_76_0/lib64-msvc-14.2)
# 查找所需的组件
find_package(Boost COMPONENTS system filesystem json REQUIRED)
if(NOT Boost_FOUND)
message("Boost not found")
endif()
include_directories(${Boost_INCLUDE_DIRS})
4. 在 CMake 中使用 Qt
Qt 也内建了良好的 CMake 支持。以往 Qt 项目往往依赖 QtCreator 或 VS 插件,但使用 CMake 可以让跨平台构建更直接:
cmake_minimum_required(VERSION 3.1.0)
project(helloworld VERSION 1.0.0 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# 开启自动处理 Qt 特有的元对象编译(MOC)、资源(RCC)和 UI 文件(UIC)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)
if(CMAKE_VERSION VERSION_LESS "3.7.0")
set(CMAKE_INCLUDE_CURRENT_DIR ON)
endif()
find_package(Qt5 COMPONENTS Widgets REQUIRED)
add_executable(helloworld
mainwindow.ui
mainwindow.cpp
main.cpp
resources.qrc
)
target_link_libraries(helloworld Qt5::Widgets)
5. 在 CMake 中使用 CUDA
CMake 3.9+ 版本对 CUDA 提供了原生支持。只需在 project 指令的 LANGUAGES 参数中增加 CUDA:
project(cuda-demo VERSION 0.1.0 LANGUAGES CXX CUDA)
6. 控制编译生成文件的输出路径
在包含多个子项目的大型工程中,CMake 默认会按照目录结构分散生成文件。为了方便调试和部署,通常需要将所有可执行文件和动态库统一输出到某个目录:
# 设置可执行文件输出路径
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
# 设置库文件输出路径
set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)