TFService MSBUILD和T4代码生成

时间:2013-11-07 00:55:24

标签: code-generation t4 visual-studio-2013 team-build azure-devops

我们正在使用T4文件来管理我们的程序集版本控制; 基本上我们正在跟随these procedure重新生成每个构建版本的程序集版本。 这是通过让.csproj使用Modeling SDK for VS 2013customize T4 regeneration configuration with MSBUILD来完成的。

这很棒!遗憾的是,Team Foundation Service的团队构建似乎不支持在构建时生成T4代码,因为构建服务器上的VS 12实例在默认安装位置没有TextTemplating.targets;假设建模服务器上没有安装Modeling / VS SDK(来自TFService构建失败日志):

  

找不到导入的项目“C:\ Program Files(x86)\ MSBuild \ 12.0 \ Microsoft \ VisualStudio \ v12.0 \ TextTemplating \ Microsoft.TextTemplating.targets”。确认声明中的路径是否正确,以及该文件是否存在于磁盘上。

是否计划将Microsoft发布的VS 2013插件(VS SDK,Modeling SDK等)安装到构建服务器映像?

是否有可以实现的解决方案,以便当我通过Git将代码提交到Team Foundation Service时,构建不会中断?

如何配置TFService的托管构建控制器以在构建期间转换我的T4文件?

2 个答案:

答案 0 :(得分:0)

我最终为构建服务器自动编译程序集的任务创建了一个不同的解决方案。而不是使用T4动态创建AssemblyInfo.cs文件以通过项目链接共享到解决方案中的其他项目(因此必须处理如何保持文件的再生为此目的的烦恼),我完全避免使用T4。与第三方远程构建机器集成,管理允许T4操作的插件和SDK等工作太多了。我从MSBuild Community Tasks找到了一个更简单的涉及this SO question的解决方案。与T4相比,该解决方案非常简单!以下是它如何动摇:

每次构建此项目Application.Versioning.csproj时,都会在第三方Nuget安装的“MSBuildCommunity Tasks”库中定义MSBUILD任务 根据当前的UTC日期时间动态生成 [AssemblyFileVersion] [AssemblyInformationalVersion] [AssemblyVersion] 并将其输入新文件, AutoVersion.cs,驻留在此项目的“属性”目录中(与AssemblyInfo.cs文件一起)。 AssemblyInfo.cs具有这些程序集属性 永远剔除,以避免多重定义的程序集属性的构建错误)。生成AutoVersion.cs(在构建之前发生),编译器 将前面提到的程序集版本控制属性集成到构建的程序集中。此版本控制反映了它们构建的UTC时间(见下文)。

.sln中的每个其他.csproj都订阅了这个动态的构建时程序集版本控制,创建了一个指向生成的AutoVersion.cs文件的文件链接。 这些程序集还必须对其AssemblyInfo.cs进行修剪。每次MyApplication.Versioning.csproj都会完成.sln中所有订阅.csprojs的程序集版本控制。 建造(或重建)。这是版本控制项目的.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");
}

答案 1 :(得分:0)

我们通过包含V&amp; M SDK文件(Microsoft.TextTemplating.Build.Tasks.dll,Microsoft.TextTemplating.targets,Microsoft.VisualStudio.TextTemplating.Sdk.Host.1X.0.dll)解决了类似的问题。源代码管理中的external_libraries文件夹。

IMO这是在大型项目中管理第三方SDK的唯一方法:在每台开发机器和构建服务器上安装都太脆弱而不实用。