Incremental get not detecting csproj changes?
Hi there,
As detailed in another post I've implemented Aarons incremental get hack to try and speed up CoreGet during a teambuild. This seems to work, however, since we've started to use this trick, we've started getting some curious build errors, that have me doubting it.
Basically, it seems that even though the source code is being refreshed (I can see this by browsing to the Sources dir of the Build directory on the build machine), it appears that old versions of the csproj files are being kept in memory (or something).
Here is a sequence of steps to be executed while using the Incremental Get trick
TeamBuild a project containing a number of files - all is OK
Locally, build the project in VS - all OK
Locally, delete, or move a .CS file in the project
Check in project
Locally build, all OK.
TeamBuild the project, and we see compile errors indicating that the .cs files cannot be found
Manually eyeball the csproj on the build server, and the <Compile> item group is correct (ie the removed file is indeed NOT within the project)
Upon executing the TeamBuild again, (with no changes) it will compile correctly
We've seen this happen with files that were moved or deleted, or indeed new files that are added to the project and referenced by existing.
It's as if the csproj is somehow being kept in memory by teambuild, and not updated even though the latest is retrieved from TFS
Here is the msbuild-ish that I added to my TFSBuild.proj:
<!--
INCREMENTAL GET HACK
PMcE: We want to do an incremental GET, but a CLEAN build. This is not possible using
the ForceGet, SkipClean and SkipInitializeWorkSpace properties out of the box.
So, using solution described here:
http://blogs.msdn.com/aaronhallberg/archive/2007/01/03/doing-an-incremental-get-in-team-build.aspx
Which DOES do an incremental GET and a CLEAN build
-->
<
PropertyGroup><
SkipClean>false</SkipClean><
SkipInitializeWorkspace>true</SkipInitializeWorkspace><
ForceGet>false</ForceGet></
PropertyGroup><
Target Name="BeforeClean"><!--
Delete BinariesRoot and TestResultsRoot to be thorough. --><
RemoveDir Condition="Exists('$(BinariesRoot)')" Directories="$(BinariesRoot)" /><
RemoveDir Condition="Exists('$(TestResultsRoot)')" Directories="$(TestResultsRoot)" /><!--
Rather than delete SolutionRoot, set IsDesktopBuild to true for the duration of the CoreClean target. --><
CreateProperty Value="true"><
Output TaskParameter="Value" PropertyName="IsDesktopBuild" /></
CreateProperty></
Target><
Target Name="AfterClean"><
CreateProperty Value="false"><
Output TaskParameter="Value" PropertyName="IsDesktopBuild" /></
CreateProperty></
Target><!--
END INCREMENTAL GET HACK
-->
Answers
- Well, it looks like my diagnosis was correct on this issue. Unfortunately, the ability to unload projects is an MSBuild 3.5 feature, so it won't work with Team Build V1 (and MSBuild 2.0). So - you're stuck with my second idea: re-ordering the Get and Clean targets.
Try out the following - make sure to place this property group after the import of Microsoft.TeamFoundation.Build.targets to make sure your declaration overrides the initial declaration:
<PropertyGroup>
<EndToEndIterationDependsOn>
BeforeEndToEndIteration;
BuildNumberOverrideTarget;
InitializeEndToEndIteration;
InitializeBuild;
PreBuild;
Clean;
TeamBuild;
DropBuild;
AfterEndToEndIteration;
</EndToEndIterationDependsOn>
<TeamBuildDependsOn>
Compile;
PostBuild;
Test;
PackageBinaries;
</TeamBuildDependsOn>
</PropertyGroup>
Note that this re-ordering should not be done for typical builds - it will only work in combination with the IncrementalGet hack (since otherwise the Clean will just delete all the files retrieved by the Get).
Also note that the original blog post that detailed this hack has also been updated: http://blogs.msdn.com/aaronhallberg/archive/2007/01/03/doing-an-incremental-get-in-team-build.aspx. Thanks, Peter, for noticing this bug!
All Replies
- I haven't had time to investigate this as of yet, but I think what is happening is that MSBuild loads the old version of the project for the Clean (since this comes before the Get), and then doesn't unload the project for the Compile even though it has changed in the meantime. I suspect there should be some way to instruct MSBuild to unload the project, but I'm not entirely sure. If not, another possibility would be to change the order of the Get and the Clean. I'll try to look into this later today.
-Aaron - Well, it looks like my diagnosis was correct on this issue. Unfortunately, the ability to unload projects is an MSBuild 3.5 feature, so it won't work with Team Build V1 (and MSBuild 2.0). So - you're stuck with my second idea: re-ordering the Get and Clean targets.
Try out the following - make sure to place this property group after the import of Microsoft.TeamFoundation.Build.targets to make sure your declaration overrides the initial declaration:
<PropertyGroup>
<EndToEndIterationDependsOn>
BeforeEndToEndIteration;
BuildNumberOverrideTarget;
InitializeEndToEndIteration;
InitializeBuild;
PreBuild;
Clean;
TeamBuild;
DropBuild;
AfterEndToEndIteration;
</EndToEndIterationDependsOn>
<TeamBuildDependsOn>
Compile;
PostBuild;
Test;
PackageBinaries;
</TeamBuildDependsOn>
</PropertyGroup>
Note that this re-ordering should not be done for typical builds - it will only work in combination with the IncrementalGet hack (since otherwise the Clean will just delete all the files retrieved by the Get).
Also note that the original blog post that detailed this hack has also been updated: http://blogs.msdn.com/aaronhallberg/archive/2007/01/03/doing-an-incremental-get-in-team-build.aspx. Thanks, Peter, for noticing this bug! - No worries, cheers for confirming..


