repo: GitHub - nodejs/node: Node.js JavaScript runtime
vcbuild.bat 是一個 Windows 平臺下的批處理腳本,用于構建 Node.js 源代碼。它使用 Visual Studio 的命令行工具 msbuild 來編譯和鏈接 Node.js 的 C++ 模塊瓷胧,生成可執(zhí)行文件和庫文件等葡公。
在 Windows 環(huán)境下俊马,如果要自己編譯 Node.js 的源代碼呆瞻,需要先安裝 Visual Studio峭范,并設置好環(huán)境變量萝嘁。然后梆掸,可以運行 vcbuild.bat 腳本來編譯源代碼,生成 Node.js 可執(zhí)行文件和庫文件等牙言。這個腳本會自動檢測操作系統(tǒng)版本和 Visual Studio 的版本沥潭,并選擇合適的編譯選項。
1.試運行
為了追蹤 vcbuild.bat 的執(zhí)行流程嬉挡,而不實際進行編譯(實際編譯太費時間)钝鸽,我們需要對腳本做一個修改
首先找到下面這行,在它前面加上 echo庞钢,這樣就不會進行實際的編譯了
msbuild node.sln xxx -> echo msbuild node.sln xxx
執(zhí)行 vcbuild.bat拔恰,觀察輸出結果
.\vcbuild.bat
Looking for Python
Python found in D:\dev\lang\python\Python310\\python.exe
Python 3.10.2
Looking for NASM
Looking for Visual Studio 2022
calling: "D:\dev\IDE\VisualStudio\2022\Community\VC\\Auxiliary\Build\vcvarsall.bat" amd64
**********************************************************************
** Visual Studio 2022 Developer Command Prompt v17.5.4
** Copyright (c) 2022 Microsoft Corporation
**********************************************************************
[vcvarsall.bat] Environment initialized for: 'x64'
Found MSVS version 17.0
configure --dest-cpu=x64
Node.js configure: Found Python 3.10.2...
Warning: Missing input files:
D:\workspace\github\node\tools\v8_gypfiles\..\..\deps\v8\tools\testrunner\utils\dump_build_config_gyp.py
INFO: configure completed successfully
Project files generated.
msbuild node.sln /m:2 /t:node /p:Configuration=Release /p:Platform=x64 /clp:NoItemAndPropertyList;Verbosity=minimal /nologo
為 Release <<===>> out\Release 創(chuàng)建的聯(lián)接
可以看到,vcbuild.bat 的執(zhí)行過程大概分為下面幾步:
- 查找 python基括,找到3.10.2版本
- 查找 NASM颜懊,用于匯編編譯
- 查找 Visual Studio 2022,使用 msbuild 編譯c++
- 調用 Visual Studio 的命令提示符腳本,它初始化了x64環(huán)境變量
- 執(zhí)行 python configure 腳本河爹,傳入各種 flags
- 最后對 node.sln 執(zhí)行 msbuild
其實腳本還有個初始化過程匠璧,不過沒有任何輸出。下面我們來弄清楚每一步都發(fā)生了什么
2.初始化
2.1 help命令
檢測第一個參數(shù)咸这,如果是 help夷恍,跳轉到幫助內容
if /i "%1"=="help" goto help
if /i "%1"=="--help" goto help
if /i "%1"=="-help" goto help
if /i "%1"=="/help" goto help
if /i "%1"=="?" goto help
if /i "%1"=="-?" goto help
if /i "%1"=="--?" goto help
if /i "%1"=="/?" goto help
:help
echo vcbuild.bat [debug/release] [msi] [doc] [test/test-all/test-addons/test-doc/test-js-native-api/test-node-api/test-internet/test-tick-processor/test-known-issues/test-node-inspect/test-check-deopts/test-npm/test-v8/test-v8-intl/test-v8-benchmarks/test-v8-all] [ignore-flaky] [static/dll] [noprojgen] [projgen] [small-icu/full-icu/without-intl] [nobuild] [nosnapshot] [nonpm] [nocorepack] [ltcg] [licensetf] [sign] [ia32/x86/x64/arm64] [vs2019/vs2022] [download-all] [enable-vtune] [lint/lint-ci/lint-js/lint-md] [lint-md-build] [package] [build-release] [upload] [no-NODE-OPTIONS] [link-module path-to-module] [debug-http2] [debug-nghttp2] [clean] [cctest] [no-cctest] [openssl-no-asm]
echo Examples:
echo vcbuild.bat : builds release build
echo vcbuild.bat debug : builds debug build
echo vcbuild.bat release msi : builds release build and MSI installer package
echo vcbuild.bat test : builds debug build and runs tests
echo vcbuild.bat build-release : builds the release distribution as used by nodejs.org
echo vcbuild.bat enable-vtune : builds Node.js with Intel VTune profiling support to profile JavaScript
echo vcbuild.bat link-module my_module.js : bundles my_module as built-in module
echo vcbuild.bat lint : runs the C++, documentation and JavaScript linter
echo vcbuild.bat no-cctest : skip building cctest.exe
goto exit
2.2 測試參數(shù)
cd到當前文件目錄,設置傳遞給 tools[test.py](http://test.py/ "test.py") 的測試參數(shù)
cd %~dp0
set JS_SUITES=default
set NATIVE_SUITES=addons js-native-api node-api
@rem CI_* variables should be kept synchronized with the ones in Makefile
set "CI_NATIVE_SUITES=%NATIVE_SUITES% benchmark"
set "CI_JS_SUITES=%JS_SUITES% pummel"
set CI_DOC=doctool
@rem Same as the test-ci target in Makefile
set "common_test_suites=%JS_SUITES% %NATIVE_SUITES%&set build_addons=1&set build_js_native_api_tests=1&set build_node_api_tests=1"
2.3 遞歸處理參數(shù)
遞歸處理腳本參數(shù)媳维,設置各種flags
:next-arg
if "%1"=="" goto args-done
if /i "%1"=="debug" set config=Debug&goto arg-ok
if /i "%1"=="release" set config=Release&set ltcg=1&set cctest=1&goto arg-ok
if /i "%1"=="clean" set target=Clean&goto arg-ok
if /i "%1"=="testclean" set target=TestClean&goto arg-ok
if /i "%1"=="ia32" set target_arch=x86&goto arg-ok
if /i "%1"=="x86" set target_arch=x86&goto arg-ok
if /i "%1"=="x64" set target_arch=x64&goto arg-ok
if /i "%1"=="arm64" set target_arch=arm64&goto arg-ok
if /i "%1"=="vs2019" set target_env=vs2019&goto arg-ok
if /i "%1"=="vs2022" set target_env=vs2022&goto arg-ok
if /i "%1"=="noprojgen" set noprojgen=1&goto arg-ok
if /i "%1"=="projgen" set projgen=1&goto arg-ok
if /i "%1"=="nobuild" set nobuild=1&goto arg-ok
if /i "%1"=="nosign" set "sign="echo Note: vcbuild no longer signs by default. "nosign" is redundant.&goto arg-ok
if /i "%1"=="sign" set sign=1&goto arg-ok
if /i "%1"=="nosnapshot" set nosnapshot=1&goto arg-ok
if /i "%1"=="nonpm" set nonpm=1&goto arg-ok
if /i "%1"=="nocorepack" set nocorepack=1&goto arg-ok
if /i "%1"=="ltcg" set ltcg=1&goto arg-ok
if /i "%1"=="licensertf" set licensertf=1&goto arg-ok
if /i "%1"=="test" set test_args=%test_args% %common_test_suites%&set lint_cpp=1&set lint_js=1&set lint_md=1&goto arg-ok
if /i "%1"=="test-ci-native" set test_args=%test_args% %test_ci_args% -p tap --logfile test.tap %CI_NATIVE_SUITES% %CI_DOC%&set build_addons=1&set build_js_native_api_tests=1&set build_node_api_tests=1&set cctest_args=%cctest_args% --gtest_output=xml:cctest.junit.xml&goto arg-ok
if /i "%1"=="test-ci-js" set test_args=%test_args% %test_ci_args% -p tap --logfile test.tap %CI_JS_SUITES%&set no_cctest=1&goto arg-ok
if /i "%1"=="build-addons" set build_addons=1&goto arg-ok
if /i "%1"=="build-js-native-api-tests" set build_js_native_api_tests=1&goto arg-ok
if /i "%1"=="build-node-api-tests" set build_node_api_tests=1&goto arg-ok
if /i "%1"=="test-addons" set test_args=%test_args% addons&set build_addons=1&goto arg-ok
if /i "%1"=="test-doc" set test_args=%test_args% %CI_DOC%&set doc=1&&set lint_js=1&set lint_md=1&goto arg-ok
if /i "%1"=="test-js-native-api" set test_args=%test_args% js-native-api&set build_js_native_api_tests=1&goto arg-ok
if /i "%1"=="test-node-api" set test_args=%test_args% node-api&set build_node_api_tests=1&goto arg-ok
if /i "%1"=="test-tick-processor" set test_args=%test_args% tick-processor&goto arg-ok
if /i "%1"=="test-internet" set test_args=%test_args% internet&goto arg-ok
if /i "%1"=="test-known-issues" set test_args=%test_args% known_issues&goto arg-ok
if /i "%1"=="test-all" set test_args=%test_args% gc internet pummel %common_test_suites%&set lint_cpp=1&set lint_js=1&goto arg-ok
if /i "%1"=="test-node-inspect" set test_node_inspect=1&goto arg-ok
if /i "%1"=="test-check-deopts" set test_check_deopts=1&goto arg-ok
if /i "%1"=="test-npm" set test_npm=1&goto arg-ok
if /i "%1"=="test-v8" set test_v8=1&set custom_v8_test=1&goto arg-ok
if /i "%1"=="test-v8-intl" set test_v8_intl=1&set custom_v8_test=1&goto arg-ok
if /i "%1"=="test-v8-benchmarks" set test_v8_benchmarks=1&set custom_v8_test=1&goto arg-ok
if /i "%1"=="test-v8-all" set test_v8=1&set test_v8_intl=1&set test_v8_benchmarks=1&set custom_v8_test=1&goto arg-ok
if /i "%1"=="lint-cpp" set lint_cpp=1&goto arg-ok
if /i "%1"=="lint-js" set lint_js=1&goto arg-ok
if /i "%1"=="jslint" set lint_js=1& Please use lint-js instead of jslint&goto arg-ok
if /i "%1"=="lint-md" set lint_md=1&goto arg-ok
if /i "%1"=="lint-md-build" set lint_md_build=1&goto arg-ok
if /i "%1"=="lint" set lint_cpp=1&set lint_js=1&set lint_md=1&goto arg-ok
if /i "%1"=="lint-ci" set lint_cpp=1&set lint_js_ci=1&goto arg-ok
if /i "%1"=="package" set package=1&goto arg-ok
if /i "%1"=="msi" set msi=1&set licensertf=1&set download_arg="--download=all"&set i18n_arg=full-icu&goto arg-ok
if /i "%1"=="build-release" set build_release=1&set sign=1&goto arg-ok
if /i "%1"=="upload" set upload=1&goto arg-ok
if /i "%1"=="small-icu" set i18n_arg=%1&goto arg-ok
if /i "%1"=="full-icu" set i18n_arg=%1&goto arg-ok
if /i "%1"=="intl-none" set i18n_arg=none&goto arg-ok
if /i "%1"=="without-intl" set i18n_arg=none&goto arg-ok
if /i "%1"=="download-all" set download_arg="--download=all"&goto arg-ok
if /i "%1"=="ignore-flaky" set test_args=%test_args% --flaky-tests=dontcare&goto arg-ok
if /i "%1"=="dll" set dll=1&goto arg-ok
if /i "%1"=="enable-vtune" set enable_vtune_arg=1&goto arg-ok
if /i "%1"=="static" set enable_static=1&goto arg-ok
if /i "%1"=="no-NODE-OPTIONS" set no_NODE_OPTIONS=1&goto arg-ok
if /i "%1"=="debug-nghttp2" set debug_nghttp2=1&goto arg-ok
if /i "%1"=="link-module" set "link_module= --link-module=%2%link_module%"&goto arg-ok-2
if /i "%1"=="no-cctest" set no_cctest=1&goto arg-ok
if /i "%1"=="cctest" set cctest=1&goto arg-ok
if /i "%1"=="openssl-no-asm" set openssl_no_asm=1&goto arg-ok
if /i "%1"=="no-shared-roheap" set no_shared_roheap=1&goto arg-ok
if /i "%1"=="doc" set doc=1&goto arg-ok
if /i "%1"=="binlog" set extra_msbuild_args=/binaryLogger:%config%\node.binlog&goto arg-ok
echo Error: invalid command line option `%1`.
exit /b 1
:arg-ok-2
shift
:arg-ok
shift
goto next-arg
:args-done
- :next-arg:處理下一個參數(shù)酿雪,即參數(shù)移位之后的 %1,如果不存在則跳轉到 args-done
- :arg-ok:當前參數(shù)%1對應的 flag 設置成功侄刽,使用 shift 進行參數(shù)移位指黎,將 %2 移動到 %1
- :arg-ok-2:它的不同在于,%1 和 %2 都被使用之后州丹,需要調用兩次 shift醋安,進行兩次參數(shù)移位
- :args-done:當 %1 為空時,參數(shù)處理完畢
- 如果 %1 不正確墓毒,拋出無效參數(shù)錯誤
2.4 參數(shù)后處理
執(zhí)行參數(shù)后處理茬故,設置 configure_flags
if defined build_release (
set config=Release
set package=1
set msi=1
set licensertf=1
set download_arg="--download=all"
set i18n_arg=full-icu
set projgen=1
set cctest=1
set ltcg=1
)
if defined msi set stage_package=1
if defined package set stage_package=1
:: assign path to node_exe
set "node_exe=%config%\node.exe"
set "node_gyp_exe="%node_exe%" deps\npm\node_modules\node-gyp\bin\node-gyp"
set "npm_exe="%~dp0%node_exe%" %~dp0deps\npm\bin\npm-cli.js"
if "%target_env%"=="vs2019" set "node_gyp_exe=%node_gyp_exe% --msvs_version=2019"
if "%target_env%"=="vs2022" set "node_gyp_exe=%node_gyp_exe% --msvs_version=2022"
:: skip building if the only argument received was lint
if "%*"=="lint" if exist "%node_exe%" goto lint-cpp
if "%config%"=="Debug" set configure_flags=%configure_flags% --debug
if defined nosnapshot set configure_flags=%configure_flags% --without-snapshot
if defined nonpm set configure_flags=%configure_flags% --without-npm
if defined nocorepack set configure_flags=%configure_flags% --without-corepack
if defined ltcg set configure_flags=%configure_flags% --with-ltcg
if defined release_urlbase set configure_flags=%configure_flags% --release-urlbase=%release_urlbase%
if defined download_arg set configure_flags=%configure_flags% %download_arg%
if defined enable_vtune_arg set configure_flags=%configure_flags% --enable-vtune-profiling
if defined dll set configure_flags=%configure_flags% --shared
if defined enable_static set configure_flags=%configure_flags% --enable-static
if defined no_NODE_OPTIONS set configure_flags=%configure_flags% --without-node-options
if defined link_module set configure_flags=%configure_flags% %link_module%
if defined i18n_arg set configure_flags=%configure_flags% --with-intl=%i18n_arg%
if defined config_flags set configure_flags=%configure_flags% %config_flags%
if defined target_arch set configure_flags=%configure_flags% --dest-cpu=%target_arch%
if defined debug_nghttp2 set configure_flags=%configure_flags% --debug-nghttp2
if defined openssl_no_asm set configure_flags=%configure_flags% --openssl-no-asm
if defined no_shared_roheap set configure_flags=%configure_flags% --disable-shared-readonly-heap
if defined DEBUG_HELPER set configure_flags=%configure_flags% --verbose
if "%target_arch%"=="x86" if "%PROCESSOR_ARCHITECTURE%"=="AMD64" set configure_flags=%configure_flags% --no-cross-compiling
if not exist "%~dp0deps\icu" goto no-depsicu
if "%target%"=="Clean" echo deleting %~dp0deps\icu
if "%target%"=="Clean" rmdir /S /Q %~dp0deps\icu
:no-depsicu
if "%target%"=="TestClean" (
echo deleting test/.tmp*
if exist "test\.tmp*" for /f %%i in ('dir /a:d /s /b test\.tmp*') do rmdir /S /Q "%%i"
goto exit
)
- build_release:構建發(fā)布版本
- msi:使用 msbuild 構建 .msi 安裝包
- package:將編譯結果打包為 zip 文件
- node_exe:設置編譯結果的 node / npm / node_gyp 的執(zhí)行路徑
- lint:執(zhí)行 js 和 cpp 代碼lint
- configure_flags:傳遞給 configure python 腳本的 flags
- deps\icu:如果 icu 庫已經(jīng)存在,則需要先刪除它蚁鳖,后面會下載 icu 庫
- TestClean:清理臨時測試文件
3.查找python
調用查找 python 的腳本磺芭,如果出錯則離開
call tools\msvs\find_python.cmd if errorlevel 1 goto :exit
下面查看 tools\msvs\find_python.cmd 的內容
設置腳本
@IF NOT DEFINED DEBUG_HELPER @ECHO OFF
echo Looking for Python
setlocal enabledelayedexpansion
- DEBUG_HELPER:如果沒有開啟 debug崖瞭,則不輸出執(zhí)行的命令
- enabledelayedexpansion:啟用延遲擴展迅诬,變量將在每次執(zhí)行該行時擴展兜叨,默認情況下擴展只發(fā)生一次
在 PATH 環(huán)境變量里查找 python
:: Use python.exe if in %PATH%
set need_path=0
for /f "delims=" %%a in ('where python.exe 2^> nul') do (
set p=%%~dpa
goto :found-python
)
- need_path:如果python不在 PATH 環(huán)境變量里理逊,則為1樊展,代表需要將其加入 PATH 環(huán)境變量
- for /f:for代表循環(huán)跑筝,/f 指對結果進行文件解析章蚣,將其分解為單獨的文本行并將每一行解析為零或多個標記
- "delims=":指定分隔符兜辞,此處分隔符為空
- %%a:變量名己英,代表每一行的文本
- ('where python.exe 2^> nul'):使用 where 命令查找 python 的位置间螟,2^> nul 指重定向錯誤輸出
- set p=%%~dpa:設置 p 為當前行中的 python 路徑
- :found-python:已經(jīng)找到 python,跳轉到指定 label
在注冊表里查找 python 安裝路徑
:: Query the 3 locations mentioned in PEP 514 for a Python InstallPath
set need_path=1
for %%k in ( "HKCU\Software", "HKLM\SOFTWARE", "HKLM\Software\Wow6432Node") do (
call :find-versions %%k
if not errorlevel 1 goto :found-python
)
goto :no-python
- need_path:python 不在 PATH 環(huán)境變量里损肛,如果找到了厢破,需要將其加入 PATH
- %%k:代表指定的注冊表路徑
- :find-versions:調用標簽,在指定的注冊表路徑中查找 python 版本
- :found-python:如果錯誤碼不是1治拿,則找到 python
:: Find Python installations in a registry location
:find-versions
for /f "delims=" %%a in ('reg query "%~1\Python\PythonCore" /f * /k 2^> nul ^| findstr /r ^^HK') do (
call :read-installpath %%a
if not errorlevel 1 exit /b 0
)
exit /b 1
- for /f "delims=" %%a in:遍歷命令執(zhí)行結果的每一行摩泪,每行內容保存在 %%a 中
- %~1\Python\PythonCore:使用第一個參數(shù)組成注冊表的鍵,也就是上面?zhèn)鬟f的注冊表路徑
- reg query:查詢注冊表的命令
- /f *:查找指定鍵下與通配符匹配的項
- /k:只查找鍵劫谅,不包括值數(shù)據(jù)
- 2^> nul:錯誤輸出重定向到空設備
- ^|:管道见坑,將前面命令的輸出作為后面命令的輸入
- findstr /r ^HK:在命令結果中查找包含指定字符串的行嚷掠,代表的轉義
- :read-installpath:將讀取到的完整注冊表路徑傳遞給標簽,用于獲取安裝路徑
- exit /b 0:如果錯誤碼不是1荞驴,則查找成功
- exit /b 1:如果查找失敗不皆,向上級返回錯誤碼 1
for /f "skip=2 tokens=1* delims=)" %%a in ('reg query "%1\InstallPath" /ve /t REG_SZ 2^> nul') do (
for /f "tokens=1*" %%c in ("%%b") do (
if not "%%c"=="REG_SZ" exit /b 1
set "p=%%d"
exit /b 0
)
)
exit /b 1
- "skip=2 tokens=1* delims=)":跳過命令結果前兩行,使用')'作為分隔符熊楼,只獲取第一部分
- /ve:對值為空的進行查詢霹娄,即查詢默認值
- /t REG_SZ:值類型為字符串
- %%b:在 %%a 后面的第二部分,')'是分隔符
- %%c:將 %%b 按空格分割后的字符串孙蒙,
- "%%c"=="REG_SZ":如果類型不是字符串项棠,則報錯
- %%d:在 %%c 后面的第二部分悲雳,空格是分隔符
已經(jīng)找到 python
:found-python
echo Python found in %p%\python.exe
call :check-python "%p%\python.exe"
if errorlevel 1 goto :no-python
endlocal ^
& set "pt=%p%" ^
& set "need_path_ext=%need_path%"
if %need_path_ext%==1 set "PATH=%pt%;%PATH%"
set "pt="
set "need_path_ext="
exit /b 0
- :check-python:檢查 python 版本
- pt=%p%:設置 python 的路徑
- need_path_ext=%need_path%:是否需要把 python 路徑加入 PATH 環(huán)境變量里
檢查 python 版本
:check-python
%1 -V
:: 9009 means error file not found
if %errorlevel% equ 9009 (
echo Not an executable Python program
exit /b 1
)
exit /b 0
4.查找NASM
nasm用于編譯 openssl 里的匯編語言挎峦,在arm64架構下需要
REM NASM is only needed on IA32 and x86_64.
if not defined openssl_no_asm if "%target_arch%" NEQ "arm64" call tools\msvs\find_nasm.cmd
if errorlevel 1 echo Could not find NASM, install it or build with openssl-no-asm. See BUILDING.md.
查找方式和 python 基本一樣
@IF NOT DEFINED DEBUG_HELPER @ECHO OFF
ECHO Looking for NASM
FOR /F "delims=" %%a IN ('where nasm 2^> NUL') DO (
EXIT /B 0
)
IF EXIST "%ProgramFiles%\NASM\nasm.exe" (
SET "Path=%Path%;%ProgramFiles%\NASM"
EXIT /B 0
)
IF EXIST "%ProgramFiles(x86)%\NASM\nasm.exe" (
SET "Path=%Path%;%ProgramFiles(x86)%\NASM"
EXIT /B 0
)
if EXIST "%LOCALAPPDATA%\bin\NASM\nasm.exe" (
SET "Path=%Path%;%LOCALAPPDATA%\bin\NASM"
EXIT /B 0
)
EXIT /B 1
5.查找node版本
調用獲取node版本的label
call :getnodeversion || exit /b 1
![nshu.io/upload_images/28865460-ccfaf87fedfc4a57.gif?imageMogr2/auto-orient/strip)
- || exit /b 1:如果標簽執(zhí)行成功,則后面的exit不會執(zhí)行合瓢,如果失敗則退出
調用獲取 node 版本的python腳本坦胶,輸出類似 21.0.0
:getnodeversion
set NODE_VERSION=
set TAG=
set FULLVERSION=
for /F "usebackq tokens=*" %%i in (`python "%~dp0tools\getnodeversion.py"`) do set NODE_VERSION=%%i
if not defined NODE_VERSION (
echo Cannot determine current version of Node.js
exit /b 1
)
- for /F:逐行讀取腳本輸出
- usebackq:開啟反引號包含的命令行模式
- tokens=*:讀取整行并存儲在 %%i 中
- 如果 NODE_VERSION 不存在則返回錯誤碼 1
下面看下 getnodeversion.py ,它主要從 node_version.h 源碼中讀取當前源碼的版本
from __future__ import print_function
import os
def get_major_minor_patch(text):
for line in text.splitlines():
if line.startswith('#define NODE_MAJOR_VERSION'):
major = line.split()[2]
elif line.startswith('#define NODE_MINOR_VERSION'):
minor = line.split()[2]
elif line.startswith('#define NODE_PATCH_VERSION'):
patch = line.split()[2]
return major, minor, patch
node_version_h = os.path.join(os.path.dirname(__file__),
'..',
'src',
'node_version.h')
with open(node_version_h) as in_file:
print('.'.join(get_major_minor_patch(in_file.read())))
然后需要決定打包名稱 TARGET_NAME晴楔,里面包含了 node 版本和系統(tǒng)架構
if not defined DISTTYPE set DISTTYPE=release
if "%DISTTYPE%"=="release" (
set FULLVERSION=%NODE_VERSION%
goto distexit
)
:distexit
if not defined DISTTYPEDIR set DISTTYPEDIR=%DISTTYPE%
set TARGET_NAME=node-v%FULLVERSION%-win-%target_arch%
goto :EOF
6.查找 visual studio 工具鏈
6.1 msvs架構
首先設置 msbuild 環(huán)境變量
@rem Set environment for msbuild
set msvs_host_arch=x86
if _%PROCESSOR_ARCHITECTURE%_==_AMD64_ set msvs_host_arch=amd64
if _%PROCESSOR_ARCHITEW6432%_==_AMD64_ set msvs_host_arch=amd64
if _%PROCESSOR_ARCHITECTURE%_==_ARM64_ set msvs_host_arch=arm64
@rem usually vcvarsall takes an argument: host + '_' + target
set vcvarsall_arg=%msvs_host_arch%_%target_arch%
@rem unless both the host and the target are the same
if %target_arch%==x64 if %msvs_host_arch%==amd64 set vcvarsall_arg=amd64
if %target_arch%==%msvs_host_arch% set vcvarsall_arg=%target_arch%
- msvs_host_arch:msvs宿主架構顿苇,x86 或者 amd64
- vcvarsall_arg:傳遞給 vcvarsall.bat 腳本的架構參數(shù),在我的64位系統(tǒng)上是 amd64
6.2 查找 vs2022
首先查找 visual studio 2022
@rem Look for Visual Studio 2022
:vs-set-2022
if defined target_env if "%target_env%" NEQ "vs2022"
goto vs-set-2019
echo Looking for Visual Studio 2022
- target_env:可以設置為 vs2022 或 vs2019
@rem VCINSTALLDIR may be set if run from a VS Command Prompt and needs to
@rem cleared first as vswhere_usability_wrapper.cmd doesn't when it fails to
@rem detect the version searched for
if not defined target_env set "VCINSTALLDIR="
call tools\msvs\vswhere_usability_wrapper.cmd "[17.0,18.0)" %target_arch% "prerelease"
if "_%VCINSTALLDIR%_" == "__" goto vs-set-2019
- VCINSTALLDIR:visual studio安裝目錄
- vswhere_usability_wrapper.cmd:查找 vs installer 目錄税弃,目的是使用 vswhere 程序
下面查看 vswhere_usability_wrapper.cmd 腳本
@if not defined DEBUG_HELPER @ECHO OFF
setlocal
if "%~3"=="prerelease" set VSWHERE_WITH_PRERELEASE=1
set "InstallerPath=%ProgramFiles(x86)%\Microsoft Visual Studio\Installer"
if not exist "%InstallerPath%" set "InstallerPath=%ProgramFiles%\Microsoft Visual Studio\Installer"
if not exist "%InstallerPath%" goto :no-vswhere
:: Manipulate %Path% for easier " handeling
set "Path=%Path%;%InstallerPath%"
where vswhere 2> nul > nul
if errorlevel 1 goto :no-vswhere
if "%2"=="arm64" (
set VSWHERE_REQ=-requires Microsoft.VisualStudio.Component.VC.Tools.ARM64
) else (
set VSWHERE_REQ=-requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64
)
set VSWHERE_PRP=-property installationPath
set VSWHERE_LMT=-version %1
vswhere -prerelease > nul
if not errorlevel 1 if "%VSWHERE_WITH_PRERELEASE%"=="1" set "VSWHERE_LMT=%VSWHERE_LMT% -prerelease"
SET VSWHERE_ARGS=-latest -products * %VSWHERE_REQ% %VSWHERE_PRP% %VSWHERE_LMT%
for /f "usebackq tokens=*" %%i in (`vswhere %VSWHERE_ARGS%`) do (
endlocal
set "VCINSTALLDIR=%%i\VC\"
set "VS150COMNTOOLS=%%i\Common7\Tools\"
exit /B 0
)
:no-vswhere
endlocal
exit /B 1
- 首先在 %ProgramFiles(x86)% 中查找 visual studio installer 目錄
- 然后在 %ProgramFiles% 中查找
- 將 installer 目錄加入 PATH 環(huán)境變量
- where vswhere:在 PATH 環(huán)境變量中查找 vswhere 纪岁,如果沒找到返回錯誤碼1
- vswhere %VSWHERE_ARGS%:執(zhí)行 vswhere 查找,返回值只有一行vs的安裝目錄
- VCINSTALLDIR:設置 VC 安裝目錄
- VS150COMNTOOLS:設置工具目錄
@rem need to clear VSINSTALLDIR for vcvarsall to work as expected
set "VSINSTALLDIR="
@rem prevent VsDevCmd.bat from changing the current working directory
set "VSCMD_START_DIR=%CD%"
set vcvars_call="%VCINSTALLDIR%\Auxiliary\Build\vcvarsall.bat" %vcvarsall_arg%
echo calling: %vcvars_call%
call %vcvars_call%
if errorlevel 1 goto vs-set-2019
if defined DEBUG_HELPER @ECHO ON
- VSINSTALLDIR:清理它的值则果,讓叫不能按照預期工作
- VSCMD_START_DIR:防止更改當前工作目錄
- vcvarsall.bat:配置 Visual C++ 環(huán)境的批處理腳本幔翰,初始化了 x64 架構下的環(huán)境變量
6.3 查找 vs2019
基本過程和查找 vs2022 相同
7.執(zhí)行 configure
7.1 處理configure參數(shù)
:msbuild-found
set project_generated=
:project-gen
@rem Skip project generation if requested.
if defined noprojgen goto msbuild
if defined projgen goto run-configure
if not exist node.sln goto run-configure
if not exist .gyp_configure_stamp goto run-configure
echo %configure_flags% > .tmp_gyp_configure_stamp
where /R . /T *.gyp* >> .tmp_gyp_configure_stamp
fc .gyp_configure_stamp .tmp_gyp_configure_stamp >NUL 2>&1
if errorlevel 1 goto run-configure
- noprojgen:跳過項目生成,直接構建
- projgen:項目生成西壮,run configure
- .tmp_gyp_configure_stamp:將 configure_flags 和 .gyp 文件的路徑寫入臨時文件中
- .gyp_configure_stamp:如果與臨時文件不一致遗增,則 run configure
7.2 運行 configure
:run-configure
del .tmp_gyp_configure_stamp 2> NUL
del .gyp_configure_stamp 2> NUL
@rem Generate the VS project.
echo configure %configure_flags%
echo %configure_flags%> .used_configure_flags
python configure %configure_flags%
if errorlevel 1 goto create-msvs-files-failed
if not exist node.sln goto create-msvs-files-failed
set project_generated=1
echo Project files generated.
echo %configure_flags% > .gyp_configure_stamp
where /R . /T *.gyp* >> .gyp_configure_stamp
- .tmp_gyp_configure_stamp:刪除臨時文件
- configure:運行 configure python 腳本
- create-msvs-files-failed:項目生成失敗
- project_generated:項目生成成功
8.msbuild 構建
8.1 執(zhí)行構建
:msbuild
@rem Skip build if requested.
if defined nobuild goto :after-build
@rem Build the sln with msbuild.
set "msbcpu=/m:2"
if "%NUMBER_OF_PROCESSORS%"=="1" set "msbcpu=/m:1"
set "msbplatform=Win32"
if "%target_arch%"=="x64" set "msbplatform=x64"
if "%target_arch%"=="arm64" set "msbplatform=ARM64"
if "%target%"=="Build" (
if defined no_cctest set target=node
if "%test_args%"=="" set target=node
if defined cctest set target="Build"
)
if "%target%"=="node" if exist "%config%\cctest.exe" del "%config%\cctest.exe"
if defined msbuild_args set "extra_msbuild_args=%extra_msbuild_args% %msbuild_args%"
@rem Setup env variables to use multiprocessor build
set UseMultiToolTask=True
set EnforceProcessCountAcrossBuilds=True
set MultiProcMaxCount=%NUMBER_OF_PROCESSORS%
msbuild node.sln %msbcpu% /t:%target% /p:Configuration=%config% /p:Platform=%msbplatform% /clp:NoItemAndPropertyList;Verbosity=minimal /nologo %extra_msbuild_args%
if errorlevel 1 (
if not defined project_generated echo Building Node with reused solution failed. To regenerate project files use "vcbuild projgen"
exit /B 1
)
if "%target%" == "Clean" goto exit
- nobuild:如果不需要構建,直接跳過
- msbcpu:設置msbuild 構建使用的 cpu 數(shù)量
- msbplatform:msbuild 構建的目標平臺
- msbuild node.sln:對解決方案執(zhí)行構建
8.2 構建后處理
:after-build
rd %config%
if errorlevel 1 echo "Old build output exists at 'out\%config%'. Please remove." & exit /B
:: Use /J because /D (symlink) requires special permissions.
if EXIST out\%config% mklink /J %config% out\%config%
if errorlevel 1 echo "Could not create junction to 'out\%config%'." & exit /B
- rd %config%:刪除配置目錄
- mklink /J:創(chuàng)建符號鏈接款青, out%config% 到 %config%
9.簽名
簽名是對可執(zhí)行文件進行數(shù)字簽名做修,以證明文件的來源和完整性,確保文件未被篡改抡草,從而提高文件的安全性饰及。
:sign
@rem Skip signing unless the `sign` option was specified.
if not defined sign goto licensertf
call tools\sign.bat Release\node.exe
if errorlevel 1 echo Failed to sign exe, got error code %errorlevel%&goto exit
下面查看 tools/sign.bat 腳本
@echo off
set timeservers=(http://timestamp.globalsign.com/scripts/timestamp.dll http://timestamp.comodoca.com/authenticode http://timestamp.verisign.com/scripts/timestamp.dll http://tsa.starfieldtech.com)
for %%s in %timeservers% do (
signtool sign /a /d "Node.js" /du "https://nodejs.org" /fd SHA256 /t %%s %1
if not ERRORLEVEL 1 (
echo Successfully signed %1 using timeserver %%s
exit /b 0
)
echo Signing %1 failed using %%s
)
echo Could not sign %1 using any available timeserver
exit /b 1
- timeservers:多個時間戳服務器的地址
- signtool sign:使用簽名工具對輸入文件進行數(shù)字簽名
10.小結
大致的構建過程就是這樣,vcbuild.bat 里還包含了其他的功能康震,可以被各種參數(shù)打開
后面有機會再進行梳理