Встраивание подвижной информации о пересмотре в Visual Studio c# проекты автоматически

Исходная проблема

В разрабатывании наших проектов я хочу, чтобы подвижный идентификатор каждого репозитория был встроен в продукте (продуктах) того репозитория (библиотека, приложение или тестовое приложение).

Я нахожу, что это делает настолько легче отладить приложение, выполняемое клиентами 8 часовых поясов далеко, если Вы знаете точно, что вошло в создание конкретной версии приложения, которое они используют. По сути, каждый проект (приложение или библиотека) в наших системах реализует способ достигнуть связанную информацию о пересмотре.

Я также нахожу очень полезным смочь видеть, было ли приложение скомпилировано с чистым (неизмененным) changesets из репозитория. 'Идентификатор Hg' полезно добавляет + к changeset идентификатору, когда там незафиксированы изменения в репозитории, таким образом, это позволяет нам легко видеть, выполняют ли люди чистое или измененную версию кода.

Мое текущее решение детализировано ниже и выполняет основные требования, но существует много проблем с ним.

Текущее решение

В данный момент, к каждому решению для Visual Studio, я добавляю следующие "Команды" командной строки события Перед сборкой:

cd $(ProjectDir)
HgID

Я также добавляю файл HgID.bat к Каталогу проекта:

@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, который определяется как:

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 (который не сохранен при управлении версиями), и то, чтобы заставлять библиотеку быть перекомпилированным с с новым 'hg идентификатор' строка в 'Версии'.

Проблемы с текущим решением

Основная проблема состоит в том, что, так как HgId.cs воссоздается в каждой предварительной сборке, таким образом, каждый раз мы должны скомпилировать что-либо, все проекты в текущем решении перекомпилированы. Так как мы хотим смочь легко отладить в наши библиотеки, мы обычно сохраняем многие библиотеки ссылаемыми в нашем решении для главного приложения. Это может закончиться во время изготовления, которое значительно дольше, чем я хотел бы.

Идеально я хотел бы, чтобы библиотеки скомпилировали, только если содержание файла HgId.cs на самом деле изменилось, в противоположность тому, чтобы быть воссозданным с точно тем же содержанием.

Вторая проблема с этим методом, это - зависимость от определенного поведения оболочки окон. Я должен был уже несколько раз изменять пакетный файл, так как оригинал работал под XP, но не Vista, следующая версия работала в соответствии с Vista, но не XP, и наконец мне удалось заставить его работать с обоими. Будет ли это работать с Windows 7, однако предположение anyones и со временем, я вижу его более вероятно, что подрядчики будут ожидать мочь создать наши приложения в своем Windows 7 boxen.

Наконец, у меня есть эстетическая проблема с этим решением, пакетные файлы и bodged вместе обрабатывают файлы по шаблону, чувствуют себя подобно неправильному способу сделать это.

Мои фактические вопросы

Как был бы, Вы решить/как Вас решаете проблему, которую я пытаюсь решить?

Что более оптимальные варианты там, чем, что я в настоящее время делаю?

Отклоненные Решения этих проблем

Прежде чем я реализовал текущее решение, я посмотрел на расширение Ключевого слова Mercurials, так как оно походило на очевидное решение. Однако, чем больше я посмотрел на него, и считайте мнения народов, тем больше, что я пришел к выводу, что это не был правильный поступок.

Я также помню проблемы, что замена ключевого слова вызвала меня в проектах в предыдущих компаниях (просто, мысль когда-либо имеющий необходимость использовать Источник, Безопасный снова, заполняет меня чувством страха *8').

Кроме того, я особенно не хочу должным быть позволять Подвижным расширениям заставить сборку завершаться. Я хочу решение быть сам содержавший, так, чтобы для приложения не было легко быть случайно скомпилированным без встроенной информации о версии просто, потому что расширение не включено, или правильное программное обеспечение помощника не было установлено.

Я также думал о записи этого в лучшем языке сценариев, тот, где я только запишу файл HgId.cs, если бы содержание на самом деле изменилось, но все опции я мог бы думать, потребовал бы, чтобы мои коллеги, подрядчики и возможно клиенты должны были установить программное обеспечение, которое они не могли бы иначе хотеть (например, cygwin).

Любые другие люди опций могут думать, ценился бы.


Обновление

Частичное решение

Играя вокруг с ним некоторое время, мне удалось заставить файл HgId.bat только перезаписывать файл HgId.cs, если это изменяется:

@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 все еще настаивает на том, чтобы компилировать все каждый раз. Я попытался искать решения и попытался проверить, "Только разрабатывают проекты запуска и зависимости от Выполненного" в Tools|Options|Projects и Solutions|Build и Выполнении, но это не имеет никакого значения.

Вторая проблема также остается, и теперь у меня нет способа протестировать, если она будет работать с Vista, так как тот подрядчик больше не с нами.

  • Если бы кто-либо может протестировать этот пакетный файл на поле Windows 7 и/или Vista, я ценил бы слушание, как это пошло.

Наконец, моя эстетическая проблема с этим решением, еще более сильно, чем это было прежде, так как пакетный файл более сложен, и это там теперь больше, чтобы пойти не так, как надо.

Если можно думать о каких-либо лучших решениях, я хотел бы услышать о них.

54
задан Mark Booth 29 March 2010 в 00:08
поделиться

7 ответов

Думаю, у меня есть для вас ответ. Это будет немного сложно, но избавит вас от необходимости делать какие-либо командные файлы. Вы можете положиться на MSBuild и Custom Tasks, которые сделают это за вас. Я использовал пакет расширений для MSBuild (доступен на CodePlex ), но вторая задача, которая вам нужна, - это то, что вы могли бы легко написать самостоятельно.

С помощью этого решения вы можете щелкнуть правой кнопкой мыши по DLL и увидеть в свойствах файла, из какой Mercurial Version пришла DLL (или EXE).

Вот шаги:

  1. Получите MBBuildExtension Pack ИЛИ Напишите настраиваемую задачу для перезаписи AssemblyInfo.cs
  2. Создайте настраиваемую сборку Задача в собственном проекте получить Mercurial Id (код ниже).
  3. Отредактируйте файлы проекта, которым требуется Mercurial Id для использования специальной задачи (код ниже).

Специальная задача для получения ртутного идентификатора: (Это должно быть хорошо протестировано и, возможно, лучше обобщено ...)

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}\n{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;

        }
    }

И измененный файл проекта: (комментарии могут помочь)

<?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)\MSBuild\ExtensionPack\MSBuild.ExtensionPack.tasks" />
  <!--Below is the required UsingTask tag that brings in our custom task.-->
  <UsingTask TaskName="BuildTasks.GetMercurialVersionNumber" 
             AssemblyFile="C:\Users\mpld81\Documents\Visual Studio 2008\Projects\LambaCrashCourseProject\BuildTasks\bin\Debug\BuildTasks.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>bin\Debug\</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>bin\Release\</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="Properties\AssemblyInfo.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)\Properties\AssemblyInfo.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 фиксировано относительно каждого проекта, который вам нужно использовать. это внутри. Таким образом, никому не нужно ничего устанавливать.

Удачи, надеюсь, это поможет!

Audie

16
ответ дан 7 November 2019 в 08:10
поделиться

Im my .hgrc У меня есть это:

[extensions]                                                                                                                                                               
hgext.keyword =                                                                                                                                                            
hgext.hgk =                                                                                                                                                                

[keyword]                                                                                                                                                                  
** =                                                                                                                                                                       

[keywordmaps]                                                                                                                                                              
Id = {file|basename},v {node|short} {date|utcdate} {author|user} 

И в моем исходном файле (Java) я делаю:

public static final String _rev = "$Id$";

После фиксации $ Id $ расширяется до чего-то вроде:

public static final String _rev = "$Id: Communication.java,v 96b741d87d07 2010/01/25 10:25:30 mig $";
2
ответ дан 7 November 2019 в 08:10
поделиться

Рассматривали ли вы использование строкового ресурса вместо строковой константы языка C #? Строковые ресурсы можно редактировать / заменять в выходных двоичных файлах после сборки с помощью инструментов, предназначенных для локализации.

Вы должны передать номер своей ртутной версии текстовому файлу, который не используется сборкой C #, а затем, используя операцию после сборки, замените ресурс версии фактическим значением из отправленного текстового файла. Если вы подписываете свои сборки строгим именем, замена строки ресурса должна произойти до подписания.

Вот как мы справились с этой проблемой в компании Borland несколько лет назад для продуктов Windows. С тех пор мир стал более сложным, но принцип все еще действует.

5
ответ дан 7 November 2019 в 08:10
поделиться

Мы решили эту проблему с помощью другой системы управления версиями - Subversion.

Что мы делаем, так это то, что у нас есть файл commonassemblyinfo.cs, и мы вставляем в него номер версии svn с помощью скрипта msbuild.

Мы буквально вызываем svninfo и очищаем вывод из части revision: part, а затем вставляем его в файл CommonAssemblyInfo.cs.

Каждый проект в нашем решении имеет ссылку на общий файл и затем компилируется, что означает, что сборки версируются при компиляции с номером версии svn, это также означает, что все зависимые библиотеки, которые мы написали, также версируются.

Мы довольно легко достигли этого с помощью файлов cruisecontrol .net и msbuild.

Я не использовал mercurial, но полагаю, что вы могли бы сделать что-то подобное с командой id? но из-за формата вывода вам нужно будет подойти к тому, как вы его использовали, несколько иначе.

У меня был бы стандартный xml-файл, который читается при запуске приложения, и вы могли бы вставить в него информацию (работает и с resx-файлами). Затем вы можете прочитать это значение в коде. Немного неприятно, но работает.

Мое любимое решение - это то, как мы это делаем, но вы не можете сделать это в Mercurial, поскольку информация о наборе изменений не является чисто числовой, как номер ревизии svn.

2
ответ дан 7 November 2019 в 08:10
поделиться

Кажется, есть несколько возможных подходов к этой проблеме. Первым и, вероятно, предпочтительным решением было бы установить сервер сборки и распространять только сгенерированные на нем сборки клиентам. Это имеет то преимущество, что вы никогда не отправляете незафиксированные изменения. Используя MSBuild, NAnt или какой-либо другой инструмент сборки на основе задач, весь процесс становится очень гибким.Мне удалось установить TeamCity и получить первые несколько сборок и запустить их с очень небольшими усилиями, но есть и другие хорошие серверы сборки. Это действительно должно быть вашим решением.

Если вы по какой-то причине настаиваете на том, что распространять сборки разработчиков среди клиентов - это нормально;), тогда вам понадобится локальное решение.

Довольно простым решением было бы использовать встроенную поддержку для автоматического увеличения номера сборки сборки:

// major.minor.build.revision
[assembly:AssemblyVersion("1.2.*")]

* заставляет номер сборки автоматически увеличиваться каждый раз, когда вы компилируете ( и есть изменения). Номер ревизии - случайное число. Отсюда вы можете отслеживать связь с идентификатором Mercurial, сохраняя обе части информации, например отправив его в какое-либо внутреннее веб-решение или что-то еще, что соответствует вашим конкретным потребностям, или обновите сгенерированную сборку. Я подозреваю, что вы можете использовать PostSharp или Mono.Cecil перезаписать сборку, например путем исправления номера ревизии на идентификатор. Если ваши сборки подписаны, перезапись должна произойти до того, как вы их подпишете, что может немного утомить вас, если у вас нет файла сборки. Обратите внимание, что VS можно настроить для компиляции с использованием вашего настраиваемого файла сборки вместо процедуры сборки по умолчанию.

Мое последнее предложение - создать отдельный проект только для hg id и использовать этап после сборки для объединения сгенерированных сборок в одну. ILMerge поддерживает повторную подпись подписанных сборок, и поэтому с этим, вероятно, будет легче работать. Обратной стороной является то, что распространение ILMerge запрещено (хотя коммерческое использование разрешено).

Это не решение, но, надеюсь, вдохновение, которое поможет вам двигаться дальше.

1
ответ дан 7 November 2019 в 08:10
поделиться

Вот что мы здесь делаем: мы не встраиваем ревизию каждый раз, когда проект создается на машинах разработчика. В лучшем случае это вызывает проблемы, о которых вы упомянули выше, а в худшем - вводит в заблуждение, потому что вы могли изменять файлы в своем рабочем пространстве.

Вместо этого мы встраиваем номер версии системы управления версиями только тогда, когда наши проекты собираются в TeamCity на отдельном сервере сборки. Эта редакция встроена в обе версии AssemblyVersion и AssemblyFileVersion , разделенные на две последние части.

По умолчанию версия заканчивается на 9999.9999, и мы разделили ревизию таким образом, чтобы ревизия 12345678 стала 1234,5678 (не то чтобы мы и близко подошли к 12-миллионной ревизии ...).

Этот процесс гарантирует, что продукт с версией 2.3.1.1256 определенно является первоначальной сборкой ревизии 11,256. Все, что разработчики создают вручную, вместо этого будет выглядеть так: 2.3.9999.9999.

Точные детали того, как достигается вышеуказанное, не имеют прямого отношения, поскольку мы не используем Mercurial, но вкратце: TeamCity обрабатывает проверку требуемой ревизии и передает ее номер в наш сценарий MSBuild, который затем выполняет перезапись AssemblyInfo. .cs с помощью написанной нами кастомной задачи.

Что мне особенно нравится в этом, так это то, что никакие файлы не изменяются нажатием F5 в Visual Studio - на самом деле, что касается Visual Studio, он работает с простым старым нормальным решением.

1
ответ дан 7 November 2019 в 08:10
поделиться

Я только что выпустил небольшую задачу MSBuild с открытым исходным кодом, которая делает именно то, что вам нужно:

  • Она помещает номер ревизии Mercurial в вашу . NET версии сборки
  • По версии можно определить, была ли сборка скомпилирована с незафиксированными изменениями
  • Не вызывает ненужных сборок, если ревизия не изменилась
  • Не зависит от сценариев Windows
  • Ничего не нужно устанавливать - вы просто добавляете небольшую DLL в ваше решение и редактируете некоторые файлы в вашем проекте

http://versioning.codeplex.com

25
ответ дан 7 November 2019 в 08:10
поделиться
Другие вопросы по тегам:

Похожие вопросы: