花活:在Windows+VSCode+CMake+Ninja环境下设定MSVC工具集版本

要素过多,完美死锁

这个花活儿是这样来的:目标平台限制,要用Windows+MSVC,然后希望用VS Code(方便,远程开发),有希望用Clangd诊断工具——于是就必须用ninja,因为MSVC工具链下是不能生成Clangd所需的compile_commands.json文件的。

要光是这样也就罢了,还有最后一项问题:由于MSVC 17.3的某个神秘bug,我的代码编译不过去(某些嵌套模板附近编译器崩溃,不是报错,正常报错就改了),三年没改,于是必须停留在17.2,于是就必须设定工具链版本。

如果是直接用CMake + Visual Studio,可以在执行cmake的configure时传递 -T host=x64,version=14.32.17.2这样的参数来指定工具链,但ninja则自成一套,你在cmake时可以传递上述-T参数,但是似乎只在configure时有效,下次cmake build时,ninja仍然是从环境变量里面自动检测VC工具链——默认的是最新的,所以只要机器上有17.3的编译器,ninja是不会去找17.2版本的。

cmake自带的-T参数失效之后,没有其他什么命令行参数可以直接指定MSVC工具链版本了。新版的Visual Studio中,手动使用时是通过先调用vcvarsall.bat再调用编辑器来指定MSVC版本的。在本地使用时,可以通过先调用vcvarsall.bat,再从同一个命令行启动vscode的方法变通实现,但如果是通过ssh远程开发,就连这个途径都没有了。

完美死锁.jpg

仔细分析的话,最稳定的途径仍然是通过vcvarsall.bat实现版本选择,但难就难在这玩意儿是个交互式脚本,它的生效不是通过额外的参数或者配置文件,而是依赖于它运行完了之后帮你处理过的那个cmd环境,这就让这玩意儿难以跟别的程序形成后台配合。

最后,我找到的解决方案是:自己写一个cmd脚本,假装自己是cmake.exe,然后在每次执行前给自己先加一个vcvarsall.bat的buff……

粗暴而有效.jpg

具体地:新建一个类似下面的bat文件:

msvc172-cmake-wrapper.bat
@echo off
set VSLANG=1033
set VCINSTALLDIR=C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\
call "%VCINSTALLDIR%\vcvarsall.bat" x64 10.0.22000.0 -vcvars_ver=14.32.17.2 > NUL
 
call C:\APP\cmake-3.25.2-windows-x86_64\bin\cmake.exe %*

第一行不解释,第二行是给ninja准备的——ninja通过解析并拦截命令行输出来工作的,而中文版的vc编译器输出的是中文信息,ninja不能识别也就不拦截,得用这个环境变量设定让vc编译器输出英文信息。

第3-4行就是给自己加vcvarsall.batbuff,参数很自由,注意 > NUL是必须要有的,因为vscode的CmakeTools插件用过解析CMake的命令行输出来工作,因此必须把额外的输出屏蔽掉,否则vscode的CmakeTools插件会报错。

也就是假装自己是cmake要装得足够像。

最后一行,把所有的命令转发给真正的cmake.exe

然后,在vscode的CmakeTools插件设置中,找到“Cmake Path”设置项,将之修改为这个msvc172-cmake-wrapper.bat文件的全路径。接下来其他的东西都跟正常用cmake+ninja一样了,-T参数也按原来的样子传递,确保cmake和ninja都满意。

我没试过故意传递不一样的会怎么样。

  • 最后更改: 2023/09/07 06:10