Автоинкремент build and revision number in Visual Studio #.net

Привык в Delphi, что при компиляции (rebuid) автоматически увеличивается номер версии. В C# принят следующий стандарт – major.minor.build.revision.

Удивился, что Visual Studio, даже в версии 2010 не имеет встроенных механизмов для автоувеличения номера версии (build и revision). Вернее он есть 1.0.*, но тогда билд увеличивается каждый день, а ревизия начинается каждый раз сначала.

Полазил по интернету – везде упоминается страшно написанный скрипт на js. Поэтому плюнул и по быстрому набросал утилитку, которая может быть полезна кому-то еще :)

Весь мой богатый опыт говорит мне, что номер версии ОЧЕНЬ важен и помогает избавится от множества ненужных и глупых проблем.

Я только начинаю работать с .net, поэтому буду благодарен за любую конструктивную критику, подсказки … :)

Версия находится в файле AssemblyInfo.cs в каталоге Properties. Решения, которые имеются в интернете мне не понравились по следующим причинам:

  • Я хотел бы версию и подверсию (major, minor) исправлять сам ручками ибо это должно быть редко и осознанно
  • build должен увеличиваться на единицу с каждым rebuild Release
  • revision должен браться из репозитория. Я должен всегда знать в какой версии репозитория находятся нужные мне изменения
  • Так я и сделал. Суть проста – утилита запускается при каждом build и

  • берёт из репозитория текущий номер ревизии и увеличивает его на единицу. Т.к. всё что я наваяю будет в следующей ревизии.
  • Делает копию в ..\Properties\AssemblyInfo.~cs
  • Находит файл ..\Properties\AssemblyInfo.cs и считывает оттуда текущий номер версии
  • Формирует новый номер версии было.было.было+1.номер_ревизии_из_репозитория
  • Записывает файл ..\Properties\AssemblyInfo.cs с изменёнными данными версии.
  • Это делает следующий код:

    Copy Source | Copy HTML
    using System;
    using System.Diagnostics;
    using System.IO;
    using System.Text;
    using System.Text.RegularExpressions;
     
    namespace vbinc
    {
        class Program
        {
            private static StringBuilder sortOutput = new StringBuilder("");
            private static int _revno;
     
            static void Main(string[] args)
            {
                try
                {
                    _revno = runBzr()+1;
     
                    string str = File.ReadAllText(@"..\..\Properties\AssemblyInfo.cs");
                    //File.OpenText(@"..\..\Properties\AssemblyInfo.cs");
     
                    string pattern_1 = @"\[assembly\: AssemblyVersion\(""(\d{1,})\.(\d{1,})\.(\d{1,})\.(\d{1,})""\)\]";
                    string pattern_2 = @"\[assembly\: AssemblyFileVersion\(""(\d{1,})\.(\d{1,})\.(\d{1,})\.(\d{1,})""\)\]";
     
                    string result = GetResult(pattern_1, str, "AssemblyVersion");
                    result = GetResult(pattern_2, result, "AssemblyFileVersion");
     
                    File.Delete(@"..\..\Properties\AssemblyInfo.~cs");
                    File.Copy(@"..\..\Properties\AssemblyInfo.cs", @"..\..\Properties\AssemblyInfo.~cs");
                    File.WriteAllText(@"..\..\Properties\AssemblyInfo.cs", result);
                    //Console.ReadKey();
                }
                catch (Exception ex)
                {
                    Console.WriteLine("ERR: " + ex.Message);
                }
            }
     
            private static string GetResult(string pattern, string str, string app)
            {
                int _bl;
     
                Regex r = new Regex(pattern);
     
                Match m = r.Match(str);
     
                _bl = Convert.ToInt32(m.Groups[3].Value)+1;
     
                string rz = string.Format("[assembly: {4}(\"{0}.{1}.{2}.{3}\")]", m.Groups[1], m.Groups[2], _bl, _revno, app);
                return r.Replace(str, rz);
            }
     
            private static int runBzr()
            {
                Process myProc = new Process();
                myProc.StartInfo.FileName = Path.Combine(ProgramFilesx86(), @"Bazaar\bzr.exe");
                myProc.StartInfo.Arguments = "revno";
                myProc.StartInfo.WorkingDirectory = Environment.CurrentDirectory;
                myProc.StartInfo.UseShellExecute = false;
                myProc.StartInfo.RedirectStandardOutput = true;
                myProc.OutputDataReceived += SortOutputHandler;
                myProc.StartInfo.RedirectStandardInput = true;
     
                myProc.Start();
     
                StreamWriter sortStreamWriter = myProc.StandardInput;
                myProc.BeginOutputReadLine();
                sortStreamWriter.Close();
                myProc.WaitForExit();
                return Convert.ToInt32(sortOutput[ 0].ToString());
            }
     
            private static void SortOutputHandler(object sendingProcess, DataReceivedEventArgs outLine)
            {
                if (!String.IsNullOrEmpty(outLine.Data))
                {
                    sortOutput.Append(outLine.Data);
                }
            }
     
            static string ProgramFilesx86()
            {
                if (8 == IntPtr.Size
                    || (!String.IsNullOrEmpty(Environment.GetEnvironmentVariable("PROCESSOR_ARCHITEW6432"))))
                {
                    return Environment.GetEnvironmentVariable("ProgramFiles(x86)");
                }
     
                return Environment.GetEnvironmentVariable("ProgramFiles");
            }
        }
    }
     

    Код писался примерно час, и то из за того, что я не знал как перехватить вывод запуска стороннего приложения. Код неоптимален и всё такое, но данную конкретную задачу выполняет.

    Теперь как это использовать? Очень просто. У вас должен быть проект в репозитории Bazaar (Очень рекомендую). Помещаем файл vbinc.exe в корень solution.

    И делаем вот так:

    Всё. С этого момента если вы будете делать Debug Build или проект не менялся – ничего не происходит. При каждом build Release запустится утилитка, которая ПЕРЕД компиляцией увеличит номер build и возьмет номер ревизии+1 из репозитория bazaar.

    Утилита работает как на win7 (vista), так и на win7x64 (vistax64)

    Не пытайтесь запускать проект, если вы не используете Bazaar в своём проекте ибо проверок практически никаких не делал. Чтобы что-то безвозвратно повредить – нет, но зачем? :)

    Можете менять как хотите – это просто полезный набросок и не более того. Учиться тут нечему, просто может кого подтолкнёт к нормальной реализации :) Для меня эта утилита делает всё что я хочу на данный момент. Цена ошибки не велика :)

    Если кому интересно – могу переписать нормально, со всеми проверками и с плагинами для разных VCS. Этим всё равно займусь, когда появится свободное время или будут активно желающие.

    P.S. Перечитал и подумал, что логичнее поменять build и ревизию местами :) Т.е. версия, подверсия, ревизия, build Так и сделаю.

    Copy Source | Copy HTML
    _bl = Convert.ToInt32(m.Groups[4].Value)+1;
     
    string rz = string.Format("[assembly: {4}(\"{0}.{1}.{2}.{3}\")]", m.Groups[1], m.Groups[2], _revno, _bl, app);