nodejs源码解析之一 (windows 编译脚本 vcbuild.bat)
vcbuild.bat 是一个 Windows 平台下的批处理脚本,用于构建 Node.js 源代码。它使用 Visual Studio 的命令行工具 msbuild 来编译和链接 Node.js 的 C++ 模块,生成可执行文件和库文件等。
在 Windows 环境下,如果要自己编译 Node.js 的源代码,需要先安装 Visual Studio,并设置好环境变量。然后,可以运行 vcbuild.bat 脚本来编译源代码,生成 Node.js 可执行文件和库文件等。这个脚本会自动检测操作系统版本和 Visual Studio 的版本,并选择合适的编译选项。
1.试运行
为了追踪 vcbuild.bat 的执行流程,而不实际进行编译(实际编译太费时间),我们需要对脚本做一个修改
首先找到下面这行,在它前面加上 echo,这样就不会进行实际的编译了
执行 vcbuild.bat,观察输出结果
可以看到,vcbuild.bat 的执行过程大概分为下面几步:
查找 python,找到3.10.2版本
查找 NASM,用于汇编编译
查找 Visual Studio 2022,使用 msbuild 编译c++
调用 Visual Studio 的命令提示符脚本,它初始化了x64环境变量
执行 python configure 脚本,传入各种 flags
最后对 node.sln 执行 msbuild
其实脚本还有个初始化过程,不过没有任何输出。下面我们来弄清楚每一步都发生了什么
2.初始化
2.1 help命令
检测第一个参数,如果是 help,跳转到帮助内容
2.2 测试参数
cd到当前文件目录,设置传递给 tools\
的测试参数
2.3 递归处理参数
递归处理脚本参数,设置各种flags
:next-arg:处理下一个参数,即参数移位之后的 %1,如果不存在则跳转到 args-done
:arg-ok:当前参数%1对应的 flag 设置成功,使用 shift 进行参数移位,将 %2 移动到 %1
:arg-ok-2:它的不同在于,%1 和 %2 都被使用之后,需要调用两次 shift,进行两次参数移位
:args-done:当 %1 为空时,参数处理完毕
如果 %1 不正确,抛出无效参数错误
2.4 参数后处理
执行参数后处理,设置 configure_flags
build_release:构建发布版本
msi:使用 msbuild 构建 .msi 安装包
package:将编译结果打包为 zip 文件
node_exe:设置编译结果的 node / npm / node_gyp 的执行路径
lint:执行 js 和 cpp 代码lint
configure_flags:传递给 configure python 脚本的 flags
deps\icu:如果 icu 库已经存在,则需要先删除它,后面会下载 icu 库
TestClean:清理临时测试文件
3.查找python
调用查找 python 的脚本,如果出错则离开
下面查看 tools\msvs\find_python.cmd 的内容
设置脚本
DEBUG_HELPER:如果没有开启 debug,则不输出执行的命令
enabledelayedexpansion:启用延迟扩展,变量将在每次执行该行时扩展,默认情况下扩展只发生一次
在 PATH 环境变量里查找 python
need_path:如果python不在 PATH 环境变量里,则为1,代表需要将其加入 PATH 环境变量
for /f:for代表循环,/f 指对结果进行文件解析,将其分解为单独的文本行并将每一行解析为零或多个标记
"delims=":指定分隔符,此处分隔符为空
%%a:变量名,代表每一行的文本
('where python.exe 2^> nul'):使用 where 命令查找 python 的位置,2^> nul 指重定向错误输出
set p=%%~dpa:设置 p 为当前行中的 python 路径
:found-python:已经找到 python,跳转到指定 label
在注册表里查找 python 安装路径
need_path:python 不在 PATH 环境变量里,如果找到了,需要将其加入 PATH
%%k:代表指定的注册表路径
:find-versions:调用标签,在指定的注册表路径中查找 python 版本
:found-python:如果错误码不是1,则找到 python
for /f "delims=" %%a in:遍历命令执行结果的每一行,每行内容保存在 %%a 中
%~1\Python\PythonCore:使用第一个参数组成注册表的键,也就是上面传递的注册表路径
reg query:查询注册表的命令
/f *: 查找指定键下与通配符匹配的项
/k:只查找键,不包括值数据
2^> nul:错误输出重定向到空设备
^|:管道,将前面命令的输出作为后面命令的输入
findstr /r ^^HK:在命令结果中查找包含指定字符串的行,^^代表^的转义
:read-installpath:将读取到的完整注册表路径传递给标签,用于获取安装路径
exit /b 0:如果错误码不是1,则查找成功
exit /b 1:如果查找失败,向上级返回错误码 1
"skip=2 tokens=1* delims=)":跳过命令结果前两行,使用')'作为分隔符,只获取第一部分
/ve:对值为空的进行查询,即查询默认值
/t REG_SZ:值类型为字符串
%%b:在 %%a 后面的第二部分,')'是分隔符
%%c:将 %%b 按空格分割后的字符串,
"%%c"=="REG_SZ":如果类型不是字符串,则报错
%%d:在 %%c 后面的第二部分,空格是分隔符
已经找到 python
:check-python:检查 python 版本
pt=%p%:设置 python 的路径
need_path_ext=%need_path%:是否需要把 python 路径加入 PATH 环境变量里
检查 python 版本
4.查找NASM
nasm用于编译 openssl 里的汇编语言,在arm64架构下需要
查找方式和 python 基本一样
5.查找node版本
调用获取node版本的label
|| exit /b 1:如果标签执行成功,则后面的exit不会执行,如果失败则退出
调用获取 node 版本的python脚本,输出类似 21.0.0
for /F:逐行读取脚本输出
usebackq:开启反引号包含的命令行模式
tokens=*:读取整行并存储在 %%i 中
如果 NODE_VERSION 不存在则返回错误码 1
下面看下
,它主要从 node_version.h 源码中读取当前源码的版本
然后需要决定打包名称 TARGET_NAME,里面包含了 node 版本和系统架构
6.查找 visual studio 工具链
6.1 msvs架构
首先设置 msbuild 环境变量
msvs_host_arch:msvs宿主架构,x86 或者 amd64
vcvarsall_arg:传递给 vcvarsall.bat 脚本的架构参数,在我的64位系统上是 amd64
6.2 查找 vs2022
首先查找 visual studio 2022
target_env:可以设置为 vs2022 或 vs2019
VCINSTALLDIR:visual studio安装目录
vswhere_usability_wrapper.cmd:查找 vs installer 目录,目的是使用 vswhere 程序
下面查看 vswhere_usability_wrapper.cmd 脚本
首先在 %ProgramFiles(x86)% 中查找 visual studio installer 目录
然后在 %ProgramFiles% 中查找
将 installer 目录加入 PATH 环境变量
where vswhere:在 PATH 环境变量中查找 vswhere ,如果没找到返回错误码1
vswhere %VSWHERE_ARGS%:执行 vswhere 查找,返回值只有一行vs的安装目录
VCINSTALLDIR:设置 VC 安装目录
VS150COMNTOOLS:设置工具目录
VSINSTALLDIR:清理它的值,让叫不能按照预期工作
VSCMD_START_DIR:防止更改当前工作目录
vcvarsall.bat:配置 Visual C++ 环境的批处理脚本,初始化了 x64 架构下的环境变量
6.3 查找 vs2019
基本过程和查找 vs2022 相同
7.执行 configure
7.1 处理configure参数
noprojgen:跳过项目生成,直接构建
projgen:项目生成,run configure
.tmp_gyp_configure_stamp:将 configure_flags 和 .gyp 文件的路径写入临时文件中
.gyp_configure_stamp:如果与临时文件不一致,则 run configure
7.2 运行 configure
.tmp_gyp_configure_stamp:删除临时文件
configure:运行 configure python 脚本
create-msvs-files-failed:项目生成失败
project_generated:项目生成成功
8.msbuild 构建
8.1 执行构建
nobuild:如果不需要构建,直接跳过
msbcpu:设置msbuild 构建使用的 cpu 数量
msbplatform:msbuild 构建的目标平台
msbuild node.sln:对解决方案执行构建
8.2 构建后处理
rd %config%:删除配置目录
mklink /J:创建符号链接, out\%config% 到 %config%
9.签名
签名是对可执行文件进行数字签名,以证明文件的来源和完整性,确保文件未被篡改,从而提高文件的安全性。
下面查看 tools/sign.bat 脚本
timeservers:多个时间戳服务器的地址
signtool sign:使用签名工具对输入文件进行数字签名
10.小结
大致的构建过程就是这样,vcbuild.bat 里还包含了其他的功能,可以被各种参数打开
后面有机会再进行梳理