代码之家  ›  专栏  ›  技术社区  ›  Jonathan Schuster

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

  •  22
  • Jonathan Schuster  · 技术社区  · 16 年前

    在我们的项目中,我们希望让我们的tfs构建将每个项目放到Drop文件夹下自己的文件夹中,而不是将所有文件放到一个平面结构中。举例来说,我们希望看到这样的情况:

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

    这基本上和我们问的问题一样 here 但是现在我们使用基于工作流的构建,这些解决方案似乎不起作用。使用customizableoutdir属性的解决方案看起来对我们最有效,但我无法识别该属性。我自定义了我们的工作流,将其作为命令行参数(p:customizableoutdir=true)传递给msbuild,但似乎msbuild忽略了它并将输出放入工作流给定的outdir中。

    我查看了构建日志,可以看到在命令行args中,customizableoutdir和outdir属性都设置为msbuild。我仍然需要传递outdir,这样我可以在最后将文件复制到teambuildoutdir。

    你知道为什么我的可定制OutDir参数不能被识别吗,或者是否有更好的方法来实现这一点?

    9 回复  |  直到 12 年前
        1
  •  14
  •   Jonathan Schuster    16 年前

    我想出了一个好办法。事实证明,由于可以将outdir设置为工作流中所需的任何内容,如果将其设置为空字符串,则msbuild将改用特定于项目的outputpath。这让我们更加灵活。以下是我的整个解决方案(基于默认生成工作流):

    在运行msbuild任务中,将outdir设置为空字符串。在同一个任务中,将commandlinearguments设置为如下所示。这将允许您从项目中引用tfs默认outdir:

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

    在每个要复制到放置文件夹的项目中,按如下方式设置输出路径:

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

    签入所有内容,您应该有一个工作构建,将每个项目部署到Drop文件夹下自己的文件夹中。

        2
  •  8
  •   Travis P    14 年前

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

    • 之前 Run MSBuild for Project 活动,我添加了一个 Assign 活动: projectName = Regex.Replace(New FileInfo(localProject).Name, "\.sln$", "") .
    • 接下来我添加了一个 Create Directory 活动: outputDirectory + "\" + projectName
    • 最后在msbuild活动中,我更改了 OutDir 输出目录+“\”+项目名称 .

    模板已填充 localProject 的完整路径名 .sln 在代理上生成的文件,例如, c:\build\path\to\MySolution.sln . assign活动将切断路径和扩展,并将输出置于 MySolution . 您需要创建 projectName 变量和导入 System.Text.RegularExpressions System.IO .

    与OP的解决方案相比,其优势在于您不必编辑每个 .csproj 从解决方案文件的名称推断出该信息。

        3
  •  4
  •   Christian Lavallee    16 年前

    我们必须这样做才能绕过这样的问题:我们有一个Silverlight和一个用于CSLA序列化的同名.NET库。库将被覆盖,我们的测试将失败。

    我用乔纳森的回答 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 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
    

    希望这有帮助!!!!

        4
  •  2
  •   ak7    14 年前
        5
  •  1
  •   Andy Morris    14 年前

    这里有一个非常简单的解决方案,不需要修改源文件或项目文件。在设置生成定义的过程->要生成的项目时,请添加每个项目(.csproj或.vbproj),而不是在要生成的项目中指定.sln文件。现在,在工作流的“为项目运行msbuild”步骤中,将outdir属性更改为以下内容:

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

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

        6
  •  1
  •   Michael Freidgeim    13 年前

    我已经安装了 package PublishedApplications 对于我的解决方案中的每个可执行文件,它都从nuget中创建了子文件夹,并在生成过程中为每个项目创建了“PublishedApplications”文件夹下的子文件夹。

        7
  •  1
  •   Richard Spence    12 年前

    不确定是否仍可以让TeamBuild2010使用最新版本的msbuild,但有一个新属性/p:GenerateProjectSpecificOutputfolder=true(如果指定),则会将每个项目的位放入$(OutDir)\$(ProjectName)。

        8
  •  0
  •   Jason Williams    16 年前

    我还没有尝试让tfs/msbuild将输出文件放在单独的文件夹中,因此无法给出直接的答案。但是,以下是一些我没有在你的链接中发现的建议:

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

    • 可以添加一个msbuild目标以将所需文件复制到所需的位置。我们已经覆盖了默认的“复制到放置文件夹”目标,将文件复制到另一个文件夹,模糊它们,对它们进行数字签名,将它们构建到安装程序中,对安装程序进行数字签名,然后复制它(以及其他有用的东西,如模糊映射文件)和自上次构建到放置文件夹以来的更改列表。最终,添加自己的后期构建目标可以最大限度地控制将什么放在哪里。(在负侧,您可能需要手动将任何新的dll或exes添加到生成后复制目标,这可能会引起不安)

        9
  •  0
  •   andrewsi Laxman Battini    13 年前

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

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