代码之家  ›  专栏  ›  技术社区  ›  Tom Kidd

让tfs将每个项目输出到自己的目录的最佳方法是什么?

  •  58
  • Tom Kidd  · 技术社区  · 15 年前

    我把一个大的代码库放到Team Foundation服务器中。我希望构建过程为我们的项目创建一个“准备好部署”的构建。

    通常我们这样做的方式是让每个项目的输出都在自己的文件夹中。例如,我们最后得到的是

    C:\project1\
                assembly1.dll
                assembly2.dll
                project1.exe
                project1.exe.config
    C:\project2\
                assembly2.dll
                assembly3.dll
                project2.exe
                project2.exe.config
    C:\project3\
                assembly1.dll
                assembly3.dll
                project3.exe
                project3.exe.config
    

    这就是我们喜欢的方式。

    不过,tfs似乎希望将所有内容都保存在同一个目录中。

    C:\output\
              assembly1.dll
              assembly2.dll
              assembly3.dll
              project1.exe
              project1.exe.config
              project2.exe
              project2.exe.config
              project3.exe
              project3.exe.config
    

    虽然它节省了一些磁盘空间(每个程序集只有一次),但这并不是我们想要的。

    指定tfs/msbuild应将输出文件放在何处的最佳方法是什么?我需要单独编辑sln/csproj文件来实现这一点吗,还是可以在tfsbuild.proj文件中进行?(即,在特定于msbuild的文件中)

    11 回复  |  直到 9 年前
        1
  •  44
  •   Mike Hadlow    15 年前

    我刚刚在博客上写了另一个方法:

    http://mikehadlow.blogspot.com/2009/06/tfs-build-publishedwebsites-for-exe-and.html 但是如果你不想费心去跟踪这个链接,这里就有完整的链接:

    在单个Uber解决方案中收集您的团队控制下的所有代码是一个很好的做法,如本模式和实践PDF《使用TFS的团队开发指南》中所述。如果您随后配置TFS构建服务器来构建此解决方案,则其默认行为是将构建输出放入单个文件夹中,即释放。

    解决方案中的任何Web应用程序项目也将输出到名为“PublishedWebsites”的文件夹。这非常好,因为这意味着您可以简单地使用robocopy部署Web应用程序。

    不幸的是,对于WinForms、控制台或库等其他项目类型,没有类似的默认行为。如果我们能有一个_PublishedApplications\Sub文件夹来输出任何选定的项目,那就太好了。幸运的是,这并不难做到。

    发布网站的工作方式非常简单。如果您查看Web应用程序的项目文件,您会注意到底部附近有一个导入:

    <Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v9.0\WebApplications\Microsoft.WebApplication.targets" />
    

    在我的计算机上,msbuildExtensionsPath属性的计算结果为c:\program file s\msbuild,如果我们打开microsoft.webapplication.targets文件,我们可以看到它是一个非常简单的msbuild文件,当生成不是桌面生成时,即它是一个tfs生成,并将输出复制到:

    $(OutDir)_PublishedWebsites\$(MSBuildProjectName)
    

    我只是简单地复制了micrsoft.webapplication.targets文件,用项目文件中的相对路径将其置于源代码管理之下,并将“已发布的网站”更改为“已发布的应用程序”,并重命名了文件ci.exe.targets。对于要输出到_publishedApplications的每个项目,我只需在项目文件底部添加此导入:

    <Import Project="<your relative path>\CI.exe.targets" />
    

    您可以编辑ci.exe.targets(或您想称之为的任何内容)来进行竞价。在我的例子中,到目前为止唯一的改变是添加几行来复制app.config文件:

    <Copy SourceFiles="$(OutDir)$(TargetFileName).config" DestinationFolder="$(WebProjectOutputDir)\bin" SkipUnchangedFiles="true" />
    

    在microsoft.webapplication.targets中有很多东西只与web应用程序相关,并且可以为其他项目类型去掉,但是我会把它作为练习留给读者。

        2
  •  35
  •   Theo    10 年前

    TFS 2012+

    我喜欢这个解决方案…

    编辑生成定义。在“过程”部分下,设置 MSBuild arguments

    /p:GenerateProjectSpecificOutputFolder=true

    这样地:

    enter image description here

        3
  •  10
  •   William D. Bartholomew    15 年前

    默认情况下,每个项目文件(*.csproj、*.vbproj等)都指定一个默认输出目录(通常是bin\debug、bin\release等)。team build实际上会覆盖这一点,这样您就不会对开发人员在项目文件中设置的属性心血来潮,而且team build还可以对输出的位置进行假设。

    覆盖此行为的最简单方法是在SolutionToBuild项组中将CustomizableOutDir设置为true,如下所示:

    <ItemGroup>
      <SolutionToBuild Include="$(BuildProjectFolderPath)\path\MySolution.sln" />
        <Properties>CustomizableOutDir=true</Properties>
      </SolutionToBuild>
    </ItemGroup>
    

    这将使放置文件夹结构与构建解决方案时在本地得到的结构大致匹配。

    此方法绝对比覆盖可能导致升级问题的核心*目标更可取。

        4
  •  6
  •   user75810    15 年前

    对于每个solutiontobuild节点,将属性outdir设置为$(outdir)\subfolder
    例如:

      <ItemGroup>
       <SolutionToBuild Include="Project1.sln" >
        <Properties>OutDir=$(OutDir)\Project1\</Properties>      
       </SolutionToBuild>
       <SolutionToBuild Include="Project2.sln" >
        <Properties>OutDir=$(OutDir)\Project2\</Properties>      
       </SolutionToBuild>
       <SolutionToBuild Include="Project3.sln" >
        <Properties>OutDir=$(OutDir)\Project3\</Properties>      
       </SolutionToBuild>
      <ItemGroup>
    

    (此项工作于2008年,但不适用于2005年。)

        5
  •  5
  •   CeejeeB    12 年前

    我在派对上回答这个问题有点晚了,但是有一个非常简单的方法来实现迈克·哈德洛的答案。有人写了一个Nuget包,完全按照Mike所说的做。你可以在这里找到它: http://www.nuget.org/packages/PublishedApplications

        6
  •  4
  •   Styxxy    9 年前

    更新tfs 2010(以及即将推出的tfs 2012)。JasonStanloome写了一篇不错的博客,概述了如何做到这一点。

    http://blog.codeassassin.com/2012/02/03/override-the-tfs-team-build-outdir-property/

    (上面的链接已断开…链接到缓存版本)

    https://webcache.googleusercontent.com/search?q=cache:4rKu4oB3TwcJ:blog.stangroome.com/2012/02/03/override-the-tfs-team-build-outdir-property/+&cd=1&hl=en&ct=clnk&gl=ca

    重写tfs team build outdir属性

    更新: with .NET 4.5 there is an easier way .

    Team Foundation Server的构建系统用户的一个非常常见的抱怨是它改变了项目输出的文件夹结构。默认情况下,Visual Studio将所有文件放入每个项目的/bin/或/bin/文件夹中,但TeamBuild仅使用平面文件夹结构,将所有文件放入放置文件夹根目录中,或者再次将//子文件夹放入放置文件夹中,所有项目输出混合在一起。

    另外,由于TeamBuild通过将msbuild.exe命令行与 MSBuild’s property precedence 此值不能轻易从msbuild本身更改,常用的解决方案是 edit the Build Process Template *.xaml file to use a different property name . 但除非绝对必要,否则我不喜欢接触工作流。

    相反,我使用目标之前的解决方案和msbuild v4的内联任务功能来重写用于在解决方案中构建单个项目的msbuild任务的默认实现。在我的另一个实现中,我防止传递OutDir属性,并传递一个名为PreferredOutDir的属性,如果需要,各个项目可以使用该属性。

    第一部分,在解决方案级别用outdir属性替换preferredOutdir属性,只需在解决方案文件所在的目录中添加一个新文件即可实现。这个新文件应该按照模式before..sln.targets命名,例如,对于名为foo.sln的解决方案文件,新文件应该是before.foo.sln.targets。这个新文件的内容应该 look like this . 确保此新文件已签入到源代码管理。

    第二部分,让每个项目控制其输出文件夹结构,只需在项目的*.csproj或*.vbproj文件中添加一行(取决于语言)。找到项目文件中没有指定Condition属性的第一个元素,然后找到该元素对应的结束标记。在结束标记的正上方添加一行,如下所示:

    <OutDir Condition=" '$(PreferredOutDir)' != '' ">$(PreferredOutDir)$(MSBuildProjectName)\</OutDir>

    在本例中,项目将输出到与项目文件同名的子文件夹(不带.csproj扩展名)下的team build drop文件夹。您可以选择不同的模式。此外,Web项目通常在team build drop文件夹的\u publishedwebsites子文件夹下创建自己的输出文件夹,为了保持这种行为,只需将outdir属性设置为与preferredoutdir属性完全相同。

    在签入之前,您可以通过从命令行运行msbuild并像team build一样指定outdir属性来验证更改是否在本地计算机上有效,例如:

    msbuild Foo.sln /p:OutDir=c:\TestDropFolder\

        7
  •  1
  •   Community CDub    7 年前

    对于那些好奇这如何与TFS 2010一起工作的人, this 《邮报》有几个答案,其中一个答案对我来说很有用。

        8
  •  0
  •   Erling Paulsen    15 年前

    每个项目可以有一个buildscript,这将完全满足您的需要。 只需创建一个新的tfsbuild文件,将您想要构建的项目添加到itemgroup(按照您想要的构建顺序),设置输出的位置。这是通过重写tfsbuild文件中的-property来完成的。

    但是我也同意前面的海报——为什么不运行一个构建脚本,在最后添加一个zip任务呢?为每个项目维护一个buildscript会增加维护开销…

        9
  •  0
  •   user386316    14 年前

    在属性组中吊起此:

    <CustomizableOutDir>true</CustomizableOutDir>
    

    它将重写全局“customizableoutdir”属性,默认情况下,该属性设置为false。在SolutionToBuild的属性中设置此项将不起作用。

        10
  •  -2
  •   John Saunders    15 年前

    您可以通过覆盖默认的coredropbuild目标实现来实现这一点。

    在tfsbuild.proj文件(默认情况下存储在teambuildtypes/<build type>下)中添加以下目标:

        <!-- Override default implementation -->
        <Target 
           Name="CoreDropBuild"
           Condition=" '$(SkipDropBuild)'!='true' and '$(IsDesktopBuild)'!='true' "
           DependsOnTargets="$(CoreDropBuildDependsOn)">
    ...
        </Target>
    

    在这个目标中,您可以根据需要操作输出。默认情况下,只需将所有内容从$(binariesroot)\$(buildType)复制到$(dropLocation)\$(buildNumber)。

    我通常使用 Microsoft.Sdc.Tasks project 用于文件复制功能。

        11
  •  -3
  •   Erik A. Brandstadmoen    15 年前

    简单解决方案:

    将所有<solutiontobuild>节点替换为<solutiontopublish>。当然,这只适用于可发布的项目(如Web项目和应用程序),而不适用于库项目。

    就这么简单:)