65.9K
CodeProject 正在变化。 阅读更多。
Home

Visual Studio 中的自动构建版本控制

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.79/5 (18投票s)

2005年5月7日

8分钟阅读

viewsIcon

233093

downloadIcon

3483

自动生成版本信息、递增和存档。

Sample Image - build_versioning.jpg

引言

您的大部分 Windows 可执行文件都包含版本信息。例如,在我的 C:\Windows\notepad.exe 的“属性”页面中,“版本”选项卡会显示“文件版本: 5.1.2600.2180”以及其他信息。在 Visual Studio 项目中,这些信息大部分或全部都存储在资源文件中(默认扩展名为 .rc)。我想要一种简单的方法来为我的 Visual Studio 项目添加自动生成版本控制,以完成以下任务:

  1. 递增生成号,以便每次编译项目时,可执行文件的版本信息都会更新。
  2. 将每个生成的可执行文件的副本存档到单独的位置,并根据其各自的版本命名文件。
  3. 允许应用程序本身轻松检索其自身版本信息,例如通过 C++ 中的预处理器常量。

在我最初的研究过程中,我首先确定似乎任何版本的 Visual Studio 都未原生支持这些功能。然后,我偶然发现了一篇关于名为 RCStamp 的实用工具的现有 Code Project 文章,该工具会解析资源文件并根据给定的格式字符串修改其中的版本信息。本文不介绍 RCStamp 的详细信息,因此我建议您先阅读其 单独的文章

RCStamp 解决了列表中的任务 #1。本文档解释了我为解决其他需求所采取的步骤。首先,我概述了我创建的实用程序应用程序,然后解释了所有这些如何在 Visual Studio 中协同工作。

生成存档(VerCopy)

我选择解决存档每个生成文件问题的方法是编写一个小型实用程序应用程序,该应用程序只需接受一个文件作为输入,然后将其复制到一个新文件中,新文件的命名基于版本信息。例如,对我的 notepad.exe 运行 VerCopy 会生成一个名为 notepad-5-1-2600.exe 的新文件。该代码仅使用文件版本的前三个数字,因为这就是我想要的。但是,源代码非常简单,可以轻松修改以满足任何人的需求。这是 C# 源代码的核心部分:

FileVersionInfo verInfo = FileVersionInfo.GetVersionInfo(PathFile);
string destFile = fileInfo.Name.Substring(0, 
    fileInfo.Name.Length-fileInfo.Extension.Length) + "-" 
    + verInfo.FileMajorPart + "-" + verInfo.FileMinorPart + "-" 
    + verInfo.FileBuildPart + fileInfo.Extension;

File.Copy(PathFile, destFile, true);

如您所见,C# 可以轻松地访问给定文件中的版本信息。

版本感知(VerHeader)

为了解决让我的项目轻松感知其自身当前版本信息的问题,我决定编写另一个小型实用程序应用程序,该应用程序解析资源文件并生成一个 C++ 头文件,其中包含预处理器常量定义(#defines):一个用于主版本号,一个用于次版本号,第三个用于生成号。同样,源代码很简单,可以快速修改以任何所需的格式输出版本数据,例如为不同语言生成包含文件。以下是源代码的样子:

string line, major, minor, build;
while ((line = RCFile.ReadLine()) != null)
{
   int pos;
   if ((pos = line.IndexOf("FILEVERSION")) < 0)
      continue;
   pos += "FILEVERSION".Length + 1;
   string[] tempInfo = line.Substring(pos).Split(" .,".ToCharArray());
   ArrayList verInfo = new ArrayList(4);
   foreach (string s in tempInfo)
      if (s.Length > 0)
         verInfo.Add(s);
   major = verInfo[0].ToString();
   minor = verInfo[1].ToString();
   build = verInfo[2].ToString();
}

outFile.WriteLine("#define MAJORVERSION {0}", major);
outFile.WriteLine("#define MINORVERSION {0}", minor);
outFile.WriteLine("#define BUILDNUMBER {0}", build);

集成到 Visual Studio

在此,我描述了将我的三个生成版本控制要求添加到 Visual Studio 项目的方法。它快速 painless,并且过程同样易于自定义以满足您的需求。我个人使用的步骤如下:

  1. 将三个实用程序可执行文件复制到解决方案文件夹。它们是 RCStamp.exeVerCopy.exeVerHeader.exe
  2. 为所需项目添加版本资源,并设置初始版本信息。
  3. 为所有配置添加对 VerHeader 的调用到预生成事件。
  4. 为所需的配置添加对 VerCopy 和 RCStamp 的调用到后生成事件。
  5. 将 VerHeader 输出文件包含在应用程序中。

添加版本资源

为此,只需在“解决方案资源管理器”中右键单击项目名称,然后选择“添加”->“添加资源…”从列表中选择“版本”,然后单击“新建”。该文件将被添加到项目“资源文件”文件夹中,并被命名为与项目同名,扩展名为“.rc”。双击它将打开资源视图。展开“版本”文件夹并双击“VS_VERSION_INFO”将显示文件的内容。随意修改它。“FILEVERSION”字段是将被 RCStamp 修改并由 VerCopy 和 VerHeader 访问的字段。请务必保存您的更改。

预生成事件

在生成应用程序之前,我们希望 VerHeader 为我们创建头文件,以便我们可以在应用程序中包含它。为此,我们在项目的预生成事件中添加对 VerHeader 的调用。这是一组用户定义的批处理命令,在生成过程开始之前执行。要访问设置,请在“解决方案资源管理器”中右键单击项目名称,然后选择“属性…”在“配置”下拉列表中选择“所有配置”,然后展开“生成事件”文件夹。在“预生成事件”下的“命令行”字段中,添加:

..\VerHeader "$(ProjectName).rc" version.info

批处理命令的工作目录是项目文件夹。通常,这是解决方案文件夹下的一个子文件夹,因此 VerHeader.exe 位于工作目录上方的某处。Visual Studio 在这些批处理命令中提供了一些宏供我们使用。$(ProjectName) 不言自明。我们使用它,因为资源文件的默认名称与项目名称相同。VerHeader 将写入一个名为“version.info”的文件。您可以添加描述,例如“正在更新版本头文件...”,它将在 Visual Studio 的生成输出窗口中显示。

后生成事件

应用程序生成后,我们希望使用 VerCopy 存储它的副本。此外,我们希望递增资源文件中的生成号。因此,我们在项目的后生成事件中添加对 VerCopy 和 RCStamp 的调用。这与预生成事件的工作方式完全相同,只是它在生成成功后直接调用。对于所需的配置,请将以下内容添加到后生成命令行:

..\VerCopy "$(TargetPath)" "..\Builds\$(ConfigurationName)"
..\RCStamp "$(ProjectName).rc" *.*.+.*

再次,我们使用 $(ProjectName) 宏来访问资源文件。$(TargetPath) 是项目输出文件的完整路径。$(ConfigurationName) 是刚刚生成的配置的名称。因此,VerCopy 会将输出文件复制到解决方案文件夹内的“Builds”文件夹中,并根据配置将其放入子文件夹。接下来,RCStamp 将递增资源文件中“FILEVERSION”字段的第三个值,我将其用作生成号。您可以在后生成“描述”字段中添加类似“正在存档和递增生成...”的内容,Visual Studio 将在生成输出窗口中显示它。

包含头文件

最后一步是将输出的头文件包含在您的应用程序中。在 C++ 中,只需添加一个 #include "version.info" 指令,现在您就可以在代码中使用预处理器常量 MAJORVERSIONMINORVERSIONBUILDNUMBER 了。例如,在控制台应用程序中,这对于向用户显示版本信息以及用版本信息标记日志非常有用。

您会发现这些步骤很容易记住,一旦您完成了它们,就可以在几分钟内完成。

注释和注意事项

  • 我为 VerHeader 输出的文件指定了“.info”扩展名。这是因为我使用源代码管理,而 Visual Studio 会自动将所有 .h 文件添加到解决方案的源代码管理中。我不需要版本头文件的每个修订版都存储在我的源代码管理库中,因此我给它一个不同的扩展名。
  • 后生成步骤仅在生成 *成功* 时执行。这就是为什么 RCStamp 的调用在后生成事件中。它也可以在预生成事件中,具有类似的行为;但是,在这种情况下,即使生成失败,生成号也会递增,而我更倾向于不这样做。
  • 预生成步骤在每次生成尝试时都会执行。因此,即使生成失败,版本头文件也会重新生成。在这种情况下,生成号没有改变,因此对 VerHeader 的调用是无用的。然而,我们第一次生成应用程序时,版本头文件不存在,因此需要先于第一次生成创建。此外,资源文件可以手动编辑,并且可能导致版本头文件在生成尝试之间过时。因此,成本较低的 VerHeader 调用应该放在预生成事件中。
  • 在我的设置中,只有生成号是自动修改的。当我觉得主版本号和/或次版本号适合应用程序时,我会手动更改它们。当然,您可以轻松自定义任何内容以满足您的需求。
  • 后生成事件的调用不必为每个生成配置执行。例如,您可以选择仅在发布生成后递增生成号,或仅存档发布生成等。

结论

我个人发现此功能在包含实验性代码的项目中非常有用。我可以对任何我想运行的生成进行性能测试,并发布具有最佳结果的生成,同时努力改进任何新的更改。结合源代码管理,我终于觉得我对不仅代码修订,而且多组修订的累积形成新生成都拥有最佳控制权。希望 Visual Studio 的未来版本能提供一个功能齐全的生成版本控制系统,但在此之前,我认为我的解决方案已经足够了。非常感谢 Peter Chen 创建 RCStamp,它是这个系统的真正支柱。

反馈

请就本文及源代码的任何反馈与我联系。无论是拼写错误、设置过程的简化,还是改进源代码的方法,都将不胜感激。我编写的实用程序既不高度功能化也不高度健壮,我很乐意进行任何重要或普遍要求的修改。我特别有兴趣了解一种脚本方法,可以快速为 Visual Studio 中的给定项目执行设置过程。

修订历史

  • 2005年5月7日
    • 原始文章。
© . All rights reserved.