How can I get TFS 2010 to build each project to a separate directory?

后端 未结 9 912
忘掉有多难
忘掉有多难 2020-12-23 12:37

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

相关标签:
9条回答
  • 2020-12-23 12:47

    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!!!

    0 讨论(0)
  • 2020-12-23 12:48

    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.

    0 讨论(0)
  • 2020-12-23 12:50

    I’ve installed the  package PublishedApplications from Nuget for each Executable in My Solution and it created subfolders under _PublishedApplications folder for each project during the build.

    0 讨论(0)
  • 2020-12-23 12:56

    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 an Assign 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 to outputDirectory + "\" + 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.

    0 讨论(0)
  • 2020-12-23 12:57

    Here's another very simple solution that requires no modification to source files or project files. When setting up your build definition's Process -> Projects to Build, instead of specifying your .sln file in the projects to build, add each project (.csproj or .vbproj). Now inside the "Run MSBuild for Project" step of your workflow, change the OutDir property to the following:

    OutputDirectory + "\" + System.IO.Path.GetFileNameWithoutExtension(serverBuildProjectItem)
    
    0 讨论(0)
  • 2020-12-23 13:01

    I haven't played with getting TFS/MSBuild to put the output files in separate folders, so can't give a direct answer. However, here are a couple of suggestions that I didn't spot in your link:

    • You could add post-build steps to the projects that copy the required files to a "deployment" structure. (This of course would also run on dev machines, which might be a pain). We use this approach for our libraries, whch are built and then copied into a shared libs (binaries) folder for other projects to reference them from.

    • You could add an MSBuild target to copy the required files where you want them. We've overridden the default "copy to drop folder" targets to copy the files to another folder, obfuscate them, digitally sign them, build them into an installer, digitally sign the installer, and then copy it (and other useful stuff like the obfuscation map files) and a list of changes since the last build to the drop folder. Ultimately adding your own post-build target gives you maximum control over exactly what gets put where. (On the minus side, you may have to manually add any new dlls or exes to the post-build copy target, which could be an irritation)

    0 讨论(0)
提交回复
热议问题