Search code examples
c#t4

Set output of t4 template as embedded resource


I've built a t4 template to output multiple files, but I need these files to be embedded content.

Does t4 offer an out-of-the-box method of doing this?

If not, might it be possible to modify the project file by switching each file's <Content> element to an <EmbeddedResource> element?


Solution

  • Here's how I set a ProjectItem to be an embedded resource: In the T4 template I have a function which takes the Project object and a FileInfo object representing the T4 template itself - there may be a simpler method but I obtained the template FileInfo using...

    var templateFileInfo = new FileInfo(this.Host.TemplateFile);
    

    The template FileInfo is used because we need the generated files (my T4 template outputs multiple files) which are generated 'under' the template file in the solution.

    [Really, what I want here is a ProjectItem representing the template, so that I can loop over the auto-generated children. If there's a better way of doing this, please comment.]

    Once I have the ProjectItem objects representing each of the T4-generated files, I enumerate each Properties collection until I find the "BuildAction" property for that ProjectItem; this is then set as "Embedded Resource" using a constant.

    private void SetGeneratedFilesAsEmbeddedResources(Project project, FileInfo templateFileInfo)
    {
        const string propBuildAction = "BuildAction";
    
        var generatedProjectItems = GetGeneratedProjectItems(project, templateFileInfo).ToList();
    
        foreach (var generatedProjectItem in generatedProjectItems)
        {
            var props = generatedProjectItem.Properties;
            if ((VSLangProj.prjBuildAction)props.Item(propBuildAction).Value != VSLangProj.prjBuildAction.prjBuildActionEmbeddedResource)
            {
                props.Item(propBuildAction).Value = VSLangProj.prjBuildAction.prjBuildActionEmbeddedResource;
            }
        }
    }
    
    private IEnumerable<ProjectItem> GetGeneratedProjectItems(Project project, FileInfo templateFileInfo)
    {
        var templateParentFolder = GetTemplateParentFolder(project, templateFileInfo);
    
        foreach (ProjectItem item in templateParentFolder.ProjectItems)
        {
            if (item.Kind != EnvDTE.Constants.vsProjectItemKindPhysicalFile || item.Name != templateFileInfo.Name)
            {
                continue;
            }
    
            foreach (ProjectItem generatedItem in item.ProjectItems)
            {
                yield return generatedItem;
            }
        }
    }
    
    private ProjectItem GetTemplateParentFolder(Project project, FileInfo templateFileInfo)
    {
        var containingFolderName = templateFileInfo.Directory.Name;
    
        foreach (ProjectItem item in project.ProjectItems)
        {
            if (item.Kind == EnvDTE.Constants.vsProjectItemKindPhysicalFolder && item.Name == containingFolderName)
            {
                return item;
            }
        }
        
        throw new Exception($"Cannot execute {templateFileInfo.Name} - Unable to locate the \"{containingFolderName}\" folder at the root level.");
    }