I was using Visual Studio 2015 when I noticed this issue, but I want a solution that will also work in 2017, 2019, and 2022 (and hopefully future versions as well)
I have a project with a post build event defined as follows to copy some dependencies into the build folder:
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%
I want the build to fail if any of the dependencies couldn't be copied, for any reason.
I just ran the build while forgetting to check out one of the dependencies from version control, and I got the following output
2>------ Build started: Project: AnglesViews, Configuration: debug_MTDLL x64 ------
2> Creating library Bin\w2012r2\vs2015\debug64md\AnglesViews.lib and object Bin\w2012r2\vs2015\debug64md\AnglesViews.exp
2> AnglesViews_vs2015.vcxproj -> C:\perforce\Drivers\DriverShared\AnglesViews\Maintenance\1.0\Bin\w2012r2\vs2015\debug64md\AnglesViews.dll
2> AnglesViews_vs2015.vcxproj -> Bin\w2012r2\vs2015\debug64md\AnglesViews.pdb (Full PDB)
2> C:\perforce\ThirdParty\libcURL\7.88.1_ssl3.0_zlib1.2.13_threaded_resolver\w2012r2\vs2015\release64md\lib\libcurl.dll
2> 1 File(s) copied
2> C:\perforce\ThirdParty\libcURL\7.88.1_ssl3.0_zlib1.2.13_threaded_resolver\w2012r2\vs2015\release64md\lib\curl.pdb
2> C:\perforce\ThirdParty\libcURL\7.88.1_ssl3.0_zlib1.2.13_threaded_resolver\w2012r2\vs2015\release64md\lib\libcurl.pdb
2> 2 File(s) copied
2> 0 File(s) copied
2> File not found - LibCurl64.DllA.manifest
2> C:\perforce\ThirdParty\zlib\1.2.13\w2012r2\vs2015\release64md\lib\zlibwapi.dll
2> 1 File(s) copied
2> C:\perforce\ThirdParty\zlib\1.2.13\w2012r2\vs2015\release64md\lib\zlibwapi.pdb
2> 1 File(s) copied
2> C:\perforce\ThirdParty\openssl\3.0\w2012r2\vs2015\release64md\bin\libcrypto-3-x64.dll
2> C:\perforce\ThirdParty\openssl\3.0\w2012r2\vs2015\release64md\bin\libssl-3-x64.dll
2> 2 File(s) copied
2> C:\perforce\ThirdParty\openssl\3.0\w2012r2\vs2015\release64md\bin\libcrypto-3-x64.pdb
2> C:\perforce\ThirdParty\openssl\3.0\w2012r2\vs2015\release64md\bin\libssl-3-x64.pdb
2> C:\perforce\ThirdParty\openssl\3.0\w2012r2\vs2015\release64md\bin\openssl.pdb
2> 3 File(s) copied
2> C:\perforce\ThirdParty\openssl\3.0\Manifests\OpenSSL64.DllA.manifest
2> 1 File(s) copied
2> C:\perforce\Drivers\DriverShared\AnglesViews\Maintenance\1.0\Bin\w2012r2\vs2015\debug64md\\OpenSSL64.DllA\libcrypto-3-x64.dll
2> C:\perforce\Drivers\DriverShared\AnglesViews\Maintenance\1.0\Bin\w2012r2\vs2015\debug64md\\OpenSSL64.DllA\libcrypto-3-x64.pdb
2> C:\perforce\Drivers\DriverShared\AnglesViews\Maintenance\1.0\Bin\w2012r2\vs2015\debug64md\\OpenSSL64.DllA\libssl-3-x64.dll
2> C:\perforce\Drivers\DriverShared\AnglesViews\Maintenance\1.0\Bin\w2012r2\vs2015\debug64md\\OpenSSL64.DllA\libssl-3-x64.pdb
2> C:\perforce\Drivers\DriverShared\AnglesViews\Maintenance\1.0\Bin\w2012r2\vs2015\debug64md\\OpenSSL64.DllA\openssl.pdb
2> C:\perforce\Drivers\DriverShared\AnglesViews\Maintenance\1.0\Bin\w2012r2\vs2015\debug64md\\OpenSSL64.DllA\OpenSSL64.DllA.manifest
2> 6 File(s) copied
2> C:\perforce\ThirdParty\icu\71.1.x\w2012r2\vs2015\release64md\lib\sbicudt71_64.dll
2> C:\perforce\ThirdParty\icu\71.1.x\w2012r2\vs2015\release64md\lib\sbicuin71_64.dll
2> C:\perforce\ThirdParty\icu\71.1.x\w2012r2\vs2015\release64md\lib\sbicuio71_64.dll
2> C:\perforce\ThirdParty\icu\71.1.x\w2012r2\vs2015\release64md\lib\sbicutu71_64.dll
2> C:\perforce\ThirdParty\icu\71.1.x\w2012r2\vs2015\release64md\lib\sbicuuc71_64.dll
2> C:\perforce\ThirdParty\icu\71.1.x\w2012r2\vs2015\release64md\lib\sbtestplug_64.dll
2> 6 File(s) copied
========== Build: 2 succeeded, 0 failed, 2 up-to-date, 0 skipped ==========
So even though the post-build event had the error File not found - LibCurl64.DllA.manifest
, VS still considered the build a success. What am I doing wrong?
That is not how variables (and ERRORLEVEL) work in batch files - which ultimately the build event content gets converted into.
The %ERRORLEVEL%
in your EXIT_ON_ERROR
variable will already be replaced (by 0 at that time) when you declare it. That is, this
set EXIT_ON_ERROR=if %errorlevel% neq 0 exit /b %errorlevel%
is already interpreted as
set EXIT_ON_ERROR=if 0 neq 0 exit /b 0
However, due to the way that this "works" it won't even work when you use !ERRORLEVEL!
and setlocal enabledelayedexpansion
(look those things up if you're intersted anyway).
You could do it like this, which is (arguably) less "noisy" anyway.
%COPY% "$(S_CURL_DIR)\lib\*.dll" %CURL_MANIFEST_DIR% || exit /b 1
...
Depending on the complexity of the tasks you need to perform (or the control you want to have over them - for example, only copy files if they do not exist in the "target directory"), you may want to ditch those events all together and rather use proper targets anyway.