Treat MSBuild exec task timeout as an error - msbuild

I'm specifying a timeout for an MSBuild Exec task like this:
<Exec Command="MyCommand.bat" Timeout="3000" />
If my command times out, MSBuild issues a warning. I would like it to instead issue an error that fails the build. How can I achieve that?

Use the OnError element and the Error Task:
<Target Name="ExecCommand">
<Exec Command="MyCommand.bat" Timeout="3000" />
<OnError ExecuteTargets="TimeoutErrorHandler"/>
</Target>
<Target Name="TimeoutErrorHandler">
<Error Text="Command timeout"/>
</Target>

Related

MSBuild exec task, exit code empty

I have the following exec task, performing checkin of assemblyinfo.cs files. I'm trying to return the exit code, but for some reason it is always empty.
<!--Checkin if all succeeded-->
<Exec Condition=" '$(LocalCompilationSuccess)' != 'Failed' and '$(LocalTestSuccess)' != 'Failed' " ContinueOnError="True"
Command='"$(TfCommand)" checkin /recursive /comment:"$(NoCICheckInComment) $(BuildDefinitionName): build succeeded, checkin changes." /override:"TeamBuild $(BuildDefinitionName)" $/SomeProject/Trnk' WorkingDirectory="$(SolutionRoot)" >
<Output TaskParameter="ExitCode" PropertyName="ErrorCode"/>
</Exec>
I've tried to read the exit code in 2 ways:
'%(ErrorCode.Identity)'
'$(ErrorCode)'
Both are empty. Any suggestions?
In general it works as you have shown.
For reference, here is a more "selfcontained" example:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
<Target Name="help">
<Exec ContinueOnError="True" Command='cmd.exe /c dir'>
<Output TaskParameter="ExitCode" PropertyName="ErrorCode"/>
</Exec>
<Message Importance="high" Text="$(ErrorCode)"/>
</Target>
</Project>
A couple of things you may want to consider however:
Make sure your Exec even executes, that is Condition evaluates to
True.
Output the ErrorCode property using the Message-Task, to see if it is actually set (to the value you expect). However, make sure MSBuild will show the output, by either using Importance='high' or by running msbuild.exe /v:d to enable detailed messages.

msbuild exec task call msbuild

I need to call exec and build a wix setup project.
Currently I have the following in my TFSbuild.proj
<PropertyGroup>
<WebRoot>$(DropLocation)\Latest\x86\Release\_PublishedWebsites\Web</WebRoot>
<DBRoot>$(DropLocation)\Latest\x86\Release\Database</DBRoot>
</PropertyGroup>
<PropertyGroup>
<Msbuildexe>"msbuild"</Msbuildexe>
<Configuration>"/p:Configuration:"Release""</Configuration>
<DefineConstants>" /p:DefineConstants:"WebRoot=$(WebRoot);DBRoot=$(DBRoot)""</DefineConstants>
<WixSolution>"$(MSBuildProjectDirectory)\Setup\Setup.sln"</WixSolution>
</PropertyGroup>
<Message Text="Bulding setup solution" />
<Message Text="$(Msbuildexe) $(Configuration) $(DefineConstants) $(WixSolution)" />
<Exec Command="$(Msbuildexe) $(Configuration) $(DefineConstants) $(WixSolution)" />
I've tried to simply as much as possible so I don't get confused where the " are meant to be. When I run this the debug message (2nd last command) outputs
"msbuild"
"/p:Configuration:"Release"" "
/p:DefineConstants:"WebRoot=\server\drops\app\Installer Build\Latest\x86\Release_PublishedWebsites\Web;DBRoot=\server\drops\app\Installer Build\Latest\x86\Release\Database""
"f:\builds\app\Installer Build\BuildType\Setup\Setup.sln"
And I get the following error in the log
'"msbuild"' is not recognized as an
internal or external command,
operable program or batch file.
f:\builds\app\Installer
Build\BuildType\TFSBuild.proj(538,5):
error MSB3073: The command ""msbuild"
"/p:Configuration:"Release"" "
/p:DefineConstants:"WebRoot=\server\drops\app\Installer Build\Latest\x86\Release_PublishedWebsites\Web;DBRoot=\server\drops\app\Installer Build\Latest\x86\Release\Database""
"f:\builds\app\Installer
Build\BuildType\Setup\Setup.sln""
exited with code 9009.
I'm not sure if this is being caused by not being able to call the msbuild command from the command line or a " issue. If it is because I can't call msbuild from the command line like this how would I go about referencing it, is there a property that points to it?
To start with, you don't need most of the quotes, especially if the paths you are using don't contain spaces, but I'd trim it down to this, allowing for spaces in the paths for $(WebRoot), $(DbRoot) and $(MSBuildProjectDirectory):
<PropertyGroup>
<WebRoot>$(DropLocation)\Latest\x86\Release\_PublishedWebsites\Web</WebRoot>
<DBRoot>$(DropLocation)\Latest\x86\Release\Database</DBRoot>
</PropertyGroup>
<PropertyGroup>
<MsbuildExe>{still-needs-a-path-to}\msbuild</MsbuildExe>
<Configuration>/p:Configuration:Release</Configuration>
<DefineConstants>/p:DefineConstants:"WebRoot=$(WebRoot);DBRoot=$(DBRoot)"</DefineConstants>
<WixSolution>"$(MSBuildProjectDirectory)\Setup\Setup.sln"</WixSolution>
</PropertyGroup>
<Message
Text="Bulding setup solution"
/>
<Message
Text="$(MsbuildExe) $(Configuration) $(DefineConstants) $(WixSolution)"
/>
<Exec
Command="$(MsbuildExe) $(Configuration) $(DefineConstants) $(WixSolution)"
/>
However, you still won't be able to execute MSBuild with this, since the path to MSBuild isn't specified. It is typically found in the $(WINDIR)\Framework\Microsoft.Net\v4.0.30319 folder. There are a few ways to get this, either encode it directly, rely on an environment variable (that has to be set up somehow), use the predefined $(MSBuildBinPath), or extract it from the registry using the MSBuild registry syntax, which would look like this:
$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSBuild\ToolsVersions\4.0\MSBuildToolsPath)
However, it isn't clear why you are running MSBuild using Exec rather than just using the MSBuild task. Change the line with Exec to this:
<MSBuild
Project="$(WixSolution)"
Properties="$(DefineConstants)"
/>
removing your declaration for <Configuration> and changing <DefineConstants> to this:
<DefineConstants>Configuration=$(Configuration);WebRoot=$(WebRoot);DBRoot=$(DBRoot)</DefineConstants>
Following up on my comment I'd suggest you try using the MSBuild Task instead of Exec:
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="BuildWiXSolution">
<!-- Include the custom build targets installed with WiX -->
<Import Project="$(MSBuildExtensionsPath)\Wix\Wix.targets"/>
<PropertyGroup>
<WebRoot>$(DropLocation)\Latest\x86\Release\_PublishedWebsites\Web</WebRoot>
<DBRoot>$(DropLocation)\Latest\x86\Release\Database</DBRoot>
</PropertyGroup>
<ItemGroup>
<WiXSolution Include="$(MSBuildProjectDirectory)\Setup\Setup.sln">
<Properties>Configuration=Release</Properties>
<AdditionalProperties>WebRoot=$(WebRoot);DBRoot=$(DBRoot)</AdditionalProperties>
</WiXSolution>
</ItemGroup>
<Target Name="BuildWiXSolution">
<MSBuild Projects="#(WiXSolution)" />
</Target>
</Project>
It allows you to keep configuration properties and additional properties together with your Wix solution.

How do you update an environment variable from inside an Exec task?

I am trying to pipe the output from a command to an Environment variable thus:
<Exec Command="for /f "tokens=*" %%i in ('svn info') do SET SVNINFO=%%i" />
and then use SVNINFO as a property in MSBuild.
While the command line counterpart:
for /f "tokens=*" %i in ('svn info') do SET SVNINFO=%i
works, the change in the value of the Environment variable when called from the Exec does not persist. (I am not able to obtain its value as a property.) Am I missing something here? Is there any better way to achieve this?
Starting with .NET 4.5 you can capture output of the Exec task using its ConsoleOutput parameter by setting ConsoleToMsBuild="true" (documentation). For example, the following target captures %TIME% value into the Time MSBuild property:
<Project>
<Target Name="Build">
<Exec Command="echo %TIME%" ConsoleToMSBuild="true">
<Output TaskParameter="ConsoleOutput" PropertyName="Time" />
</Exec>
<Message Text="Current time is $(Time)" />
</Target>
</Project>
Maybe using the Exec Task Output is a better way:
<Project DefaultTargets="DefaultTarget" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="Exe">
<Exec Command="echo %PATH%">
<Output TaskParameter="Outputs" PropertyName="ExecOutput" />
</Exec>
</Target>
<Target Name="DefaultTarget" DependsOnTargets="Exe">
<Message Text="Result from Exec is $(ExecOutput)" />
</Target>
</Project>

How do you stop MSBuild execution without raising an error?

With MSBuild, as soon as an error occurs, the execution of the project is stopped unless ContinueOnError=true.
Is there a way to stop the execution of the project without raising an error?
I'd like to have this possibility because I have an existing set of msbuild project files and in some circumstances, I would need to stop processing the projects without raising an error because it is a normal exit point for the process and I don't want the person using the script to think something is wrong.
I know I could just set some property and put all remaining tasks conditional on this but I would like to avoid that.
As you explain it, you want to stop your build under special circumstance without raising an error because it is a normal exit point. Why not create a target doing nothing that will serve as your exit point. Under your special conditions you will call this target.
<target Name="BuildProcess">
<Message Text="Build starts"/>
...
<CallTarget Targets="Exit"
Condition="Special Condition"/>
<CallTarget Targets="Continue"
Condition="!(Special Condition)"/>
...
</target>
<target Name="Continue">
<Message Text="Build continue"/>
</target>
<target Name="Exit">
<!-- This target could be removed -->
<!-- Only used for logging here -->
<Message Text="Build ended because special condition occured"/>
</target>
The way to do this is the create another target to wrap the target you're interested in conditioning.
So if you have a scenario with a target like this:
<Target Name="MainTarget">
command - run under a certain condition
command - run under a certain condition
command - run under a certain condition
command - run under a certain condition
command - run under a certain condition
</Target>
The point is that you want to save having to use the condition statement a whole bunch of times, right?
To address this, you can do this:
<Target Name="MainWrapper" DependsOnTargets="EstablishCondition;MainTarget" />
<Target Name="EstablishCondition">
<SomeCustomTask Input="blah">
<Output PropertyName="TestProperty" TaskParameter="value" />
</SomeCustomTask>
</Target>
<Target Name="MainTarget" Condition="$(TestProperty)='true'">
command
command
command
command
command
</Target>
Eventually found an elegant solution for a similar issue. I just needed to rephrase my concern from "Break/interrupt MSBuild execution" to "Skip the next targets".
<PropertyGroup>
<LastInfoFileName>LastInfo.xml</LastInfoFileName>
<NewInfoFileName>NewInfo.xml</NewInfoFileName>
</PropertyGroup>
<Target Name="CheckSomethingFirst" BeforeTargets="DoSomething">
<Message Condition="ConditionForContinue"
Text="Let's carry on with next target" />
<WriteLinesToFile Condition="ConditionForContinue"
File="$(NewInfoFileName)"
Lines="#(SomeText)"
Overwrite="true" />
<Message Condition="!ConditionForContinue"
Text="Let's discard next target" />
<Copy Condition="!ConditionForContinue"
SourceFiles="$(LastInfoFileName)"
DestinationFiles="$(NewInfoFileName)" />
</Target>
<Target Name="DoSomething" Inputs="$(NewInfoFileName)"
Outputs="$(LastInfoFileName)">
<Message Text="DoSomethingMore" />
<Copy SourceFiles="$(NewInfoFileName)"
DestinationFiles="$(LastInfoFileName)" />
</Target>
This works ok with a command like:
msbuild.exe Do.targets /t:DoSomething
where target DoSomething Inputs/Outputs are correctly checked after the CheckSomethingFirst target was executed.

Teamcity build loops on successful build

I have set up a build with Teamcity. See my build file below.
When the build is succesful and the tests pass, the build process just runs again and again indefinitely in a loop.
When the build fails, this does not happen.
I have tried to first set 60 second pause on buildtriggering, and finally disabled build triggering altogether. No difference.
What else could be the cause of this?
My MSBuild file looks like this:
<Project DefaultTargets="Build;Test" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5">
<PropertyGroup>
<DeployDirectory>$(MSBuildProjectDirectory)\..\bin</DeployDirectory>
<DependencyDirectory>$(MSBuildProjectDirectory)\Dependencies</DependencyDirectory>
<LinqToSqlMapFolder>$(DeployDirectory)\LinqToSql</LinqToSqlMapFolder>
<NCoverVersionForMSI>$(BUILD_NUMBER)</NCoverVersionForMSI>
<NCoverVersionPeriod>$(BUILD_NUMBER)</NCoverVersionPeriod>
</PropertyGroup>
<ItemGroup>
<ProjectFiles Include="**\*.vbproj"/>
<ConfigFiles Include="**\*.config"/>
<MapFiles Include="**\*.linqtosql.config"/>
<TestAssemblies Include="$(DeployDirectory)\*.Test.dll"/>
<Dependencies Include="$(DependencyDirectory)\**\*" />
</ItemGroup>
<Target Name="Clean">
<MSBuild Projects="#(ProjectFiles)" Targets="Clean"/>
</Target>
<Target Name="Build">
<MSBuild Projects="#(ProjectFiles)" Targets="Rebuild">
<Output TaskParameter="TargetOutputs" ItemName="BuildOutput"/>
</MSBuild>
<Copy SourceFiles="#(BuildOutput)" DestinationFolder="$(DeployDirectory)" />
<Copy SourceFiles="#(Dependencies)" DestinationFolder="$(DeployDirectory)" SkipUnchangedFiles="true" />
<Copy SourceFiles="#(ConfigFiles)" DestinationFolder="$(DeployDirectory)" SkipUnchangedFiles="true" />
<Copy SourceFiles="#(MapFiles)" DestinationFolder="$(LinqToSqlMapFolder)" SkipUnchangedFiles="true" />
</Target>
<UsingTask AssemblyFile="$(DependencyDirectory)\Gallio\Gallio.MsBuildTasks.dll" TaskName="Gallio" />
<Target Name="Test">
<Gallio IgnoreFailures="true" Files="#(TestAssemblies)">
<Output TaskParameter="ExitCode" PropertyName="ExitCode"/>
</Gallio>
</Target>
</Project>
While it appears that this wasn't your issue, I ran into a similar looping problem of my own. I had enabled labeling in the project configuration. I was also using a check for modifications every 60 seconds rule to trigger the build. As a result, upon successful build, TeamCity would tag the build in the VCS and then 60 seconds later, it would see (it's own) modification and trigger another build.
To fix our problem, we just disabled labeling because we didn't want it anyways, but you can also configure a rule to ignore particular authors such that it won't trigger on modifications it made.
It appears there were some problems with the install of teamcity, and a backup of configuration following a reinstall solved the problem with exactly same configuration and buildscript.