CSDN博客

img colayungnew

用 Visual Studio.NET 和 Visual SourceSafe 进行团队开发

发表于2004/11/3 11:29:00  2279人阅读

分类: Visual SourceSafe

用 Visual Studio.NET 和 Visual SourceSafe 进行团队开发

使用 Visual Studio.NET Visual SourceSafe 进行团队开发

BuildIt Visual Studio .NET 的自动生成工具

Michael Monteiro

Sapient Corporation

2003 年 2 月

摘要:BuildIt 是一种 Microsoft_ .NET 控制台应用程序,它自动执行在 MSDN_ Library 中 patterns & practices 中的 “Team Development with Visual Studio .NET and Visual SourceSafe” 一文中概述的生成过程。 BuildIt 由 Sapient Corporation 设计、开发和测试,并且由 Microsoft(包括 Microsoft patterns & practices 和 Visual Studio_ .NET 开发系统的小组成员)审查。(28 页打印页)

*

使用 BuildIt:

消除了创建、测试和维护自定义生成脚本所需的时间。

使小组的生成过程的可重复性和一致性更高。

BuildIt 是为启动开发 .NET 分布式应用程序的生成过程而设计的。 可下载的程序为 Microsoft Visual C#_ 开发工具和 Microsoft Visual Basic_ .NET 开发系统提供完整的源代码和全面的文档。

注意 BuildIt 目前支持构建包含某些安装项目的解决方案,这些项目是用 Visual Basic .NET、Visual C# 和 Visual Studio .NET 开发的。 它尚未针对用其他 .NET 语言编写的项目或其他安装项目(例如,Wise 或 InstallShield 的安装项目)进行测试。

尽管 BuildIt 已经过测试和审查并被视为可靠的代码集,但是代码和文档均按“原样”提供,您可以使用和扩展它们。

本文包括以下部分:

下载和安装 BuildIt

测试安装

用户指南

部署和运行

已知问题

设计与实现

类引用

常见问题

小结

下载和安装 BuildIt

单击此处可下载 BuildIt

安装进程将在您的 程序 菜单中创建一个 Microsoft Application Blocks for .NET子菜单。 在 Microsoft Application Blocks for .NET 子菜单上,有一个 Sapient BuildIt 子菜单,其中包括用来启动 BuildIt Visual Studio .NET 解决方案的选项。

测试安装

下面的步骤可用来创建 Program Files/BuildIt/Code/BuildItTest 中的两个示例解决方案,这些步骤有助于确认 BuildIt 是否正常运行。

向源代码管理中添加项目

在 Visual SourceSafe_ (VSS) 版本控制系统中,创建一个名为 BuildItTest 的新项目。

$/BuildItTest 的工作文件夹设置为 Program Files/BuildIt/Code/BuildItTest

向 VSS 中添加程序集版本文件:

将 Program Files/BuildIt/Code/BuildItTest/AssemblyVersionInfo.cs 添加到 $/BuildItTest 中。

对于AssemblyVersionInfo.vb,重复上述操作。

为 Web 项目 (Project2) 创建一个虚拟目录:

浏览至 Program Files/BuildIt/Code/BuildItTest/Solution2/Project 2

右击 Project 2,然后单击 属性

单击 Web Sharing 选项卡,然后单击 Share this folder

接受默认的别名,然后单击 确定 关闭 编辑别名 对话框。

单击 确定 关闭 属性 对话框。

向 VSS 中添加解决方案: =

打开 Program Files/BuildIt/Code/BuildItTest/Solution1/Solution1.sln

在“解决方案资源管理器”中,右击 Solution1,然后单击 Add Solution to Source Control

浏览至 $/BuildItTest,然后单击 确定(单击 可创建 VSS 项目)。

如果系统提示为 项目1” 指定位置,请浏览至 $/BuildItTest/Solution1,然后单击 确定

对于 Solution2,重复步骤 a 到 d。

在 VSS 中共享程序集版本文件:

$/BuildItTest/AssemblyVersionInfo.cs 共享为 $/BuildItTest/Solution1/项目1”

$/BuildItTest/AssemblyVersionInfo.vb 共享为 $/BuildItTest/Solution2/Project 2

向项目中添加程序集版本文件

向 Solution1 中的 Project1 中添加 AssemblyVersionInfo.cs:

打开 Program Files/BuildIt/Code/BuildItTest/Solution1/Solution1.sln

在“解决方案资源管理器”中,右击 Project 1,指向 Add,然后单击 Add Existing Item

浏览至 Program Files/BuildIt/Code/BuildItTest/Solution1/Project1/AssemblyVersionInfo.cs,然后单击 打开

Check Out for Edit 对话框中,单击 Check Out 签出 Project1.csproj

打开 AssemblyVersionInfo.cs 并记下版本号 (1.0.0.*)。

按 CTRL+SHIFT+B 生成解决方案。

在“解决方案资源管理器”中,右击 Project 1,然后单击 Check In Now (Recursive)

要将 AssemblyVersionInfo.vb 添加到 Solution2 中的 Project2 中,请重复步骤 a 到 g。

更新配置设置:

打开 Program Files/BuildIt/Code/BuildItTest/BuildIt.exe.config

更新 sourceControl 设置。

部署 BuildIt:

打开 Program Files/BuildIt/Code/BuildIt.sln

按 CTRL+SHIFT+B 生成解决方案(确保生成 Release 版本)。

将 .exe 文件和所有的 .dll 文件从 Program Files/BuildIt/Code/BuildIt/bin/Release 复制到 Program Files/BuildIt/Code/BuildItTest

执行 BuildIt 并验证:

打开 Program Files/BuildIt/Code/BuildItTest/BuildNumber.xml 并注意下一个内部版本号是 1。

打开 Program Files/BuildIt/Code/BuildItTest/BuildIt.bat,以检查 BuildIt 命令行。

运行 BuildIt.bat 以生成解决方案。

打开 Program Files/BuildIt/Code/BuildItTest/BuildIt.log,以查看是否有错误。

打开 Program Files/BuildIt/Code/BuildItTest/BuildReport.log,以检查生成的结果。

打开 BuildNumber.xml 并注意下一个内部版本号已递增到 2。

打开 AssemblyVersionInfo.cs AssemblyVersionInfo.vb,并注意版本号已从 1.0.0.* 更新到 1.0.1.*。

浏览至 Program Files/BuildIt/Code/BuildItTest/Archive/1,以查看生成的程序集是否已存档到以内部版本号命名的文件夹。

浏览至 Program Files/BuildIt/Code/BuildItTest/Latest,以查看生成的程序集是否已复制到 Latest 文件夹。

显示 $/BuildItTest 的历史记录,以查看 VSS 项目是否已用内部版本号进行标记。

用户指南

下列主题提供有关使用 BuildIt 的更多信息:

维护内部版本号

生成解决方案

检查生成报告

重新生成解决方案

对生成进行存档

以电子邮件发送生成结果

配置程序集的版本

维护内部版本号

BuildIt 使用内部版本号来标记解决方案(在 VSS 中管理该解决方案的源)并更新程序集版本号(如果启用了相应的生成选项)。 在每次成功生成结束时,BuildIt 都通过按 1 递增现有的内部版本号来生成新的内部版本号(如果生成失败,则内部版本号不递增)。 新的内部版本号随后保存到一个 XML 文件中,该文件的名称和位置由包含在 BuildIt.exe.config 文件中的 buildNumberFilePath 属性来定义。

下面的示例显示名为 BuildNumber.xml 的内部版本号 XML 文件的内容。

<buildNumber>1000</buildNumber> 

注意 在运行 BuildIt 之前,必须创建内部版本号 XML 文件并用起始内部版本号对其进行初始化。 还要注意内部版本号必须为正整数。

生成解决方案

BuildIt 可被配置为生成单个解决方案或多个解决方案。 对于包含跨解决方案文件引用的多解决方案系统,解决方案必须以正确的顺序列出。 例如,如果 Solution2 依赖 Solution1,请将 Solution1 列在 Solution2前面,这样可确保先生成 Solution1

下面的示例来自 BuildIt.exe.config,它显示如何将该生成工具配置为生成多解决方案系统:

<solutions latestRootFolderFullName="c:/System" 
 buildNumberFilePath="c:/System/BuildNumber.xml"> 
  <solution path="c:/System/Solution1/Solution1.sln"/> 
  <solution path="c:/System/Solution2/Solution2.sln"/> 
</solutions> 

检查生成报告

在每次生成结束时,BuildIt 都在工作文件夹中生成一个名为 BuildReport.log 的生成报告。 生成报告可用任何文本编辑器查看。 生成报告包含摘要信息以及成功和失败解决方案的列表。 如果有任何解决方案无法生成,则可以使用 Visual Studio .NET 手动生成这些解决方案,以找出其失败的确切原因。

============================================= 
Build Report Generated by BuildIt 
============================================= 
Build Number:    8016 
Build Start:    9/12/2002 6:50:09 PM 
Build End:    9/12/2002 6:50:43 PM 
Build Duration:  1 minute(s) 
Archive Folder:  c:/System/Archive/8016/ 
Latest Folder:    c:/System/Latest/ 
Build Results:    1 succeeded, 0 failed 
============================================= 
Succeeded 
============================================= 
c:/System/Solution1/Solution1.sln 
============================================= 
Failed 
============================================= 

重新生成解决方案

如果在生成过程中出现生成中断,则需要 rebuild(重新生成)。 在重新生成解决方案之前,必须完成下列步骤。

在生成中断之后重新生成

1.如果在修复时需要更改代码,请将必需的文件从 VSS 手动签出到生成计算机。

注意 在签出文件时,使用在该生成工具的配置文件中指定的 VSS 凭据。 这有助于标识为了修复中断而修改的文件。

2.进行代码更改,使用 Visual Studio .NET 重新编译,然后测试重新编译的文件。

3.在问题得到解决之后,签入修改后的文件。

4.用在以前的生成过程中生成的标签手动更新每个签入文件的标签,如下所示:

a. 右击文件名,然后单击 Show History

b. 在 History Options 对话框中,单击 确定

c. 单击要更新其标签的文件的版本,然后单击 详细信息

d. 用在生成过程中使用的标签(例如,“Build 100”)更新 Label 字段。

5. 用 /rebuild 选项手动运行 BuildIt。

对生成进行存档

按照 MSDN Library 中的“Team Development with Visual Studio .NET and Visual SourceSafe”一文下的 “Consider Maintaining Previous Builds” 一节中的说明,可以将 BuildIt 配置为将以前的生成结果存档到指定的位置。

在启用了 archiveBuild 生成选项时,该生成工具将生成的程序集复制到以当前的内部版本号命名的文件夹,该文件夹位于由配置文件中的 archiveRootFolderFullName 属性指定的存档根文件夹下。

在默认情况下,BuildIt 只对从 Visual C# 和 Visual Basic .NET 项目生成的输出结果进行存档。 要对从其他项目(例如,Visual Studio .NET 安装项目)生成的输出结果进行存档,请将 folder 元素添加到 additionalFoldersToArchive 元素中。

下面的示例来自 BuildIt.exe.config,它显示如何启用此选项。

<options> 
  <archiveBuild mode="on" archiveRootFolderFullName="c:/System/Archive"> 
    <additionalFoldersToArchive> 
      <folder fullName=" c:/System/Solution1/Setup1/Debug"  
       destFolderName="Debug/Setup1" /> 
      <folder fullName=" c:/System/Solution1/Setup1/Release"  
       destFolderName="Release/Setup1" /> 
    </additionalFoldersToArchive> 
  </archiveBuild> 
</options> 

通过电子邮件发送生成结果

按照“Team Development with Visual Studio .NET and Visual SourceSafe”一文下的 “Emailing Build Results” 一节中的说明,可以将 BuildIt 配置为通过电子邮件将生成结果发送到指定的电子邮件地址。

在启用了 sendBuildReport 生成选项时,该生成工具将生成的包含名为 BuildReport.log的生成报告的电子邮件消息,发送到由 toAddress 属性指定的电子邮件地址。

下面的示例来自 BuildIt.exe.config,它显示如何启用此选项:

<options> 
  <sendBuildReport mode="on" smtpServer="255.255.255.255" 
   toAddress="you@yourcompany.com"/> 
</options> 

配置程序集的版本

按照如下方式可将 BuildIt 配置为用存储在由 buildNumberFilePath 属性指定的 XML 文件中的当前内部版本号更新程序集的版本:

<solutions buildNumberFilePath="c:/System/BuildNumber.xml"> 

程序集的版本通常是在 AssemblyVersion 属性(此属性在 AssemblyInfo.cs 或 AssemblyInfo.vb 文件中定义)中指定的。 但是,在将该生成工具配置为更新程序集的版本时,AssemblyVersion属性应当在一个单独的文件(例如,AssemblyVersionInfo.csAssemblyVersionInfo.vb)中定义,此文件在 VSS 中跨解决方案的所有 .NET 项目之间共享。 这允许 BuildIt 更新一个文件并将所做的更改传递到解决方案中的所有项目。 有关详细信息,请参阅“Team Development with Visual Studio .NET and Visual SourceSafe”一文的“Consider Centralizing Assembly Version Numbers”一节。

注意 在安装 BuildIt 时,会将两个名为 AssemblyVersionInfo.csAssemblyVersionInfo.vb 的示例文件复制到 BuildItTest 目录中。

版本号以物理形式表示为用句点隔开的四段数字,如下面的代码示例所示。

<major version>.<minor version>.<build number>.<revision> 

当启用了 updateAssemblyVersion 生成选项时,该生成工具用当前的内部版本号更新 AssemblyVersion 属性的 build number 段。

下面的示例来自 BuildIt.exe.config,它显示如何启用此选项。

<options> 
  <updateAssemblyVersion mode="on"  
   csAssemblyVersionVSSPath="$/System/AssemblyVersionInfo.cs"   
   vbAssemblyVersionVSSPath="$/System/AssemblyVersionInfo.vb" /> 
</options> 

部署和运行

这一部分包括下列管理主题:

部署 BuildIt

配置 BuildIt

保护 BuildIt

对 BuildIt 进行故障排除

部署 BuildIt

在部署任何应用程序时,标识已存在的任何依赖项是很重要的。 BuildIt(作为一个名为 BuildIt.exe 的单程序集来实现)具有下列依赖项:

BuildIt.exe.config(用于配置设置)

BuildNumber.xml(用于维护下一个内部版本号)

注意 内部版本号 XML 文件的名称和位置由 BuildIt.exe.config 文件中的 buildNumberFilePath 属性来定义。 还要注意,在运行 BuildIt 之前,必须创建并用起始内部版本号初始化内部版本号 XML 文件。 有关详细信息,请参阅“维护内部版本号”一节。

Microsoft.ApplicationBlocks.ExceptionManagent.dllMicrosoft.ApplicationBlock.ExceptionManagent.Interfaces.dll(用于异常管理)

Interop.SourceSafeTypeLib.dll(用于与 VSS API (SSAPI.dll) 进行互操作)

Visual SourceSafe 6.0c客户端程序(用于访问远程 VSS 数据库)

Visual Studio .NET(用于生成 .NET 解决方案)

可根据部署小组的需要利用这些信息来选择部署方法。 对于 BuildIt,可以考虑两种部署方法: XCOPY 部署或 Windows 安装程序部署。 以下各个部分更详细地描述这些选项。

XCOPY 部署

用 XCOPY 命令部署 BuildIt 是最方便的方法;但是,它要求部署该工具的人员具有更多的知识。 要使用这一方法进行部署,请使用 XCOPY 将必需的程序集以及应用程序配置文件 (BuildIt.exe.config) 复制到目标计算机上的目录中。 然后,使用 Installutil.exe 系统实用工具调用 ExceptionManagerInstaller 类,以便创建默认的异常发布程序在将该类写入 Windows 事件日志时所需的事件源。 例如,使用以下命令行:

Installutil.exe Microsoft.ApplicationBlocks.ExceptionManagement.dll 

有关 Exception Management Application Block 的详细信息,请参阅 MSDN Library 中的“Microsoft Application Blocks for .NET”。

Windows 安装程序部署

用 Windows 安装程序部署 BuildIt 需要预先执行的工作比使用 XCOPY 部署时要多,但对于部署该工具的人员来说,需要的知识则较少。 要使用 Visual Studio.NET 安装项目和部署项目进行部署,则将 BuildItException ManagementException Management Interfaces 项目输出添加到应用程序文件夹以及 BuildIt.exe.config 文件中。 然后,以自定义操作形式添加异常管理项目输出,这样可确保 ExceptionManagerInstaller 类在安装时进行实例化。

配置 BuildIt

该生成工具的行为是使用应用程序配置文件来控制的。 应用程序配置文件是基于 XML 的文档,它们存储在应用程序文件夹层次的根目录中。 应用程序配置文件的名称采用应用程序名.应用程序扩展名.config 形式。 对于该生成工具,应用程序配置文件的名称为 BuildIt.exe.config。

这一部分包括有关配置 BuildIt 的下列主题:

设置生成选项

配置跟踪

配置异常管理

运行多个版本的 Visual Studio .NET

注意 在对配置文件进行修改后,所做的更改将在下次执行 BuildIt 后生效。

设置生成选项

可通过将生成选项的 mode 属性设置为 on/off 来启用或禁用任何生成选项,如下面的代码示例所示:

<options> 
  <sendBuildReport mode="on" smtpServer="255.255.255.255" 
   toAddress="you@company.com"/> 
</options> 

注意 如果模式属性被忽略或者其值不是 on/off,就会引发异常。 还要注意模式属性的值区分大小写。

配置跟踪

BuildIt 使用 System.Diagnostics 命名空间中的 Trace类来将跟踪输出结果生成到控制台和工作目录中名为 BuildIt.log 的文件中。 下面是跟踪输出的示例。

Validating command-line arguments 
Reading BuildIt settings from configuration file 
Starting build 100 at 11/1/2002 1:57:53 PM 
Updating assembly version file $/System/AssemblyVersionInfo.cs 
Labelling source 
Getting source from label 
Backing up latest folder 
Building Debug version of solution C:/System/Solution1/Solution1.sln 
Building Release version of solution C:/System/Solution1/Solution1.sln 
Building Debug version of solution C:/System/Solution2/Solution2.sln 
Building Release version of solution C:/System/Solution2/Solution2.sln 
2 solution(s) succeeded, 0 failed 
Deleting latest backup folder 
Archiving additional folders 
Generating build report 
Not sending build report because the option was not enabled 
Ending build at 11/1/2002 1:58:40 PM - 0 minute(s) to complete 

可以使用在 BuildIt.exe.config 文件中定义的名为 traceLevel 的 TraceSwitch来启用和禁用跟踪。可通过将 TraceSwitch 设置为 0 来禁用跟踪,将该开关设置为 3 来启用跟踪。 将 TraceSwitch 设置为 4 可启用更详细的跟踪输出(详细模式)。

下面的代码示例显示如何启用跟踪。

<system.diagnostics>  
  <switches> 
    <add name="traceLevel" value="3"/> 
  </switches> 
</system.diagnostics> 

另外,BuildIt 可被配置为将跟踪输出追加到跟踪文件(如果已经存在一个跟踪文件),或者可配置为改写跟踪文件。 建议的设置是改写跟踪文件,以防它变得太大。 下面的代码示例显示如何进行此设置。

<appSettings> 
  <add key="appendTraceOutput" value="off" /> 
</appSettings> 

注意 如果 appendTraceOutput 项被忽略,BuildIt 会在默认情况下改写跟踪文件。

配置异常管理

BuildIt 使用 Microsoft 的 Exception Management Application Block(异常管理应用程序块),可以使用配置文件中的 XML 设置对该应用程序块进行配置。 如果这些设置被忽略,Exception Management Application Block 会在默认情况下将异常发布到应用程序日志中。 然而,可以指定其他发布程序。

在撰写本文时,配置文件不包含 Exception Management 设置。 因此,所有的异常都发布到可使用事件查看器进行查看的应用程序日志中。 有关详细信息,请参见 MSDN Library 中的“Exception Management Application Block Overview”。

用运行多个版本的Visual Studio .NET

在默认情况下,BuildIt 使用最新安装的 Visual Studio .NET 版本来生成解决方案。 但是,如果在同一台计算机上安装了多个版本,则开发人员可能希望 BuildIt 使用某个特定版本。 要强制 BuildIt 使用特定版本的 Visual Studio .NET,请在配置文件的 appSettings 部分中包括 visualStudioProgID 项。 下面的代码示例显示如何进行该操作。

<appSettings> 
  <add key="
visualStudioProgID
" value="VisualStudio.Solution.7" /> 
</appSettings> 

注意 如果 visualStudioProgID 项被忽略,BuildIt 会在默认情况下使用最新安装的 Visual Studio .NET 版本。

保护 BuildIt

与保护所有应用程序一样,必须确保所有敏感的数据和资源不会受到未经授权的访问。 对于 BuildIt,主要考虑以下两个方面:

防止对应用程序配置文件进行未经授权的访问

BuildIt 所需要的访问权限

防止对配置文件进行未经授权的访问

因为该生成工具的配置文件包含敏感数据(例如,VSS 用户名和密码),所以应当确保只有经过授权的用户才能查看或更改这些设置。 可通过使用 Windows NTFS 文件权限来保护配置文件。

BuildIt 所需要的访问权限

BuildIt 使用 Microsoft 的 Exception Management Application Block(异常管理应用程序块)将异常发布到事件日志中。 另外,BuildIt 还对文件系统进行读/写操作。 因此,需要确保 BuildIt 使用的安全原则具有执行这些操作的适当权限。

BuildIt 进行故障排除

可以使用两种机制来帮助开发人员对该生成工具进行故障排除: 跟踪和异常管理。 在启用了跟踪时,BuildIt 在工作文件夹中生成一个名为 BuildIt.log 的跟踪文件,此文件可用来确定该生成工具已经执行的步骤。 这在尝试确定该生成工具为何不按预期方式运行时非常有用。

在出现异常时,BuildIt 使用 Microsoft 的 Exception Management Application Block(异常管理应用程序块)将异常发布到应用程序日志中。 开发人员随后可以使用“事件查看器”来查看有关异常的详细信息。

结合使用这两个机制可提供用来对该生成工具进行故障排除的信息。

已知问题

BuildIt 在大多数情况下非常有效,但是在少数情况下,它存在一些已知问题。 表 1 列出了 BuildIt 的已知问题。

1 BuildIt 的已知问题

问题 说明 解决办法

BuildIt 在生成两个或多个同名项目时会引发错误。

当 BuildIt 尝试将项目输出(如果该输出已经存在)复制到 Latest 文件夹时,会出现错误。 如果 BuildIt 遇到两个同名的项目,即使它们驻留在不同的解决方案中,也会出现上述错误。 如果 BuildIt 在该工具的单次运行过程中再次遇到同一个解决方案,同样也会出现上述问题。

确保所有项目的名称在整个系统中保持唯一,并确保 App.config 文件不包含重复的解决方案项。

在生成过程中将出现 Set Project Location 对话框。

在生成过程中会出现 Set Project Location 对话框,提示用户为 Web 项目的工作副本设置位置。 在尝试加载未映射到相应的虚拟目录的 Web 项目时,会出现该对话框。

为了防止在运行 BuildIt 的过程中出现该对话框,请在运行 BuildIt 之前确保所有的 Web 项目都映射到虚拟目录。 一个方便的检查方法就是在运行 BuildIt 之前,在 Visual Studio .NET 中手动打开每个解决方案。如果该对话框在运行 BuildIt 时出现,只需通过为该 Web 项目的工作副本设置位置来响应该对话框。Visual Studio .NET 随后会自动创建虚拟目录。

设计与实现

这一部分包括下面的设计与实现主题:

问题说明

设计目标

解决方案说明

增强功能

问题说明

BuildIt 是为解决下列问题而设计的:

开发人员在创建、测试和维护自定义生成脚本上花费宝贵的开发时间,但这些脚本通常不能在不同项目中重新使用。

开发人员的时间通常比较紧,这使得他们很少有时间彻底测试和记录他们的生成脚本。

设计目标

BuildIt 的设计目标是:

解决方案必须遵循 MSDN Library 中的“Team Development with Visual Studio .NET and Visual SourceSafe”一文中概述的步骤。

解决方案必须记录完善、可维护且易于使用。

解决方案必须灵活;它应当可以在无需重新编码和重新编译的情况下重新配置。

解决方案必须可靠;它必须处理生成过程中出现的任何异常。

解决方案说明

BuildIt 是一个由许多类组成的控制台应用程序,这些类共同运行以自动执行生成过程。例如:

验证命令行选项,通过 BuildItSectionHandler 检索生成工具设置,并通过 BuildManager 启动生成过程。

从该工具的配置文件检索设置。 这些设置用于修改运行时行为。

组合生成过程。

公开在生成过程中使用的 VSS 操作(例如,签入和签出)。

为存储在资源文件中的错误信息提供类型安全的访问。

封装 BuildIt 支持的命令行参数。

逻辑流如下所示:

用适当的命令行参数启动 BuildIt。

Main1 类调用 BuildInitializer 类的 开始 方法。

BuildInitializer 类验证命令行参数,然后使用 BuildItSectionHandler 类从该工具的配置文件检索设置。 BuildItSectionHandler 通过名为 BuildItSettings 的类型安全的结构返回设置。

BuildInitializer 创建 BuildManager 的实例,并将 BuildItSettings 结构传递给 Constructor

BuildInitializer 随后针对 BuildManager 调用 Build,以便启动由命令行参数指示的生成或重新生成过程。

配置文件设置

名为 BuildIt.exe.config 的 BuildIt 配置文件包含在运行时读取的设置。 这允许在无需重新编译解决方案的情况下修改该工具的行为。

文件中的设置指出了:

所生成的解决方案。

连接到指定的 VSS 数据库所需要的信息。

是否用内部版本号更新程序集版本。

是否将以前生成的程序集存档到指定的位置。

是否通过电子邮件将生成结果发送到指定的电子邮件地址。

注意 如果缺少或者错误地指定了任何设置,那么,BuildIt 会引发异常,除非本文中另有声明。

下面的代码示例显示配置文件设置的格式。

<configuration> 
  <configSections> 
    <section name="BuildIt"  
     type="Sapient.Framework.Tools.BuildIt.BuildItSectionHandler,     
     BuildIt"/> 
  </configSections> 
  <appSettings> 
    <add key="appendTraceOutput" value="off" /> 
    <add key="enableCustomMessageFilter" value="on"/> 
    <add key="visualStudioProgID" value="VisualStudio.Solution.7"/> 
  </appSettings> 
  <buildIt> 
    <sourceControl username="username" password="password"  
     iniFilePath="c:/Program Files/Microsoft Visual Studio/VSS/srcsafe.ini"  
     srcVSSRootFolder="$/System" srcFileRootFolder="c:/System"/> 
    <solutions latestRootFolderFullName="c:/System"  
     buildNumberFilePath="c:/System/BuildNumber.xml"> 
      <solution path=" c:/System/Solution1/Solution1.sln"/> 
      <solution path=" c:/System/Solution2/Solution2.sln"/> 
    </solutions> 
    <options> 
      <sendBuildReport mode="on/off" smtpServer="255.255.255.255"  
       toAddress="you@yourcompany.com"/> 
      <archiveBuild mode="on/off"  
        archiveRootFolderFullName="c:/System/Archive"> 
        <additionalFoldersToArchive> 
          <folder fullName="c:/System/Solution1/Setup1/Debug"  
           destFolderName="Debug/Setup1" /> 
          <folder fullName=" c:/System/Solution1/Setup1/Release"  
           destFolderName="Release/Setup1" /> 
        </additionalFoldersToArchive> 
      </archiveBuild> 
      <updateAssemblyVersion mode="on/off"    
       vbAssemblyVersionVSSPath="$/System/AssemblyVersionInfo.vb" 
       csAssemblyVersionVSSPath="$/System/AssemblyVersionInfo.cs"/>   
    </options> 
  </buildIt> 
</configuration> 

ConfigSections 元素

configSections 元素用于将配置文件中的某一节链接到某个配置节处理程序类。 上述代码中的 section元素用于将 BuildIt 节链接到 Sapient.Framework.Tools.BuildIt 命名空间中的 BuildItSectionHandler 类。

AppSettings 元素

appSettings 元素包含可表示为项-值对的应用程序特定的设置。 目前,BuildIt 定义三个特定设置:

appendTraceOutput。 当此项设置为 on 时,BuildIt 将跟踪输出追加到名为 BuiltIt.log 的跟踪文件。

注意 如果此项被忽略,或者如果此值没有设置为 on,BuildIt 会改写跟踪文件。

enableCustomMessageFilter。 当此项设置为 on 时,BuildIt 会启用一个自定义的消息筛选器,以便在等待同步调用的响应时处理待发的 COM 消息。 在实现此筛选器后,就可以在对象繁忙时重试被拒绝的对 Visual Studio .NET 自动化的调用。 如果 Visual Studio .NET 自动化对象很忙,而且此筛选器未被启用,就会引发异常。

注意 如果此项被忽略,或者如果此值没有设置为 on,BuildIt 就启用消息筛选器。

visualStudioProgID。 如果此项的值设置为 progID,则在生成解决方案时,将强制 BuiltIt 使用特定版本的 Visual Studio .NET。
注意 如果此项被忽略,BuildIt 就将使用最新安装的 Visual Studio .NET 版本。

SourceControl 元素

sourceControl 元素包括有关在生成过程中使用的 VSS 数据库的信息。此元素具有如下属性:

username。 用于登录到数据库的 VSS 用户的名称。

password。 VSS 用户的密码。

iniFilePath。 用来查找数据库的 VSS .ini 文件路径(例如,C:/Program Files/Microsoft Visual Studio/VSS/srcsafe.ini)。

srcVSSRootFolder。 包含正在生成的源代码的 VSS 根文件夹(例如,$/System)。 BuildIt 从这个 VSS 项目执行 Get 递归操作

srcFileRootFolder。 在其中生成源代码的文件系统根文件夹(例如,C:/System)。 BuildIt 将通过 Get 递归操作获得的文件复制到此目录。

Solutions 元素

solutions 元素包含有关所生成的解决方案的信息。此元素具有如下属性:

latestRootFolderFullName。 包含生成完成后最新生成的程序集的根文件夹的名称(例如,C:/System)。有关详细信息,请参阅 MSDN Library 中的“Team Development with Visual Studio .NET and Visual SourceSafe”一文下的“Copying Output to the Latest Folder”一节。

注意 MSDN Library 中的“Team Development with Visual Studio .NET and Visual SourceSafe”一文声明“single solution or partitioned-single solution systems do not require a Latest folder because you use project references rather than file references to refer to assemblies”(单解决方案或经分区的单解决方案系统无需 Latest 文件夹,这是由于在引用程序集时使用项目引用,而不使用文件引用)。 但是,因为单解决方案系统可以变成多解决方案系统,所以 BuildIt 总是将生成输出复制到 latest 文件夹。 这允许开发人员在引用从另一个解决方案生成的程序集时使用文件引用。

buildNumberFilePath。 用来生成下一个内部版本号的 XML 文件(例如,C:/System/BuildNumber.xml)。 有关详细信息,请参阅 MSDN Library 中的“Team Development with Visual Studio .NET and Visual SourceSafe”一文下的“Generating Build Version Numbers”一节。

注意Team Development with Visual Studio .NET and Visual SourceSafe”一文未指出内部版本号的生成方式。 在每次成功生成结束时,BuildIt 通过增加存储在指定的 XML 文件中的内部版本号来生成内部版本号。 成功的生成是指不包含任何生成错误的生成。

Solution 元素

solution 元素包含有关特定解决方案的信息。 此元素具有一个属性:

path。 所生成的解决方案文件(例如,C:/System/Solution1/Solution1.sln)。

Options 元素

options 元素包含与各个生成选项相对应的子元素。 每个生成选项都由一个不同的元素来表示。

SendBuildReport 元素

sendBuildReport 元素控制在生成完成时是否将生成报告发送到指定的电子邮件地址。 有关详细信息,请参阅 MSDN Library 中的“Team Development with Visual Studio .NET and Visual SourceSafe”一文下的“Email Build Results”一节。

此元素包含下列属性:

mode。 如果此属性设置为“on”,BuildIt 会将生成报告发送到指定的电子邮件地址。

smtpServer。 用来转发电子邮件的 SMTP 服务器的 IP 地址。

toAddress。 用来接收生成报告的电子邮件地址。

archiveBuild 元素控制是否将生成的程序集存档到以内部版本号命名的文件夹。 有关详细信息,请参阅MSDN Library 中的“Team Development with Visual Studio .NET and Visual SourceSafe”一文下的“Consider Maintaining Previous Builds”一节。

此元素包含下列属性:

“模式”。 如果此属性设置为“on”,BuildIt 就会将生成的程序集复制到以内部版本号命名的文件夹。

archiveRootFolderFullName。 包含以前存档的生成的根文件夹的名称(例如,C:/System/Archive)。

用来对元素进行存档的其他文件夹

在默认情况下,BuildIt 只对 Visual C# 和 Visual Basic .NET 项目的输出进行存档。 要对从其他项目(例如,Visual Studio .NET 安装项目)生成的输出进行存档,请将 folder 元素添加到 additionalFoldersToArchive 元素中。

folder 元素包含下列属性:

fullName。 要存档的源文件夹的全名(例如,C:/System/Solution1/Setup1/Debug)。

destFolderName。 要存档到的目标文件夹的名称(例如,Debug/Setup1)。

注意 如果启用了 archiveBuild 选项,BuildIt 会将每个指定文件夹中的文件存档到存档根文件夹下的指定目标文件夹中。

UpdateAssemblyVersion 元素

updateAssemblyVersion 元素控制是否使用内部版本号来控制所生成的程序集的版本。 有关详细信息,请参阅 MSDN Library 中的“Team Development with Visual Studio .NET and Visual SourceSafe”一文下的“Controlling Assembly Version”一节。

此元素包含下列属性:

mode。 如果此属性设置为“on”,BuildIt 会用内部版本号控制所生成的程序集的版本。

vbAssemblyVersionVSSPath。 VSS 路径(例如,$/System/AssemblyVersionInfo.vb),其中包含在生成的所有 Visual Basic .NET 项目之间共享的程序集版本文件。 如果解决方案不包含任何 Visual Basic 项目,请将此属性值留空。 有关详细信息,请参阅 MSDN Library 中的“Team Development with Visual Studio .NET and Visual SourceSafe”一文下的“Consider Centralizing Assembly Version Numbers”一节。

csAssemblyVersionVSSPath。 VSS 路径(例如,$/System/AssemblyVersionInfo.cs),其中包含在生成的所有 Visual C# 项目之间共享的程序集版本文件。 如果解决方案不包含任何 Visual C# 项目,请将此属性值留空。 有关详细信息,请参阅 MSDN Library 中的“Team Development with Visual Studio .NET and Visual SourceSafe”一文下的“Consider Centralizing Assembly Version Numbers”一节。

增强功能

这一节包含一系列在第一版的 BuildIt 中所考虑到的、但由于时间限制最终尚未包含在其中的增强功能。 共享这些功能是为了公开在 BuildIt 的开发过程中涉及到的基本想法、提出可将 BuildIt 扩展到的领域,以及邀请您成为社区成员,以便使用本文末尾处的反馈部分发表想法和代码。 可能的增强功能包括:

定义一个接口以允许 BuildIt 与源代码管理提供程序(例如,ISourceControlProvider)协作,而不是与 Visual SourceSafe 协作。 这样做是为了使开发人员能够将 BuildIt 配置为使用另一个源代码管理提供程序(例如,Rational ClearCase)。

通过在缺少特定设置时实现默认行为来简化 BuildIt 的配置。 目前,如果应用程序配置文件中缺少特定的生成设置,BuildIt 会引发错误。 一个更宽松的 BuildIt 版本将在某个设置被忽略时,简单地实现某一默认行为。

通过利用相对路径(而不是使用完全限定路径)来进一步简化 BuildIt 的配置。 目前,所有的文件路径都必须是完全限定路径。 完全限定路径比相对路径灵活,但比它麻烦。 相反,只需通过使用某些相对于源根文件夹(例如,解决方案文件、内部版本号 XML 文件等)的路径,就可以简单地进行配置。

允许开发人员用 updateAssemblyVersion 生成选项指定无限多个程序集版本文件。 目前,BuildIt 允许开发人员指定一个 Visual C# 和一个 Visual Basic .NET 程序集版本文件。 这种限制基于以下假设:开发人员将与 VSS 中所有的 Visual C# 项目共享这个 Visual C# 程序集版本文件,并与所有的 Visual Basic .NET 项目共享这个 Visual Basic .NET 程序集版本文件。 然而,开发人员可能喜欢更新个别的程序集版本文件,而不是依赖 VSS 文件共享。

定义可供开发人员扩展生成过程的“生成后”事件。 目前,只能通过修改 BuildIt 源代码来扩展生成过程。 一个更灵活的设计将是实现一个在生成结束时引发的事件。 开发人员随后可以将事件处理程序传递到此事件,从而执行自定义的逻辑。 使用此策略,还可以允许开发人员自定义生成过程的其他部分(例如,预生成、生成、生成后)。 允许开发人员指定 BuildIt 配置文件的名称。 目前,BuildIt 在名为 BuildIt.exe.config 的标准应用程序配置文件中查找配置设置。因此,开发人员不能使用 BuildIt 同时生成两个不同的解决方案。 相反,开发人员必须将 BuildIt 程序集复制到另一个目录。 更友好的解决方案会允许开发人员将配置文件的名称指定为命令行参数,而不是使用标准的应用程序配置文件。

使 BuildIt 自动为任何尚未映射到虚拟目录的 Web 项目创建一个虚拟目录。 目前,Visual Studio .NET 提示用户为 Web 项目的工作副本设置位置(如果这样的位置不存在)。 BuildIt 能自动创建虚拟目录,而不是提示用户接受默认位置。

类引用

BuildInitializer

BuildItSectionHandler

BuildManager

SourceSafeHelper

BuildItResourceManager

BuildItCommandLineArgs

BuildInitializer

BuildInitializer 类验证命令行选项、通过 BuildItSectionHandler检索生成工具的设置,并通过 BuildManager 启动生成过程。

备注

BuildInitializer 类实现一个名为 Start公共方法,此方法从控制台应用程序的 static/SharedMain 方法调用。 开始 方法首先通过调用名为 AreCommandLineArgsValid私有方法来验证所有命令行参数。 如果这些参数有效,BuildInitializer 会使用 BuildItSectionHandler 类检索生成工具的设置。 最后一步是通过调用BuildManager 上的 Build 来启动生成过程。

构造函数

BuildInitializer

公共方法

Start

私有方法

AreCommandLineArgsValid

确定给定的多个命令行参数是否有效。

IsCommandLineArgValid

确定给定的单个命令行参数是否有效。

Usage

将用法信息写入控制台。 在所有命令行参数都无效时调用。

BuildItSectionHandler

BuildItSectionHandler 类用从生成工具配置文件读取的设置填充和返回 BuildItSettings 结构。

备注

BuildItSectionHandler 类(该类可实现 IConfigurationSectionHandler 接口)分析生成工具配置文件中 BuildIt 元素中的配置设置。 它将 BuildIt 元素中的信息加载到名为 BuildItSettings 的结构中。

BuildItSettings 结构定义几个属性,这些属性为在配置文件中定义的配置设置提供类型安全访问。例如,BuildItSettings 结构定义三个可返回不同生成选项的属性: OptionSendBuildReportOptionArchiveBuildOptionUpdateAssemblyVersionBuildItSettings 结构还定义一个名为 Solutions 的属性和一个名为 SourceControlInfo 的属性,这两个属性与配置文件中的特定元素相对应。

构造函数

BuildItSectionHandler

初始化 BuildItSectionHandler 类的新实例。

公共方法

Create

用从生成工具配置文件读取的设置填充和返回 BuildItSettings 结构。

私有方法

CreateBuildOption

用从生成工具配置文件读取的设置创建和填充 BuildOption 结构。

BuildManager

BuildManager 类组合生成过程。

备注

BuildInitializer 使用 BuildManager 类来协调生成过程。 BuildManager 实现一个采用两个参数的构造函数: 一个参数的类型是 BuildItSettings,另一个参数的类型是 BuildItResourceManager。 在创建 BuildManager 类的实例时,此类用传递到其构造函数的参数初始化私有成员变量。 这使得 BuildManager 在生成过程中可以使用设置和错误信息。

BuildManager 还公开一个名为 Build公共方法,此方法采用 BuildType 枚举来确定是启动 Build 还是启动 Rebuild。 BuildType 枚举按如下方式定义。

[C#]

public enum BuildType {Build, Rebuild};

Rebuild 被定义为在解析生成中断之后而进行的系统生成。 RebuildBuild 的区别在于,在执行 Rebuild 时,会忽略一些步骤。 例如,在执行 Build 时,BuildManager 会在 VSS 中创建一个新标签,但在执行 Rebuild 时不会这样做。

在确定了 BuildType 之后,BuildManager 会调用几个私有方法以执行生成过程的各个部分。

构造函数

BuildManager

初始化 BuildManager 类的新实例。

公共方法

Build

组合生成过程。

私有方法

ArchiveAdditionalFolders 将在 additionalFoldersToArchive 段(位于生成工具配置文件中)中指定的文件夹的内容复制到存档根文件夹中。

BackupLatestFolder

备份包含最新生成的程序集的 Latest 文件夹。 Latest 文件夹的位置是在生成工具配置文件中指定的。

BuildAssemblyVersionRegEx

生成一个正则表达式,用来替换 AssemblyVersion 属性的内部版本号组件。此表达式取决于 AssemblyVersion 属性是 Visual Basic 属性还是 Visual C# 属性。

BuildSolutionAndCopyOutput

生成一个解决方案并将生成的输出复制到给定的目标目录。

BuildSolutionConfig

使用指定的生成配置(例如,Release、Debug 等)生成给定的解决方案。

BuildSolutions

生成给定的解决方案(一个或多个)。

CopyFiles(string, string)

将给定的源文件夹中的所有文件复制到给定的目标文件夹,其方法是,在将 excludeDebugFiles 标志设置为 false(假)的情况下,调用该方法的重载版本。

CopyFiles(string, string, bool)

将给定源文件夹中的所有文件复制到给定的目标文件夹。 如果 excludeDebugFiles 标志设置为 true(真),则复制除调试文件以外的所有文件。

CopyVSProjectOutput

将所有 Visual Basic 和 Visual C# 项目的输出(Release 或 Debug)复制到给定的目标根文件夹。

CopySolutionOutput

将解决方案输出(Release 或 Debug)复制到给定的目标根文件夹。

DeleteArchiveFolder

删除存档文件夹(如果它存在的话)。 存档文件夹的位置是在生成工具配置文件中指定的。

DeleteLatestBackupFolder

删除 latest backup 文件夹(如果存在)。 latest backup 文件夹与 latest 文件夹位于同一个位置。

DeleteLatestFolder

删除 latest 文件夹(如果存在)。

GenerateBuildReport

生成一个名为 BuildReport.log 的生成报告并将其保存到工作文件夹。

GenerateLabel

基于内部版本号生成一个标签。

GetSourceFromLabel

使用给定的标签从 VSS 获取源根项目。

GetVSProjectLangType

确定用来创建指定文件的语言(Visual Basic 或 Visual C#)。使用文件扩展名来确定语言。

IncrementBuildNumber

按 1 递增内部版本号,然后通过调用 WriteBuildNumber 将其写入内部版本号 XML 文件。

LabelSource

用给定的标签标记 VSS 中的源根项目。

ReadBuildNumber

从内部版本号 XML 文件读取内部版本号。 此文件的名称和位置是在生成工具配置文件中指定的。

RestoreLatestFolder

从 latest backup 文件夹还原 latest 文件夹(如果 latest 文件夹存在的话)。

SendBuildReport

将给定的生成报告发送到适当的电子邮件收件人(如果在生成工具配置文件中启用了该选项)。

UpdateAssemblyVersionInfo

获取 Visual Basic 和 Visual C# AssemblyVersion 文件的 VSS 路径(在生成工具配置文件中指定),并针对每个文件调用一次该方法的重载版本(如果在生成工具配置文件中启用了该选项)。

UpdateAssemblyVersionInfo(string)

从 VSS 获取指定的 AssemblyVersion 文件,通过正则表达式用内部版本号更新 AssemblyVersion 属性,然后将该文件重新签入 VSS 中。

WriteBuildNumber

将给定的内部版本号写入内部版本号 XML 文件。

SourceSafeHelper

SourceSafeHelper 包装 VSS 自动化对象,以使其自身更便于与 VSS 协作。

备注

在生成过程中,BuildManager 使用 SourceSafeHelper 类来与 VSS 进行交互。该类公开几个公共方法(例如,CheckinCheckout),这些方法隐藏与 VSS 自动化对象协作时的实现细节。

注意SourceSafeHelper 类并不将所有 VSS 操作的方法都公开。 它只公开生成工具所需的操作。

构造函数

SourceSafeHelper

初始化 SourceSafeHelper 类的新实例。

公共方法

Checkin 将给定项重新签入 VSS。

Checkout

将给定项从 VSS 签出。

GetFromLabel

从 VSS 获取给定项的特定版本。 如果该项是一个项目,则 get 操作是递归的。

Label

用指定的标签标记给定的 VSS 项。

UndoCheckout

删除给定的 VSS 项的签出状态。

私有方法

GetItemsRecursively 从版本化的 VSSItem(项目或文件)以递归方式获取项。版本化项可以是项目或文件。 如果 VSSItem 是一个文件,则此方法只是获取该文件。 否则,它以递归方式获取项目中的所有项。

GetVSSItem

获取对 VSSItem(即,项目或文件)的引用。

BuildItResourceManager

BuildItResourceManager 类提供对存储在资源文件中的错误信息的类型安全访问。

备注

BuildItResourceManager 类是从 System.Resources 命名空间中的 ResourceManager 类派生的,它用于从包含在名为 BuildIt.exe 的生成工具程序集中的资源文件中检索错误信息。它公开几个公共方法(例如,GetBuildSolutionFailedString),以便对资源值提供类型安全访问。这有助于消除在用无效的资源名称调用 ResourceManager.GetString(string name) 方法时可能出现的错误。

构造函数

BuildItResourceManager

初始化 BuildItResourceManager 类的新实例。

公共方法

GetBuildSolutionFailedString

从生成工具资源文件获取 BuildSolutionFailed 错误信息。其余的 get 方法执行类似的功能;它们对存储在生成工具资源文件中的错误信息提供类型安全访问。

私有方法

BuildItCommandLineArgs

BuildItCommandLineArgs 类封装由该生成工具支持的命令行参数集合。

备注

BuildItCommandLineArgs 类是从 System.Collections.Specialized 命名空间中的 StringCollection 类派生的,它代表 BuildIt 支持的命令行参数集。BuildInitializer 使用该类来确定命令行参数是否有效。

构造函数

BuildItCommandLineArgs

初始化 BuildItCommandLineArgs 类的新实例。

公共方法

ToString

通过提供有效命令行参数的字符串表示形式来重写 System.Object::ToString() 方法。

常见问题

我是否可以修改 BuildIt 源代码?

是的,您可以修改和扩展 BuildIt 源代码。 例如,您可以更改 Visual Source Safe 特定的调用,并生成一个与自己的源代码管理产品协作的 Helper 类。 请将您所做的更改告诉我们! 如果要与我们分享您的体验,请参阅下面的“反馈”部分。

我是否需要将 Visual Studio 安装到 BuildIt 的运行位置?

是的,您需要将 Visual Studio 安装到用 BuildIt 进行生成的服务器或工作站上 — 仅仅安装可重新发布的 .NET Framework 是不够的。

小结

本文已经描述了如何牢记来创建具有简单和可扩展特性的 BuildIt,以便您能够使其完全适合自己的特定生成过程需要。 请使用“反馈”下提供的电子邮件地址,将有关针对 BuildIt 的想法告诉我们。

作者简介

Michael Monteiro 是 Sapient Corporation 的技术顾问,同时还是一位具有八年以上经验的 .NET Microsoft 认证专家。 他目前在一个负责为 Sapient 中的技术社区提供结构指导的小组中工作。 他的联系电子邮件是 mmonte@sapient.com

关于 Sapient

Sapient 是一个领先的商业和技术咨询公司,它通过以固定的价格快速应用和支持高级技术来帮助 Global 2000 客户实现重要的业务结果。 Sapient 成立于 1991 年,其雇员超过 1,500 人,且办事处遍及亚特兰大、剑桥(马萨诸塞州)、芝加哥、达拉斯、杜塞尔多夫、伦敦、洛杉矶、米兰、慕尼黑、新德里、纽约、旧金山、多伦多和华盛顿。有关 Sapient 的更多信息,可在 http://www.sapient.com/ 中找到。

合作者

非常感谢下列投稿人和审阅者: Bernard Chen (Sapient Corporation)、Brett Keown、Craig Skibo、David Lewis、 Deyan Lazarov、Dimitris Georgakopoulos (Sapient Corporation)、Edward Jezierski、Filiberto Selvas Pati?o、Jeff Pflum、Joe Hom (Avanade)、Ken Hardy、Kenny Jones、Korby Parnell、Martin Born、Mick Das、Mike Pietraszak、Niel Sutton、Oded Ye Shekel、Rajiv Sodhi (Sapient Corporation)、Ray Escamilla、Rich Knox、Russell Christopher 和 Sumit Sharma (Sapient Corporation)。

反馈

您是否有任何问题、意见和建议? 要提供针对 BuildIt 的反馈,请将电子邮件发送到 buildit@sapient.com

如果希望与 Microsoft 联系以获得反馈,请将电子邮件发送到 devfdbck@microsoft.com


 
0 0

相关博文

我的热门文章

img
取 消
img