问题描述:为了更好地满足不同场景下的开发需求,我们需要弄清楚如何在不同的平台上开发C++项目。要求使用cmake构建工具和conan包管理工具。
Linux下的c++开发环境搭建
- 🟢 1️⃣ 安装 C++ 编译器
Ubuntu 自带 gcc 或 g++,但一般建议先更新到较新版本。
sudo apt update
sudo apt install build-essential
-
build-essential包含 gcc、g++、make 等基本编译工具。 -
🟠 2️⃣ 安装 CMake
sudo apt install cmake
- 🔵 3️⃣ 安装 Conan(C++ 包管理器)
Conan 推荐使用 Python 的 pip 安装:
# Python 3.11 起 使用虚拟环境
# 安装 venv(如果没有)
sudo apt install python3-venv
# 创建一个新的虚拟环境
python3 -m venv ~/.venvs/conan-env
# 激活虚拟环境
source ~/.venvs/conan-env/bin/activate
# 在虚拟环境中安装 conan ,注意到国内可能存在的网络环境问题
pip install conan -i https://pypi.tuna.tsinghua.edu.cn/simple
# 确认安装
conan --version
# 旧的python版本下可用
sudo apt install -y python3-pip
pip3 install conan -i https://pypi.tuna.tsinghua.edu.cn/simple
conan --version
- 初始化 Conan 的配置文件(profile)
执行以下命令,Conan 会自动根据你当前系统生成一个默认 profile
conan profile detect
执行完毕后会自动在 ~/.conan2/profiles/ 下生成一个 default 文件,可以根据实际需求进行手动修改。
- 配置 Conan远程仓库(可选步骤)
Conan 默认使用本地缓存,但可以配置远程仓库(如 conan-center):
# 添加远程仓库
conan remote add conan-center https://center.conan.io
# 查看已配置的远程仓库
conan remote list
- 🟣 4️⃣ 创建项目文件夹结构(示例)
mkdir MyCppProject
cd MyCppProject
mkdir src include build
touch src/main.cpp
touch CMakeLists.txt
- 🟤 5️⃣ 示例代码和配置
// src/main.cpp
#include <fmt/core.h>
int main() {
fmt::print("Hello, C++ with Conan and CMake!\n");
return 0;
}
- CMakeLists.txt 示例
cmake_minimum_required(VERSION 3.15)
project(MyCppProject)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)
find_package(fmt CONFIG REQUIRED) # 例如你的依赖是 fmt
add_executable(${PROJECT_NAME} src/main.cpp)
target_link_libraries(${PROJECT_NAME} PRIVATE fmt::fmt)cmake_minimum_required(VERSION 3.15)
project(MyCppProject)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)
# 只需要 find_package 即可
find_package(fmt CONFIG REQUIRED) # 例如你的依赖是 fmt
add_executable(${PROJECT_NAME} src/main.cpp)
target_link_libraries(${PROJECT_NAME} PRIVATE fmt::fmt)
- 🟢 6️⃣ 配置 Conan
创建 conanfile.txt
[requires]
# 假设项目需要 `fmt` 库(一个流行的 C++ 格式化库)
fmt/10.2.1
eigen/3.4.0
[generators]
CMakeDeps
CMakeToolchain
- 安装依赖
cd build
conan install .. --build=missing
- 🟠 7️⃣ 配置并构建项目
cmake .. -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake -DCMAKE_BUILD_TYPE=Release
cmake --build .
构建成功后运行
./MyCppProject
如果一切正常,应该输出:
Hello, C++ with CMake & Conan!
一个完整小项目演示
要求:现在请利用上面的环境(在 Ubuntu 系统下搭建 C++ 开发环境,并且使用 CMake 作为构建工具、Conan 作为包管理工具),完成一个小任务:矩阵数据的序列化与反序列化。要求包括:
- 编写C++ 代码,实现Eigen::MatrixXd矩阵数据的序列化与反序列化。
- 需要写单元测试用例
- 通过 CMake 构建项目
- 可使用的三方库以及版本:
eigen/3.4.0和nlohmann/3.11.3,gtest/1.14.0
示例方案:
其中 CMakeLists.txt 内容为
cmake_minimum_required(VERSION 3.15)
project(MyCppProject)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)
# 头文件目录
include_directories(${PROJECT_SOURCE_DIR}/include)
# 查找依赖
find_package(Eigen3 REQUIRED)
find_package(nlohmann_json REQUIRED)
find_package(GTest REQUIRED)
# 主程序
add_executable(MyCppProject src/main.cpp src/matrix_serializer.cpp)
target_link_libraries(MyCppProject PRIVATE nlohmann_json::nlohmann_json Eigen3::Eigen)
# 单元测试
enable_testing()
add_executable(tests test/test_matrix_serializer.cpp src/matrix_serializer.cpp)
target_link_libraries(tests PRIVATE GTest::gtest GTest::gtest_main nlohmann_json::nlohmann_json Eigen3::Eigen)
add_test(NAME MatrixSerializerTests COMMAND tests)
conanfile.txt为
[requires]
eigen/3.4.0
nlohmann_json/3.11.3
gtest/1.14.0
[generators]
CMakeDeps
CMakeToolchain
Windows平台下开发C++项目
使用CMake预设
在Windows平台下开发C++项目,主流的方式是VS。打开VS,选择新建CMake项目后,项目文件夹下会生成CMakePresets.json文件和CMakeSettings.json,这里有必要先弄清楚这些设置文件的含义。
-
CMakeSettings.json是 Visual Studio 特有的配置文件,在 CMake 官方推出预设机制前,VS 为了更好地支持 CMake 项目而设计的配置方式。它用于定义 VS 中 CMake 项目的构建配置,包括指定编译器、构建目录、构建类型(Debug/Release)、环境变量等等。它仅在 Visual Studio 中生效,不被其他 IDE(如 CLion、VS Code)或 CMake 官方工具识别。它的格式和语法是 VS 自定义的,与 CMake 官方标准无关。适合纯 VS 环境下的开发,无需考虑跨平台兼容性。 -
CMakePresets.json是 CMake 官方推出的标准化配置文件(CMake 3.19+ 支持),旨在统一不同 IDE/工具对 CMake 项目的配置方式。它用于定义项目的通用构建预设,包括构建类型(Debug/Release)、生成器(如 Ninja、Visual Studio)、工具链路径、缓存变量等,可被所有支持 CMake 预设的工具识别。它能跨平台、跨工具兼容(支持 VS、VS Code、CLion、CMake 命令行等),是项目级的配置文件,通常包含团队共用的基础配置,需要提交到版本控制(如 Git)中,语法遵循 CMake 官方规范。CMakePresets.json的内容分为几类 preset:configurePresets用于定义 CMake 配置(生成器、编译器、选项等);buildPresets定义构建参数(并行度、目标等);testPresets定义测试运行方式。 -
CMakeUserPresets.json同样是 CMake 官方预设机制的一部分,用于补充CMakePresets.json。它用于存储用户个人的自定义配置(如本地路径、个人偏好的编译选项等),不会影响团队其他成员的配置,属于用户级配置文件,通常 不提交到版本控制(需加入.gitignore),避免覆盖他人配置,继承或扩展CMakePresets.json中的预设,添加个人化修改(如本地工具链路径)。它必须继承于CMakePresets.json中的配置,并覆盖CMakePresets.json中的相同项。
| 维度 | CMakeSettings.json |
CMakePresets.json |
CMakeUserPresets.json |
|---|---|---|---|
| 来源 | Visual Studio 特有 | CMake 官方标准 | CMake 官方标准 |
| 适用范围 | 仅 VS 环境 | 跨 IDE/工具(通用) | 跨 IDE/工具(用户个人) |
| 版本控制 | 可提交(团队共享) | 必须提交(团队共享) | 不提交(个人使用) |
| 兼容性 | 非标准,仅 VS 识别 | 标准,所有支持 CMake 预设的工具都识别 | 同左 |
在VS中打开:工具--选项--CMake--常规,第一项有三个选项:
VS中的cmake配置-
始终使用 CMake 预设,VS2022 会 完全依赖
CMakePresets.json/CMakeUserPresets.json作为配置来源。如果项目里没有CMakePresets.json,那么配置会失败,VS 不会回退到CMakeSettings.json。 -
如果可用请使用 CMake 预设,否则使用 CMakeSettings.json 这是 VS 的默认策略,优先检查项目中是否存在
CMakePresets.json/CMakeUserPresets.json。若存在预设文件,则使用其配置;若不存在,则回退到使用 VS 特有的CMakeSettings.json文件。这种方式兼顾了跨平台标准化(CMake 预设)和 VS 传统工作流(CMakeSettings.json),适合需要兼容旧项目的场景。 -
从不使用 CMake 预设 强制 VS 忽略任何
CMakePresets.json/CMakeUserPresets.json文件,仅使用 VS 特有的CMakeSettings.json进行配置。适合的场景:项目比较老,还没有采用 CMake Presets,或者只打算在 Visual Studio 里开发,不考虑跨 IDE 的配置一致性。
当项目中同时存在 CMakePresets.json 和 CMakeSettings.json 时,VS 会根据CMake 项目属性的配置决定优先使用哪一个。而CMakeUserPresets.json 则始终用于覆盖或补充 CMakePresets.json,不影响 CMakeSettings.json。
优先推荐使用 CMakePresets.json + CMakeUserPresets.json 的组合,尤其适合跨平台、多工具协作的项目。
使用conan
Conan 是一个开源的C/C++包管理器,用于管理和构建C/C++项目所需的依赖库。它是去中心化的 C/C++ 包管理器,它借鉴了像 npm、pip、cargo 等现代语言包管理器的理念,但针对 C++ 的复杂性(平台、编译器、架构、标准库、构建系统等)做了深度适配。
# 安装conan
pip install conan
conan --version
conan profile detect
配置远程仓库,conancenter是官方远程仓库,在安装时已经默认添加,可以在国内的网络下正常访问,个人开发者使用它即可。可以在C盘user/.conan2文件夹下查看配置。
conan remote add conancenter https://center.conan.io
conan install 官方文档 https://docs.conan.org.cn/2/reference/commands/install.html
摘取部分内容如下:
--build 参数决定缺失的二进制包(或者匹配不到的)是否从源码构建,以及在什么情况下构建。
conan install . --build=missing
常见值(参考conan install — conan 2.20.1 文档 - Conan 文档):
| 值 | 含义 |
| ---------------------- | ----------------------------------------------------------------------------------- |
| never | 全部使用二进制包;如果二进制包不存在就报错。 |
| missing | 对于那些没有合适的二进制包的包,从源代码构建。 |
| cascade | 如果一个包有依赖被构建,那么这些依赖也可能被构建。详见文档。 |
| [pattern] | 对匹配 pattern 的包强制从源码构建,例如 --build="pkg/*"。 |
| ~[pattern] | 排除匹配 pattern 的包不从源码构建。 |
| missing:[pattern] | 类似 missing,但仅对匹配 pattern 的包。 |
| compatible:[pattern] | 实验性选项。如果没有兼容的二进制包,并且按照某些兼容策略允许,那么对匹配 pattern 的依赖使用可兼容的包构建。|
在早期版本的conan 1 中,推荐的配置是将conan_toolchain.cmake手动指定到专门的 generators文件夹中,而在conan 2中官方现在推荐的做法是,在conanfile.py或者 conanfile.txt文件中使用自动的layout配置。
参阅 使用 layout() 方法
from conan import ConanFile
from conan.tools.cmake import cmake_layout
class CompressorRecipe(ConanFile):
settings = "os", "compiler", "build_type", "arch"
generators = "CMakeToolchain", "CMakeDeps"
def layout(self):
cmake_layout(self)
def requirements(self):
self.requires("gtest/1.14.0")
在此文件的基础上,运行下面的命令
conan install . --build=never -r=conancenter --update
即可看到build目录下有了generators文件夹,文件夹中包含一系列配置文件。
CMake结合conan的示例方式(VS生成器)
下面举一个最简单的CMake配合conan的使用示例,注意是在Windows环境下,其他操作系统的指令稍有不同!
文件层级:
CPP_Demo_Example/
├── src/
│ └── main.cpp
│
├── CMakeLists.txt
│
├── conanfile.py
│
└── CMakePresets.json
首先写一个简单的主程序:
// src/main.cpp
#include <fmt/core.h>
#include <fmt/color.h>
int main() {
fmt::print(fmt::fg(fmt::color::cyan) | fmt::emphasis::bold,
"Conan is a MIT-licensed, Open Source. This is an example.");
fmt::print(fmt::fg(fmt::color::white),
"package manager for C and C++ development\n");
return 0;
}
CMake预设:
{
"version": 4,
"configurePresets": [
{
"name": "VS2022_x64-Base",
"hidden": true,
"generator": "Visual Studio 17 2022",
"architecture": {
"value": "x64",
"strategy": "external"
},
"cacheVariables": {
"CMAKE_INSTALL_PREFIX": "${sourceDir}/install",
"CMAKE_GENERATOR_PLATFORM": "x64"
},
"vendor": {
"microsoft.com/VisualStudioSettings/CMake/1.0": {
"clangTidyChecks": "-format-style='file'",
"enableMicrosoftCodeAnalysis": true,
"enableClangTidyCodeAnalysis": true
}
}
},
{
"name": "VS2022_x64-Debug",
"inherits": "VS2022_x64-Base",
"binaryDir": "${sourceDir}/build/Debug",
"cacheVariables": {
"BUILD_TYPE_FOR_CONAN": "Debug"
}
},
{
"name": "VS2022_x64-Release",
"inherits": "VS2022_x64-Base",
"binaryDir": "${sourceDir}/build/Release",
"cacheVariables": {
"BUILD_TYPE_FOR_CONAN": "Release"
}
},
{
"name": "VS2022_x64-RelWithDebInfo",
"inherits": "VS2022_x64-Base",
"binaryDir": "${sourceDir}/build/RelWithDebInfo",
"cacheVariables": {
"BUILD_TYPE_FOR_CONAN": "RelWithDebInfo"
}
}
],
"buildPresets": [
{
"name": "Debug",
"configurePreset": "VS2022_x64-Debug",
"configuration": "Debug"
},
{
"name": "Release",
"configurePreset": "VS2022_x64-Release",
"configuration": "Release"
},
{
"name": "RelWithDebInfo",
"configurePreset": "VS2022_x64-RelWithDebInfo",
"configuration": "RelWithDebInfo"
}
]
}
对应CMake配置
# CMakeLists.txt
cmake_minimum_required(VERSION 3.31)
project(Demo_example CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
# 设置 Conan 工具链文件路径 输出目录为build/generators(在conan配置中使用cmake_layout后默认的路径)
set(CONAN_TOOLCHAIN_PATH "${CMAKE_SOURCE_DIR}/build/generators/conan_toolchain.cmake")
if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|AMD64")
set(CONAN_ARCH "x86_64")
else()
set(CONAN_ARCH "x86")
endif()
set(CONAN_OS ${CMAKE_SYSTEM_NAME})
execute_process(
COMMAND
conan install ${CMAKE_SOURCE_DIR} --output=${CMAKE_SOURCE_DIR}
--build=missing --update --settings os=${CONAN_OS} --settings arch=${CONAN_ARCH}
--settings build_type=${BUILD_TYPE_FOR_CONAN}
RESULT_VARIABLE result
)
if(result)
message(FATAL_ERROR "Failed to execute conan install command")
endif()
# 加载 Conan 工具链文件
include(${CONAN_TOOLCHAIN_PATH})
list(PREPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/build/generators)
list(PREPEND CMAKE_PREFIX_PATH ${CMAKE_SOURCE_DIR}/build/generators)
find_package(fmt REQUIRED)
add_executable(${PROJECT_NAME} src/main.cpp)
target_link_libraries(${PROJECT_NAME} PRIVATE fmt::fmt)
和conan的配置
from conan import ConanFile
from conan.tools.cmake import CMake, cmake_layout,CMakeToolchain, CMakeDeps
class MyRecipe(ConanFile):
name = "CPP_Template_Project"
user = "JohnJi"
channel = "develop"
description = "This is an example to explain how to use conan with cmake. By JohnJi"
settings = "os", "compiler", "build_type", "arch"
# The cmake_layout() sets the folders and cpp attributes to follow the structure of a typical CMake project.
# https://docs.conan.io/2.0/reference/tools/cmake/cmake_layout.html
# https://docs.conan.org.cn/2/tutorial/consuming_packages/the_flexibility_of_conanfile_py.html#consuming-packages-flexibility-of-conanfile-py-use-layout
def layout(self):
cmake_layout(self)
# The requirements() method is used to specify the dependencie_s of a package.
# https://docs.conan.io/2.0/reference/conanfile/methods/requirements.html
def requirements(self):
# self.requires("eigen/3.4.0")
self.requires("fmt/11.2.0")
# The purpose of generate() is to prepare the build, generating the necessary files.
# https://docs.conan.io/2.0/reference/conanfile/methods/generate.html
def generate(self):
tc = CMakeToolchain(self)
tc.user_presets_path = False # 禁用 Conan 生成的 user-presets.json
deps = CMakeDeps(self)
deps.generate()
# 生成最终的 toolchain 文件(默认是 conan_toolchain.cmake)
tc.generate()
# The build() method is used to define the build from source of the package.
# https://docs.conan.io/2.0/reference/conanfile/methods/build.html
def build(self):
cmake = CMake(self)
cmake.configure()
cmake.build()
# The package() method is in charge of copying files from the source_folder and the temporary build_folder to the package_folder
## https://docs.conan.io/2/reference/conanfile/methods/package.html#
def package(self):
cmake = CMake(self)
cmake.install()
在根目录下运行:
cmake --preset=VS2022_x64-Debug
cmake --build --preset Debug
即可生成Demo_example.exe
解释一下
这个示例匹配的具体要求为:要求在windows系统下搭建c++开发环境,c++项目使用cmake构建工具和conan包管理工具,而且使用vscode编辑器。系统中已经安装了vs 2022,要求使用Visual Studio 17 2022生成器。CMake预设中还开启了在VS中的clang-tidy 静态检查。
预设文件中写了一个 BUILD_TYPE_FOR_CONAN这是为了将 build_type 参数传给 conan ,conan必须获得正确的build_type才能最终生成正确的产物。而这里指定了"Visual Studio 17 2022"这种多配置生成器,cmake中的CMAKE_BUILD_TYPE参数对它不起作用,因此只能另外手动指定一个名称传递给它。
CMake结合conan的示例方式(Ninja生成器)
实际上,Ninja生成器才是目前被广泛推荐的,包括微软官方也更倾向于推荐Ninja生成器而不是VS生成器。
下面在上文中示例项目的基础上进行修改,首先是简化CMake预设文件:
{
"version": 4,
"configurePresets": [
{
"name": "Ninja_x64-Base",
"hidden": true,
"generator": "Ninja",
"cacheVariables": {
"CMAKE_INSTALL_PREFIX": "${sourceDir}/install"
}
},
{
"name": "Ninja_x64",
"inherits": "Ninja_x64-Base",
"binaryDir": "${sourceDir}/build/${presetName}"
}
],
"buildPresets": [
{
"name": "Debug",
"configurePreset": "Ninja_x64",
"configuration": "Debug"
},
{
"name": "Release",
"configurePreset": "Ninja_x64",
"configuration": "Release"
},
{
"name": "RelWithDebInfo",
"configurePreset": "Ninja_x64",
"configuration": "RelWithDebInfo"
}
]
}
随后修改CMakeLists.txt
cmake_minimum_required(VERSION 3.31)
project(Demo_example CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CONAN_TOOLCHAIN_PATH "${CMAKE_SOURCE_DIR}/build/generators/conan_toolchain.cmake")
# 使用ninja生成器时,可以使用CMAKE_BUILD_TYPE变量
execute_process(
COMMAND
conan install ${CMAKE_SOURCE_DIR} --output=${CMAKE_SOURCE_DIR}
--build=missing --update
--settings build_type=${CMAKE_BUILD_TYPE}
RESULT_VARIABLE result
)
if(result)
message(FATAL_ERROR "Failed to execute conan install command")
endif()
# 加载 Conan 工具链文件
include(${CONAN_TOOLCHAIN_PATH})
list(PREPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/build/generators)
list(PREPEND CMAKE_PREFIX_PATH ${CMAKE_SOURCE_DIR}/build/generators)
find_package(fmt REQUIRED)
add_executable(${PROJECT_NAME} src/main.cpp)
target_link_libraries(${PROJECT_NAME} PRIVATE fmt::fmt)
和conan的配置
from conan import ConanFile
from conan.tools.cmake import CMake, cmake_layout,CMakeToolchain, CMakeDeps
class MyRecipe(ConanFile):
name = "CPP_Template_Project"
user = "JohnJi"
channel = "develop"
description = "This is an example to explain how to use conan with cmake. By JohnJi"
settings = "os", "compiler", "build_type", "arch"
def layout(self):
cmake_layout(self)
def requirements(self):
self.requires("fmt/11.2.0")
def generate(self):
tc = CMakeToolchain(self)
tc.user_presets_path = False
# 重要:在这里指定Ninja生成器
# 参考 https://docs.conan.org.cn/2/examples/tools/cmake/cmake_toolchain/use_different_toolchain_generator.html
tc.generator = "Ninja"
deps = CMakeDeps(self)
deps.generate()
tc.generate()
def build(self):
cmake = CMake(self)
cmake.configure()
cmake.build()
def package(self):
cmake = CMake(self)
cmake.install()
推荐在VS中打开、开发项目,如果要使用 VScode 则必须在x64 Native Tools Command Prompt for VS 2022控制台中打开(开始菜单 → Visual Studio 2022 → x64 Native Tools Command Prompt)。在控制台中输入命令 code即可打开vscode。这样做的原因是这个控制台已经预先配置好了cl.exe等一系列工具链,不会报错找不到生成器、编译器等。如果不使用这个预先配置好的控制台且还要使用VS Code,则必须在环境变量中配置一系列东西。
在 Developer Command Prompt 环境下的控制台中,可以输入 code来打开VS code,也可以输入命令:
cmake --preset Ninja_x64
cmake --build --preset Debug
可以看到顺利可执行文件被正确生成了,且速度确实更快。
归根到底,Windows平台下最推荐的C++开发方案仍然是VS,或者Clion这样的IDE。VSCode对大型C++项目的支持还是不能和专业的IDE相比。
参考阅读
- 了解使用 conanfile.py 与 conanfile.txt 的灵活性
- CMakeToolchain:使用 CMakePresets 构建您的项目
- 理解 Conan 包布局
- 使用 CMakePresets.json 简化 CMake 项目初始化繁杂步骤
- https://github.com/conan-io/conan-training2
- 使用 Conan 构建一个简单的 CMake 项目