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

时间:2009-03-30 20:15:05

标签: tfs msbuild

我将一个大型代码库放入Team Foundation Server。我希望构建过程能够创建项目的“准备部署”构建。

我们这样做的正常方法是让每个项目的输出都在自己的文件夹中。因此,例如,我们结束了像

这样的事情
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 个答案:

答案 0 :(得分:44)

我刚刚在这里写了另一种方法:

http://mikehadlow.blogspot.com/2009/06/tfs-build-publishedwebsites-for-exe-and.html 但是,如果你不愿意关注这个链接,那么它就是完整的:

通常很好的做法是在单一的超级解决方案中收集团队控制下的所有代码,如本模式与实践PDF,团队开发与TFS指南中所述。如果您然后配置TFS构建服务器来构建此解决方案,那么它的默认行为是将构建输出放在单个文件夹中,即“Release”。

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

不幸的是,其他项目类型(如WinForms,控制台或库)没有类似的默认行为。如果我们可以拥有一个带有任何所选项目输出的_PublishedApplications \ sub文件夹,那将是非常好的。幸运的是,这并不难。

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

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

在我的机器上,MSBuildExtensionsPath属性的计算结果为C:\ Program Files \ MSBuild,如果我们打开Microsoft.WebApplication.targets文件,我们可以看到它是一个非常简单的MSBuild文件,可以识别构建不是桌面构建的时候,即它是TFS构建,并将输出复制到:

$(OutDir)_PublishedWebsites\$(MSBuildProjectName)

我只是复制了Micrsoft.WebApplication.targets文件,将其置于源代码控制下,并使用项目文件中的相对路径,并将_PublishedWebsites更改为_PublishedApplications并重命名为文件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应用程序相关,可以为其他项目类型删除,但我会将其作为读者的练习。

答案 1 :(得分:35)

TFS 2012 +

我喜欢这个解决方案......

编辑您的构建定义。在“处理”部分下,将MSBuild arguments设置为

/p:GenerateProjectSpecificOutputFolder=true

像这样:

enter image description here

答案 2 :(得分:10)

默认情况下,每个项目文件(* .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>

如果构建解决方案,这将使drop文件夹结构大致与本地获取的结构相匹配。

这种方法绝对比覆盖可能导致升级问题的Core *目标更好。

答案 3 :(得分:6)

对于每个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>

(这适用于TF2008,但不适用于TF2005。)

答案 4 :(得分:5)

我回答这个问题的时候有点迟了但是有一个非常简单的方法可以实现Mike Hadlows的答案。有人编写了一个与迈克谈论的完全一致的nuget包。您可以在此处找到它:http://www.nuget.org/packages/PublishedApplications

答案 5 :(得分:4)

TFS 2010更新(以及即将推出的TFS 2012)。 Jason Stangroome写了一篇很好的博客文章,概述了如何做到这一点。

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 //文件夹中,但Team Build只使用一个平面文件夹结构,将所有文件放在drop文件夹根目录中,或者再次使用//子文件夹放在drop中文件夹,所有项目输出混合在一起。

     

此外,因为Team Build通过MSBuild.exe命令行和MSBuild’s property precedence设置OutDir属性来实现这一点,所以这个值不能在MSBuild本身内轻松更改,而流行的解决方案是edit the Build Process Template *.xaml file to use a different property name 。但除非绝对必要,否则我不想触摸工作流程。

     

相反,我使用MS之前的解决方案解决方案和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>

     

在此示例中,项目将输出到与项目文件名称相同的子文件夹下的Team Build投递文件夹(不带.csproj扩展名)。您可以选择不同的模式。此外,Web项目通常在Team Build drop文件夹的_PublishedWebSites子文件夹下创建自己的输出文件夹,以保持此行为,只需将OutDir属性设置为完全等于PreferredOutDir属性。

     

您可以在签入之前验证您的更改是否在本地计算机上有效,只需从命令行运行MSBuild并像Team Build一样指定OutDir属性,例如:

     

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

答案 6 :(得分:1)

对于那些对TFS 2010的工作原理感到好奇的人来说,this帖子有几个答案,其中链接的答案非常适合我。

答案 7 :(得分:0)

每个项目可以有一个buildcript,它可以完全按照你想要的那样做。 只需创建一个新的TFSBuild文件,将要构建的项目添加到项目组(按照您希望它们构建的顺序),设置您希望输出的位置。这是通过覆盖TFSBuild文件中的 - 属性来完成的。

但我同意上一张海报 - 你为什么不用一个构建脚本运行,并在最后添加一个zip任务?维护每个项目的buildcript会增加维护开销...

答案 8 :(得分:0)

将其吊起在一个属性组中:

<CustomizableOutDir>true</CustomizableOutDir>

它将覆盖全局'CustomizableOutDir'属性,默认情况下,该属性设置为False。在SolutionToBuild的属性中设置它将不起作用。

答案 9 :(得分:-2)

您可以通过覆盖默认的CoreDropBuild目标实现来实现此目的。

在您的TFSBuild.proj文件中(默认情况下存储在TeamBuildTypes /&lt; Build Type&gt;下)添加以下目标:

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

在此目标中,您可以根据需要操作输出。默认是只复制从$(BinariesRoot)\ $(BuildType)到$(DropLocation)\ $(BuildNumber)的所有内容。

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

答案 10 :(得分:-3)

简单解决方案:

全部替换&lt; SolutionToBuild&gt; &lt; SolutionToPublish&gt;的节点。这当然只适用于可发布的项目(例如Web项目和应用程序),而不适用于图书馆项目。

就这么简单:)