如何让TFS 2010将每个项目构建到单独的目录中?

时间:2010-05-06 20:41:26

标签: tfs msbuild tfs2010 team-build

在我们的项目中,我们希望我们的TFS构建将每个项目放在drop文件夹下的自己的文件夹中,而不是将所有文件都放到一个平面结构中。为了说明,我们希望看到类似的内容:

DropFolder/
  Foo/
    foo.exe
  Bar/
    bar.dll
  Baz
    baz.dll

这与问here的问题基本相同,但现在我们正在使用基于工作流的构建,这些解决方案似乎不起作用。使用CustomizableOutDir属性的解决方案看起来对我们来说效果最好,但我无法识别该属性。我自定义了我们的工作流程,将其作为命令行参数传递给MSBuild(/ p:CustomizableOutDir = true),但似乎MSBuild只是忽略它并将输出放入工作流程给出的OutDir中。

我查看了构建日志,我可以看到CustomizableOutDir和OutDir属性都在命令行args中设置为MSBuild。我仍然需要传递OutDir,以便我可以在最后将我的文件复制到TeamBuildOutDir。

知道为什么我的CustomizableOutDir参数没有得到识别,或者是否有更好的方法来实现这个目标?

9 个答案:

答案 0 :(得分:14)

我想出了一个很好的方法。事实证明,由于您可以将OutDir设置为工作流中的任何内容,因此如果将其设置为空字符串,MSBuild将使用特定于项目的OutputPath。这让我们变得更加灵活。这是我的整个解决方案(基于默认构建工作流程):

在“运行MSBuild”任务中,将OutDir设置为空字符串。在同一任务中,将CommandLineArguments设置为如下所示。这将允许您从项目中引用TFS默认OutDir:

String.Format("/p:CommonOutputPath=""{0}\\""", outputDirectory)

在要复制到drop文件夹的每个项目中,像这样设置OutputPath:

<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <OutputPath Condition=" '$(CommonOutputPath)'=='' ">bin\Release\</OutputPath>
    <OutputPath Condition=" '$(CommonOutputPath)'!='' ">$(CommonOutputPath)YourProjectName\bin\Release\</OutputPath>
</PropertyGroup>

检查所有内容,您应该有一个工作版本,将每个项目部署到drop文件夹下的自己的文件夹中。

答案 1 :(得分:8)

我也解决了这个问题,我认为它比这个线程上的现有解决方案更清晰。

  • Run MSBuild for Project活动之前,我添加了Assign个活动:projectName = Regex.Replace(New FileInfo(localProject).Name, "\.sln$", "")
  • 接下来,我添加了Create Directory个活动:outputDirectory + "\" + projectName
  • 最后,在MSBuild活动中,我将OutDir更改为outputDirectory + "\" + projectName

模板已使用代理上构建的localProject文件的完整路径名填充.sln,例如c:\build\path\to\MySolution.sln。 assign活动将路径和扩展名切断,将输出放在MySolution中。您需要创建projectName变量,然后导入System.Text.RegularExpressionsSystem.IO

优于OP的解决方案的优势在于您无需编辑每个.csproj,该信息是从解决方案文件的名称推断出来的。

答案 2 :(得分:4)

我们必须这样做才能绕过我们拥有Silverlight和.Net库以及CSLA序列化同名的问题。该库将被覆盖,我们的测试将失败。

我使用了Jonathan的答案和Jim Lamb's post,但我发现你还需要将OutDir设置为空。

因此,您需要为MSBuild活动执行这些参数(如果使用以下宏,则还需要为Clean设置活动参数,否则会收到没有设置OutputPath的警告):

  • 将CommandLineArguments设置为 String.Format("/p:SkipInvalidConfigurations=true;TeamBuildOutDir=""{0}"" {1}", BinariesDirectory, MSBuildArguments)
  • 将OutDir设置为空(是BinariesDirectory)

我还创建了一个可以在visual studio中运行的宏,它从配置中删除了OutputPath,并为所有配置添加了一个包含OutputPath的PropertyGroup,如下所示:

<PropertyGroup Label="OutputPathLabel">
  <OutputPath Condition="'$(TeamBuildOutDir)'=='' ">bin\$(Configuration)\</OutputPath>
  <OutputPath Condition="'$(TeamBuildOutDir)'!='' ">$(TeamBuildOutDir)\$(SolutionName)\$(MSBuildProjectName)\$(Configuration)\</OutputPath>
</PropertyGroup>

这是宏:

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

希望这有帮助!!!

答案 3 :(得分:2)

答案 4 :(得分:1)

这是一个非常简单的解决方案,无需修改源文件或项目文件。设置构建定义的过程时 - &gt;要构建的项目,而不是在要构建的项目中指定.sln文件,添加每个项目(.csproj或.vbproj)。现在,在工作流程的“运行MSBuild for Project”步骤中,将OutDir属性更改为以下内容:

outputDirectory + "/" + serverBuildProjectItem.Substring(serverBuildProjectItem.LastIndexOf("/"), serverBuildProjectItem.Length - serverBuildProjectItem.LastIndexOf("/")).Replace(".csproj", String.Empty)

这会将每个项目的构建输出放入以项目命名的子目录中。

答案 5 :(得分:1)

我已经为Nuget为My Solution中的每个可执行文件安装了package PublishedApplications,并且在构建期间为每个项目在_PublishedApplications文件夹下创建了子文件夹。

答案 6 :(得分:1)

不确定你是否仍然可以让团队构建2010使用最新版本的msbuild,但是有一个新属性/ p:GenerateProjectSpecificOutputFolder = true,当指定时会将这些位丢弃到$(OutDir)\ $(ProjectName)\中每个项目。

答案 7 :(得分:0)

我没有玩TFS / MSBuild将输出文件放在单独的文件夹中,因此无法给出直接答案。但是,这里有一些我没有在您的链接中发现的建议:

  • 您可以将后期构建步骤添加到将所需文件复制到“部署”结构的项目中。 (这当然也可以在dev机器上运行,这可能会很痛苦)。我们将这种方法用于我们的库,然后构建它们,然后将其复制到共享库(二进制文件)文件夹中以供其他项目引用。

  • 您可以添加MSBuild目标以将所需文件复制到所需位置。我们已经覆盖默认的“复制到放置文件夹”目标,将文件复制到另一个文件夹,对它们进行模糊处理,对它们进行数字签名,将它们构建到安装程序中,对安装程序进行数字签名,然后复制它(以及其他有用的东西,如混淆映射文件)以及自上次构建到drop文件夹以来的更改列表。最终添加自己的后期构建目标可以让您最大限度地控制放在哪里。 (在负面,您可能必须手动将任何新的dll或exes添加到构建后的复制目标,这可能会令人恼火)

答案 8 :(得分:0)

这是另一个非常简单的解决方案,无需修改源文件或项目文件。设置构建定义的过程时 - &gt;要构建的项目,而不是在要构建的项目中指定.sln文件,添加每个项目(.csproj或.vbproj)。现在,在工作流程的“运行MSBuild for Project”步骤中,将OutDir属性更改为以下内容:

OutputDirectory + "\" + System.IO.Path.GetFileNameWithoutExtension(serverBuildProjectItem)