2

在工作区只有一个工作文件夹的项目中,我的构建脚本运行良好。现在我正在处理一个需要 2 个工作文件夹的新项目,我之前脚本的所有签出和签入命令都失败了,没有找到文件。

显然,我不了解这里工作区实现的关键部分......我有一个依赖于其他项目的项目,第二个工作文件夹基本上是第 3 方文件夹,其中引用了各种已发布的 DLL 和标头编译我的项目所需的文件。有 2 个活动文件夹,本地文件夹是:

$(SourceDir)\TEAM-MAIN\地址终结器
$(SourceDir)\TEAM-MAIN\HH-CAHPS Project\MAINLINE\3rd 方

构建的代码工作正常,但自定义 AfterGet 在以下条目上失败:

<!-- Check out all of the assemblyInfo files -->
<Exec Command="$(TfCommand) checkout AssemblyInfo.cs /recursive"
      WorkingDirectory="$(MSBuildProjectDirectory)\..\sources"
      ContinueOnError="false"/>

如果我有一个工作文件夹并将源代码移动到足够高的位置以获取所有需要的文件,该项目当然可以工作,但我不想通过 43 个其他项目来做我想做的事情,更不用说与他们的汇编文件...

我也试过:

<!-- Check out all of the assemblyInfo files -->
<Exec Command="$(TfCommand) checkout AssemblyInfo.cs /recursive"
      WorkingDirectory="$(SolutionRoot)"
      ContinueOnError="false"/>

同样的问题,找不到任何程序集文件...我检查了构建日志,并且肯定看到在构建阶段检查了程序集文件...

任务“获取”
  获取 TeamFoundationServerUrl="http://pgpd-team01:8080/" BuildUri="vstfs:///Build/Build/1430" Force=True Overwrite=False PopulateOutput=False Preview=False Recursive=True Version="C7564" 工作区="SBN01P-TFS03_61"
<剪辑>
  获取 C:\Users\tfsservice\AppData\Local\Temp\InfoTurn\Address Finalizer\Sources\Address Finalizer\Address Finalizer\Properties\AssemblyInfo.cs;C7525。

如果有人有任何想法或可以向我指出一些文章以更好地解释多个工作文件夹的工作原理,我将不胜感激。

一些构建变量的值:

MSBuildProjectDirectory:C:\Users\tfsservice\AppData\Local\Temp\InfoTurn\Address Finalizer\BuildType

解决方案根目录:C:\Users\tfsservice\AppData\Local\Temp\InfoTurn\Address Finalizer\Sources

为了提供更多信息,我添加了以下命令:

    <!-- 报告我们的工作文件夹是什么 -->
    <执行
      Command='$(TfCommand) 工作文件夹'
      WorkingDirectory="$(SolutionRoot)\TEAM-MAIN\Address Finalizer"/>

结果是:

任务“执行”
  命令:
  “C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\PrivateAssemblies\..\tf.exe”工作文件夹
  ==================================================== ==============================
  工作区:SBN01P-TFS03_61 (tfsservice)
  服务器:http://pgpd-team01:8080/
   $/InfoTurn/TEAM-MAIN/Address Finalizer:C:\Users\tfsservice\AppData\Local\Temp\InfoTurn\Address Finalizer\Sources\TEAM-MAIN\Address Finalizer
   $/InfoTurn/TEAM-MAIN/HH-CAHPS Project/MAINLINE/第 3 方:C:\Users\tfsservice\AppData\Local\Temp\InfoTurn\Address Finalizer\Sources\TEAM-MAIN\HH-CAHPS Project\MAINLINE\3rd聚会

我发现以下工作目录可以工作:

WorkingDirectory="$(SolutionRoot)\TEAM-MAIN\Address Finalizer"

但是以下两个没有,请注意第二个是我的第二个工作文件夹:

WorkingDirectory="$(SolutionRoot)"
WorkingDirectory="$(SolutionRoot)\TEAM-MAIN\HH-CAHPS 项目\MAINLINE\3rd Party"

我为标签任务得到的错误是最有用的:

使用程序集“C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\PrivateAssemblies\Microsoft.TeamFoundation.Build.Tasks.VersionControl.dll”中的“标签”任务。
任务“标签”
  标签 TeamFoundationServerUrl="http://pgpd-team01:8080/" BuildUri="vstfs:///Build/Build/1507" Name="Address Finalizer 2.0.1 Build 039" Recursive=True Comments="Automated build: Address Finalizer 2.0.1 Build 039" Version="W" Child="replace" Files="C:\Users\tfsservice\AppData\Local\Temp\InfoTurn\Address Finalizer\Sources"
C:\Users\tfsservice\AppData\Local\Temp\InfoTurn\Address Finalizer\BuildType\TFSBuild.proj(310,5,310,5):错误:错误:无法确定工作区。

结帐的实际错误是:

任务“执行”
  命令:
  “C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\PrivateAssemblies\..\tf.exe”检查 AssemblyInfo.cs /recursive
  在您的工作区中的 C:\Users\tfsservice\AppData\Local\Temp\InfoTurn\Address Finalizer\Sources\AssemblyInfo.cs 中找不到匹配项。
C:\Users\tfsservice\AppData\Local\Temp\InfoTurn\Address Finalizer\BuildType\TFSBuild.proj(280,5): error MSB3073: The command ""C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE \PrivateAssemblies\..\tf.exe" checkout AssemblyInfo.cs /recursive" 以代码 1 退出。
4

2 回答 2

2

可以同时使用不相交的工作空间映射 + 递归。你只需要小心。

这将获取 Foo & Bar 中的所有 C# 文件:

$/project/dir1/foo -> c:\code\foo
$/project/dir2/bar -> c:\code\bar

tf get c:\code\*.cs -recursive

这通常会获取 Foo & Bar 下的所有内容,但如果无法将工作目录明确解析为工作区,则会失败:

$/project/foo -> c:\code1\foo
$/project/bar -> c:\code2\bar

tf get $/project/* -recursive

这可能有效(与上述相同的警告),但它可能不会达到您的预期!

$/project/foo -> c:\code1\foo
$/project/bar -> c:\code2\bar

tf get c:\code* -recursive

还有更多的变化;该行为取决于您的工作区是如何定义的,以及同一台机器上是否存在具有相似本地路径的其他工作区。你能准确地说明你的构建定义吗?您是否可以添加一些调试语句来准确验证 $(SolutionRoot) 和其他 MSBuild 属性在执行目标时等于什么?

/ 编辑 /

正如我所说,不相交的映射+递归是非常棘手的。事实证明,行为不仅因文件规范与工作区定义的细微细节而异,而且因命令而异!与我的第一个示例不同,这失败了:

$/project/dir1/foo -> c:\code\foo
$/project/dir2/bar -> c:\code\bar

tf checkout c:\code\*.cs -recursive

迷茫了吗?我是,而且我在 TFS 团队工作了几年!

让我从各种评论线程中总结一下我的建议:

  • 超级小心。在将任何内容放入 msbuild 脚本之前,请登录构建机器并测试它!交互式测试时,您将获得更好的错误消息和更快的反馈。这很重要,因为您尝试做的事情非常脆弱。从表面上看你的脚本,这里有一些更正让你开始:
    • Checkout 任务应使用服务器路径以获得最佳结果。修复后,您可以从 $(SolutionRoot)\TEAM-MAIN\Address Finalizer 或 $(SolutionRoot)\TEAM-MAIN\HH-CAHPS Project\MAINLINE\3rd Party 运行它,没有区别。[不幸的是,不是普通的旧 $(SolutionRoot)——如上所示,Checkout 不如 Get 聪明] 它在使用本地 itemspec 时似乎可以从地址终结器工作的原因是因为您实际上在该本地目录下有一些匹配的文件. 大概您在第 3 方下没有任何 AssemblyInfo.cs 文件。相比之下,使用 $/InfoTurn/TEAM-MAIN/AssemblyInfo.cs 等服务器路径将扩大范围,以便 TFS 搜索所有 TEAM-MAIN 以查找匹配文件以进行结帐。
    • 同样,您编写的 Label 任务需要使用服务器路径 + 从直接映射的文件夹运行。或者,您可以使用完全限定的版本规范 (Wwsname;domain\wsowner) 而不是缩写 (W)。
  • 更好的是,首先不要使用不相交的工作空间。查看所有被映射的本地路径:它们的共同祖先本身应该是一个映射文件夹。否则 (a) 从那里运行的命令将是不明确的,需要额外的参数,和/或根本不起作用 (b) 从单独映射的子文件夹运行的命令不会在整个工作区中查询 [除非您使用服务器路径]。为避免这一切,请选择一个映射作为“根”,然后确保所有后续映射都指向其下方的某个位置。一旦你建立了一个单一的、统一的根,事情就会变得容易得多。tf 命令从它内部的任何地方运行都会更加可预测(对于新手和专家来说!)。有多种方法可以设置满足您目标的统一工作空间:

    • 切换回一个大映射,然后隐藏您不想要的文件夹。请注意,您可以在 cloaks 下创建正映射(例如 cloak $/InfoTurn/TEAM-MAIN/HH-CAHPS Project,同时仍然映射 $/InfoTurn/TEAM-MAIN/HH-CAHPS Project/MAINLINE/3rd Party)。这为您提供了一个干净整洁的工作空间,并且没有任何时髦的副作用。缺点是涉及的工作量。如果有大量文件夹,您可以使用脚本来创建斗篷,但即便如此,随着新文件夹的添加或现有文件夹的重命名,仍然存在保持它们最新的问题。
    • 找到一个文件夹,它是您当前映射的父文件夹,并为其创建一个单级映射。例如,您可以创建一个一级映射 $/InfoTurn/TEAM-MAIN/* -> $(SolutionRoot)\TEAM-MAIN。这将拉下 TEAM-MAIN 的所有直接子级,但不会递归更深,除了您已经使用完全递归添加的两个文件夹。缺点是要下载和标记一些额外的文件。
    • 更改您当前的映射,以便一个文件夹映射到另一个文件夹之下。例如,您可以映射 $/InfoTurn/TEAM-MAIN/Address Finalizer -> $(SolutionRoot) 和 $/InfoTurn/TEAM-MAIN/HH-CAHPS Project/MAINLINE/3rd Party -> $(SolutionRoot)\3rd Party。现在 $(SolutionRoot) 是您需要的所有代码的统一根。此解决方案的问题在于,它可能会破坏任何依赖于在您的构建配置和其他也尝试构建地址终结器而不使用时髦映射之间保持恒定的相对路径的 makefile。
    • 将您的 3rd Party 文件夹移动到源代码控制结构中更自然的位置。毕竟,您可能不是在 TEAM-MAIN 工作的唯一需要参考 3rd 方组件的人。如果你们都同意将共享资源保留在根目录并适当地制作您的 makefile,那么大多数关于将深层路径映射到工作区的问题都会消失。
  • 更好的是,不要在构建过程中签出/签入文件。严重地。考虑到所有可能的边缘情况,人类容易处理的常见情况(例如锁、版本冲突)是自动化的噩梦。如果您尝试使用持续集成,那么上帝会帮助您……同时,您所有重要的构建都在破坏,而您调试的功能我真的看不到任何好处。反而:

    • 将所有系统范围的程序集信息(包括构建编号)合并到一个 AssemblyInfo.cs 文件中。
    • 从每个项目导入的通用 MSBuild *.targets 文件中引用此 C# 文件。(如果您还没有重构项目级任务,那么这是重构项目级任务的好机会)。
    • 让 Team Build 根据需要修改此 C# 文件。例如,您可以在 CoreCompile 任务之前随时填写内部版本号。请注意,我们只是覆盖磁盘上的内容,而不是触及源代码控制。
    • 如果您希望您的桌面版本也具有增量编号(我个人不明白这一点),请将类似的任务添加到您的通用 *.targets 文件中,并将其从 Team Builds 中排除。
于 2009-11-24T16:27:42.443 回答
1

我的脚本现在正在运行,有几个小问题导致了问题。一是对于许多命令,您必须引用确切的工作目录(本地文件夹)才能使命令正常工作。另一个问题是需要将设置属性移到自定义任务中,以便其他任务可以正确使用它们。所以希望我的完整构建脚本包括在下面将帮助其他人。将其粘贴到任何编辑器中都会使其更易于阅读。

  <!-- 生成版本控制所需的所有项目特定变量 -->
  <属性组>
    <VersioningFile>$(SolutionRoot)\TEAM-MAIN\Address Finalizer\Address Finalizer\versionNumber.txt</VersioningFile>
    <TfCommand>"$(TeamBuildRefPath)\..\tf.exe"</TfCommand>
    <WorkingDirectory>$(SolutionRoot)\TEAM-MAIN\Address Finalizer</WorkingDirectory>
    <WorkingDirectory2>$(SolutionRoot)\TEAM-MAIN\HH-CAHPS Project\MAINLINE\3rd Party</WorkingDirectory2>
    <DllName>Address_Finalizer.dll</DllName>
    <PublishedFolder>$(SolutionRoot)\TEAM-MAIN\HH-CAHPS Project\MAINLINE\3rd Party\bin\PG 文件导入</PublishedFolder>
    <LabelPath>未知</LabelPath>
  </属性组>

  <!-- 仅在将其用作其他构建的模板时编辑属性-->

  <Import Project="$(MSBuildExtensionsPath)\ExtensionPack\MSBuild.ExtensionPack.tasks"/>
  <Import Project="$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets"/>

  <目标名称="AfterGet">   
    <Message Text="FOOBAR 版本文件:$(VersioningFile)" />
    <Message Text="FOOBAR 工作目录:$(WorkingDirectory)" />

    <!-- 报告我们的工作文件夹是什么 -->
    <执行
      Command='$(TfCommand) 工作文件夹'
      工作目录="$(工作目录)"/>

    <!-- 生成版本# -->
    <CallTarget Targets="GenerateVersion"/>

    <!-- 版本化程序集 -->
    <CallTarget Targets="VersionAssemblies"/>      

    <!-- 为 QA 配置部署 -->
    <CallTarget Targets="ConfigureForQA"/>

    <!-- 将标签应用于所有内容 -->
    <CallTarget Targets="LabelWorkingDirectory"/>
    <CallTarget Targets="LabelWorkingDirectory2"/>
  </目标>

  <目标名称="AfterDropBuild">
    <!-- 生成部署所需的所有项目特定变量,具体取决于版本控制 -->
    <属性组>
      <ReleaseDLL>$(DropLocation)\$(BuildNumber)\Release\$(DllName)</ReleaseDLL>
      <PublishedDLL>$(PublishedFolder)\$(DllName)</PublishedDLL>
    </属性组>

    <!-- 查看已发布的 DLL -->
    <执行
      Command='$(TfCommand) checkout /lock:checkout "$(PublishedDLL)"'
      工作目录="$(工作目录)" />

    <!-- 将发布复制到已发布-->
    <复制
      SourceFiles="$(ReleaseDLL)"
      DestinationFolder="$(PublishedFolder)"/>

    <!-- 签入已发布的DLL -->
    <执行
      Command='$(TfCommand) checkin /override:Automated /noprompt /comment:"$(VersionComment)" "$(PublishedDLL)"'
      工作目录="$(工作目录)" />
  </目标>

  <目标名称="生成版本">
    <!-- 查看版本文件 -->
    <执行
      Command='$(TfCommand) checkout /lock:checkout "$(VersioningFile)"'
      WorkingDirectory="$(SolutionRoot)"/>

    <!-- 增加文件中的修订号并保存 -->
    <Version VersionFile="$(VersioningFile)" BuildType="None" RevisionType="Increment">
      <Output TaskParameter="Major" PropertyName="Major" />
      <Output TaskParameter="Minor" PropertyName="Minor" />
      <Output TaskParameter="Build" PropertyName="Build" />
      <Output TaskParameter="Revision" PropertyName="Revision" />
    </版本>

    <!-- 将修订版填充为 3 位数。-->
    <属性组>
      <PaddedRevision Condition="$(Revision) < 1000">$(Revision)</PaddedRevision>
      <PaddedRevision Condition="$(Revision) < 100">0$(Revision)</PaddedRevision>
      <PaddedRevision Condition="$(Revision) < 10">00$(Revision)</PaddedRevision>
    </属性组>

    <!-- 为日志文件生成我们的版本字符串的样子 -->
    <CreateProperty Value="地址终结器 $(Major).$(Minor).$(Build) Build $(PaddedRevision)">
      <Output TaskParameter="Value" PropertyName="ProjectVersion" />
    </CreateProperty>

    <CreateProperty Value="自动构建:$(ProjectVersion)">
      <Output TaskParameter="Value" PropertyName="VersionComment" />
    </CreateProperty>

    <Message Text="新版本为:$(ProjectVersion)"/>

    <!-- 签入版本文件-->
    <执行
      Command='$(TfCommand) 签入 /noprompt /comment:"$(VersionComment)" "$(VersioningFile)"'
      WorkingDirectory="$(SolutionRoot)"/>
  </目标>

  <目标名称="VersionAssemblies">
    <!-- 获取所有目录下的所有 assemblyinfo 文件(应该只有一个) -->
    <项目组>
      <AssemblyInfoList Include="$(SolutionRoot)\**\assemblyinfo.cs"/>
    </ItemGroup>

    <Message Text="FOOBAR 构建目录:$(MSBuildProjectDirectory)"/>
    <Message Text="FOOBAR 解决方案根:$(SolutionRoot)"/>
    <Message Text="FOOBAR 工作目录:$(WorkingDirectory)"/>

    <!-- 查看所有的 assemblyInfo 文件 -->
    <执行
      Command="$(TfCommand) 签出 AssemblyInfo.cs /recursive"
      工作目录="$(工作目录)"
      ContinueOnError="假"/>

    <!-- 更改所有 assemblyInfo 文件的版本号 -->
    <MSBuild.ExtensionPack.Framework.AssemblyInfo AssemblyFileVersion="$(Major).$(Minor).$(Build).$(Revision)" AssemblyInfoFiles="@(AssemblyInfoList)"/>
    <MSBuild.ExtensionPack.Framework.AssemblyInfo AssemblyVersion="$(Major).$(Minor).$(Build).$(Revision)" AssemblyInfoFiles="@(AssemblyInfoList)"/>

    <!-- 检入所有的 assemblyInfo 文件 -->
    <执行
      Command='$(TfCommand) checkin /override:Automated /comment:"$(VersionComment)" /noprompt AssemblyInfo.cs /recursive'
      工作目录="$(工作目录)"
      ContinueOnError="假"/>
  </目标>

  <目标名称="ConfigureForQA">
    <!-- 获取所有 QA app.config 文件(应该只有一个)-->
    <CreateItem Include="$(SolutionRoot)\**\app-qa.config">
      <输出任务参数=“包含”项目名称=“AppConfigFiles”/>
    </创建项目>

    <!-- 将 QA app.config 复制到 app.config 以使其成为驱动配置文件 -->
    <复制
      SourceFiles="@(AppConfigFiles)"
      OverwriteReadOnlyFiles="True"
      DestinationFiles="@(AppConfigFiles->'$(SolutionRoot)\%(RecursiveDir)app%(Extension)')"/>
  </目标>

  <目标名称="标签工作目录">
    <Message Text="FOOBAR 版本:$(ProjectVersion)" />
    <Message Text="FOOBAR 评论:$(VersionComment)" />
    <Message Text="FOOBAR 路径:$(LabelPath)" />

    <!-- 在工作区应用标签 -->
    <标签
      TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
      BuildUri="$(BuildUri)"
      名称="$(ProjectVersion)"
      文件="$(工作目录)"
      评论="$(版本评论)"
      ContinueOnError="假"
      递归=“真” />
  </目标>

  <目标名称="标签工作目录2">
    <Message Text="FOOBAR 版本:$(ProjectVersion)" />
    <Message Text="FOOBAR 评论:$(VersionComment)" />
    <Message Text="FOOBAR 路径:$(LabelPath)" />

    <!-- 在工作区应用标签 -->
    <标签
      TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
      BuildUri="$(BuildUri)"
      名称="$(ProjectVersion)"
      文件="$(WorkingDirectory2)"
      评论="$(版本评论)"
      ContinueOnError="假"
      递归=“真” />
  </目标>
于 2009-12-02T13:59:20.853 回答