Msbuild ignores continueonerror? - tfs

I am trying to invoke MSbuild for a couple of projects twice. First time without any clean, but if this fails I will invoke a clean followed by a new build. (The reasoning is that I want my build to be fast but if that fails fall back to clean, restore nuget packages build etcetera). This works but the build will still fail if the first call failed (it has continueonerror set to true so I dont want it to fail...). Here are the relevant parts of the build file:
<ItemGroup>
<ProjectsToPublish Include="X.sln" />
</ItemGroup>
<Target Name="RestoreAllPackages">
<Message Text="#(ProjectsToPublish)" />
<Exec Command='"$(MSBuildProjectDirectory)\.nuget\nuget.exe" restore "$(MSBuildProjectDirectory)\%(ProjectsToPublish.Identity)"'
/>
</Target>
<Target Name="Build" >
<MSBuild Projects="#(ProjectsToPublish)" Properties="SkipRestore=True;RunCodeAnalysis=False;Retries=10;RetryDelayMilliseconds=50"
BuildInParallel="true" ContinueOnError="WarnAndContinue" />
<!-- MSBuildLastTaskResult outcome of previous task-->
<PropertyGroup>
<FastBuildFailed>false</FastBuildFailed>
<FastBuildFailed Condition="'$(MSBuildLastTaskResult)' == 'false'" >true</FastBuildFailed>
</PropertyGroup>
<Message Importance="high" Text="Initial build failed? $(FastBuildFailed)" />
<Message Importance="high" Text="Initial build failed will retry" Condition="'$(FastBuildFailed)'" />
<CallTarget Targets="FullBuild" Condition="'$(FastBuildFailed)'" />
</Target>
<Target Name="FullBuild" DependsOnTargets="RestoreAllPackages">
<!--Fake property below to reexecute build exact same properties prevents the build lform re-->
<MSBuild Projects="#(ProjectsToPublish)" Properties="SkipRestore=True;RunCodeAnalysis=False;FakeProperty=one" BuildInParallel="true" />
</Target>
<Target Name="RestoreAllPackages">
<Message Text="#(ProjectsToPublish)" />
<Exec Command='"$(MSBuildProjectDirectory)\.nuget\nuget.exe" restore "$(MSBuildProjectDirectory)\%(ProjectsToPublish.Identity)"'
/>
</Target>

For anyone with this rather exotic error. It seems that when a build is run inside of a TFS build server, the build server parses log output and will fail a build even with a ContinueOnError setting. My workaround ended up being <Exec Command="$(MSBuildBinPath)\msbuild.exe #(ProjectsToPublish) /noconlog " ContinueOnError="true" />
Basically spawning a new MSBuild using Exec and making sure that it did not output anything using /noconlog

Rather than using <OnError..., you can assign ContinueOnError the value 'WarnAndContinue', and then use a condition to check the $(MSBuildLastTaskResult) property.
Generic example:
<Error ContinueOnError="WarnAndContinue" />
<Message Importance="High" Text ="$(MSBuildLastTaskResult)" />
<!-- (Returns false) -->
(I believe both "WarnAndContinue" and $(MSBuildLastTaskResult) were introduced in MSBuild 4.0; they should be available on your TFS 2012 build server.)

Related

What makes msbuild wait another node building the same project?

I've made a test building my project solution twice in parallel to see what happens. Turns out one MSBuild node waits the other to finish before trying to build my solution and then says it's up-to-date. Ex.:
<!-- ProjectA.proj -->
<Project>
<ItemGroup>
<Project Include="MyProject.sln" />
</ItemGroup>
<Target Name="Build">
<MSBuild Projects="#(Project)" Targets="Build" BuildInParallel="true" />
</Target>
</Project>
<!-- Test.proj -->
<Project>
<ItemGroup>
<Project Include="ProjectA.proj" />
<Project Include="ProjectA.proj" />
</ItemGroup>
<Target Name="Build">
<MSBuild Projects="#(Project)" Targets="Build" BuildInParallel="true" />
</Target>
</Project>
<!-- When I check the build log it says
1> Project is building MyProject.sln (2) ...
1> Project is building MyProject.sln (2:2) ...
2> Building with tools...
2> ...
2:2> Building with tools...
2> ...
2> ...
2> ...
2> Done building
2:2> Target ... skipped because it was previously built successfully
2:2> Done building
-->
This is a desired behaviour, but when I try to rely on this to make my build workflow oh the build server, my projects are built twice simultaneously, giving me lots of errors.
I do not understand the difference and don't know what to do to find out the problem.

Microsoft Tfs build variable gets lost

I am running build with next targets:
<Target Name="BeforeCompile">
<Message Text="Build no: $(BuildNumber)" />
</Target>
Then in between I run build (using nmake). After that I want to deploy firmware:
<Target Name="AfterCompile">
<MSBuild Projects="$(MSBuildProjectFile)" Targets="CustomDeployMagicFirmware" Properties="Id=1" />
</Target>
<Target Name="CustomDeployMagicFirmware">
<Message Text="...Deploying bootloader files to build machine - folder: $(BuildNumber)" />
<Exec Command='xcopy "$(SolutionRoot)\Repository\bootloader\*.axf" \\machine\bootloader\$(BuildNumber) /y /q'/>
<Exec Command='xcopy "$(SolutionRoot)\Repository\bootloader\*.hex" \\machine\bootloader\$(BuildNumber) /y /q'/ -->
</Target>
The problem is that in target "BeforeCompile" I get properly message output for $(BuildNumber). But later I have Message Test output like:
...Deploying bootloader files to build machine - folder:
There looks like variable $(BuildNumber) is not set anymore. Also command xcopy copies files to folder bootloader and not to folder bootloader\$(BuildNumber).
What do I do wrong? Which things can influence variable contents?
The error is in this line
<MSBuild Projects="$(MSBuildProjectFile)" Targets="CustomDeployMagicFirmware" Properties="Id=1" />
The MSBuild task spawns a new MSBuild.exe process and you are not passing the property $(BuildNumber) to the new process. Change it like below and this would work
<MSBuild Projects="$(MSBuildProjectFile)" Targets="CustomDeployMagicFirmware" Properties="Id=1;BuildNumber=$(BuildNumber)" />
Use <CallTarget> instead of <MSBuild> to invoke your CustomDeployMagicFirmware target.

jdtCompileLogPublisher javac

I have ant build script that compiles and generates the compilation logs(in file compileLog.xml) of my project. I use jdtCompileLogPublisher to publish these results the RTC.
Now the situation is when i have the sources in the project its working fine. When i don't have any sources in the project(this is a one of the requirement that there will be an empty project with out sources), compilation step is skipped as there are no sources,and jdtCompileLogPublisher is failing because, the file compileLog.xml which is an input for this step is not created as compilation itself has been skipped.
I tried creating a new compileLog.xml if it is not available. As a result of this I'm encountering new issues as file is empty.
Why javac is skipped when there are no java files to compile?
Is there any possibility to make the compiler to execute javac in build.xml
Version ant: 1.7.1.
snippet of my code related to :
<target name="compile" depends="prepare" description="Compile the source code">
<mkdir dir="${nameBuild}/${dir.build}/bin" />
<!-- Add target ${label.jdkversion} to make it possible to build with a certain jdk version-->
<javac compiler="org.eclipse.jdt.core.JDTCompilerAdapter" destdir="${nameBuild}/${dir.build}/bin" debug="true" deprecation="on" encoding="ISO-8859-1" source="${buildInfo.jdkVersion}" target="${buildInfo.jdkVersion}" debuglevel="lines,source" failonerror="false" errorProperty="buildFailed">
<compilerarg line="-warn:+raw" />
<compilerarg line="-warn:-serial" />
<compilerarg line="-enableJavadoc" />
<compilerarg line="-log source/${name}/compileLog.xml" />
<src path="${name}/${dir.src}" />
<classpath refid="application.classpath" />
</javac>
<jdtCompileLogPublisher buildResultUUID="${buildInfo.buildResultUUID}" repositoryAddress="${repositoryAddress}" userId="${userId}" passwordFile="${psFile}" filePath="compileLog.xml" />
<fail if="buildFailed" message="Compile ${name} failed." />
</target>
I modified the tag
<jdtCompileLogPublisher buildResultUUID="${buildInfo.buildResultUUID}" repositoryAddress="${repositoryAddress}" userId="${userId}" passwordFile="${psFile}" filePath="compileLog.xml" />
to
<jdtCompileLogPublisher buildResultUUID="${buildInfo.buildResultUUID}" repositoryAddress="${repositoryAddress}" userId="${userId}" passwordFile="${psFile}" filePath="compileLog.xml" failonerror="false" />
Which solved my problem

MSBuild & TFS2010 Find Dlls

I am trying to create a custom MSBuild task that will run my nUnit tests either locally or during a TFS2010 build. The script works great locally but I can't seem to find the test dlls on the TFS build server. I'm using a task in the MSBuild.ExtensionPack to run the unit tests (again this works fine locally).
The Assemblies list is always empty. TargetDir shows the path is "C:\Builds\2\Product1\ci.product1.acme.com\Binaries\" which looks right to me. I also tried to kick off my target after the CoreCompile target instead within the AfterBuild target with no change.
I've probably made some stupid mistake but I'm in kill me mode at this point. Please help.
<PropertyGroup>
<RunTFSBuild>false</RunTFSBuild>
<SolutionDirectory>$(MSBuildProjectDirectory)\..</SolutionDirectory>
<ExtensionTasksPath>$(SolutionDirectory)\_shared\MSBuild\</ExtensionTasksPath>
<TPath>$(ExtensionTasksPath)MSBuild.ExtensionPack.tasks</TPath>
<NUnitOutputFile>$(SolutionDirectory)\nUnitResults.xml</NUnitOutputFile>
<NUnitOutputFileAsMsTest>$(SolutionDirectory)\nUnitResultsAsMsTestResults.xml</NUnitOutputFileAsMsTest>
<ToolPath>$(SolutionDirectory)\_shared\MSBuild\nUnit_2.5.7</ToolPath>
</PropertyGroup>
<Import Project="$(TPath)" />
<Target Name="AfterBuild">
<CallTarget Condition="$(RunTFSBuild)!='true'" Targets="NUnitTestRunner" />
<CallTarget Condition="$(RunTFSBuild)=='true'" Targets="NUnitTestRunner;TFSNUnitTestRunner" />
</Target>
<Target Name="NUnitTestRunner">
<ItemGroup >
<Assemblies Include="$(SolutionDirectory)\**\bin\$(Configuration)\*.nUnit.Tests.dll" />
</ItemGroup>
<ItemGroup Condition="$(RunTFSBuild)=='true'">
<Assemblies Include="$(TargetDir)\**\*.nUnit.Tests.dll" />
</ItemGroup>
<Message Text="SolutionDirectory=$(SolutionDirectory)" />
<Message Text="ExtensionTasksPath=$(ExtensionTasksPath)" />
<Message Text="TargetDir=$(TargetDir)" />
<Message Text="TPath=$(TPath)" />
<Message Text="NUnitOutputFile=$(NUnitOutputFile)" />
<Message Text="Running nUnit tests from: $(Assemblies)" />
1) Try to change property RunTFSBuild
<RunTFSBuild Condition="'$(RunTFSBuild)'==''">false</RunTFSBuild>
2) Replace sections (it may be just cleaning of project, but it may be very important due to some limitations of CallTarget):
<Target Name="AfterBuild" DependsOnTargets="NUnitTestRunner;TFSNUnitTestRunner" />
<Target Name="TFSNUnitTestRunner"
Condition="$(RunTFSBuild)=='true'">
<!-- TFSNUnitTestRunner Body -->
</Target>
3) If 1) and 2) willn't be helpful try to set verbose of the build to diagnostic (msbuld key /v:diag). Find all calls of TFSNUnitTestRunner in the log and you will see what is actually happened on TFS.
EDIT: *Assemblies* is declared as ItemGroup. Use #(Assemblies) to access items:
<Message Text="Running nUnit tests from: #(Assemblies)" />
Property $(Assemblies) will always be empty in your case.

MSBuild conditional Exec?

I am building various projects using the <MSBuild Projects="... markup. I am then executing some command line tools after the project is built.
E.g
<Target Name="Name">
<MSBuild Projects="" />
<Exec Command="" />
</Target>
I notice that the project is only built as required and get the following output when the build script is run: "Skipping target "CoreCompile" because all output files are up-to-date". This is great but how do I make my <Exec... commands use the same condition so that they are only run when necessary as well?
Update: I've implemented gregmac's suggestion but its still executing the command regardless. This is what I've got now:
<Target Name="Name">
<MSBuild Projects="">
<Output TaskParameter="TargetOutputs" ItemName="AssembliesBuiltByChildProjects" />
</MSBuild>
<Exec Command="" Condition="'#(AssembliesBuiltByChildProjects)'!=''" />
Any further help is much appreciated. This is a bit of a sticking point for me.
Thanks for any tips.
Alan
You should be able to use the TargetOutputs parameter:
<MSBuild Projects="" >
<Output TaskParameter="TargetOutputs" ItemName="AssembliesBuiltByChildProjects" />
</MSBuild>
<Message Text="Assemblies built: #(AssembliesBuiltByChildProjects)" /> <!-- just for debug -->
<Exec Command="" Condition="'#(AssembliesBuiltByChildProjects)'!=''" />
If you can add the following to each of your projects:
<Target Name="DoStuffWithNewlyCompiledAssembly">
<Exec Command="" />
</Target>
... then you only need to add a property:
<Target Name="Name">
<MSBuild Projects="" Properties="TargetsTriggeredByCompilation=DoStuffWithNewlyCompiledAssembly" />
</Target>
This works because someone smart at Microsoft added the following line at the end of the CoreCompile target in Microsoft.[CSharp|VisualBasic][.Core].targets (the file name depends on the language and MSBuild/Visual Studio version).
<CallTarget Targets="$(TargetsTriggeredByCompilation)" Condition="'$(TargetsTriggeredByCompilation)' != ''"/>
So if you specify a target name in the TargetsTriggeredByCompilation property, your target will run if CoreCompile runs-- and your target will not run if CoreCompile is skipped (e.g. because the output assembly is already up-to-date with respect to the code).
You are asking wrong question.
Exec does not have any condition, But you can have condition on Target element which can be used like this.
<Target Name="Name" Condition="#(AssembliesBuiltByChildProjects)'!=''">
<MSBuild Projects="">
<Output TaskParameter="TargetOutputs" ItemName="AssembliesBuiltByChildProjects" />
</MSBuild>
<Exec Command=""/>
</Target>
<Target Name="Name" Condition="#(AssembliesBuiltByChildProjects)'==''">
...
</Target>
I did manage to find a solution to fit my needs although it may not be the optimal solution.
See my answer to my other question here: MSBuild Post-Build
Thanks,
Alan

Resources