Ctrl K
ComunidadGuías…

makev.bat

Rama master · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
@setlocal EnableDelayedExpansion EnableExtensions

@IF NOT DEFINED VERBOSE_MAKE @echo off

REM Option flags
set /a shift_counter=0
set /a flag_local=0

REM Option variables
set compiler=
set subcmd=
set target=build

set V_EXE=./v.exe
set V_BOOTSTRAP=./v_win_bootstrap.exe
set V_OLD=./v_old.exe
set V_UPDATED=./v_up.exe
set V_STAGE=./v_stage.exe
set V_STAGE_C=./v_stage.c
REM Current vc snapshots declare these Windows helpers as external functions.
REM Older snapshots embed static definitions, which can coexist with these.
set V_BOOTSTRAP_NATIVE="%~dp0vlib\time\ticks_windows.c" "%~dp0vlib\v\winthread_helper_windows.c" "%~dp0cmd\v\toolcache_windows_helpers.c"
REM Native core helpers are cached objects in normal builds. The direct stage
REM compile must link them too; this stage explicitly uses -gc none.
set V_STAGE_NATIVE="%~dp0vlib\builtin\core_windows.c" "%~dp0vlib\v\modulecache\file_metadata.c" %V_BOOTSTRAP_NATIVE% "%~dp0vlib\sync\thread_helper.c"
set V_C_FILE=./vc/v_win.c
REM Snapshots generated before vlang/v#30606 declare the SDK-backed Windows
REM helpers (time ticks, Win32 threads, toolcache file/ACL probes) but do not
REM embed their bodies, so the bootstrap must link these sources explicitly.
REM Newer snapshots embed them with static linkage, so linking the external
REM definitions as well is harmless. The other V_STAGE_NATIVE helpers are not
REM referenced by existing vc snapshots and are left out of the bootstrap.
set V_BOOTSTRAP_NATIVE="%~dp0vlib\time\ticks_windows.c" "%~dp0vlib\v\winthread_helper_windows.c" "%~dp0cmd\v\toolcache_windows_helpers.c"
REM Portable vc snapshots need the full V1 compiler until the new driver is built.
REM Keep this aligned with VC_BOOTSTRAP_DEFINE in the Unix makefiles.
set VC_BOOTSTRAP_DEFINE=-DCUSTOM_DEFINE_v1_fallback
REM Existing vc bootstraps may predate the TCC Win64 CRT prelude fix, so keep
REM their cgen single-threaded while they build a fresh compiler from sources.
REM TODO: remove this after vc/v_win.c is regenerated with the fixed CRT prelude.
set V_BOOTSTRAP_VFLAGS=-no-parallel
set where_exe=where.exe
if not ["%SystemRoot%"] == [""] if exist "%SystemRoot%\System32\where.exe" set where_exe=%SystemRoot%\System32\where.exe

REM TCC variables
set tcc_url=https://github.com/vlang/tccbin
set tcc_dir=%~dp0thirdparty\tcc
set tcc_exe=%tcc_dir%\tcc.exe
if "%PROCESSOR_ARCHITECTURE%" == "x86" ( set tcc_branch="thirdparty-windows-i386" ) else ( set tcc_branch="thirdparty-windows-amd64" )
if "%~1" == "-tcc32" set tcc_branch="thirdparty-windows-i386"

REM VC settings
set vc_url=https://github.com/vlang/vc
set vc_dir=%~dp0vc

REM Let a particular environment specify their own TCC and VC repos (to help mirrors)
if /I not ["%TCC_GIT%"] == [""] set tcc_url=%TCC_GIT%
if /I not ["%TCC_BRANCH%"] == [""] set tcc_branch=%TCC_BRANCH%

if /I not ["%VC_GIT%"] == [""] set vc_url=%VC_GIT%

pushd "%~dp0"

:verifyopt
REM Read stdin EOF
if ["%~1"] == [""] goto :init

REM Target options
if !shift_counter! LSS 1 (
	if "%~1" == "help" (
		if not ["%~2"] == [""] set subcmd=%~2& shift& set /a shift_counter+=1
	)
	for %%z in (build clean cleanall check help latest_tcc rebuild) do (
		if "%~1" == "%%z" set target=%~1& shift& set /a shift_counter+=1& goto :verifyopt
	)
)

REM Compiler option
for %%g in (-gcc -msvc -tcc -tcc32 -clang) do (
	if "%~1" == "%%g" set compiler=%~1& set compiler=!compiler:~1!& shift& set /a shift_counter+=1& goto :verifyopt
)

REM Standard options
if "%~1" == "--local" (
	if !flag_local! NEQ 0 (
		echo The flag %~1 has already been specified. 1>&2
		exit /b 2
	)
	set /a flag_local=1
	set /a shift_counter+=1
	shift
	goto :verifyopt
)

echo Undefined option: %~1
exit /b 2

:init
goto :!target!

:check
echo.
echo Check everything
"%V_EXE%" test-all
exit /b !ERRORLEVEL!

:cleanall
call :clean
if !ERRORLEVEL! NEQ 0 exit /b !ERRORLEVEL!
echo.
echo Cleanup vc
echo  ^> Purge TCC binaries
if exist "%tcc_dir%" (
	rmdir /s /q "%tcc_dir%"
	if !ERRORLEVEL! NEQ 0 exit /b !ERRORLEVEL!
)
echo  ^> Purge vc repository
if exist "%vc_dir%" (
	rmdir /s /q "%vc_dir%"
	if !ERRORLEVEL! NEQ 0 exit /b !ERRORLEVEL!
)
exit /b 0

:clean
echo Cleanup build artifacts
echo  ^> Purge debug symbols
del *.pdb *.lib *.bak *.out *.ilk *.exp *.obj *.o *.a *.so

echo  ^> Delete old V executable(s)
del v*.exe
exit /b 0

:rebuild
call :cleanall
if !ERRORLEVEL! NEQ 0 exit /b !ERRORLEVEL!
goto :build

:latest_tcc
call :download_tcc
if !ERRORLEVEL! NEQ 0 goto :error
echo  ^> TCC is up to date.
exit /b 0

:help
if [!subcmd!] == [] (
	call :usage
) else (
	call :help_!subcmd!
)
if !ERRORLEVEL! NEQ 0 echo Invalid subcommand: !subcmd!
exit /b !ERRORLEVEL!

:build
if !flag_local! NEQ 1 (
	call :download_tcc
	if !ERRORLEVEL! NEQ 0 goto :error
	if exist "%vc_dir%" (
		pushd "%vc_dir%"
		if !ERRORLEVEL! NEQ 0 goto :error
		echo Updating vc...
		echo  ^> Sync with remote !vc_url!
		git pull --rebase --quiet
		if !ERRORLEVEL! NEQ 0 (
			popd
			goto :error
		)
		popd
	) else (
		call :cloning_vc
		if !ERRORLEVEL! NEQ 0 goto :error
	)
	echo.
)

REM Teach pre-uint snapshots the new spelling without changing the download.
powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0cmd\tools\vc_compat_uint.ps1" -Source "%V_C_FILE%" -Destination "%vc_dir%\v_win_compat.c"
if !ERRORLEVEL! NEQ 0 goto :error
set "V_C_FILE=%vc_dir%\v_win_compat.c"

echo Building V...
if not [!compiler!] == [] goto :!compiler!_strap


REM By default, use tcc, since we have it prebuilt:
:tcc_strap
:tcc32_strap
call :build_bootstrap_with_tcc
if !ERRORLEVEL! NEQ 0 goto :bootstrap_self_hosted_fallback
call :build_fresh_v_with_tcc
if !ERRORLEVEL! NEQ 0 goto :self_hosted_fallback
call :move_updated_to_v
if !ERRORLEVEL! NEQ 0 goto :compile_error
goto :success

:bootstrap_self_hosted_fallback
echo  ^> Snapshot bootstrap failed; rebuilding "%V_UPDATED%" with the self-hosted compiler "%V_EXE%"
call :build_self_hosted_with_tcc
if !ERRORLEVEL! NEQ 0 goto :compile_error
call :move_updated_to_v
if !ERRORLEVEL! NEQ 0 goto :compile_error
goto :success

:self_hosted_fallback
echo  ^> Stage build failed; rebuilding "%V_UPDATED%" with the self-hosted compiler "%V_EXE%"
call :build_self_hosted_with_tcc
if !ERRORLEVEL! NEQ 0 goto :tcc_retry_with_host_bootstrap
call :move_updated_to_v
if !ERRORLEVEL! NEQ 0 goto :compile_error
goto :success

:tcc_retry_with_host_bootstrap
echo  ^> TCC-built bootstrap failed; retrying bootstrap with Clang/GCC before compiling "%V_EXE%" with TCC
call :build_bootstrap_with_clang
if !ERRORLEVEL! NEQ 0 call :build_bootstrap_with_gcc
if !ERRORLEVEL! NEQ 0 (
	if [!compiler!] == [] goto :clang_strap
	goto :compile_error
)
call :build_fresh_v_with_tcc
if !ERRORLEVEL! NEQ 0 (
	if [!compiler!] == [] goto :clang_strap
	goto :compile_error
)
call :move_updated_to_v
if !ERRORLEVEL! NEQ 0 goto :compile_error
goto :success

:build_fresh_v_with_tcc
call :build_stage_with_tcc
set stage_error=!ERRORLEVEL!
if !stage_error! NEQ 0 (
	call :try_delete "%V_STAGE%"
	exit /b !stage_error!
)
echo  ^> Compiling "%V_EXE%" with "%V_STAGE%"
REM V3 supplies the absolute bundled-TCC root itself. A relative -B here would
REM override it after V3 changes into its isolated link directory.
set updated_vflags=-cc "!tcc_exe!"
"%V_STAGE%" %V_BOOTSTRAP_VFLAGS% -keepc -g -showcc !updated_vflags! -o "%V_UPDATED%" cmd/v
set stage_error=!ERRORLEVEL!
call :try_delete "%V_STAGE%"
exit /b !stage_error!

:build_self_hosted_with_tcc
if not exist "%V_EXE%" (
	echo  ^> Self-hosted compiler not found: "%V_EXE%"
	exit /b 1
)
echo  ^> Compiling "%V_UPDATED%" from cmd/v with the self-hosted compiler "%V_EXE%"
set updated_vflags=-cc "!tcc_exe!"
"%V_EXE%" %V_BOOTSTRAP_VFLAGS% -g !updated_vflags! -o "%V_UPDATED%" cmd/v
set self_hosted_error=!ERRORLEVEL!
if !self_hosted_error! NEQ 0 call :try_delete "%V_UPDATED%"
exit /b !self_hosted_error!

:clang_strap
call :build_bootstrap_with_clang
if !ERRORLEVEL! NEQ 0 (
	if not [!compiler!] == [] goto :error
	goto :gcc_strap
)

call :build_stage_with_clang
if !ERRORLEVEL! NEQ 0 goto :compile_error
echo  ^> Compiling "%V_EXE%" with "%V_STAGE%"
set updated_vflags=!clang_vflags!
"%V_STAGE%" %V_BOOTSTRAP_VFLAGS% -keepc -g -showcc !updated_vflags! -o "%V_UPDATED%" cmd/v
set stage_error=!ERRORLEVEL!
call :try_delete "%V_STAGE%"
if !stage_error! NEQ 0 goto :compile_error
call :move_updated_to_v
if !ERRORLEVEL! NEQ 0 goto :compile_error
goto :success

:gcc_strap
call :build_bootstrap_with_gcc
if !ERRORLEVEL! NEQ 0 (
	if not [!compiler!] == [] goto :error
	goto :msvc_strap
)

call :build_stage_with_gcc
if !ERRORLEVEL! NEQ 0 goto :compile_error
echo  ^> Compiling "%V_EXE%" with "%V_STAGE%"
set updated_vflags=-cc "!gcc_exe!"
"%V_STAGE%" %V_BOOTSTRAP_VFLAGS% -keepc -g -showcc !updated_vflags! -o "%V_UPDATED%" cmd/v
set stage_error=!ERRORLEVEL!
call :try_delete "%V_STAGE%"
if !stage_error! NEQ 0 goto :compile_error
call :move_updated_to_v
if !ERRORLEVEL! NEQ 0 goto :compile_error
goto :success

:msvc_strap
set VsWhereDir=%ProgramFiles(x86)%
set HostArch=x64
if "%PROCESSOR_ARCHITECTURE%" == "x86" (
	echo Using x86 Build Tools...
	set VsWhereDir=%ProgramFiles%
	set HostArch=x86
)

if not exist "%VsWhereDir%/Microsoft Visual Studio/Installer/vswhere.exe" (
	echo  ^> MSVC not found
	if not [!compiler!] == [] goto :error
	goto :compile_error
)

for /f "usebackq tokens=*" %%i in (`"%VsWhereDir%/Microsoft Visual Studio/Installer/vswhere.exe" -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath`) do (
	set InstallDir=%%i
)

if exist "%InstallDir%/Common7/Tools/vsdevcmd.bat" (
	call "%InstallDir%/Common7/Tools/vsdevcmd.bat" -arch=%HostArch% -host_arch=%HostArch% -no_logo
) else if exist "%VsWhereDir%/Microsoft Visual Studio 14.0/Common7/Tools/vsdevcmd.bat" (
	call "%VsWhereDir%/Microsoft Visual Studio 14.0/Common7/Tools/vsdevcmd.bat" -arch=%HostArch% -host_arch=%HostArch% -no_logo
)

set ObjFile=.v.c.obj

echo  ^> Bootstrapping "%V_BOOTSTRAP%" before compiling "%V_EXE%" with MSVC
REM Bootstrap order for -msvc: bundled TCC first (fastest, no external
REM toolchain needed), then MSVC itself (already confirmed present -
REM vsdevcmd.bat has already run above - and explicitly what -msvc asked
REM for), then Clang, then GCC as the remaining fallbacks. Any of these can
REM fail to compile vc/v_win.c on its own (e.g. a stale snapshot only some
REM toolchains can parse - see vlang/v#29025 and vlang/tccbin#96), which is
REM why every option here is still tried in order rather than stopping at
REM the first choice.
REM A bootstrap can also compile successfully but fail at runtime, for example
REM while setting up process pipes. Accept it only after it builds the stage.
call :build_msvc_stage tcc
if !ERRORLEVEL! NEQ 0 call :build_msvc_stage msvc
if !ERRORLEVEL! NEQ 0 call :build_msvc_stage clang
if !ERRORLEVEL! NEQ 0 call :build_msvc_stage gcc
if !ERRORLEVEL! NEQ 0 (
	echo Could not build a working bootstrap compiler before compiling with MSVC
	call :try_delete "%ObjFile%"
	call :try_delete "%V_STAGE%"
	goto :compile_error
)

echo  ^> Compiling "%V_EXE%" with "%V_STAGE%"
set updated_vflags=-cc msvc
"%V_STAGE%" %V_BOOTSTRAP_VFLAGS% -keepc -g -showcc !updated_vflags! -o "%V_UPDATED%" cmd/v
set msvc_error=!ERRORLEVEL!
call :try_delete "%ObjFile%"
call :try_delete "%V_STAGE%"
if %msvc_error% NEQ 0 goto :compile_error
call :move_updated_to_v
if !ERRORLEVEL! NEQ 0 goto :compile_error
goto :success

:download_tcc
if exist "%tcc_dir%" (
	pushd "%tcc_dir%"
	if !ERRORLEVEL! NEQ 0 exit /b !ERRORLEVEL!
	echo Updating TCC
	echo  ^> Syncing TCC from !tcc_url!
	if exist "lib\advapi32.def" git checkout -- lib\advapi32.def >nul 2>nul
	git pull --rebase --quiet
	if !ERRORLEVEL! NEQ 0 (
		set tcc_update_error=!ERRORLEVEL!
		popd
		exit /b !tcc_update_error!
	)
	popd
) else (
	call :bootstrap_tcc
	if !ERRORLEVEL! NEQ 0 exit /b !ERRORLEVEL!
)

call :patch_tcc_defs
if !ERRORLEVEL! NEQ 0 goto :error

if not exist "%tcc_exe%" echo  ^> TCC not found, even after cloning& goto :error
echo.
exit /b 0

:patch_tcc_defs
set "advapi32_def=%tcc_dir%\lib\advapi32.def"
if not exist "%advapi32_def%" exit /b 0
for %%G in (RegEnumKeyExW RegEnumValueW RegQueryInfoKeyW) do (
	findstr /x /c:"%%G" "%advapi32_def%" >nul || >>"%advapi32_def%" echo %%G
)
exit /b 0

:compile_error
echo.
echo Backend compiler error
goto :error

:error
call :try_delete "%vc_dir%\v_win_compat.c"
echo.
echo Exiting from error
echo ERROR: please follow the instructions in https://github.com/vlang/v/wiki/Installing-a-C-compiler-on-Windows
exit /b 1

:success
call :try_delete "%vc_dir%\v_win_compat.c"
"%V_EXE%" run cmd/tools/detect_tcc.v
if !ERRORLEVEL! NEQ 0 exit /b !ERRORLEVEL!
echo  ^> V built successfully!
echo  ^> To add V to your PATH, run `%V_EXE% symlink`.
echo  ^> Note: Antivirus programs may sometimes tell you there is a virus in V (there aren't any).  They can also slow compilation by a considerable amount.  Consider adding exemptions for the V install directory as well as your V project folders.

:version
echo.
echo | set /p="V version: "
"%V_EXE%" version
if !ERRORLEVEL! NEQ 0 exit /b !ERRORLEVEL!
"%V_EXE%" run .github/problem-matchers/register_all.vsh
if !ERRORLEVEL! NEQ 0 exit /b !ERRORLEVEL!
goto :eof

:usage
echo Usage:
echo     makev.bat [target] [compiler] [options]
echo.
echo Compiler:
echo     -msvc ^| -gcc ^| -tcc ^| -tcc32 ^| -clang    Set C compiler
echo.
echo Target:
echo     build[default]    Compiles V using the given C compiler
echo     clean             Clean build artifacts and debugging symbols
echo     cleanall          Cleanup entire ALL build artifacts and vc repository
echo     check             Check that tests pass, and the repository is in a good shape for Pull Requests
echo     help              Display help for the given target
echo     latest_tcc        Update the bundled TCC without rebuilding V
echo     rebuild           Fully clean/reset repository and rebuild V
echo.
echo Examples:
echo     makev.bat -msvc
echo     makev.bat -gcc --local
echo     makev.bat build -tcc --local
echo     makev.bat -tcc32
echo     makev.bat help clean
echo.
echo Use "make help <target>" for more information about a target, for instance: "make help clean"
echo.
echo Note: Any undefined/unsupported options will be ignored
exit /b 0

:help_help
echo Usage:
echo     makev.bat help [target]
echo.
echo Target:
echo     build ^| clean ^| cleanall ^| help    Query given target
exit /b 0

:help_clean
echo Usage:
echo     makev.bat clean
echo.
exit /b 0

:help_cleanall
echo Usage:
echo     makev.bat cleanall
echo.
exit /b 0

:help_build
echo Usage:
echo     makev.bat build [compiler] [options]
echo.
echo Compiler:
echo     -msvc ^| -gcc ^| -tcc ^| -tcc32 ^| -clang    Set C compiler
echo.
echo Options:
echo    --local     Use the local vc repository without
echo                syncing with remote
exit /b 0

:help_latest_tcc
echo Usage:
echo     makev.bat latest_tcc
echo.
exit /b 0

:help_rebuild
echo Usage:
echo     makev.bat rebuild [compiler] [options]
echo.
echo Compiler:
echo     -msvc ^| -gcc ^| -tcc ^| -tcc32 ^| -clang    Set C compiler
echo.
echo Options:
echo    --local     Use the local vc repository without
echo                syncing with remote
exit /b 0

:bootstrap_tcc
echo Bootstrapping TCC...
echo  ^> TCC not found
if "!tcc_branch!" == "thirdparty-windows-i386" ( echo  ^> Downloading TCC32 from !tcc_url! , branch !tcc_branch! ) else ( echo  ^> Downloading TCC64 from !tcc_url! , branch !tcc_branch! )
git clone --filter=blob:none --quiet --branch !tcc_branch! !tcc_url! "%tcc_dir%"
if !ERRORLEVEL! NEQ 0 exit /b !ERRORLEVEL!
git --no-pager -C "%tcc_dir%" log -n3
exit /b !ERRORLEVEL!

:cloning_vc
echo Cloning vc...
echo  ^> Cloning from remote !vc_url!
git clone --filter=blob:none --quiet "%vc_url%"
exit /b !ERRORLEVEL!

:build_msvc_stage
call :build_bootstrap_with_%~1
if !ERRORLEVEL! NEQ 0 exit /b !ERRORLEVEL!
call :build_stage_with_%~1
set stage_error=!ERRORLEVEL!
if !stage_error! NEQ 0 call :try_delete "%V_STAGE%"
exit /b !stage_error!

:select_tcc_openlibm
REM Direct TCC links need OpenLibm for C99 math that msvcrt lacks (remainder).
REM Every Windows TCC bundle ships lib\openlibm.o built for its own compiler, as
REM vlib/math expects. Use the installed checkout's object before each link: an
REM existing checkout keeps its branch, so it can differ from tcc_branch.
set "TCC_OPENLIBM="
if exist "%tcc_dir%\lib\openlibm.o" set TCC_OPENLIBM="%tcc_dir%\lib\openlibm.o"
exit /b 0

:build_bootstrap_with_tcc
if not exist "!tcc_exe!" (
	echo  ^> TCC not found
	exit /b 1
)
call :select_tcc_openlibm
echo  ^> Attempting to build "%V_BOOTSTRAP%" (from %V_C_FILE%) with "!tcc_exe!"
"!tcc_exe!" %VC_BOOTSTRAP_DEFINE% -B"%tcc_dir%" -bt10 -g -w -o "%V_BOOTSTRAP%" "%V_C_FILE%" %V_BOOTSTRAP_NATIVE% %TCC_OPENLIBM% -ladvapi32 -lws2_32 -lbcrypt -Wl,-stack=33554432
exit /b !ERRORLEVEL!

:build_bootstrap_with_msvc
REM Only reachable from msvc_strap, where vsdevcmd.bat has already put cl.exe
REM on PATH and MSVC's presence is already confirmed. Tried right after the
REM bundled TCC there, since -msvc means MSVC was explicitly requested.
REM Clang/GCC are the remaining fallbacks for when neither TCC nor cl.exe can
REM compile %V_C_FILE% (e.g. a stale vc/v_win.c snapshot that only some
REM toolchains can parse - see vlang/v#29025 and vlang/tccbin#96 for two
REM concrete cases).
where cl >nul 2>&1
if !ERRORLEVEL! NEQ 0 (
	echo  ^> MSVC's cl.exe not found on PATH
	exit /b 1
)
echo  ^> Attempting to build "%V_BOOTSTRAP%" (from %V_C_FILE%) with MSVC
cl /nologo %VC_BOOTSTRAP_DEFINE% /volatile:ms /bigobj /MD /we4013 /utf-8 /w /std:c11 /D_CRT_DECLARE_NONSTDC_NAMES=1 /Fe"%V_BOOTSTRAP%" "%V_C_FILE%" %V_BOOTSTRAP_NATIVE% kernel32.lib user32.lib dbghelp.lib ws2_32.lib bcrypt.lib advapi32.lib /link /STACK:33554432
set msvc_bootstrap_error=!ERRORLEVEL!
call :try_delete "%ObjFile%"
exit /b !msvc_bootstrap_error!

:build_bootstrap_with_clang
call :resolve_executable clang
if [!resolved_exe!] == [] (
	echo  ^> Clang not found
	exit /b 1
)
set "clang_exe=!resolved_exe!"
REM Keep Clang's installed target and sysroot together. Visual Studio's Clang
REM has an MSVC SDK, while LLVM-MinGW supplies its own GNU Windows runtime.
set "clang_target="
for /f "delims=" %%i in ('""!clang_exe!" -dumpmachine 2^>nul"') do set "clang_target=%%i"
if [!clang_target!] == [] (
	echo  ^> Could not determine Clang's default target
	exit /b 1
)
call :configure_clang_target
if !ERRORLEVEL! NEQ 0 exit /b !ERRORLEVEL!
echo  ^> Attempting to build "%V_BOOTSTRAP%" (from %V_C_FILE%) with Clang
if !clang_msvc! EQU 1 (
	REM Older snapshots select native __int128, whose helpers are absent from the MSVC runtime.
	"!clang_cl_exe!" /nologo %VC_BOOTSTRAP_DEFINE% /DV_INT128_PORTABLE=1 /bigobj /MD /utf-8 /w /std:c11 /D_CRT_DECLARE_NONSTDC_NAMES=1 /Fe"%V_BOOTSTRAP%" "%V_C_FILE%" %V_BOOTSTRAP_NATIVE% kernel32.lib user32.lib dbghelp.lib ws2_32.lib bcrypt.lib advapi32.lib /link /STACK:33554432
) else (
	"!clang_exe!" %VC_BOOTSTRAP_DEFINE% -std=c99 -municode -g -w -fwrapv -Wno-error=implicit-function-declaration -Wno-error=incompatible-function-pointer-types -o "%V_BOOTSTRAP%" "%V_C_FILE%" %V_BOOTSTRAP_NATIVE% -ladvapi32 -lws2_32 -lbcrypt -Wl,--stack=33554432
)
if !ERRORLEVEL! NEQ 0 (
	echo Clang bootstrap failed for target !clang_target!. Check its Windows SDK, headers, and libraries.
	"!clang_exe!" --version
	exit /b 1
)
exit /b 0

:configure_clang_target
set clang_msvc=0
set clang_vflags=-cc "!clang_exe!"
if not "!clang_target:msvc=!" == "!clang_target!" (
	set clang_msvc=1
	for %%i in ("!clang_exe!") do set "clang_cl_exe=%%~dpiclang-cl.exe"
	if not exist "!clang_cl_exe!" (
		echo  ^> Clang targets MSVC but clang-cl.exe was not found beside it
		exit /b 1
	)
	set clang_vflags=-cc "!clang_cl_exe!"
)
exit /b 0

:generate_stage_c
echo  ^> Generating "%V_STAGE_C%" with "%V_BOOTSTRAP%"
REM Older vc snapshots have broken process-spawn output pointers. Emit C without
REM spawning a compiler, then let this batch script link the fresh stage instead.
REM Match the stage C dialect and avoid requiring an unlinked GC library.
"%V_BOOTSTRAP%" %V_BOOTSTRAP_VFLAGS% -gc none -g !stage_vflags! -o "%V_STAGE_C%" cmd/v
set stage_error=!ERRORLEVEL!
if !stage_error! NEQ 0 call :try_delete "%V_STAGE_C%"
exit /b !stage_error!

:build_stage_with_tcc
set stage_vflags=-cc "!tcc_exe!"
call :generate_stage_c
if !ERRORLEVEL! NEQ 0 exit /b !ERRORLEVEL!
call :select_tcc_openlibm
echo  ^> Compiling "%V_STAGE%" from "%V_STAGE_C%" with TCC
"!tcc_exe!" -B"%tcc_dir%" -bt10 -g -w -o "%V_STAGE%" "%V_STAGE_C%" %V_STAGE_NATIVE% %TCC_OPENLIBM% -ldbghelp -lws2_32 -L"%~dp0vlib\crypto\rand\internal\libraries\bcrypt" -lbcrypt -I "%~dp0thirdparty\stdatomic\win" -ladvapi32 -Wl,-stack=33554432
set stage_error=!ERRORLEVEL!
call :try_delete "%V_STAGE_C%"
exit /b !stage_error!

:build_stage_with_gcc
set stage_vflags=-cc "!gcc_exe!"
call :generate_stage_c
if !ERRORLEVEL! NEQ 0 exit /b !ERRORLEVEL!
echo  ^> Compiling "%V_STAGE%" from "%V_STAGE_C%" with GCC
"!gcc_exe!" -std=gnu11 -municode -g -w -fwrapv -o "%V_STAGE%" "%V_STAGE_C%" %V_STAGE_NATIVE% -ldbghelp -lws2_32 -L"%~dp0vlib\crypto\rand\internal\libraries\bcrypt" -lbcrypt -I "%~dp0thirdparty\stdatomic\win" -ladvapi32 -Wl,--stack=33554432
set stage_error=!ERRORLEVEL!
call :try_delete "%V_STAGE_C%"
exit /b !stage_error!

:build_stage_with_msvc
set stage_vflags=-cc msvc
call :generate_stage_c
if !ERRORLEVEL! NEQ 0 exit /b !ERRORLEVEL!
echo  ^> Compiling "%V_STAGE%" from "%V_STAGE_C%" with MSVC
cl /nologo /volatile:ms /bigobj /MD /utf-8 /w /std:c11 /D_CRT_DECLARE_NONSTDC_NAMES=1 /Fe"%V_STAGE%" "%V_STAGE_C%" %V_STAGE_NATIVE% kernel32.lib user32.lib dbghelp.lib ws2_32.lib bcrypt.lib advapi32.lib /link /STACK:33554432
set stage_error=!ERRORLEVEL!
call :try_delete "%V_STAGE_C%"
call :try_delete "v_stage.obj"
exit /b !stage_error!

:build_stage_with_clang
set stage_vflags=!clang_vflags!
if !clang_msvc! EQU 1 set stage_vflags=-cc msvc
call :generate_stage_c
if !ERRORLEVEL! NEQ 0 exit /b !ERRORLEVEL!
echo  ^> Compiling "%V_STAGE%" from "%V_STAGE_C%" with Clang
if !clang_msvc! EQU 1 (
	REM The snapshot compiler also embeds its old int128 helper header in this stage.
	"!clang_cl_exe!" /nologo /DV_INT128_PORTABLE=1 /bigobj /MD /utf-8 /w /std:c11 /D_CRT_DECLARE_NONSTDC_NAMES=1 /Fe"%V_STAGE%" "%V_STAGE_C%" %V_STAGE_NATIVE% kernel32.lib user32.lib dbghelp.lib ws2_32.lib bcrypt.lib advapi32.lib /link /STACK:33554432
) else (
	"!clang_exe!" -std=gnu11 -municode -g -w -fwrapv -Wno-int-conversion -o "%V_STAGE%" "%V_STAGE_C%" %V_STAGE_NATIVE% -ldbghelp -lws2_32 -L"%~dp0vlib\crypto\rand\internal\libraries\bcrypt" -lbcrypt -I "%~dp0thirdparty\stdatomic\win" -ladvapi32 -Wl,--stack=33554432
)
set stage_error=!ERRORLEVEL!
call :try_delete "%V_STAGE_C%"
exit /b !stage_error!

:build_bootstrap_with_gcc
call :find_gcc_exe
if !ERRORLEVEL! NEQ 0 (
	echo  ^> GCC not found
	exit /b 1
)
echo  ^> Attempting to build "%V_BOOTSTRAP%" (from %V_C_FILE%) with GCC "!gcc_exe!"
"!gcc_exe!" %VC_BOOTSTRAP_DEFINE% -std=c99 -municode -g -w -o "%V_BOOTSTRAP%" "%V_C_FILE%" %V_BOOTSTRAP_NATIVE% -ladvapi32 -lws2_32 -lbcrypt -Wl,-stack=33554432
if !ERRORLEVEL! NEQ 0 (
	echo In most cases, compile errors happen because the version of GCC installed is too old
	"!gcc_exe!" --version
	exit /b 1
)
exit /b 0

:find_gcc_exe
REM Prefer MinGW target-prefixed drivers when present, so PATH conflicts do not pick an unrelated gcc.exe.
set "gcc_exe="
if "%PROCESSOR_ARCHITECTURE%" == "x86" (
	call :resolve_executable i686-w64-mingw32-gcc
	if not [!resolved_exe!] == [] (
		set "gcc_exe=!resolved_exe!"
		exit /b 0
	)
) else (
	call :resolve_executable x86_64-w64-mingw32-gcc
	if not [!resolved_exe!] == [] (
		set "gcc_exe=!resolved_exe!"
		exit /b 0
	)
)
call :resolve_executable gcc
if not [!resolved_exe!] == [] (
	set "gcc_exe=!resolved_exe!"
	exit /b 0
)
exit /b 1

:resolve_executable
set "resolved_exe="
for %%i in ("%~1") do if exist "%%~fi" (
	set "resolved_exe=%%~fi"
	exit /b 0
)
for /f "usebackq delims=" %%i in (`where "%~1" 2^>nul`) do (
	set "resolved_exe=%%~fi"
	exit /b 0
)
exit /b 1

:eof
popd
endlocal
exit /b 0

:try_delete
REM Best-effort delete of a just-executed intermediate compiler binary.
REM Windows (or a real-time antivirus scanner) can briefly hold a lock on an
REM exe right after the process running it exits, so retry a couple of times
REM with short waits before giving up silently - a leftover intermediate
REM binary here does not affect build success either way, and the noisy
REM native "Access is denied." message is not worth surfacing to the user.
REM
REM CONTRACT for callers: this always `exit /b 0`, regardless of whether the
REM delete actually succeeded. Every call site MUST capture the real
REM compile/link step's !ERRORLEVEL! into a named variable BEFORE calling
REM this (see the "set msvc_error=!ERRORLEVEL!" / "set stage_error=!ERRORLEVEL!"
REM pattern at its call sites) and check that saved variable afterward, never
REM !ERRORLEVEL! directly after a `call :try_delete` - this always succeeds
REM and would silently swallow a real failure otherwise.
REM
REM cmd/tools/makev_test.py exercises stage generation/link failures and cleanup
REM through cmd.exe on Windows. Compiler discovery and MSVC installation setup
REM still require validation with real Windows toolchains.
if not exist "%~1" exit /b 0
del "%~1" >nul 2>&1
if not exist "%~1" exit /b 0
ping 192.0.2.1 -n 1 -w 100 >nul
del "%~1" >nul 2>&1
if not exist "%~1" exit /b 0
ping 192.0.2.1 -n 1 -w 250 >nul
del "%~1" >nul 2>&1
exit /b 0

:smoke_test_updated
REM Install only a compiler that builds and runs hello world with the toolchain that
REM built it: updated_vflags, set beside every build of %V_UPDATED%. The driver's default
REM C compiler is not usable on every machine that can build V (a -msvc --local clone has
REM no bundled TCC and may have no gcc), and a working -msvc build must still install.
REM A compiler that falls back to the V1 compiler still exits 0, so check its output too.
echo  ^> Smoke testing "%V_UPDATED%"
"%V_UPDATED%" version
if !ERRORLEVEL! NEQ 0 (
	echo  ^> "%V_UPDATED%" version failed
	exit /b 1
)
"%V_UPDATED%" !updated_vflags! -o "%V_UPDATED%.smoke.exe" examples/hello_world.v > "%V_UPDATED%.smoke.log" 2>&1
set smoke_error=!ERRORLEVEL!
call :smoke_log_is_clean "%V_UPDATED%.smoke.log"
if !ERRORLEVEL! NEQ 0 set smoke_error=1
if !smoke_error! EQU 0 (
	"%V_UPDATED%.smoke.exe" > nul
	set smoke_error=!ERRORLEVEL!
)
call :try_delete "%V_UPDATED%.smoke.exe"
call :try_delete "%V_UPDATED%.smoke.log"
if !smoke_error! NEQ 0 (
	echo  ^> Smoke test failed; "%V_UPDATED%" was not installed
	exit /b !smoke_error!
)
exit /b 0

:smoke_log_is_clean
REM Exit 1 when the compile log shows that the compiler fell back to another compiler.
if not exist "%~1" exit /b 0
findstr /i /c:"falling back" /c:"could not build" /c:"retrying with" "%~1" >nul
if !ERRORLEVEL! EQU 0 exit /b 1
exit /b 0

:move_updated_to_v
REM The V1 compatibility compiler is installed on demand by `make v1`.
REM Compiling cmd/v again would only produce another copy of the new driver.
if not exist "%V_UPDATED%" exit /b 1
call :smoke_test_updated
if !ERRORLEVEL! NEQ 0 exit /b !ERRORLEVEL!
@REM del "%V_EXE%" &:: breaks if `makev.bat` is run from `v up` b/c of held file handle on `%V_EXE%`
if exist "%V_EXE%" (
	move /Y "%V_EXE%" "%V_OLD%" >nul
	if !ERRORLEVEL! NEQ 0 exit /b !ERRORLEVEL!
)
REM sleep for at most 100ms
ping 192.0.2.1 -n 1 -w 100 >nul
move /Y "%V_UPDATED%" "%V_EXE%" >nul
exit /b !ERRORLEVEL!