TFService MSBUILD и генерация кода T4

Мы используем файл T4 для управления версиями нашей сборки; В основном мы следуем эти процедуры для повторного создания версий сборки для каждой сборки. Это достигается за счет того, что файл .csproj использует SDK моделирования для VS 2013. чтобы настроить конфигурацию регенерации T4 с помощью MSBUILD .

Это отлично работает! К сожалению, командная сборка для Team Foundation Service не поддерживает генерацию кода T4 во время сборки, поскольку экземпляры VS 12 на сервере сборки не имеют TextTemplating.targets в месте установки по умолчанию; предположительно SDK Modeling/VS не установлены на сервере сборки (из журналов ошибок сборки TFService):

Импортированный проект «C:\Program Files (x86)\MSBuild\12.0\Microsoft\VisualStudio\v12.0\TextTemplating\Microsoft.TextTemplating.targets» не найден. Убедитесь, что путь в объявлении правильный и что файл существует на диске.

Планируется ли установка надстроек Microsoft VS 2013 (VS SDK, Modeling SDK и т. д.) в образ сервера сборки?

Есть ли обходной путь, который я могу реализовать, чтобы при фиксации кода в Team Foundation Service через Git сборка не прерывалась?

Как настроить хост-контроллер сборки TFService для преобразования моих файлов T4 во время сборки?


person TheFastCat    schedule 07.11.2013    source источник


Ответы (2)


В итоге я создал другое решение задачи автоматического создания версий наших сборок сервером сборки. Вместо того, чтобы использовать T4 для динамического создания файла AssemblyInfo.cs для совместного использования через ссылку на элемент в других проектах в рамках решения (и, следовательно, иметь дело с раздражением от выяснения того, как поддерживать регенерацию файла в актуальном состоянии для этой цели), я полностью избегал Т4. Было слишком много работы по интеграции со сторонней машиной удаленной сборки, управлению надстройками и SDK, которые позволяют манипулировать T4 и т. д. Существует гораздо более простое решение, включающее Задачи сообщества MSBuild, которые я нашел в этом вопросе SO. Решение элегантно простое по сравнению с T4! Вот как это трясется:

Каждый раз при сборке этого проекта Application.Versioning.csproj задачи MSBUILD, определенные в сторонней библиотеке MSBuildCommunity Tasks, установленной Nuget, динамически генерируют [AssemblyFileVersion], [AssemblyInformationalVersion]. , [AssemblyVersion] на основе текущей даты и времени в формате UTC и введите их в новый файл AutoVersion.cs, который находится в каталоге «Properties» этого проекта (вместе с файлом AssemblyInfo.cs). файл). В AssemblyInfo.cs эти атрибуты сборки навсегда отбракованы, чтобы избежать ошибок сборки множественно определенных атрибутов сборки). После создания AutoVersion.cs (что происходит непосредственно перед сборкой) компилятор интегрирует вышеупомянутые атрибуты управления версиями сборки в построенную сборку. Эта версия отражает время UTC, в которое они были созданы (см. ниже).

Каждый другой файл .csproj в .sln подписывается на это динамическое управление версиями сборки во время сборки, создавая ссылку на созданный файл AutoVersion.cs. У этих сборок также должен быть обрезан файл AssemblyInfo.cs. Управление версиями сборки для всех подписанных .csprojs в .sln выполняется каждый раз при сборке (или перестроении) MyApplication.Versioning.csproj. Вот .csproj для проекта управления версиями:

  <!-- START DYNAMIC ASSEMBLY VERSIONING WORK-->
  <!--MSBuild Community Tasks path as installed via the nuget package 'Install-Package MSBuildTasks'-->

 <PropertyGroup>
    <MSBuildCommunityTasksPath>$(MSBuildThisFileDirectory)..\.build</MSBuildCommunityTasksPath>
    <My-PropertiesDir>Properties</My-PropertiesDir>
  </PropertyGroup>
  <PropertyGroup>

   <!--this should only be incremented (starting at zero) for MAJOR application releases this should never be reset only incremented!--> 
    <ManualMajorVersion>0</ManualMajorVersion>

   <!--3 digits maximum should only be manually incremented (starting at zero) for feature releases-->
   <!--!this should be reset to '0' at the start of each caldenar Year-->  
    <ManualMinorVersion>0</ManualMinorVersion>
  </PropertyGroup>

   <!--Import MSBuild Community Tasks library installed from Nuget -->
   <!--This library contains defined MSBUILD tasks useful for dynamically generating Assembly information via MSBUILD https://github.com/loresoft/msbuildtasks--> 
  <Import Project="$(MSBuildCommunityTasksPath)\MSBuild.Community.Tasks.Targets" />
  <Target Name="BeforeBuild">
    <Time Format="yy.MM.dd.HHmm" Kind="Utc">
      <Output TaskParameter="FormattedTime" PropertyName="My-VersionNumber" />
    </Time>
    <Message Text="Auto versioning from UTC time: $(My-VersionNumber) ...">
    </Message>
    <Time Format="yy" Kind="Utc">
      <Output TaskParameter="FormattedTime" PropertyName="Year" />
    </Time>
    <Time Format="MM" Kind="Utc">
      <Output TaskParameter="FormattedTime" PropertyName="Month" />
    </Time>
    <Time Format="dd" Kind="Utc">
      <Output TaskParameter="FormattedTime" PropertyName="Day" />
    </Time>
    <Time Format="HH" Kind="Utc">
      <Output TaskParameter="FormattedTime" PropertyName="Hour" />
    </Time>
    <Time Format="mm" Kind="Utc">
      <Output TaskParameter="FormattedTime" PropertyName="Minute" />
    </Time>
    <ItemGroup>
      <My-AssemblyInfo Include="$(My-PropertiesDir)\AutoVersion.cs" />
      <Compile Include="@(My-AssemblyInfo)" />
    </ItemGroup>
    <MakeDir Directories="$(My-PropertiesDir)" />
    <PropertyGroup>
      <GeneratedAssemblyVersion>$(ManualMajorVersion).$(Year)$(ManualMinorVersion).$(Month)$(Day).$(Hour)$(Minute)</GeneratedAssemblyVersion>
    </PropertyGroup>
    <AssemblyInfo OutputFile="@(My-AssemblyInfo)" CodeLanguage="CS" AssemblyFileVersion="$(GeneratedAssemblyVersion)" AssemblyInformationalVersion="$(GeneratedAssemblyVersion)" AssemblyVersion="$(GeneratedAssemblyVersion)" Condition="$(GeneratedAssemblyVersion) != '' " />
  </Target>
  <!-- END DYNAMIC ASSEMBLY VERSIONING WORK-->

а также модульный тест, чтобы проверить для себя:

/// <summary> A test to validate the configured, auto-generated assembly versioning is working as expected </summary>
[Test]
public void AssemblyVersioningTest()
{
    DirectoryInfo currentDirectory = new DirectoryInfo(Directory.GetCurrentDirectory());
    DirectoryInfo versioningDir = new DirectoryInfo(currentDirectory.FullName + "\\" + VERSIONING_DYNAMIC_FILE_DIRECTORY);

    // verify versioning directory located/loaded/exists
    Assert.IsTrue(versioningDir.Exists);

    // locate the VERSIONING_DYNAMIC_FILE file within the VERSIONING_DYNAMIC_FILE_DIRECTORY directory
    string dynamicFilePath = versioningDir.FullName + "\\" + VERSIONING_DYNAMIC_FILE;

    // get the FileInfo for the file that is used to dynamically generate assembly versioning
    FileInfo dynamicVersioningFileInfo = new FileInfo(dynamicFilePath);
    Assert.IsTrue(dynamicVersioningFileInfo.Exists);

    // get the two digit creation Dates/Times for the assembly's file as strings
    // since that's what the versioning reflects
    DateTime dynamicVersioningFileLastWriteTime = dynamicVersioningFileInfo.LastWriteTime;

    #region    Created VERSIONING_DYNAMIC_FILE
    string dynamicVersioningFileLastWriteTimeYear = dynamicVersioningFileLastWriteTime.ToUniversalTime().ToString("yy");

    string dynamicVersioningFileLastWriteTimeMonth = dynamicVersioningFileLastWriteTime.ToUniversalTime().ToString("MM");
    string dynamicVersioningFileLastWriteTimeDay = dynamicVersioningFileLastWriteTime.ToUniversalTime().ToString("dd");

    string dynamicVersioningFileLastWriteTimeHour = dynamicVersioningFileLastWriteTime.ToUniversalTime().ToString("HH");
    string dynamicVersioningFileLastWriteTimeMinute = dynamicVersioningFileLastWriteTime.ToUniversalTime().ToString("mm");
    #endregion Created VERSIONING_DYNAMIC_FILE

    // get *this* assembly from the .sln using reflection since this assembly consumes/is dependent upon the versioning functionality we
    // are testing
    Assembly theAssemblyExecutingThisTest = Assembly.GetExecutingAssembly();

    // get this assembly's version
    // we will investigate this to compare it to a reverse-engineering of what we would
    // expect it to be based 
    AssemblyName testingAssemblyName = theAssemblyExecutingThisTest.GetName();
    Version testingAssemblyVersion = testingAssemblyName.Version;

    #region    Generated Assembly Versioning
    // get the first two digits of the assemblyVersion.MinorVersion - these represent the year
    string testingAssemblyVersionMinorYear = testingAssemblyVersion.Minor.ToString().Substring(0, 2);
    // the rest of the digits represent the manually-configured version and can be 1-3 chars long
    string testingAssemblyVersionMinorManual = GetMinorManualFromVersionString(testingAssemblyVersion.Minor.ToString());

    string testingAssemblyVersionBuildMonth = testingAssemblyVersion.Build.ToString("D4").Substring(0, 2);
    string testingAssemblyVersionBuildDay = testingAssemblyVersion.Build.ToString("D4").Substring(2, 2);

    string testingAssemblyVersionRevisionHour = testingAssemblyVersion.Revision.ToString("D4").Substring(0, 2);
    string testingAssemblyVersionRevisionMinute = testingAssemblyVersion.Revision.ToString("D4").Substring(2, 2);
    #endregion Generated Assembly Versioning

    // verify the assembly's minor version: last two digits match of assembly file creation year
    // (pad minorversion 4 spaces in case released minor version is empty)
    Assert.AreEqual(dynamicVersioningFileLastWriteTimeYear,
        testingAssemblyVersionMinorYear,
        "Assembly's minor version: last two digits do not match assembly file last write time year");

    // verify the assembly's minor version is comprised of two digit 'released minor version' + two digit year of assembly file creation date
    Assert.AreEqual(dynamicVersioningFileLastWriteTimeYear + testingAssemblyVersionMinorManual,
        testingAssemblyVersionMinorYear + testingAssemblyVersionMinorManual,
        "Assembly's minor version: not comprised of two digit year of assembly file last write time date + dynamically-sized 'minor manual version' + ");

    // verify the Assembly's build version is comprised of two digit month + two digit day of assembly file creation date
    Assert.AreEqual(dynamicVersioningFileLastWriteTimeMonth + dynamicVersioningFileLastWriteTimeDay,
        testingAssemblyVersionBuildMonth + testingAssemblyVersionBuildDay,
        "Assembly's build version: not comprised of two digit month + two digit day of assembly file last write time date");

    // verify the Assembly's revision version is comprised two digit hour + two digit minute of assembly file creation date
    Assert.AreEqual(dynamicVersioningFileLastWriteTimeHour + dynamicVersioningFileLastWriteTimeMinute,
        testingAssemblyVersionRevisionHour + testingAssemblyVersionRevisionMinute,
        "Assembly's revision version: comprised two digit hour + two digit minute of assembly file last write time date");
}
person TheFastCat    schedule 08.11.2013

Мы решили аналогичную проблему, включив файлы V&M SDK (Microsoft.TextTemplating.Build.Tasks.dll, Microsoft.TextTemplating.targets, Microsoft.VisualStudio.TextTemplating.Sdk.Host.1X.0.dll) в папку external_libraries в исходном коде. контроль.

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

person Tim Sparkles    schedule 27.03.2014