How do you stop MSBuild execution without raising an error? - msbuild

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.

Related

MSBuild afterbuild step to copy code into a temp folder

I'm trying to place my compiled web application into a temporary directory after it has been built.
I have the following but it doesn't seem to work. It drops and create directors but the msbuild task doesn't seem to copy the compile output into the obj/publish directory that i need it to?
<Target Name="AfterBuild">
<CallTarget Targets="Publish" />
</Target>
<Target Name="Publish">
<RemoveDir Directories="$(SolutionDir)AsycLearn\obj\publish\" ContinueOnError="true" />
<MakeDir Directories="$(SolutionDir)AsycLearn\obj\publish\"/>
<MSBuild Projects="AsycLearn.csproj" Targets="ResolveReferences;_CopyWebApplication" Properties="WebProjectOutputDir=$(SolutionDir) AsycLearn\obj\publish\;OutDir=$(SolutionDir) AsycLearn\obj\publish\bin\" />
</Target>
any ideas? Thanks
It looks like you need to quote your output paths because they contain spaces. Otherwise the value that gets read will be truncated at the first space. That will also terminate the parameter set that gets passed to any other tasks.
Properties="WebProjectOutputDir=$(SolutionDir) AsycLearn\obj\publish\;OutDir=$(SolutionDir) AsycLearn\obj\publish\bin\"
should be
Properties="WebProjectOutputDir="$(SolutionDir) AsycLearn\obj\publish\";OutDir="$(SolutionDir) AsycLearn\obj\publish\bin\""
The escape sequence " is needed because we want the resolved value to include quotes.

MSBuild script dependsOnTargets order of execution

I have this line in my build script
<Target Name="Deploy" DependsOnTargets="ServicesInstall;SitesTransfer" >
What I want to know is, in this example, what order will the targets get executed. Also, if ServiceInstall has dependencies do they get executed before or after SiteTransfer. In other words is the execution done in a depth first or breadth first manner?
Thanks,
Sachin
As an experiment I tried this:
<Target Name="Deploy" DependsOnTargets="ServicesInstall;SitesTransfer" />
<Target Name="ServicesInstall" DependsOnTargets="ServicesInstallDependency">
<Message Text="ServicesInstall" />
</Target>
<Target Name="ServicesInstallDependency">
<Message Text="ServicesInstallDependency" />
</Target>
<Target Name="SitesTransfer">
<Message Text="SitesTransfer" />
</Target>
and this was the output:
...
1>ServicesInstallDependency:
1> ServicesInstallDependency
1>ServicesInstall:
1> ServicesInstall
1>SitesTransfer:
1> SitesTransfer
...
However, I suspect the exact sequence is undefined. It is not documented on msdn.
In other words, msbuild will only guarantee that the constraints you specify are satisfied. If you need to guarantee SitesTransfer and its dependencies are executed before ServicesInstall, you should make ServicesInstall depend on SitesTransfer.

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.

MSBuild ContinueOnError

I have an MSBuild project as follows:
<Target Name="StopApplications">
<BizTalk.BuildGenerator.Tasks.StopApplication MessageBoxConnection="$(BizTalkManagementDatabaseConnectionString)" ApplicationName="x.Direct.Brackmills"/>
<BizTalk.BuildGenerator.Tasks.StopApplication MessageBoxConnection="$(BizTalkManagementDatabaseConnectionString)" ApplicationName="x.Direct.Manhattan"/>
</Target>
<Target Name="RemoveApplications">
<Exec Command="BTSTask RemoveApp -ApplicationName:x.Direct.Brackmills -Server:$(BizTalkDatabaseServerName) -Database:$(BizTalkManagementDatabaseName)" />
<Exec Command="BTSTask RemoveApp -ApplicationName:x.Direct.Manhattan -Server:$(BizTalkDatabaseServerName) -Database:$(BizTalkManagementDatabaseName)" />
</Target>
My problem is that when calling the "RemoveApplications" target, the ContinueOnError does not work as I'd expect. I have a long list of applications to stop and remove. They won't all allways be present so I need the script to continue when it finds they're not there. This seems to work find for the "StopApplications" target but when it hits a missing application in the "RemoveApplications" target I get the message:
"Done building target "RemoveApplications" in project "cleardownApplications.proj" -- FAILED. Build continuing because "ContinueOnError" on the task "CallTarget" is set to "true".
But then, it drops out of "RemoveApplications" and moved onto "AddApplications"
Any help gratefully received,
Thanks
Rob.
I've solved this a bit differently and uses a separate target to check if the application exists before removing it.
<Target Name="ApplicationExists">
<BizTalk2006.Application.Exists Application="$(ApplicationName)">
<Output TaskParameter="DoesExist" PropertyName="ApplicationExists" />
</BizTalk2006.Application.Exists>
</Target>
The I use that exists-target as an "condition" in other targets.
<Target Name="DeleteApplication" Condition="$(ApplicationExists)=='True'" DependsOnTargets="ApplicationExists">
<BizTalk2006.Application.Stop Application="$(ApplicationName)"/>
<BizTalk2006.Application.Delete Application="$(ApplicationName)"/>
</Target>

try...finally equivalent in MsBuild

How can I run a certain cleanup task after my "Test" target runs, regardless of whether the Test target succeeded or failed (like the try...finally construct in C#/Java).
The Target element has an OnError attribute you could set to a target to execute on error, but as it only executes if the target is in error, it only solves half your scenario.
Have you considered chaining together targets to represent the test 'steps' you'd like to execute?
<PropertyGroup>
<TestSteps>TestInitialization;Test;TestCleanup</TestSteps>
</PropertyGroup>
The 'TestInitialization' target is where you can perform any test initialization, the 'Test' target executes the test, the 'TestCleanup' target does any sort of post test clean up.
Then, execute these targets using the CallTarget task, using the RunEachTargetSeparately attribute set to True. This will execute all the targets, regardless of success or failure.
The complete sample is below:
<Project DefaultTargets = "TestRun"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003" >
<!-- Insert additional tests between TestInitialization and TestCleanup as necessary -->
<PropertyGroup>
<TestSteps>TestInitialization;Test;TestCleanup</TestSteps>
</PropertyGroup>
<Target Name = "TestRun">
<CallTarget Targets="$(TestSteps)" RunEachTargetSeparately="True" />
</Target>
<Target Name = "TestInitialization">
<Message Text="Executing Setup..."/>
</Target>
<Target Name = "Test">
<Message Text="Executing Test..."/>
<!-- this will fail (or should unless you meet the conditions below on your machine) -->
<Copy
SourceFiles="test.xml"
DestinationFolder="c:\output"/>
</Target>
<Target Name = "TestCleanup">
<Message Text="Executing Cleanup..."/>
</Target>
</Project>
Or use <OnError> to call your target in the error case, and DependsOnTargets or CallTarget to call your same target in the normal case.