How can I get TFS 2010 to build each project to a separate directory?
Solution 1
I figured out a nice way to do it. It turns out that since you can set the OutDir to whatever you want within the workflow, if you set it to the empty string, MSBuild will instead use the project-specific OutputPath. That lets us be a lot more flexible. Here's my entire solution (based on the default build workflow):
In the Run MSBuild task, set OutDir to the empty string. In that same task, set your CommandLineArguments to something like the following. This will allow you to have a reference to the TFS default OutDir from your project:
String.Format("/p:CommonOutputPath=""{0}\\""", outputDirectory)
In each project you want copied to the drop folder, set the OutputPath like so:
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<OutputPath Condition=" '$(CommonOutputPath)'=='' ">bin\Release\</OutputPath>
<OutputPath Condition=" '$(CommonOutputPath)'!='' ">$(CommonOutputPath)YourProjectName\bin\Release\</OutputPath>
</PropertyGroup>
Check everything in, and you should have a working build that deploys each of your projects to its own folder under the drop folder.
Solution 2
I solved this problem, too, and I think it's cleaner than the existing solutions on this thread.
- Before the
Run MSBuild for Project
activity, I added anAssign
activity:projectName = Regex.Replace(New FileInfo(localProject).Name, "\.sln$", "")
. - Next I added a
Create Directory
activity:outputDirectory + "\" + projectName
- Finally in the MSBuild activity I changed
OutDir
tooutputDirectory + "\" + projectName
.
The template already populates localProject
with the full path name of the the .sln
file being built on the Agent, e.g., c:\build\path\to\MySolution.sln
. The assign activity chops off the path and extension, placing the output in MySolution
. You'll need to create the projectName
variable, and import System.Text.RegularExpressions
and System.IO
.
The advantage over OP's solution is that you don't have to edit each .csproj
, that information is inferred from the solution file's name.
Solution 3
We had to do this to bypass the problem where we have a Silverlight and a .Net library with the same name for CSLA serialization. The library would be overwritten and our tests would fail.
I used Jonathan's answer and Jim Lamb's post, but I found that you also need to set OutDir to empty.
So, you need to do these parameters for the MSBuild activities (if you use the following Macro, you need to set the activity parameters for Clean too, otherwise you get warnings that OutputPath is not set):
- Set CommandLineArguments to
String.Format("/p:SkipInvalidConfigurations=true;TeamBuildOutDir=""{0}"" {1}", BinariesDirectory, MSBuildArguments)
- Set OutDir to empty (was BinariesDirectory)
I have also created a macro that you can run in visual studio that removes the OutputPath from the configurations, and adds a PropertyGroup that contains the OutputPath for all configs like so :
<PropertyGroup Label="OutputPathLabel">
<OutputPath Condition="'$(TeamBuildOutDir)'=='' ">bin\$(Configuration)\</OutputPath>
<OutputPath Condition="'$(TeamBuildOutDir)'!='' ">$(TeamBuildOutDir)\$(SolutionName)\$(MSBuildProjectName)\$(Configuration)\</OutputPath>
</PropertyGroup>
Here's the Macro :
Public Sub SetTeamBuildOutDir()
Dim projectObjects = DTE.Solution.Projects
For Each project In projectObjects
If project.ProjectItems IsNot Nothing Then
SetTeamBuildOutDirRecursive(project)
End If
Next
End Sub
Sub SetTeamBuildOutDirRecursive(ByVal proj As Project)
If proj.ConfigurationManager Is Nothing Then
For Each subProj As ProjectItem In proj.ProjectItems
If subProj.SubProject IsNot Nothing Then
SetTeamBuildOutDirRecursive(subProj.SubProject)
End If
Next
Else
SetTeamBuildOutDir(proj)
End If
End Sub
Sub SetTeamBuildOutDir(ByVal project As Project)
'Do not handle .vdproj
If project.FullName.ToLower().EndsWith(".vdproj") Then
Exit Sub
End If
Dim needToSave = False
Dim msproject = ProjectRootElement.Open(project.FullName)
Dim outputPathGroupExists = False
Dim outputPropertyGroup As ProjectPropertyGroupElement = Nothing
Dim lastConfigPropertyGroup As ProjectPropertyGroupElement = Nothing
For Each propertyGroup In msproject.PropertyGroups
If propertyGroup.Label = "OutputPathLabel" Then
outputPathGroupExists = True
outputPropertyGroup = propertyGroup
End If
If Not String.IsNullOrEmpty(propertyGroup.Condition) AndAlso _
propertyGroup.Condition.TrimStart().StartsWith("'$(Configuration)") Then
lastConfigPropertyGroup = propertyGroup
End If
'Remove the OutputPath from the configurations
Dim outputPathElement As ProjectPropertyElement = Nothing
For Each element As ProjectPropertyElement In propertyGroup.Children
If element.Name = "OutputPath" Then
outputPathElement = element
End If
Next
If outputPathElement IsNot Nothing Then
propertyGroup.RemoveChild(outputPathElement)
needToSave = True
End If
Next
'If we want to always remove the group and add it back (in case of modifications to the group)
'If outputPathGroupExists Then
' msproject.RemoveChild(outputPropertyGroup)
' outputPathGroupExists = False
'End If
If Not outputPathGroupExists Then
Dim propertyGroup = msproject.CreatePropertyGroupElement()
propertyGroup.Label = "OutputPathLabel"
'Need to insert the PropertyGroup before the CSharp targets are included
msproject.InsertAfterChild(propertyGroup, lastConfigPropertyGroup)
Dim isDbProject = project.FullName.ToLower().EndsWith(".dbproj")
Dim outputEmpty = propertyGroup.AddProperty("OutputPath", IIf(Not isDbProject, "bin\$(Configuration)\", "sql\$(Configuration)\"))
outputEmpty.Condition = "'$(TeamBuildOutDir)'=='' "
Dim outputTeamBuild = propertyGroup.AddProperty("OutputPath", "$(TeamBuildOutDir)\$(SolutionName)\$(MSBuildProjectName)\$(Configuration)\")
outputTeamBuild.Condition = "'$(TeamBuildOutDir)'!='' "
needToSave = True
End If
If needToSave Then
'checkout the project file with tfs
Shell("C:\Program Files\Microsoft Visual Studio 10.0\Common7\IDE\tf.exe checkout " & project.FullName, , True)
'Save the project file
msproject.Save()
End If
End Sub
Hope this helps!!!
Solution 4
The steps here don't require a project file modification - http://lajak.wordpress.com/2011/05/07/customize-binaries-folder-in-tfs-team-build/
Solution 5
Not sure if you can still get team build 2010 to use the latest version of msbuild, but there's a new property /p:GenerateProjectSpecificOutputFolder=true when specified will drop the bits into $(OutDir)\$(ProjectName)\ for each project.
Related videos on Youtube
Jonathan Schuster
Software-developer-turned-computer-scientist out to improve programming languages and make software development easier for everyone.
Updated on January 07, 2020Comments
-
Jonathan Schuster over 4 years
In our project, we'd like to have our TFS build put each project into its own folder under the drop folder, instead of dropping all of the files into one flat structure. To illustrate, we'd like to see something like this:
DropFolder/ Foo/ foo.exe Bar/ bar.dll Baz baz.dll
This is basically the same question as was asked here, but now that we're using workflow-based builds, those solutions don't seem to work. The solution using the CustomizableOutDir property looked like it would work best for us, but I can't get that property to be recognized. I customized our workflow to pass it in to MSBuild as a command line argument (/p:CustomizableOutDir=true), but it seems MSBuild just ignores it and puts the output into the OutDir given by the workflow.
I looked at the build logs, and I can see that the CustomizableOutDir and OutDir properties are both getting set in the command line args to MSBuild. I still need OutDir to be passed in so that I can copy my files to TeamBuildOutDir at the end.
Any idea why my CustomizableOutDir parameter isn't getting recognized, or if there's a better way to achieve this?
-
Michael Freidgeim over 11 yearsConsider to use package PublishedApplications available on Nuget nuget.org/packages/PublishedApplications
-
-
MrHinsh - Martin Hinshelwood almost 14 yearsDoes this not cause problems with MSTest?
-
Jonathan Schuster almost 14 yearsWe're not using MSTest for this project (or any automated testing, for that matter), so I honestly don't know. If you find out one way or another, though, that'd be great info to post.
-
stuartd almost 13 yearsNote however that WebApplication projects need an OutputPath of just bin\ rather than bin\$(Configuration)\
-
Case over 12 yearsThis worked perfectly and did exactly what I want with very little effort. The only change that I made was to use Path.Combine instead of manually concatenating paths. For those curious, unit tests seem to work fine when using this route. Wish I could upvote you twice!
-
Ali Mst about 12 yearsAll this does is put your output in a solution level folder... If you have more than one project in the solution they will still be grouped into that solution level folder.
-
Michael Freidgeim over 11 yearsIt should be actually a comment to Andy Morris answer stackoverflow.com/a/11748510/52277
-
Michael Freidgeim over 11 yearsRajani suggested (see below) to use System.IO.Path.GetFileNameWithoutExtension(serverBuildProjectItem ) instead
-
Mark over 2 yearsThis was the answer I needed! Thank you so much! I wish these properties were better documented.