2014-09-03

Add reference to project from Text Template T4

In this example we add project reference taken from EnvDTE.CodeClass.

<#@ template debug="true" hostspecific="true" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ assembly Name="EnvDTE" #>
<#@ assembly Name="EnvDTE80" #>
<#@ assembly Name="VSLangProj" #>
<#@ assembly Name="VSLangProj80" #>
<#@ import namespace="System.Diagnostics" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.Text.RegularExpressions" #>
<#@ output extension=".txt" #>
<# 
    // Initialize dte variable.
    IServiceProvider hostServiceProvider = (IServiceProvider)Host;
    dte = (EnvDTE80.DTE2)System.Runtime.InteropServices.
        Marshal.GetActiveObject("VisualStudio.DTE.12.0"); 
#>
<#+
    // Visual Studio entry object.
    EnvDTE80.DTE2 dte;
 
    EnvDTE.ProjectItem GetCurrentProjectItem()
    {
        return dte.Solution.FindProjectItem(Host.TemplateFile);
    }
 
    public EnvDTE.Project GetProject(EnvDTE.ProjectItem a_project_item)
    {
        object parent = a_project_item.Collection.Parent;
 
        if (parent is EnvDTE.Project)
            return parent as EnvDTE.Project;
 
        return GetProject(parent as EnvDTE.ProjectItem);
    }
 
    public void AddReference(EnvDTE.CodeClass a_module_class) 
    {          
        EnvDTE.Project current_project = GetCurrentProjectItem().ContainingProject;
        EnvDTE.Project reference_project = GetProject(a_module_class.ProjectItem);
        VSLangProj80.VSProject2 vsproject2 = 
            current_project.Object as VSLangProj80.VSProject2;
 
        if (!vsproject2.References.Cast<VSLangProj.Reference>().
                Where(el => el.SourceProject != null).Any(
                    el => reference_project.FileName == el.SourceProject.FileName))
        {
            VSLangProj.Reference reference = 
                vsproject2.References.AddProject(reference_project);
            reference.CopyLocal = false; 
        }
    }
#>

2014-09-02

T4 text transformation error - The message filter indicated that the application is busy

Error    1    Running transformation: System.Runtime.InteropServices.COMException (0x8001010A): The message filter indicated that the application is busy. (Exception from HRESULT: 0x8001010A (RPC_E_SERVERCALL_RETRYLATER))
   at EnvDTE.CodeFunction.get_Access()
   at Microsoft. VisualStudio. TextTemplating. GeneratedTextTransformation. GetModuleFunctions(CodeClass a_module_class) in d:\ ... Service. Generator.tt:line 310
   at Microsoft. VisualStudio. TextTemplating. GeneratedTextTransformation. TransformText() in d:\ ... Service. Generator.tt:line 27. Line=310, Column=0

Concurrent projects compiling must be set in options (so this error only applied to VS2013 and newer). See: How to do a parallel build in Visual Studio 2013?

More than one project must contain *. tt text transform. And those projects must have enabled transform on build. See: Understanding T4: MSBuild Integration

The error message looks very cryptic. In the end I found out there is problem with concurrent projects building, when probably two or more projects try to run text transform simultaneously.

One very undesirable solution is to disable concurrent build.

Second is to manipulate project dependencies in such manner that problematic projects run one by one. See: How to: Create and Remove Project Dependencies

We may also use some locks in tt code.

The open question which I haven't investigated is how this behaves with msbuild.