What makes msbuild wait another node building the same project? - tfs

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.

Related

MSBuild (for Delphi) Make-like functionality

I am trying to set up dependencies for a set of Delphi packages, such at if Application A uses Package 1 and Package 2, and Application B uses Package 1 only, then only Application A is rebuilt when Package 2 is changed.
At the moment, I am using a Group File, and each Package and Application is built (re-linked), even if it or one of it's dependencies is not changed.
UPDATE:
Unit1.pas is in Package 1
Unit2.pas is in Package 2
Application A uses Package 1 and Package 2
Application B use Package 2
What I want is that if I change Unit1.pas, then ONLY Package 1 and Application B is rebuilt. make will do this, but I can't work out how MSBUILD could be made to work.
My worked example uses a group file, which might be the underlying problem.
OK, I think I have sorted this out. Groupprojs will always rebuild / relink, so that is not what I want.
I have created a targets file, such that it defines the 4 projects and the dependencies of each. It has to be all the dependencies, both code and binary (i.e. the packages).
Msbuild has an attribute 'DependsOnTarget', but this doesn't achieve what I want, so I have added dependencies on both the code and packages. The final build.targets file looks like this ...
<Project ToolsVersion="3.5"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003"
DefaultTargets="Default">
<ItemGroup>
<Package1 Include=".\Package1\Package1.dproj"/>
<Package2 Include=".\Package2\Package2.dproj"/>
<App6 Include=".\App6\Project6.dproj"/>
<App7 Include=".\App7\Project7.dproj"/>
</ItemGroup>
<ItemGroup>
<Package1ExternalSourceFiles Include=".\Package1\*.pas" />
<Package2ExternalSourceFiles Include=".\Package2\*.pas" />
<App6ExternalSourceFiles Include=".\App6\*.pas" />
<App7ExternalSourceFiles Include=".\App7\*.pas" />
<Package1BinaryFiles Include=".\Package1\Win32\Release\Package1.bpl" />
<Package2BinaryFiles Include=".\Package2\Win32\Release\Package2.bpl" />
<App6BinaryFiles Include=".\App6\bin\Project6.exe" />
<App7BinaryFiles Include=".\App7\bin\Project7.exe" />
</ItemGroup>
<Target Name="App6" Inputs="#(App6ExternalSourceFiles);#(Package1BinaryFiles);#(Package2BinaryFiles)" Outputs="#(App6BinaryFiles)" DependsOnTargets="Package1;Package2">
<Message Text="App6" />
<MSBuild Projects="#(App6)"/>
</Target>
<Target Name="App7" Inputs="#(App7ExternalSourceFiles);#(Package1BinaryFiles)" Outputs="#(App7BinaryFiles)" DependsOnTargets="Package1">
<Message Text="App7" />
<MSBuild Projects="#(App7)"/>
</Target>
<Target Name="Package1" Inputs="#(Package1ExternalSourceFiles)" Outputs="#(Package1BinaryFiles)">
<Message Text="Package1" />
<MSBuild Projects="#(Package1)"/>
</Target>
<Target Name="Package2" Inputs="#(Package2ExternalSourceFiles)" Outputs="#(Package2BinaryFiles)">
<Message Text="Package2" />
<MSBuild Projects="#(Package2)"/>
</Target>
<Target Name="Build">
<CallTarget Targets="Package1;Package2;App6;App7"/>
</Target>
</Project>
Whether this is scalable I am not sure, we could potentially have a large number of projects and dependencies.
UPDATE: Actually the binary dependency on the Applications isn't needed, if they are shipped separate from the packages. Recompiling Package 1, without changing it's interface would mean that App6 and App7 are not rebuilt.

Msbuild ignores continueonerror?

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.)

How do I setup a TFS 2010 Workflow build process similar to my current NAnt process

I have a build process is in NAnt and resembles the following structure (a lot has been abreviated). I'm having some confussion on what I can put into Workflow from my NAnt Script and what I should convert to MSBuild(or not). Various targets within the NAnt script will set properties, copy files, delete files, make calls to external processes such as compiling VB6 projects. Any tips on porting this over to workflow would be greatly appreciated. Please let me know if you have questions.
<?xml ... >
<project ...>
<!-- Get and combine paths -->
<properties name="" value=""/>
<properties name="" value=""/>
<properties name="" value=""/>
.
.
.
.
<target name="Main">
<!--Set Log Folder Name to include date and time.
<mkdir dir="${LogDir}"/>
<call target="DeleteTicketsFile"/>
<call target="GetTickets"/>
<call target="WriteTicketsToFile"/>
<call target="WriteProperties"/>
<call target="DeleteFolders" failonerror="true"/>
<call target="GetLatest" failonerror="true"/>
<call target="BuildDOTNETSolution" failonerror="true"/>
<call target="BuildVB6Projects" failonerror="true"/>
.
.
.
<target name="BuildDOTNETSolution">
<property name="ProjectName" value="Localcache" />
<echo message="VCVarsAllBatFile = ${VCVarsAllBatFile}"/>
<exec program="${VCVarsAllBatFile}"/>
<property name="dotnetSlnFile" value="${path::combine(ProductDir, 'dot.net.sln')}"/>
<property name="dotnetOutFile" value="${path::combine(LogDir, 'dotnet.out.txt')}"/>
<echo message="dotnetSlnFile = ${dotnetSlnFile}"/>
<echo message="dotnetOutFile = ${dotnetOutFile}"/>
<delete file="${dotnetOutFile}" if="${file::exists(dotnetOutFile)}" failonerror="false"/>
<!-- Build .NET solution in Release mode -->
<exec program="${DevenvExe}">
<environment>
<variable name="COMSUPPORT" value="N"/>
<variable name="COPYEXECENV" value="N"/>
</environment>
<arg value='"${dotnetSlnFile}"'/>
<arg value='/Rebuild "Release|Any CPU"'/>
<arg value='/Out "${dotnetOutFile}"'/>
</exec>
</target>
</project>
My suggestion is to replace NAnt entirely and use TFS Build Workflow to customize and maintain your build. That said, if your NAnt script it pretty involved that can take some time (especially if you're new to TFS Workflow-based builds). My suggestion to start is to simply have the Workflow Build use the InvokeProcess Activity to execute the NAnt build via the command-line (nant.exe). Then you can incrementally move pieces of the build out of the NAnt script into the workflow.
For example, the entire BuildDotNETSolution target looks like it doesn't do anything special, all this is already included in the DefaultTemplate.xaml build workflow, you simply specify the SolutionsToBuild argument in the Build Definition.

ANT: Using conditional tags, <IF>

I would like to do something like this
<target name="clean" description="clean">
<if>
<available file="${build}" type="dir" />
<then>
<delete dir="${build}" />
</then>
</if>
</target>
As per suggestions found on stackoverflow.com, i downloaded ant-contrib-1.0b3.jar and put it into my path
Additionally, under Ant Build configuration, in classpath, i have
When running my ANT script, i still get
BUILD FAILED
C:\Repositories\blah\build.xml:60: Problem: failed to create task or type if
Cause: The name is undefined.
Action: Check the spelling.
Action: Check that any custom tasks/types have been declared.
Action: Check that any <presetdef>/<macrodef> declarations have taken place.
What am i missing? Please advise
Alternately, once can include the following line in your ANT script
<taskdef resource="net/sf/antcontrib/antlib.xml" />
As long as ant-contribs is in your path, nothing else is needed
This solution is a bit cleaner, as one gains access to ALL the tags, not just the ones manually specified
Same issue resolved in that way.
I put at the start of the buid XML file the following lines:
<taskdef resource="net/sf/antcontrib/antlib.xml">
<classpath>
<pathelement location="your/path/to/ant-contrib-${version}.jar" />
</classpath>
</taskdef>
The standard ant way would be something like =
<target name="check">
<condition property="delbuild">
<available file="${build}" type="dir"/>
</condition>
</target>
<target name="delbuild" depends="check" if="delbuild">
<delete dir="${build}"/>
<!-- .. -->
</target>
A snippet with the Ant Plugin Flaka, a recent alternative to antcontrib. Installation in Eclipse as usual via Preferences | Ant | Runtime | Global Entries | ant-flaka-1.02.-1.02.jar =
<project xmlns:fl="antlib:it.haefelinger.flaka">
<!-- some standalone if construct -->
<fl:when test=" '${build}'.isdir ">
<delete dir="${build}"/>
</fl:when>
<!-- some if/then/else construct -->
<fl:choose>
<!-- if -->
<when test=" '${buildtype}' eq 'prod' ">
<!-- then -->
<echo>..starting ProductionBuild</echo>
</when>
<when test=" '${buildtype}' eq 'test' ">
<!-- then -->
<echo>..starting TestBuild</echo>
</when>
<!-- else -->
<otherwise>
<fl:unless test="has.property.dummybuild">
<fail message="No valid buildtype !, found => '${buildtype}'"/>
</fl:unless>
<echo>.. is DummyBuild</echo>
</otherwise>
</fl:choose>
</project>
output with ant -f build.xml -Dbuildtype=prod or
ant -f build.xml -Dbuildtype=prod -Ddummybuild=whatever
[echo] ..starting ProductionBuild
output with typo => ant - build.xml -Dbuildtype=testt
BUILD FAILED
/home/rosebud/workspace/AntTest/build.xml:21: No valid buildtype !, found => 'testt'
output with ant -f build.xml -Ddummybuild=whatever
[echo] .. is DummyBuild
I got the same issue. I resolved in the following way.
Sometimes this might be compatibility problem.
Right click on build.xml.
Go to "Run As" --> 2 Ant... Select Classpath tab check Ant Home version (Sometimes eclipse selects default ant version).
If the version listed is different, then change Ant Home Classpath
to C:\XXXX\ant\X.X.X.
Finally click on the User Entries --> Add External JARS..--> add
ant-contrib.x.x.jar form C:\XXXX\ant\X.X.X\ant-contrib\ directory.
On the tasks tab for your Ant Runtime do you see the 'if' task?
export ANT_HOME=/path/to/ant/apache-ant-1.8.2
I couldn't get it to work until I had ANT_HOME set properly. Java kept picking another ant installed on the box.

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