ZH ·
🌏 English

使用 CMake 管理 C/C++ 项目:从基础到第三方库集成

目前,我的几乎所有 C/C++ 项目都使用 CMake 进行管理。CMake 语法简洁、功能强大,且绝大多数主流 C/C++ 库都内建了对 CMake 的支持。在我的日常工作中,经常会用到以下具有代表性的库:

下面我将从一个最简单的 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} )

其中核心指令是这三行:

注意:前提是 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_DIRBoost_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)      

参考资料