Wednesday, June 10, 2009

Preserving Output Directory Structure With Team Build 2008

The default TFS drop folder scheme is a train-wreck where all the binaries and output files from the entire solution are summarily dumped into a single folder (albeit with some special-case handling for websites). Its just gross.

What I wanted, of course, was a series of folders by project containing only the build artefacts relevant for that project: ‘xcopy ready’ as I like to say. Pretty sensible yes? Quite a popular request if one Googles around: one wonders why the train-wreck scheme was implemented at all. But I digress.

Contrary to what you (and I) may have read, you actually have to do this using AfterBuild, not AfterCompile (which fires way too early in the build process[1]). So the canned solution is:

Put this in your TFSBuild.proj:




    <!--  TEAM PROJECT

(this turns off the ‘train-wreck’ scheme, and goes back to each project’s output going to their bin\debug or whatever folder)

Put this in any project file that you want to capture the output from:

  <Target Name="AfterBuild" Condition=" '$(IsDesktopBuild)'!='true' ">

    <Message Text="Copying output files from $(OutDir) to $(TeamBuildOutDir)" />


      <FilesToCopy Include="$(OutDir)\**\*.*" />


    <Copy SourceFiles="@(FilesToCopy)" DestinationFiles="@(FilesToCopy ->'$(TeamBuildOutDir)\$(AssemblyName)\%(RecursiveDir)%(Filename)%(Extension)')" />


And hey presto:



A series of output folders, by assembly, containing only the artefacts relevant to that assembly, as opposed to the

Note however that with this scheme you get all files marked ‘Copy to Output Directory’ but not files marked as Content, which makes this currently unusable for deploying websites and means it’s not strictly speaking xcopy-ready[2]. Hopefully there is an easy fix to this, otherwise I’ll be diving back into the SNAK codebase where I’ve solved this previously.


[1] before serialization assemblies are generated, and before the obj folder has been copied to the bin folder. A good diagnostic is to put <Exec Command="dir" WorkingDirectory="$(OutDir)"/> into an AfterCompile target, and take a look at what you get back.
[2] I have a big thing about this, which is why I dislike the ClickOnce build output so much. Build and deploy need to be considered separately.


un said...

Thanks for posting this it was the first hit on google when I started researching how to fix this problem in my own build. Saved me a lot of time.

My theory as to why they do this is that MS build products are aimed at teams producing in-house small apps. This is the same reason they introduced the useless web site system in VS 2005 (which is only good if you intend to distribute exact copies of a small preconfigured system.)

un said...

... but then of course you fall right in a cow pat.

CustomizableOutputDir works great if you only have C# (or presumably VB) projects, but is completely ignored by C++ projects... >:(

piers7 said...

That's shockingly bad.

Unknown said...

We have done this but builds still clear all the output folders of other projects. How do you stop that?

piers7 said...

Sorry Chris I'm not entirely with you. What's above is quite literally all I've done. Can you explain the problem a bit more?

Do you have other things happening in your builds? 'Clean's for example?

Justin said...

Thank you for the post. This has managed to resolve part of my problem. I have a SmartClient, Windows Service and a Web Service that belong to one Solution file (along with other referenced assembly projects). Editing the csproj files with the AfterBuild target got the SmartClient and Windows Service files copied in their respective folders after the TFS Build. However, I cannot do the same with the WebService. The reason why the Solution file contains all these projects is because they need to use the same version of the dll's for serialization purposes.
How can I get the webservice files dropped in a specified output directory?

Popular Posts