Project Output Directories in Team Build  
Author Message
simon burgess





PostPosted: Team Foundation Server - Build Automation, Project Output Directories in Team Build Top

Most of my VS projects have the output directory defined in the project settings as /bin, however when I run a Team Build, no bin directories are created, there are just obj directories. Why is this and can I change it because in some circumstances (like building a windows service) some of the output is not copied to the obj folder, it is copied to the Binaries folder.

Visual Studio Team System4  
 
 
Patrick Carnahan - MSFT





PostPosted: Team Foundation Server - Build Automation, Project Output Directories in Team Build Top

The output directory is affected by the msbuild property OutDir, which is set in the Microsoft.TeamFoundation.Build.targets file. The OutDir is computed on a per platform/configuration basis, and uses two other properties to determine the final location.

If you look at the beginning of the targets file (Microsoft.TeamFoundation.Build.targets), you will see two properties being set: SolutionRoot and BinariesRoot. SolutionRoot is calculated if it is not already overridden in the TfsBuild.proj file, and then BinariesRoot is calculated as $(SolutionRoot)\..\Binaries. Both of these properties are conditionally set iff there is not a value currently in them.

Moving on down the targets file you will see a target called ComputeOutDirForAllConfigurations. The purpose of this target is to create an output directory for each platform and flavor combination that is defined in the TfsBuild.proj file. This directory will be computed as the following:

$(BinariesRoot)\$(Platform)\$(Flavor) (if the flavor is NOT 'Any CPU')

$(BinariesRoot)\$(Flavor) (if the flavor IS 'Any CPU')

The targets file will then iterate over each platform/flavor to build and invoke the build target with a few properties set, but most importantly for you it defines OutDir as the platform/flavor directory that was described above. So, in order to override where the binaries are placed, you should override the definition of the property BinariesRoot in your TfsBuild.proj. Although it may not be exactly what you want, a good place to start might be:

<BinariesRoot>$(SolutionRoot)\bin</BinariesRoot>

When building msbuild.exe also pays attention to another property which you may find useful, IntermediateOutputPath. By overriding this property you can change where the intermediate files are placed during the build process (this defines that your 'obj' directory houses the intermediate build output).

When overriding property values please make sure to place them in the individual TfsBuild.proj file and avoid editing the Microsoft.TeamFoundation.Build.targets file as this may change in future versions.

Hope this helps answer your question!

Patrick


 
 
Alex Bogli





PostPosted: Team Foundation Server - Build Automation, Project Output Directories in Team Build Top

Hi

I wanted to broaden the question and ask if it is possible to have the projects still compiling into the bin directory instead of this common binaries directory (Setting the BinariesRoot does not help because it is still one common directory)

Any help is very much appreciated!

Cheers,

Alex


 
 
Aaron Hallberg





PostPosted: Team Foundation Server - Build Automation, Project Output Directories in Team Build Top

If I understand properly, the question is whether you can have individual projects compile into directories based on the project name, rather than having all projects compile into the same directory

This is not possible without modifications to Microsoft.TeamFoundation.Build.projects, which defines the generic TF Build build process. This file can typically be found at C:\Program Files\MSBuild\Microsoft\VisualStudio\v8.0\TeamBuild. Note that it is not generally recommended that you do this, since this file is likely to change in future versions of TF Build.

The TF Build process is set up to build solutions and not projects. So - one issue you will run into even if you do decide to modify the targets file is that you cannot currently set the $(OutDir) property for individual projects. Your best bet might be to set the $(OutDir) property passed into the <MSBuild> tasks which are responsible for actually compiling your solutions to the empty string. I believe this would result in individual projects using their individual output directories (though I haven't verified this)...

-Aaron



 
 
Alex Bogli





PostPosted: Team Foundation Server - Build Automation, Project Output Directories in Team Build Top

Well, I tried both setting /p:OutDir="" in the TFSBuild.rsp file and <OutDir></OutDir> in the first property group TFSBuild.proj file, but nothing helped. I still don't get the bin directories in the respective project folders.


 
 
Aaron Hallberg





PostPosted: Team Foundation Server - Build Automation, Project Output Directories in Team Build Top

Right - you will need to modify the targets file to do what you want to do. This particular property is set in the targets file, and this will override whatever values you pass in from the rsp file or from TfsBuild.proj. Look for the <MSBuild> task with the Build target, and simply set the OutDir property passed into it to the empty string (rather than $(OutDir), which it is normally).

Note that this will affect all Build Types on the build machine. Alternatively, you can copy the entire CoreCompile target into your TfsBuild.proj file and modify it there - this will only affect the particular Build Type you are dealing with.

-Aaron



 
 
Alex Bogli





PostPosted: Team Foundation Server - Build Automation, Project Output Directories in Team Build Top

Ups, sorry for that, it didn't appear to me.
But still, there's some difficulties: if I pass in OutDir='', it complains that the OutDir does not end with a slash. If I pass in OutDir=, it says

C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Microsoft.Common.targets(2313,9): error MSB3023: No destination specified for Copy. Please supply either "DestinationFiles" or "DestinationDirectory".
C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Microsoft.Common.targets(2843,9): error MSB4044: The "FindUnderPath" task was not given a value for the required parameter "Path".

Because of this, I will try to compile the projects individually instead of the whole solution, maybe the OutDir is then properly set.


 
 
Aaron Hallberg





PostPosted: Team Foundation Server - Build Automation, Project Output Directories in Team Build Top

Hmm... I'm not sure why this doesn't work - it looks like Microsoft.Common.targets should use the $(OutputPath) as the value $(OutDir) if $(OutDir) is the empty string. In any case, you could just pass in "bin" as your value for $(OutDir) directly and this should do the trick.

The next problem you'll run into, if you try this, is that the binaries will not get copied to the drop location... Solving this will be pretty tricky, since you'll have to copy each set of binaries from a different directory (and may wish to copy each set to a different directory as well, assuming you want to preserve your file structure on the drop).

-Aaron



 
 
Alex Bogli





PostPosted: Team Foundation Server - Build Automation, Project Output Directories in Team Build Top

Great, it works!

I removed the OutDir=${OutDir} completely from the task's command line and it then compiles every project to it's default location. It even works for Web Deployment Projects, which have (or can have) different locations (we use .\Binaries\).

Thanks very much for your help, that is a major step in our effort to have continuous integration!


Alex


 
 
simon burgess





PostPosted: Team Foundation Server - Build Automation, Project Output Directories in Team Build Top

Alex, can you confirm the steps required to achieve this I have removed OutDir=$(OutDir) in the call to MSBuild in the .targets file but I am still getting a common binaries folder. Did you find you had to remove all instances of OutDir=$(OutDir) in the .targets file as there are a few. Also did you keep <OutDir></OutDir> in tfsbuild.proj and /p:OutDir="" in tfsbuild.rsp
 
 
thompsop





PostPosted: Team Foundation Server - Build Automation, Project Output Directories in Team Build Top

Here's what I did. I modified the proj file created by foundationserver, and instead of having it pass in $(SolutionstoBuild) list containing paths to .SLN files, instead I set it to a bootstrap.proj located in a different directory in my source tree.

In that bootstrap.proj (ignore the name :) )

<PropertyGroup>
<BuildProperties>OutDir=bin\$(Configuration)\</BuildProperties>

</PropertyGroup

Then,

in a build target in the bootstrap.proj

<MSBuild Projects=$(MyProjectsToBuild) Targets=$(Targets) Properties=$(BuildProperties) />

Overriding the properties being sent into MSBuild to the solutions allows you to override what the Microsoft.Targets from Team Build puts in there.


 
 
Alex Bogli





PostPosted: Team Foundation Server - Build Automation, Project Output Directories in Team Build Top

Simon,
you are right, I removed all occurences of OutDir (I guess it was 3 times). Be aware that I had to add the following lines to my msbuild file so that the tests are still running (seems that the tests need the directory, although it can be empty):

<Target Name="BeforeTest">
<!--
The tests won't run if the binaries directory does not exist -->
<
MakeDir
Directories="$(BinariesRoot)\%(ConfigurationToBuild.FlavorToBuild)"
Condition="!Exists('$(BinariesRoot)\%(ConfigurationToBuild.FlavorToBuild)')" />
</
Target>


 
 
Joey Bradshaw





PostPosted: Team Foundation Server - Build Automation, Project Output Directories in Team Build Top

Removing the $(OutDir) property from C:\Program Files\MSBuild\Microsoft\VisualStudio\v8.0\TeamBuild\Microsoft.TeamFoundation.Build.targets will cause the output for every project to go to its "normal" location. (e.g. ..\<MyProject>\bin\<Configuration>)

 

However this hack prevents code analysis and unit tests from running and requires more “hacks” to get those features to run.

 

I’ve been down this path already at my company and I am in the process of removing these hacks to get the default behavior out of Team Build while still accomplishing the simple goal of “harvesting” the output for individual projects. This should be simple but the default behavior of Team Build is to throw all assemblies/output from all projects within a build into the $(BinariesRoot)\%(ConfigurationToBuild.FlavorToBuild) directory. Not only that but the %(ConfigurationToBuild.FlavorToBuild) can change suddenly and is completely at the whim of any developer if they decide to change the .sln file especially for web projects.

 

This is a huge oversight in Team Build and has proven difficult to overcome. I still see no way to harvest the assemblies from multiple projects into separate folders without introducing hacks into the MS defined targets files. I’m hoping someone at MS who actually uses Team Build can share their solution for packing files at the end of a successful build without resorting to hacks or complicated project to assembly mapping.

 

Here is an example of what I mean by harvesting. At the end of a build I should be able to copy the files from some location on the build server that equates to each .cproj file and “package” it in whatever means I deem suitable, such as zipping it up.

 

$(BinariesRoot)\%(ConfigurationToBuild.FlavorToBuild)\

Project1

            Assembly1.dll

Assembly2.dll

Assembly1.exe

 

Project2

Assembly5.dll

Assembly73.dll

 

Project3

Assembly9.dll

Assembly9.dll.config

Assembly10.dll

 

With the out of box experience of Team Build this is not possible. Below is what you get by default and as you can see there is no way to determine which file came from which project.

 

$(BinariesRoot)\%(ConfigurationToBuild.FlavorToBuild)\

Assembly1.dll

Assembly1.exe

Assembly2.dll

Assembly5.dll

Assembly9.dll

Assembly9.dll.config

Assembly10.dll

Assembly73.dll

 

Anyone have a solution that doesn’t involve modifying predefined MS targets files….


 
 
TimBailey99





PostPosted: Team Foundation Server - Build Automation, Project Output Directories in Team Build Top

Check this post out, seems to address the problem that you ( and I) are having.

http://blogs.msdn.com/aaronhallberg/archive/2007/06/07/preserving-output-directory-structures-in-orcas-team-build.aspx

cheers

Tim


 
 
Thompsonson





PostPosted: Team Foundation Server - Build Automation, Project Output Directories in Team Build Top

Quote:

Removing the $(OutDir) property from C:\Program Files\MSBuild\Microsoft\VisualStudio\v8.0\TeamBuild\Microsoft.TeamFoundation.Build.targets will cause the output for every project to go to its "normal" location. (e.g. ..\<MyProject>\bin\<Configuration>)

However this hack prevents code analysis and unit tests from running and requires more “hacks” to get those features to run.

This is a hack that works for us - we need the binaries in the \<MyProject>\bin\ folder for the deployment projects to pick up. I've not edited the actual targets file, but copied the CoreCompile target to the Build Type and edited it there....

I edited the properties of the <MSBuild> task in the CoreCompile target.

Removed OutDir=$(OutDir) from the Properties.

And added PostBuildEvent=copy *.* $(OutDir) this meant the contents of the bin folder were copied to the OutDir after it had been built. Tests and the drop can be done on the binaries now....

Ended up with this:

<!-- Build using MSBuild task -->

<MSBuild

Condition=" "

Projects=" "

Properties="Configuration=%(ConfigurationToBuild.FlavorToBuild);Platform=%(ConfigurationToBuild.PlatformToBuild);SkipInvalidConfigurations=true;VCBuildOverride=$(MSBuildProjectDirectory)\TFSBuild.vsprops;FxCopDir=$(FxCopDir);ReferencePath=$(ReferencePath);TeamBuildConstants=$(TeamBuildConstants);$(CodeAnalysisOption);PostBuildEvent=copy *.* $(OutDir)"

Targets="Build" />

Note: didn't edit the CoreCompile in the targets file, copied it to the TFSBuild.proj file and edited it there.

Thought this may help.

Matt