[Jenkins]Build c++ solution file using devenv.com - windows

I have a VS2008 solution file named MySolution.sln containing three projects ProjectName1, ProjectName2, ProjectName3 and ProjectName4. I am using jenkins to build the solution(to built only ProjectName1/2/3 and not ProjectName4). Below is the batch which has been provided to jenkins.
The issue is whenever lets say, the statement "%VSDIR%\devenv.com" "MySolution.sln" /Build "Release|x64" /Project ProjectName1 failed to build the particular project my %errorlevel% is not updated to non negative integer (>0). Irrespective of pass/fail the %errorlevel% is always "0"
The way jenkins calls this is
cmd /c call C:\Users\John\AppData\Local\Temp\hudson343434346343.bat
Any ideas on this?
Batch script:
#ECHO OFF
set VSDIR=C:\Program Files (x86)\Microsoft Visual Studio 9.0\Common7\IDE
"%VSDIR%\devenv.com" "MySolution.sln" /Clean "Release|x64"
"%VSDIR%\devenv.com" "MySolution.sln" /Build "Release|x64" /Project ProjectName1
IF %ERRORLEVEL% EQU 0 (
"%VSDIR%\devenv.com" "MySolution.sln" /Build "Release|x64" /Project ProjectName2
IF %ERRORLEVEL% EQU 0 (
"%VSDIR%\devenv.com" "MySolution.sln" /Build "Release|x64" /Project ProjectName3
IF %ERRORLEVEL% EQU 0 (
) ELSE (
echo "Failed to build ProjectName3"
)
) ELSE (
echo "Failed to build ProjectName2"
)
) ELSE (
echo "Failed to build ProjectName1"
)
unset VSDIR

just use MSBuild plug-in. You are all set.

I have several suggestions to make this simpler and more reliable.
You do not need to do a separate call to devenv.com in order to do a /Clean. Simply replace the /Build command in subsequent devenv.com calls with /Rebuild, which will clean before building from scratch.
After each call to devenv.com immediately do:
if ERRORLEVEL 1 echo Error building Solution X & goto some-label
This will detect any ERRORLEVEL of 1 or higher. This approach will not require any parentheses or ELSE clauses, and avoid the need to nest the subsequent IF statements.
You can also build the 3 (out of 4) projects with a single devenv.com call. This would require you to specify a solution build configuration that excludes (skips) the fourth project. For example:
"%VSDIR%\devenv.com" "MySolution.sln" /Rebuild "Release-Jenkins|x64"
The above approach requires first loading the solution in the Visual Studio IDE, going into the Configuration Manager, and creating a new solution build configuration (based on a copy of the existing Release build configuration). Name the new solution build configuration Release-Jenkins (or whatever), then make sure the project you wish to exclude is unchecked for each of the variations of Win32/x64/AnyCPU. Also, be sure to UNCHECK the checkbox to Create new project configurations, as you do not require any new project configurations. Also create a corresponding solution build configuration for Debug builds, i.e. Debug-Jenkins.
The unset command works in bash, but not for cmd.exe shells. To undefine the variable, do:
set VSDIR=
with nothing following the = sign. Or even better, add the command SETLOCAL on its own line immediately after #ECHO OFF. The SETLOCAL statement will ensure any environment variable changes are discarded when the batch file exits.

Related

How do I save cmd output from msbuild to a log file in a for loop?

I have several Visual Studio 2015 solution files that I would like to build using a command line. I would like the output stored in a single file
Here is the contents of a batch file that builds the solutions.
echo off
call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat"
set _SCRIPT_DRIVE=%~d0
echo DRIVE %_SCRIPT_DRIVE%
set baseDir=%_SCRIPT_DRIVE%\Source\Service\4.0\Branches\ARES
echo baseDir = %baseDir%
set buildLog=%baseDir%\Build.log
rem these are the directory names / solution name of the projects I want to build
rem you can build 'n' solutions and use your own instead of me giving you the solution and code
set thirdParty=Cert,ManagedHooks,NLog,Newtonsoft.Json,RabbitMQ,MDTE
echo.
echo building %thirdParty%
echo.
for %%p in (%thirdParty%) do (
echo.
echo building %%p
cd %%p
set thirdPartySolutionFile=%%p%.sln
echo solution file : %thirdPartySolutionFile%
MSBuild %thirdPartySolutionFile% /t:Rebuild /m /p:Configuration=Debug > %buildLog%
cd ..
)
I only get the last project build information stored in the log file
>> is the answer
https://www.tutorialspoint.com/batch_script/batch_script_appending_files.htm
> overwrites the %buildLog% file.
>> will append the input to this file
so the line where you call the build tool and send the output to the file shoul d be changed to have the >> in it instead of >
MSBuild %thirdPartySolutionFile% /t:Rebuild /m /p:Configuration=Debug >> %buildLog%

How to display NUnit test results only when some tests failed?

When I build a Visual Studio 2010 project, I want to run unit tests with NUnit and display test results only when some tests have failed.
I have setup a post-build event in Visual Studio to call a batch file like below:
$(ProjectDir)RunUnitTest.bat "$(SolutionDir)packages\NUnit.Runners.2.6.0.12051\tools\nunit-console.exe" "$(TargetPath)"
Then in RunUnitTest.bat, I call nunit-console.exe and pass in the test project dll.
#echo off
REM runner is the full path to nunit-console.exe
set runner=%1
REM target is the full path to the dll containing unit tests
set target=%2
"%runner%" "%target%"
if errorlevel 1 goto failed
if errorlevel 0 goto passed
:failed
echo some tests failed
goto end
:passed
echo all tests passed
goto end
:end
echo on
After that, NUnit generates TestResult.xml containing test results, so how do I display it in user friendly way? It'll be the best if it displays inside Visual Studio, but other options are open too.
You might want to consider XSLT to perform a transformation and display the results from TestResult.xml.
I ended up using nunit-summary to generate all pass summary html reports and nunit-results to create failed test reports in html.
This approach is quiet easy to setup.
First, download nunit-summary and nunit-results from launchpad and put them in TestRunner folder under the test project.
Then, add a post-build event to call a batch file.
Lastly, add the batch file to TestRunner folder under test project. It should contain the following files at the least:
nunit-results.exe
nunit-results.tests.dll
nunit-results.tests.pdb
nunit-summary.exe
`nunit-core.dll
nunit.util.dll
RunUnitTests.bat
Post-build event for the project containing unit tests:
"$(ProjectDir)TestRunner\RunUnitTests.bat" "$(SolutionDir)packages\NUnit.Runners.2.6.0.12051\tools\nunit-console.exe" "$(TargetPath)" "$(TargetDir)"
Scripts in RunUnitTest.bat
REM This batch file does the followings:
REM 1. runs unit test with nunit-console.exe and produces a TestResult.xml
REM 2. if one or more tests failes, it calls unit-results.exe to convert TestResult.xml to
REM Usage: RunUnitTests.bat "path-to-nunit.exe" "path-to-test.dll" "path-to-output-folder"
#echo off
REM get input arguments
set runner=%1
set target=%2
set output=%3
REM remove double quotes
set runner=%runner:"=%
set target=%target:"=%
set output=%output:"=%
REM prepare and clean up TestResult folder
if not exist "%output%TestResults\nul" md "%output%TestResults"
del "%output%\TestResults\*.*" /q
"%runner%" "%target%"
if errorlevel 1 goto failed
if errorlevel 0 goto passed
:failed
echo some tests failed
"%~dp0nunit-results.exe" "%output%TestResult.xml"
"%output%TestResults\index.html"
exit 1
:passed
echo all tests passed
"%~dp0nunit-summary.exe" "%output%TestResult.xml" -out=TestResults\TestSummary.html
"%output%TestResults\TestSummary.html"
exit 0

Visual Studio incremental build: XML documentation file is created too late

I have a DLL project for Visual Studio 2005 that has "XML documetation file" turned on.
Whenever I do an incremental build, during post-build event execution there is no XML documentation file in the output directory.
If I pause the build during post-build event (using sleep utility from GnuWin32 CoreUtils), I can see the file in the output directory with a name like vs5BB5.tmp. But this file is not renamed to MyLib.xml until post-build event (and "AfterBuild" target, as I have some customizations there) are finished.
For a clean build in Studio and for MSBuild started from a command line everything works as expected - XML documentation file is created before post-build events.
Why this happens, and how do I fix incremental builds?
Was just having the same issue. This is a known problem with Visual Studio and incremental builds. See this post on microsoft connect.
I solved it with a conditional xcopy like the one below:
if exist "$(TargetDir)$(TargetName).xml" xcopy $(TargetDir)$(TargetName).xml $(ProjectDir)......\bin\ /C /I /R /Y
SF
Just having this problem myself....
what I found is that the xml file is named a .tmp file, so you can copy this tmp file to where you want, its just a bit of a "messy" work around.
I'm also quite tempted to write myself a command line tool thats called something like :-
WaitForThenCopy <source path> <target path> <milliseconds to wait>
only problem is it would have to be non blocking and you wouldn't know if it worked or not.
I'm using a simple batch file to do the copying instead of the default copy command that detects the tmp file and copies/renames this instead.
REM There is a bug in VS where the xml documentation is written to a tmp file
REM during incremental builds, preventing access during post-build events.
REM See http://connect.microsoft.com/VisualStudio/feedback/details/470485/strange-file-not-found-error-xml-documentation-file-renamed-during-incremental-build
REM As a work around for following script tries to catch this situation and copys/remanes
REM this tmp-file instead.
REM .SYNOPSIS
REM CopyXmlDocumentation "X:\path\to\source.xml" "Y:\target\dir"
if exist "%~1%" (
REM if the file exists, copy it as-is
copy /Y "%~1" "%~2"
) else (
REM else we try to copy the .tmp file and rename it to the desired target name
REM we assume that the tmp file is named "vsXXXX.tmp" where XXXX is an arbitrary string
copy /Y "%~d1\%~p1\vs*.tmp" "%~2\%~n1%~x1"
)

How to find Windows SDK's SetEnv.cmd / SetEnv.cmd Does not work correctly

We have a Team City Build Server running and want to compile a Visual C++ project. So far this would be easy, since I've setup our Windows Build Agent with the Windows SDK, but we don't have a solution / project file.
The project files are instead created with CMake. CMake seems to be a little bit dumb (can't generate Solution when Visual Studio is not installed), but with some tricks, I could get it to do it. The solution can then be built with MSBuild.
And here comes the problem. For this to work automatically, I need to call the Windows SDK's SetEnv.cmd. And I can't seem to find it automatically. It's in the bin sub directory of the Windows SDK, but neither bin nor the root are in the path, and the %mssdk% environment variable is set by the SetEnv.cmd and is not available beforehand!
Adding the Windows SDK\bin dir to the PATH leads to SetEnv.cmd no longer working (exits with a message like The x86 compilers are not currently installed and Jump target Set_x86 not found.
The start menu link is calling the SetEnv.cmd with the Windows SDK dir as working directory instead. But if I add the root directory to the PATH, Bin\SetEnv.cmd is not available.
How can I find SetEnv.cmd automatically? Even setting an environment variable to the full path of the setenv.cmd doesn't work, and when I define %mssdk% as the sdk dir, then call %mssdk%\bin\SetEnv doesn't work as well. I also tried to define %mssdk%, then cd %mssdk%, then calling bin\SetEnv. Also compilers not found in all these cases. It also doesn't work if I manually cd to the root or bin dir on a command line and then call SetEnv.cmd...
The start menu link works fine though.
For the record, my solution for now, as strange as this is, is the following:
I created a MSBuild file that creates the solution file with CMake on the command line, then invokes the created solution with a MSBuild task. The MSBuild file can be easily built from TeamCity, though I needed some additional tricks to satisfy CMake's stupid looking for the compiler, though I won't invoke it thing. Not really satisfying, but it works.
My solution (sets %WindowsSdkPath%, so that SetEnv.cmd could be found under %WindowsSdkPath%Bin\):
#ECHO OFF
IF "%WindowsSdkVersion%"=="" (
CALL :SetWindowsSdkVersionHelper HKCU > nul 2>&1
IF ERRORLEVEL 1 CALL :SetWindowsSdkVersionHelper HKLM > nul 2>&1
IF ERRORLEVEL 1 GOTO ERROR_NOWSDK
)
CALL :SetWindowsSdkPathHelper > nul 2>&1
IF ERRORLEVEL 1 GOTO ERROR_NOWSDK
GOTO END
:SetWindowsSdkPathHelper
SET WindowsSdkPath=
FOR /F "tokens=1,2*" %%i in ('REG QUERY "HKLM\SOFTWARE\Microsoft\Microsoft SDKs\Windows\%WindowsSdkVersion%" /V InstallationFolder') DO (
IF "%%i"=="InstallationFolder" (
SET "WindowsSdkPath=%%k"
)
)
IF "%WindowsSdkPath%"=="" EXIT /B 1
EXIT /B 0
:SetWindowsSdkVersion
CALL :GetWindowsSdkVersionHelper HKCU > nul 2>&1
IF ERRORLEVEL 1 CALL :GetWindowsSdkVersionHelper HKLM > nul 2>&1
IF ERRORLEVEL 1 EXIT /B 1
EXIT /B 0
:SetWindowsSdkVersionHelper
SET WindowsSdkVersion=
FOR /F "tokens=1,2*" %%i in ('REG QUERY "%1\SOFTWARE\Microsoft\Microsoft SDKs\Windows" /V "CurrentVersion"') DO (
IF "%%i"=="CurrentVersion" (
SET "WindowsSdkVersion=%%k"
)
)
IF "%WindowsSdkVersion%"=="" EXIT /B 1
EXIT /B 0
:ERROR_NOWSDK
ECHO The Windows SDK %WindowsSdkVersion% could not be found.
EXIT /B 1
:END
I was inspired for this by the SetEnv.cmd itself...
Mac, nice answer!
Now I would like to run msbuild with my project file. But before I should run SetEnv.Cmd - right?
So, here we go:
run_Macs_code.bat REM see above
call "%WindowsSdkPath%\bin\Setenv.cmd" /Release /x86 /xp
cd E:\client
msbuild client.proj
Now it's working :)

Visual Studio, MS Build

I am trying to build multiple .sln files inside a batch file. Everything works great so far. I am trying to add a check inside the batch file, so if number of errors is greater than 0 then the batch file stops executing and doesn't build the next .sln files. How can I do that? Basically something like:
msbuild test.sln
(check if build error > 0
stop)
msbuild test2.sln
MSBUILD will set the ERRORLEVEL, so something along the lines of:
msbuild test.sln
IF NOT ERRORLEVEL 0 exit 1
Edit: Apparently it should be:
msbuild test.sln
IF ERRORLEVEL 1 exit 1
msbuild.exe test.sln
if errorlevel 1 goto :errors
msbuild.exe test2.sln
if errorlevel 1 goto :errors
:: ...
:: Everything was fine.
echo Build completed without errors.
goto :eof
:error
echo Build failed.
In my opinion it's much easier to use a custom msbuild file here and use the msbuild task with your set of solutions. See here for the details.

Resources