搭建一个自己喜欢的开发环境,VScode+MinGW64+CMake+vcpkg

  • ~7.99K 字
  • 次阅读
  • 条评论
  1. 1. 配置 VScode
    1. 1.1. 认识 C/C++ 插件
  2. 2. 安装 MinGW64
  3. 3. 安装 CMake 插件
    1. 3.1. 新建工程
    2. 3.2. 对接 clang-tidy
  4. 4. 安装 vcpkg
    1. 4.1. 配置 triplets
    2. 4.2. 对接 cmake
  5. 5. 后记

咱知道配置一个喜欢的 C++ 开发环境一直都是一个难题,在 windows 下更是一言难尽,除非你愿意吃 Visual Studio 这一坨大史。

而我这种追求简约又爱折腾的人自然不愿意看 Visual Studio 不明不白地拉几十 G 的翔,而且这玩意启动就得半天。所以我一直以来都是用的 vscode+winlibs 这一套极简配置,够用,除了那个来自深渊的灵魂拷问:包管理呢?你怎么装第三方库啊?

C++ 一直以来都没有也不会有统一的包管理工具,linux 那边很多都是系统用什么包管理,C++ 就跟着用什么。那么微软有什么包管理工具呢?

答案自然就是

WinGet

开个玩笑,这个是装应用的,答案实际上是 vcpkg

vcpkg 基本是唯一解了(Conan: 我有意见),它的确也非常好用。虽然很多人见到它是在 Visual Studio,但它本身是跨平台的,支持 linux。

好了,不废话了,看看要怎么折腾吧。

配置 VScode

不会还有人没用过 vscode 吧?

这个,好用,没几个人有资格批评它。

安装过程千篇一律,我不再细讲。我谈谈 C++功能插件的问题,现在有两种选择:

  • 一是微软自己插件: C/C++ for Visual Studio Code
  • 二是 LLVM 的 clangd 插件

二者的主要区别在于代码提示,前者用的 IntelliSense,后者不知道如何称呼,但实际使用来看 IntelliSense 的提示更完整。
比如跳转定义,clangd 需要你把完整表达式写出来才能跳转,而前者只要你把你需要跳转的哪部分写了就能跳:

1
2
TypeA var = namespaceA::func(
// 半个括号构不成完整表达式,于是你没法跳转到namespaceA和func的定义处

而且二者的悬浮提示的样式也不同,也会导致一点习惯问题。还有 clangd 不支持多工作文件夹。

很多人说微软给的 C/C++ 插件性能很差,其实说的就是 IntelliSense 性能差,的确 IntelliSense 比 clangd 慢了很多,但还是在我的可接受范围内,再说了另一个也耗性能的代码静态分析,二者都是用的 clang-tidy,一慢大家还是慢。

认识 C/C++ 插件

前面说了 C/C++ 插件包含一个 IntelliSense 提供代码感知等功能,借助 clang-tidy 进行代码分析。实际上,它还自带一个代码分析,且是有 i18n 的,比如:

1
应输入“;” C/C++(65)

后面的 C/C++(65) 表明是 C/C++ 插件自己给出的提示。
而 clang-format 的提示如下:

1
expected ';' after expression C/C++(clang-diagnostic-error)

后面的 C/C++(clang-diagnostic-error) 表明是 clang-tidy 给出的提示。

行内提示的插件叫 Error Lens

前者只会给出一些简单的比如语法错误,未声明符号等错误,但比 clang-tidy 快的多。由于依靠的是 IntelliSense 流程得到的数据库,如果由于某些原因未得到更新,可能会包含一些多余的符号,即有个符号是声明了的但是是在别的文件内,这里并没有包含进来,而 IntelliSense 会认为你可以用这个符号了,于是没提示报错,此时你可以在右下角点一下 IntelliSense 的重新扫描。

update-intelliscense.png

此外 IntelliSense 和 clang-tidy 是相对独立的,所以如果有配置出现问题,导致 clang-tidy 出现找不到某个头文件等报错,而此时 IntelliSense 如果是正常的,那么你会发现你仍然可以通过 ctrl+m1 进行代码跳转,并且 clang-tidy 仍然报它的错。

安装 MinGW64

MinGW64 自身已经不分发新版的安装程序了,新版都是由下游提供,比如 msys2。
这里我选择winlibs,下载之前要先了解几点:

  1. 线程模型的选择:一般来讲 windows 下直接选择 win32 即可,当然选择(对非 windows 系统)适用性更广的 POSIX 也行,还有一个叫 MCF 的我也不知道是什么时候出现的,可以自行了解。
  2. C 语言运行时:如果你不需要让软件跑在旧版 windows 中,就选 UCRT,否则选 MSVCRT,UCRT 是 MSVCRT 的替代品,但是放弃了兼容性。
  3. 是否要带 LLVM/Clang/LLD/LLDB:我的建议是带,一是你多了个编译速度更快的 clang 编译器,二是 C/C++ 插件自带的 clang-tidy 似乎有毛病,找标准库时总是去找 msvc 的标准库,把 vs 卸了它竟然直接报找不到标准库,于是可以用 winlibs 内的 clang-tidy 来替代。

了解以上几点后,选一个自己需要的版本下载,然后解压,最后把里面的 bin、include、x86_64-w64-mingw32\include 三个添加到 path 系统变量中即可。

此时启动一个终端,执行 gcc -v 应该会输出版本信息等,否则就是 path 没配置好:

1
2
3
4
5
6
7
8
9
10
PS C:\WINDOWS\system32> gcc -v
Using built-in specs.
COLLECT_GCC=D:\softwares\env\mingw64\bin\gcc.exe
COLLECT_LTO_WRAPPER=D:/softwares/env/winlibs-x86_64-posix-seh-gcc-14.2.0-llvm-19.1.1-mingw-w64ucrt-12.0.0-r2/bin/../libexec/gcc/x86_64-w64-mingw32/14.2.0/lto-wrapper.exe
OFFLOAD_TARGET_NAMES=nvptx-none
Target: x86_64-w64-mingw32
Configured with: ../configure --prefix=/R/winlibs_staging_ucrt64/inst_gcc-14.2.0/share/gcc --build=x86_64-w64-mingw32 --host=x86_64-w64-mingw32 --enable-offload-targets=nvptx-none --with-pkgversion='MinGW-W64 x86_64-ucrt-posix-seh, built by Brecht Sanders, r2' --with-tune=generic --enable-checking=release --enable-threads=posix --disable-sjlj-exceptions --disable-libunwind-exceptions --disable-serial-configure --disable-bootstrap --enable-host-shared --enable-plugin --disable-default-ssp --disable-rpath --disable-libstdcxx-debug --disable-version-specific-runtime-libs --with-stabs --disable-symvers --enable-languages=c,c++,fortran,lto,objc,obj-c++ --disable-gold --disable-nls --disable-stage1-checking --disable-win32-registry --disable-multilib --enable-ld --enable-libquadmath --enable-libada --enable-libssp --enable-libstdcxx --enable-lto --enable-fully-dynamic-string --enable-libgomp --enable-graphite --enable-mingw-wildcard --enable-libstdcxx-time --enable-libstdcxx-pch --with-mpc=/c/Prog/winlibs_staging_ucrt/custombuilt64 --with-mpfr=/c/Prog/winlibs_staging_ucrt/custombuilt64 --with-gmp=/c/Prog/winlibs_staging_ucrt/custombuilt64 --with-isl=/c/Prog/winlibs_staging_ucrt/custombuilt64 --disable-libstdcxx-backtrace --enable-install-libiberty --enable-__cxa_atexit --without-included-gettext --with-diagnostics-color=auto --enable-clocale=generic --with-libiconv --with-system-zlib --with-build-sysroot=/R/winlibs_staging_ucrt64/gcc-14.2.0/build_mingw/mingw-w64 CFLAGS='-D__USE_MINGW_ANSI_STDIO=0 -I/c/Prog/winlibs_staging_ucrt/custombuilt64/include/libdl-win32 -march=nocona -msahf -mtune=generic -O2 -Wno-error=format' CXXFLAGS='-D__USE_MINGW_ANSI_STDIO=0 -Wno-int-conversion -march=nocona -msahf -mtune=generic -O2' LDFLAGS='-pthread -Wl,--no-insert-timestamp -Wl,--dynamicbase -Wl,--high-entropy-va -Wl,--nxcompat -Wl,--tsaware' LD=/c/Prog/winlibs_staging_ucrt/custombuilt64/share/binutils/bin/ld.exe
Thread model: posix
Supported LTO compression algorithms: zlib zstd
gcc version 14.2.0 (MinGW-W64 x86_64-ucrt-posix-seh, built by Brecht Sanders, r2)

安装 CMake 插件

winlibs 自带 CMake 本体,所以我们只需要安装插件即可。
安装一下插件:

新建工程

如果你更我一样,是多工作文件夹,选择 cmake 插件的图标,选择活动文件夹,或者打开文件夹内的一个 cpp 文件,cmake 也会自动选择活动文件夹

chose-cmake-icon-and-set-dir.png

然后那么在按下 F1,输入 cmake,选择快速入门,创建项目,这个过程有看不懂的选项可以直接跳过选默认值,因为都可以在 CMakeLists.txt 内修改

create-cmake-proj.png

这个时候就会出现一个 CMakeLists.txt 文件,有了这个就算是一个 CMake 工程了:

1
2
3
4
cmake_minimum_required(VERSION 3.5.0)
project(test VERSION 0.1.0 LANGUAGES C CXX)

add_executable(test main.cpp)

如果创建时文件夹是全空的,那么 CMake 应该会自动生成一个 main.cpp 文件:

1
2
3
4
5
#include <iostream>

int main(int, char**){
std::cout << "Hello, from test!\n";
}

此时你可以点右下角的生成试图编译项目,当然可能成功,也可能不成功,因为我们还没选编译器,回到 cmake 图标处,点击 __unspec__ 修改工具包,如果这里是空的,点击扫描工具包,应该会把 winlibs 内的编译器给扫出来,如果没扫出来,并且重启 vscode 也没扫出来,且确认 path 配置无误后,搜索 cmake.additionalKits 或点击这个链接,手动添加路径。

choose-cmake-toolkit.png

点击配置右边的图标,或在 CMakeLists.txt 内 ctrl+s 保存,会看到 cmake 生成配置:

1
2
...
[cmake] -- Build files have been written to: D:/blabla/test/build

配置缓存在 build 文件夹内,构建产物默认也在这里,如果切换了工具包,建议清除配置缓存,或者直接删除整个 build 文件夹在再生成新的配置。

关于 CMake 的更多功能请自行搜索。

对接 clang-tidy

现在让 clang-tidy 能通过 cmake 读取工程的诸如 include 列表等配置,同时这一步,我们将解决此前clang-tidy 各种莫名报错的问题。

如果要自定义 clang-tidy 程序路径,请搜索设置:C_Cpp.codeAnalysis.clangTidy.path

首先把 C_Cpp.codeAnalysis.clangTidy.useBuildPath 这个开启

然后点右下角 c/c++ 配置

CC++properties.png

找到高级设置中的编译命令,设置 build/compile_commands.json

setting_compile_commands.json.png

每当 CMake 完成配置后,默认会在 build 目标下生成一个 compile_commands.json 文件,clang-tidy 可以通过该文件分析工程中的代码。
如果该文件不存在,请检查设置 cmake.buildDirectory

这个时候之前那些恼人的 use of undeclared identifier '_Float32' 就消失了,当然 clang-tidy 实际上还是不支持这些类型,如果你写一句

1
std::float128_t a;

clang-tidy 还是会报错,但是你可以正常编译。

安装 vcpkg

安装过程非常简单,先找个地方 clone 下来:

1
git clone https://github.com/microsoft/vcpkg.git

然后运行 bootstrap-vcpkg.bat,然后就能看到一个 vcpkg.exe,这个就是程序本体了,同样添加到 path 中,同时创建名为VCPKG_ROOT的环境变量,值同样为 vcpkg 的所在文件夹路径,在终端中输入 vcpkg --version 验证,如果是 vscode 内的终端记得先重启 vscode。

安装完后,此时就能通过例如 vcpkg install fmt 安装库了…
吗?
还不行,vcpkg 在 windows 下默认选择 vs 作为工具链,这样安装的库由于 abi 不同无法被 GCC 编译的程序链接。所以我们需要额外配置

配置 triplets

triplets 又称三元组,是交叉编译的概念,打开 vcpkg 下的 triplets 文件夹,你可以看到 vcpkg 内置的 triplets,其中 community 文件夹内的由社区提供,而 vcpkg 在 windows 下默认选择的就是 x64-windows.cmake

我们可以这样指定 triplets:

1
vcpkg install fmt:x64-mingw-static

当然,每次都手动指定比较麻烦,如何指定默认 triplets 呢?
根据微软文档,vcpkg 先从 %VCPKG_DEFAULT_TRIPLET% 环境变量读取 triplets,再根据平台选择,所以我们只需要

set-default-triplets.png

就行力。

对接 cmake

假设我们用 vcpkg 安装了一个库,比如fmt

那么我们在 CMakeLists.txt 中引入库:

1
2
3
4
5
6
7
8
9
10
11
12
cmake_minimum_required(VERSION 3.5.0)
project(test VERSION 0.1.0 LANGUAGES C CXX)

# 查找名为fmt的库
find_package(fmt CONFIG REQUIRED)

add_executable(test main.cpp)

# 链接这个库
target_link_libraries(test PRIVATE fmt::fmt)
# 或者链接仅头文件版本,当你安装完一个库时,很多都会提示可以怎么链接
# target_link_libraries(test PRIVATE fmt::fmt-header-only)

此时你 ctrl+s 保存,生成配置,然后报错:

1
2
3
4
5
[cmake]   Could not find a package configuration file provided by "fmt" with any of
[cmake] the following names:
[cmake]
[cmake] fmtConfig.cmake
[cmake] fmt-config.cmake

其实问题很明显,cmake 怎么知道你 vcpkg 给我装了库?
所以,对于我们 cmake 插件,一个较好的配置方法是找到设置 cmake.configureArgs,添加这项 -DVCPKG_TARGET_TRIPLET=x64-mingw-statics
然后 cmake 配置时就会立刻明白:原来要找 vcpkg 啊,好好好,我要一个 fmt 库,要 x64-mingw-statics 的。

当然,如果你换了 triplets,记得改这个,此外也有别的配置方法,注意重复配置的覆盖关系。

现在修改main.cpp,试图编译吧:

1
2
3
4
5
6
7
#include <iostream>
#include <fmt/core.h>

int main() {
fmt::print("Hello World!\n");
}

后记

本文就结束了,其实我装 vcpkg 时碰到的最大问题就是总找不到库,网上搜几圈,各种都是讲设置 CMAKE_TOOLCHAIN_FILE 的,然而就是没一个提到 cmake 插件的 cmake.configureArgs 设置,我甚至还是写在工作区设置的,在用户设置里找半天没发现问题,全然没发现不知道谁在 VCPKG_TARGET_TRIPLET 里面写的 triplets 压根对不上,导致我在其他地方各种改配置结果屁用没有。

分享这一刻
让朋友们也来瞅瞅!