自动在 Visual Studio c# 项目中嵌入 mercurial 修订信息 [英] Embedding mercurial revision information in Visual Studio c# projects automatically

查看:23
本文介绍了自动在 Visual Studio c# 项目中嵌入 mercurial 修订信息的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在构建我们的项目时,我希望每个存储库的 mercurial id 嵌入到该存储库的产品(库、应用程序或测试应用程序)中.

In building our projects, I want the mercurial id of each repository to be embedded within the product(s) of that repository (the library, application or test application).

我发现如果您确切地知道构建他们正在使用的特定版本的应用程序的过程,那么调试由 8 个时区以外的客户运行的应用程序会变得非常容易.因此,我们系统中的每个项目(应用程序或库)都实现了一种获取相关修订信息的方法.

I find it makes it so much easier to debug an application being run by customers 8 timezones away if you know precisely what went into building the particular version of the application they are using. As such, every project (application or library) in our systems implements a way of getting at the associated revision information.

我还发现能够查看应用程序是否已使用存储库中的干净(未修改)变更集进行编译非常有用.当存储库中有未提交的更改时,'Hg id' 会很有用地将 + 附加到变更集 ID,这样我们就可以轻松查看人们是否正在运行代码的干净版本或修改版本.

I also find it very useful to be able to see if an application has been compiled with clean (un-modified) changesets from the repository. 'Hg id' usefully appends a + to the changeset id when there are uncommitted changes in a repository, so this allows us to easily see if people are running a clean or a modified version of the code.

我当前的解决方案详述如下,满足基本要求,但存在一些问题.

My current solution is detailed below, and fulfills the basic requirements, but there are a number of problems with it.

目前,对于每个 Visual Studio 解决方案,我添加了以下预构建事件命令行"命令:

At the moment, to each and every Visual Studio solution, I add the following "Pre-build event command line" commands:

cd $(ProjectDir)
HgID

我还在项目目录中添加了一个 HgID.bat 文件:

I also add an HgID.bat file to the Project directory:

@echo off
type HgId.pre > HgId.cs

For /F "delims=" %%a in ('hg id') Do <nul >>HgID.cs set /p =            @"%%a"

echo ;                  >> HgId.cs
echo     }              >> HgId.cs
echo }                  >> HgId.cs

连同一个 HgId.pre 文件,其定义为:

along with an HgId.pre file, which is defined as:

namespace My.Namespace {
/// <summary> Auto generated Mercurial ID class. </summary>
internal class HgID {
    /// <summary> Mercurial version ID [+ is modified] [Named branch]</summary>
    public const string Version =

当我构建应用程序时,会在所有库上触发预构建事件,创建一个新的 HgId.cs 文件(不受版本控制)并导致使用新的 ' 重新编译库'Version' 中的 hg id' 字符串.

When I build my application, the pre-build event is triggered on all libraries, creating a new HgId.cs file (which is not kept under revision control) and causing the library to be re-compiled with with the new 'hg id' string in 'Version'.

主要问题是由于每次预构建时都会重新创建HgId.cs,所以每次我们需要编译任何东西时,都会重新编译当前解决方案中的所有项目.由于我们希望能够轻松地调试到我们的库中,因此我们通常会在我们的主应用程序解决方案中保留许多引用的库.这可能会导致构建时间比我希望的要长得多.

The main problem is that since the HgId.cs is re-created at each pre-build, so every time we need to compile anything, all projects in the current solution are re-compiled. Since we want to be able to easily debug into our libraries, we usually keep many libraries referenced in our main application solution. This can result in build times which are significantly longer than I would like.

理想情况下,我希望库仅在 HgId.cs 文件的内容实际发生更改时才进行编译,而不是使用完全相同的内容重新创建.

Ideally I would like the libraries to compile only if the contents of the HgId.cs file have actually changed, as opposed to having been re-created with exactly the same contents.

此方法的第二个问题是它依赖于 windows shell 的特定行为.我已经不得不多次修改批处理文件,因为原始版本在 XP 下工作而不是 Vista 下,下一个版本在 Vista 下工作但不是 XP,最后我设法让它同时工作.然而,它是否适用于 Windows 7 是任何人的猜测,随着时间的推移,我认为承包商更有可能希望能够在他们的 Windows 7 boxen 上构建我们的应用程序.

The second problem with this method is it's dependence on specific behaviour of the windows shell. I've already had to modify the batch file several times, since the original worked under XP but not Vista, the next version worked under Vista but not XP and finally I managed to make it work with both. Whether it will work with Windows 7 however is anyones guess and as time goes on, I see it more likely that contractors will expect to be able to build our apps on their Windows 7 boxen.

最后,我对这个解决方案有一个美学问题,批处理文件和拼凑在一起的模板文件感觉像这样做的错误方式.

Finally, I have an aesthetic problem with this solution, batch files and bodged together template files feel like the wrong way to do this.

你将如何解决/你如何解决我试图解决的问题?

How would you solve/how are you solving the problem I'm trying to solve?

还有什么比我目前正在做的更好的选择?

What better options are out there than what I'm currently doing?

在实施当前的解决方案之前,我查看了 Mercurials 关键字扩展,因为它似乎是显而易见的解决方案.然而,我越看它并阅读人们的意见,我就越得出结论,这不是正确的做法.

Before I implemented the current solution, I looked at Mercurials Keyword extension, since it seemed like the obvious solution. However the more I looked at it and read peoples opinions, the more that I came to the conclusion that it wasn't the right thing to do.

我还记得在以前公司的项目中关键字替换给我带来的问题(一想到不得不再次使用 Source Safe 就让我感到恐惧*8').

I also remember the problems that keyword substitution has caused me in projects at previous companies (just the thought of ever having to use Source Safe again fills me with a feeling of dread *8').

此外,我并不特别想启用 Mercurial 扩展来完成构建.我希望解决方案是自包含的,这样就不会因为没有启用扩展或没有安装正确的帮助软件而在没有嵌入版本信息的情况下意外编译应用程序.

Also, I don't particularly want to have to enable Mercurial extensions to get the build to complete. I want the solution to be self contained, so that it isn't easy for the application to be accidentally compiled without the embedded version information just because an extension isn't enabled or the right helper software hasn't been installed.

我还想用一种更好的脚本语言编写它,我只会编写 HgId.cs 文件 如果 内容实际上已更改,但我能想到的所有选项都需要我的同事、承包商和可能的客户必须安装他们可能不想要的软件(例如 cygwin).

I also thought of writing this in a better scripting language, one where I would only write HgId.cs file if the content had actually changed, but all of the options I could think of would require my co-workers, contractors and possibly customers to have to install software they might not otherwise want (for example cygwin).

人们能想到的任何其他选项将不胜感激.

Any other options people can think of would be appreciated.

玩了一段时间后,我设法让 HgId.bat 文件只在 HgId.cs 文件发生变化时覆盖它:

Having played around with it for a while, I've managed to get the HgId.bat file to only overwrite the HgId.cs file if it changes:

@echo off

type HgId.pre > HgId.cst

For /F "delims=" %%a in ('hg id') Do <nul >>HgId.cst set /p =            @"%%a"

echo ;                  >> HgId.cst
echo     }              >> HgId.cst
echo }                  >> HgId.cst

fc HgId.cs HgId.cst >NUL
if %errorlevel%==0 goto :ok
copy HgId.cst HgId.cs
:ok
del HgId.cst

此解决方案的问题

即使不再每次都重新创建 HgId.cs,Visual Studio 仍然坚持每次都编译所有内容.我尝试寻找解决方案并尝试在工具|选项|项目和解决方案|构建和运行中检查仅构建启动项目和运行依赖项",但没有任何区别.

Problems with this solution

Even though HgId.cs is no longer being re-created every time, Visual Studio still insists on compiling everything every time. I've tried looking for solutions and tried checking "Only build startup projects and dependencies on Run" in Tools|Options|Projects and Solutions|Build and Run but it makes no difference.

第二个问题仍然存在,现在我没有办法测试它是否适用于 Vista,因为那个承包商已经不在我们身边了.

The second problem also remains, and now I have no way to test if it will work with Vista, since that contractor is no longer with us.

  • 如果有人可以在 Windows 7 和/或 Vista 机器上测试此批处理文件,我将不胜感激.

最后,我对这个解决方案的审美问题比以前更严重,因为批处理文件更复杂,而且现在还有更多问题.

Finally, my aesthetic problem with this solution, is even stronger than it was before, since the batch file is more complex and this there is now more to go wrong.

如果您能想到任何更好的解决方案,我很乐意听取他们的意见.

If you can think of any better solutions, I would love to hear about them.

推荐答案

我想我有一个答案.这将有点复杂,但它使您不必执行任何批处理文件.您可以依靠 MSBuild 和自定义任务为您执行此操作.我已经使用了 MSBuild 的扩展包(可在 CodePlex 处获得) - 但你需要的第二个任务是你可以很容易地写自己.

I think I have an answer for you. This will be a bit involved, but it gets you away from having to do any batch files. You can rely on MSBuild and Custom Tasks to do this for you. I've used the extension pack for MSBuild (Available at CodePlex) - but the second task you need is something you could just as easily write yourself.

使用此解决方案,您可以右键单击 DLL,然后在文件属性中查看 DLL(或 EXE)来自哪个 Mercurial 版本.

With this solution, you can right click on the DLL and see in the file properties which Mercurial Version the DLL (or EXE) came from.

步骤如下:

  1. 获取MBBuildExtension PackOR 编写自定义任务以覆盖装配信息.cs
  2. 创建自定义Build Task在自己的项目中获取Mercurial Id(代码如下).
  3. 编辑需要使用自定义任务的 Mercurial Id(代码如下).
  1. Get the MBBuildExtension Pack OR Write Custom Task to overwrite AssemblyInfo.cs
  2. Create a Custom Build Task in its own project to get the Mercurial Id(code below).
  3. Edit project files that need the Mercurial Id to use Custom Task (code below).

用于获取 mercurial id 的自定义任务:(这需要经过很好的测试,并且可能更好地概括......)

Custom Task to Get mercurial id: (This would need to be tested well and perhaps better generalized...)

using System;
using System.Diagnostics;
using Microsoft.Build.Utilities;
using Microsoft.Build.Framework;


namespace BuildTasks
{
    public class GetMercurialVersionNumber : Task
    {
        public override bool Execute()
        {
            bool bSuccess = true;
            try
            {
                GetMercurialVersion();
                Log.LogMessage(MessageImportance.High, "Build's Mercurial Id is {0}", MercurialId);
            }
            catch (Exception ex)
            {
                Log.LogMessage(MessageImportance.High, "Could not retrieve or convert Mercurial Id. {0}
{1}", ex.Message, ex.StackTrace);
                Log.LogErrorFromException(ex);
                bSuccess = false;
            }
            return bSuccess;
        }

        [Output]
        public string MercurialId { get; set; }

        [Required]
        public string DirectoryPath { get; set; }

        private void GetMercurialVersion()
        {
            Process p = new Process();
            p.StartInfo.UseShellExecute = false;
            p.StartInfo.RedirectStandardOutput = true;
            p.StartInfo.RedirectStandardError = true;
            p.StartInfo.CreateNoWindow = true;
            p.StartInfo.WorkingDirectory = DirectoryPath;
            p.StartInfo.FileName = "hg";
            p.StartInfo.Arguments = "id";
            p.Start();

            string output = p.StandardOutput.ReadToEnd().Trim();
            Log.LogMessage(MessageImportance.Normal, "Standard Output: " + output);

            string error = p.StandardError.ReadToEnd().Trim();
            Log.LogMessage(MessageImportance.Normal, "Standard Error: " + error);

            p.WaitForExit();

            Log.LogMessage(MessageImportance.Normal, "Retrieving Mercurial Version Number");
            Log.LogMessage(MessageImportance.Normal, output);

            Log.LogMessage(MessageImportance.Normal, "DirectoryPath is {0}", DirectoryPath);
            MercurialId = output;

        }
    }

以及修改后的项目文件:(评论可能会有所帮助)

And the modified Project File: (The comments may help)

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <!--this is the import tag for the MSBuild Extension pack. See their documentation for installation instructions.-->
  <Import Project="C:Program Files (x86)MSBuildExtensionPackMSBuild.ExtensionPack.tasks" />
  <!--Below is the required UsingTask tag that brings in our custom task.-->
  <UsingTask TaskName="BuildTasks.GetMercurialVersionNumber" 
             AssemblyFile="C:Usersmpld81DocumentsVisual Studio 2008ProjectsLambaCrashCourseProjectBuildTasksinDebugBuildTasks.dll" />
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProductVersion>9.0.30729</ProductVersion>
    <SchemaVersion>2.0</SchemaVersion>
    <ProjectGuid>{D4BA6C24-EA27-474A-8444-4869D33C22A9}</ProjectGuid>
    <OutputType>Library</OutputType>
    <AppDesignerFolder>Properties</AppDesignerFolder>
    <RootNamespace>LibraryUnderHg</RootNamespace>
    <AssemblyName>LibraryUnderHg</AssemblyName>
    <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
    <FileAlignment>512</FileAlignment>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <DebugSymbols>true</DebugSymbols>
    <DebugType>full</DebugType>
    <Optimize>false</Optimize>
    <OutputPath>binDebug</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <DebugType>pdbonly</DebugType>
    <Optimize>true</Optimize>
    <OutputPath>binRelease</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="System" />
    <Reference Include="System.Core">
      <RequiredTargetFramework>3.5</RequiredTargetFramework>
    </Reference>
    <Reference Include="System.Xml.Linq">
      <RequiredTargetFramework>3.5</RequiredTargetFramework>
    </Reference>
    <Reference Include="System.Data.DataSetExtensions">
      <RequiredTargetFramework>3.5</RequiredTargetFramework>
    </Reference>
    <Reference Include="System.Data" />
    <Reference Include="System.Xml" />
  </ItemGroup>
  <ItemGroup>
    <Compile Include="Class1.cs" />
    <Compile Include="PropertiesAssemblyInfo.cs" />
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)Microsoft.CSharp.targets" />

  <Target Name="Build" DependsOnTargets="BeforeBuild">
    <!--This Item group is a list of configuration files to affect with the change. In this case, just this project's.-->
    <ItemGroup>
      <AssemblyInfoFiles Include="$(MSBuildProjectDirectory)PropertiesAssemblyInfo.cs" />
    </ItemGroup>
    <!--Need the extension pack to do this. I've put the Mercurial Id in the Product Name Attribute on the Assembly.-->
    <MSBuild.ExtensionPack.Framework.AssemblyInfo AssemblyInfoFiles="@(AssemblyInfoFiles)"
                                                  AssemblyProduct="Hg: $(MercurialId)"
                                                  />
    <!--This is here as an example of messaging you can use to debug while you are setting yours up.-->
    <Message Text="In Default Target, File Path is: @(AssemblyInfoFiles)" Importance="normal" />
  </Target>  

  <Target Name="BeforeBuild">
    <!--This is the custom build task. The Required Property in the task is set using the property name (DirectoryPath)-->
    <BuildTasks.GetMercurialVersionNumber DirectoryPath="$(MSBuildProjectDirectory)">
      <!--This captures the output by reading the task's MercurialId Property and assigning it to a local
          MSBuild Property called MercurialId - this is reference in the Build Target above.-->
      <Output TaskParameter="MercurialId" PropertyName="MercurialId" />
    </BuildTasks.GetMercurialVersionNumber>
  </Target>
  <!--<Target Name="AfterBuild">
  </Target>-->

</Project>

最后注意:构建任务项目只需要构建一次.不要在每次完成其余解决方案时都尝试构建它.如果这样做,您会发现 VS2008 锁定了 dll.还没有弄清楚,但我认为最好的办法是根据需要构建 dll,然后仅将 dll 与您的代码一起分发,确保 dll 的位置相对于您需要使用的每个项目是固定的它在.这样,没有人必须安装任何东西.

Last Note: The build tasks project only needs to be built once. Don't try to build it every time you do the rest of your solution. If you do, you will find that VS2008 has the dll locked. Haven't figured that one out yet, but I think the better thing to do is build the dll as you want it, then distribute ONLY the dll with your code, ensuring that the dll's location is fixed relative to every project you need to use it in. That way, no one has to install anything.

祝你好运,我希望这会有所帮助!

Good luck, and I hope this helps!

奥迪

这篇关于自动在 Visual Studio c# 项目中嵌入 mercurial 修订信息的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆