如何使Visual Studio将生成后事件错误识别为构建失败(兼容多版本)
问题原因分析
你的脚本里的EXIT_ON_ERROR变量存在批处理变量即时展开的问题:当你定义set EXIT_ON_ERROR=if %errorlevel% neq 0 exit /b %errorlevel%时,%errorlevel%会被立即替换成执行这条set命令时的错误码(通常是0)。后续调用%EXIT_ON_ERROR%时,实际执行的是if 0 neq 0 exit /b 0,永远不会触发退出逻辑,所以即使xcopy找不到文件报错,构建也不会终止。
另外补充xcopy的退出码规则,方便你按需调整:
- 0:文件复制成功
- 1:找到匹配文件但未复制(比如文件已最新)
- 2:未找到匹配文件,或用户按Ctrl+C终止
- 4:初始化错误
- 5:写入错误
解决方案
以下几种方案均兼容Visual Studio 2015、2017、2019、2022及后续版本:
方案1:直接写判断语句,弃用变量
把每个%EXIT_ON_ERROR%替换成实际的判断命令,彻底避免变量展开问题:
set OPENSSL_MANIFEST_ROOT=OpenSSL$(C_BITNESS).DllA set OPENSSL_MANIFEST_DIR="$(TargetDir)\%OPENSSL_MANIFEST_ROOT%\" set CURL_MANIFEST_ROOT=LibCurl$(C_BITNESS).DllA set CURL_MANIFEST_DIR="$(TargetDir)\%CURL_MANIFEST_ROOT%\" set COPY=xcopy /Y /D %COPY% "$(S_CURL_DIR)\lib\*.dll" %CURL_MANIFEST_DIR% if %errorlevel% neq 0 exit /b %errorlevel% %COPY% "$(S_CURL_DIR)\lib\*.pdb" %CURL_MANIFEST_DIR% if %errorlevel% neq 0 exit /b %errorlevel% %COPY% "$(S_CURL_BASE_DIR)\Manifests\%CURL_MANIFEST_ROOT%.manifest" %CURL_MANIFEST_DIR% if %errorlevel% neq 0 exit /b %errorlevel% %COPY% "$(S_ZLIB_DIR)\lib\*.dll" "$(TargetDir)" if %errorlevel% neq 0 exit /b %errorlevel% %COPY% "$(S_ZLIB_DIR)\lib\*.pdb" "$(TargetDir)" if %errorlevel% neq 0 exit /b %errorlevel% %COPY% "$(S_OPENSSL_DIR)\bin\*.dll" %OPENSSL_MANIFEST_DIR% if %errorlevel% neq 0 exit /b %errorlevel% %COPY% "$(S_OPENSSL_DIR)\bin\*.pdb" %OPENSSL_MANIFEST_DIR% if %errorlevel% neq 0 exit /b %errorlevel% %COPY% "$(S_OPENSSL_BASE_DIR)\Manifests\%OPENSSL_MANIFEST_ROOT%.manifest" %OPENSSL_MANIFEST_DIR% if %errorlevel% neq 0 exit /b %errorlevel% %COPY% "$(TargetDir)\%OPENSSL_MANIFEST_ROOT%\*" %CURL_MANIFEST_DIR%\%OPENSSL_MANIFEST_ROOT%\ if %errorlevel% neq 0 exit /b %errorlevel% %COPY% "$(SIMBAENGINE_THIRDPARTY_DIR)\icu\$(S_ICU_V)\$(C_BLD_RELDIR)\lib\*.dll" "$(TargetDir)" if %errorlevel% neq 0 exit /b %errorlevel%
方案2:启用延迟变量扩展,修正变量定义
在脚本开头启用延迟变量扩展,用!errorlevel!代替%errorlevel%,确保取到的是执行xcopy后的实际错误码:
@echo off setlocal enabledelayedexpansion set OPENSSL_MANIFEST_ROOT=OpenSSL$(C_BITNESS).DllA set OPENSSL_MANIFEST_DIR="$(TargetDir)\%OPENSSL_MANIFEST_ROOT%\" set CURL_MANIFEST_ROOT=LibCurl$(C_BITNESS).DllA set CURL_MANIFEST_DIR="$(TargetDir)\%CURL_MANIFEST_ROOT%\" set COPY=xcopy /Y /D set EXIT_ON_ERROR=if !errorlevel! neq 0 exit /b !errorlevel! %COPY% "$(S_CURL_DIR)\lib\*.dll" %CURL_MANIFEST_DIR% %EXIT_ON_ERROR% %COPY% "$(S_CURL_DIR)\lib\*.pdb" %CURL_MANIFEST_DIR% %EXIT_ON_ERROR% %COPY% "$(S_CURL_BASE_DIR)\Manifests\%CURL_MANIFEST_ROOT%.manifest" %CURL_MANIFEST_DIR% %EXIT_ON_ERROR% %COPY% "$(S_ZLIB_DIR)\lib\*.dll" "$(TargetDir)" %EXIT_ON_ERROR% %COPY% "$(S_ZLIB_DIR)\lib\*.pdb" "$(TargetDir)" %EXIT_ON_ERROR% %COPY% "$(S_OPENSSL_DIR)\bin\*.dll" %OPENSSL_MANIFEST_DIR% %EXIT_ON_ERROR% %COPY% "$(S_OPENSSL_DIR)\bin\*.pdb" %OPENSSL_MANIFEST_DIR% %EXIT_ON_ERROR% %COPY% "$(S_OPENSSL_BASE_DIR)\Manifests\%OPENSSL_MANIFEST_ROOT%.manifest" %OPENSSL_MANIFEST_DIR% %EXIT_ON_ERROR% %COPY% "$(TargetDir)\%OPENSSL_MANIFEST_ROOT%\*" %CURL_MANIFEST_DIR%\%OPENSSL_MANIFEST_ROOT%\ %EXIT_ON_ERROR% %COPY% "$(SIMBAENGINE_THIRDPARTY_DIR)\icu\$(S_ICU_V)\$(C_BLD_RELDIR)\lib\*.dll" "$(TargetDir)" %EXIT_ON_ERROR% endlocal
注:启用延迟扩展不会影响Visual Studio宏$(...)的解析,因为这些宏是VS在执行批处理前就替换完成的。
方案3:用标签+call命令实现判断逻辑
把错误判断逻辑写成批处理标签,通过call命令调用,确保每次都取最新的错误码:
set OPENSSL_MANIFEST_ROOT=OpenSSL$(C_BITNESS).DllA set OPENSSL_MANIFEST_DIR="$(TargetDir)\%OPENSSL_MANIFEST_ROOT%\" set CURL_MANIFEST_ROOT=LibCurl$(C_BITNESS).DllA set CURL_MANIFEST_DIR="$(TargetDir)\%CURL_MANIFEST_ROOT%\" set COPY=xcopy /Y /D %COPY% "$(S_CURL_DIR)\lib\*.dll" %CURL_MANIFEST_DIR% call :EXIT_ON_ERROR %COPY% "$(S_CURL_DIR)\lib\*.pdb" %CURL_MANIFEST_DIR% call :EXIT_ON_ERROR %COPY% "$(S_CURL_BASE_DIR)\Manifests\%CURL_MANIFEST_ROOT%.manifest" %CURL_MANIFEST_DIR% call :EXIT_ON_ERROR %COPY% "$(S_ZLIB_DIR)\lib\*.dll" "$(TargetDir)" call :EXIT_ON_ERROR %COPY% "$(S_ZLIB_DIR)\lib\*.pdb" "$(TargetDir)" call :EXIT_ON_ERROR %COPY% "$(S_OPENSSL_DIR)\bin\*.dll" %OPENSSL_MANIFEST_DIR% call :EXIT_ON_ERROR %COPY% "$(S_OPENSSL_DIR)\bin\*.pdb" %OPENSSL_MANIFEST_DIR% call :EXIT_ON_ERROR %COPY% "$(S_OPENSSL_BASE_DIR)\Manifests\%OPENSSL_MANIFEST_ROOT%.manifest" %OPENSSL_MANIFEST_DIR% call :EXIT_ON_ERROR %COPY% "$(TargetDir)\%OPENSSL_MANIFEST_ROOT%\*" %CURL_MANIFEST_DIR%\%OPENSSL_MANIFEST_ROOT%\ call :EXIT_ON_ERROR %COPY% "$(SIMBAENGINE_THIRDPARTY_DIR)\icu\$(S_ICU_V)\$(C_BLD_RELDIR)\lib\*.dll" "$(TargetDir)" call :EXIT_ON_ERROR goto :EOF :EXIT_ON_ERROR if %errorlevel% neq 0 exit /b %errorlevel% goto :EOF
额外说明
如果你想忽略xcopy中“文件已最新未复制”的情况(退出码1),可以把判断条件改成if %errorlevel% gtr 1 exit /b %errorlevel%,这样只有真正的错误(退出码≥2)才会终止构建。
内容的提问来源于stack exchange,提问作者Bwmat
相关产品推荐
相关产品推荐

