考虑到C++主流还是用CMake做构建,最近也稍微折腾了下CMake
然后C++的话还是要用些包管理的,不然手动管理太抽象了
所以还需要配置下vcpkg的东西
虽然微软这确实有文档讲怎么配,不过讲得不是很清楚
<0x00> 前置准备
系统方面
CMake
既然是CMake配置,所以系统里肯定要安装CMake的
命令行敲cmake能找到就行,我这里采用scoop安装
scoop install cmake
vcpkg
然后是包管理器vcpkg
网上很多教程是下载源码的,我这里也是采用scoop安装
scoop install vcpkg
采用scoop安装的话会自动添加VCPKG_ROOT的环境变量,指向的就是安装路径
虽然这个主要是方便MSBuild找vcpkg的,跟这篇博客没关系
(自己下载源码的话手动添加一个就可以了)
VSCode方面

主要是C/C++ Extension Pack和CMake Tools
<0x01> CMake工程配置
首先新建一个文件夹,相当于是工程文件夹了
快速创建CMake工程
VSCode中Ctrl+Shift+P调出命令面板
输入CMake: Quick Start后回车
在输入完名字之类的东西之后,会让你设置一个preset
这个比较关键,选择最后从编译器创建
(windows平台下貌似直接绑定MSVC,选别的编译器也会换成MSVC)
这时候会创建一些文件,先不管
初始化vcpkg
打开终端窗口,输入下面的命令
vcpkg new --application
这时候也会创建一些文件,也不需要管
让CMake认识vcpkg
打开CMakePresets.json,里面可以看到一些配置
{
"version": 8,
"configurePresets": [
{
"name": "clang",
"displayName": "Clang 18.1.8 x86_64-pc-windows-msvc",
"description": "Using compilers: C = C:\\Users\\cookie\\scoop\\apps\\llvm\\current\\bin\\clang.exe, CXX = C:\\Users\\cookie\\scoop\\apps\\llvm\\current\\bin\\clang++.exe",
"binaryDir": "${sourceDir}/out/build/${presetName}",
"cacheVariables": {
"CMAKE_INSTALL_PREFIX": "${sourceDir}/out/install/${presetName}",
"CMAKE_C_COMPILER": "C:/Users/cookie/scoop/apps/llvm/current/bin/clang.exe",
"CMAKE_CXX_COMPILER": "C:/Users/cookie/scoop/apps/llvm/current/bin/clang++.exe",
"CMAKE_BUILD_TYPE": "Debug"
}
}
]
}
(这里贴出我的结果,指定为clang)
这时候,CMake其实不知道vcpkg在哪里,我们需要让它知道vcpkg在哪
我先贴出最后的结果
{
"version": 8,
"configurePresets": [
{
"name": "default",
"binaryDir": "${sourceDir}/out/build",
// 添加的环境变量
"environment": {
// 这里写上vcpkg的安装路径
"VCPKG_ROOT": "C:\\Users\\cookie\\scoop\\apps\\vcpkg\\current",
// 魔法上网的话
"HTTP_PROXY": "127.0.0.1:1000",
"HTTPS_PROXY": "127.0.0.1:1000"
},
"cacheVariables": {
// 让CMake可以找到vcpkg,一般这样写就可以
"CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake",
"CMAKE_INSTALL_PREFIX": "${sourceDir}/out/install/${presetName}",
// 因为windows下vcpkg自己会覆盖编译器设置为MSVC,所以也可以不指定
"CMAKE_BUILD_TYPE": "Debug"
}
}
]
}
已经通过注释指出需要添加的部分,写完就好了,顺便删点不需要的
这里相当于是一个虚拟环境,如果有其他要用到的环境变量的话也要写进environment里
<0x02> 实战测试
这里做一个简单的opengl开发演示
vcpkg中添加需要的包
打开终端,指定vcpkg需要glfw3和glad这两个包
vcpkg add port glfw3
vcpkg add port glad
CMake中引用并链接包
然后,打开CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(CmakeTest)
add_executable(CmakeTest main.cpp)
# 引用包
find_package(glad CONFIG REQUIRED)
find_package(glfw3 CONFIG REQUIRED)
# 链接包
target_link_libraries(CmakeTest PRIVATE glfw)
target_link_libraries(CmakeTest PRIVATE glad::glad)
在代码中使用
最后,打开main.cpp
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow *window);
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;
int main()
{
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
while (!glfwWindowShouldClose(window))
{
processInput(window);
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwTerminate();
return 0;
}
void processInput(GLFWwindow *window)
{
if(glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
}
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
glViewport(0, 0, width, height);
}
(这段代码是Learn OpenGL的代码,直接复制就能跑,所以就删注释了)
编译成功并运行的话应该是这样的
一切正常的话,恭喜完成配置,之后这样配置也能用
<0x03> 进阶设置
如果使用的包有可选特性
在vcpkg中,有一些包是有可选特性的
比如说imgui这个库,有很多可选特性,比如docking,opengl3,win32等等
比方说做opengl的开发,添加包的时候可以这么写
vcpkg add port imgui[opengl3-binding,glfw-binding,docking-experimental]
然后修改CMakeLists.txt部分,加上下面的东西
find_package(imgui CONFIG REQUIRED)
# example改成自己的项目
target_link_libraries(example PRIVATE imgui::imgui)
然后打开main.cpp测试下
#include "imgui.h"
#include "imgui_impl_glfw.h"
#include "imgui_impl_opengl3.h"
#include <fmt/core.h>
#include <glad/glad.h>
#include <GLFW/glfw3.h>
// glfw错误回调
static void glfw_error_callback(int error, const char *description)
{
fmt::println("GLFW Error {}: {}", error, description);
}
int main(int, char **)
{
glfwSetErrorCallback(glfw_error_callback);
if (!glfwInit())
{
return -1;
}
// 设定opengl版本
const char *glsl_version = "#version 130";
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);
// 创建窗口
GLFWwindow *window = glfwCreateWindow(1280, 720, "ImGui GLFW+OpenGL3 with docking example", nullptr, nullptr);
if (window == nullptr)
{
return -1;
}
glfwMakeContextCurrent(window);
glfwSwapInterval(1);
// 初始化glad
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
fmt::println("Failed to initialize GLAD");
return -1;
}
// 初始化imgui
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO &io = ImGui::GetIO();
(void)io;
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;
// 启用docking
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable;
ImGui::StyleColorsDark();
// 启用docking的后处理
ImGuiStyle& style = ImGui::GetStyle();
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
{
style.WindowRounding = 0.0f;
style.Colors[ImGuiCol_WindowBg].w = 1.0f;
}
ImGui_ImplGlfw_InitForOpenGL(window, true);
ImGui_ImplOpenGL3_Init(glsl_version);
// 渲染循环
while (!glfwWindowShouldClose(window))
{
// 初始化渲染
glfwPollEvents();
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame();
// 渲染demo窗口
ImGui::ShowDemoWindow();
// 真正开始渲染
ImGui::Render();
int display_w, display_h;
glfwGetFramebufferSize(window, &display_w, &display_h);
glViewport(0, 0, display_w, display_h);
glClearColor(.1f, .1f, .1f, 1);
glClear(GL_COLOR_BUFFER_BIT);
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
// docking处理
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
{
GLFWwindow* backup_current_context = glfwGetCurrentContext();
ImGui::UpdatePlatformWindows();
ImGui::RenderPlatformWindowsDefault();
glfwMakeContextCurrent(backup_current_context);
}
glfwSwapBuffers(window);
}
// 退出的处理
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplGlfw_Shutdown();
ImGui::DestroyContext();
glfwDestroyWindow(window);
glfwTerminate();
return 0;
}
(稍微修改了imgui给的示例,使用了glad和fmt)

对于多人开发的情况
前面提到,我们可以用CMakePresets.json来统一设定CMake的变量
但对于多人开发的情况,这个方式有一些弊端
比如,windows下,不同人编译器的位置就可能不一样
这时候,可以引入CMakeUserPresets.json文件做具体的设定
比方说接着上面的配置,我们可以把CMakePresets.json改造成这样
{
"version": 8,
"configurePresets": [
{
"name": "default",
"binaryDir": "${sourceDir}/out/build",
"cacheVariables": {
"CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake",
"CMAKE_INSTALL_PREFIX": "${sourceDir}/out/install/${presetName}",
"CMAKE_BUILD_TYPE": "Debug"
}
}
]
}
加上CMakeUserPresets.json,内容为
{
"version": 8,
"configurePresets": [
{
"name": "My default",
"inherits": "default",
"environment": {
"SCOOP_ROOT": "C:/Users/cookie/scoop",
"VCPKG_ROOT": "$env{SCOOP_ROOT}/apps/vcpkg/current",
"HTTP_PROXY": "127.0.0.1:1000",
"HTTPS_PROXY": "127.0.0.1:1000"
}
}
]
}
CMakePresets.json定义总体的框架,CMakeUserPresets.json定义具体的位置
在git设置中,排除CMakeUserPresets.json就可以了