Renan Araújo
Renan Araújo

Reputation: 3631

Include T4 generated files on the same folder of the template file

I'm trying to add a saved file to the current project of my T4 template file. The file is saved as expected by the SaveOutput method, but when I use the IncludeInCurrentProject method, that calls the ProjectItems.AddFromFile (docs here), and then the file is added on the root of the project.

I'd like to include the file generated by the SaveOutput method on that exactly location on the project.

This is what I have right now:

enter image description here

Here is my .tt file:

<#@ template debug="true" hostspecific="true" language="C#" #>
<#@ output extension=".sql" #>
<#@ assembly name="Microsoft.VisualBasic.dll" #>
<#@ assembly name="EnvDTE" #>
<#@ import Namespace="System.IO" #>
<#@ import namespace="EnvDTE" #>

<#
    var MigrationName = Microsoft.VisualBasic.Interaction.InputBox("InputBox Label", "Description");
    if (string.IsNullOrWhiteSpace(MigrationName))
        return "";

    var MigrationVersion = System.DateTime.Now.ToString("yyyyMMddHHmm");
    var MigrationFileName = MigrationVersion + "-" + MigrationName + ".sql";
    var MigrationFilePath = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(Host.TemplateFile), "", MigrationFileName));
#>

My T4 template goes here

<#
SaveOutput(MigrationFilePath);
IncludeInCurrentProject(MigrationFilePath)
    .Open(Constants.vsViewKindPrimary)
    .Activate();
#>
<#+
    private void SaveOutput(string filename)
    {
        File.WriteAllText(filename, GenerationEnvironment.ToString());
        GenerationEnvironment.Clear();
    }

    private ProjectItem IncludeInCurrentProject(string filename)
    {
        return CurrentProject.ProjectItems.AddFromFile(filename);
    }

    private Project CurrentProject
    {
        get 
        {
            if (_currentProject == null) {
                var serviceProvider = (IServiceProvider)this.Host;
                var dte = serviceProvider.GetService(typeof(DTE)) as DTE;

                foreach(Project project in dte.Solution.Projects) 
                {
                    // workaround. dte.Solution.FindProjectItem(Host.TemplateFile).ContainingProject didn't work
                     string projectPath = project.FullName.Replace("\\" + project.FileName, string.Empty);
                     if(Host.TemplateFile.Contains(projectPath))
                     {
                        _currentProject = project;
                        break;
                     }
                }
            }
            return _currentProject;
        }
    }


    private Project _currentProject;
#>

How can I include my generated files on the same folder of the T4 template file?

Upvotes: 2

Views: 699

Answers (2)

Renan Ara&#250;jo
Renan Ara&#250;jo

Reputation: 3631

The CurrentProject.ProjectItems always references the ProjectItems in the root.

I used the file path to navigate through the ProjectItems and call the AddFromFile on the correct one. You can check the GetCurrentProjectItem method, this behaviour is implemented there.

Now the template works fine:

<#@ template debug="true" hostspecific="true" language="C#" #>
<#@ output extension=".sql" #>
<#@ assembly name="Microsoft.VisualBasic.dll" #>
<#@ assembly name="EnvDTE" #>
<#@ import Namespace="System.IO" #>
<#@ import Namespace="System.Text.RegularExpressions" #>
<#@ import namespace="EnvDTE" #>

<#
    var migrationName = Microsoft.VisualBasic.Interaction.InputBox("InputBox Label", "Description");

    Regex alphaNumericRegex = new Regex("[^a-zA-Z0-9-_]");
    migrationName = alphaNumericRegex.Replace(migrationName, string.Empty);

    if (string.IsNullOrWhiteSpace(migrationName))
        return "";

    var migrationVersion = System.DateTime.Now.ToString("yyyyMMddHHmm");
    var migrationFileName = migrationVersion + "-InsertCourt" + migrationName + ".sql";
    var migrationFilePath = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(Host.TemplateFile), migrationFileName));
#>

My T4 template goes here

<#
SaveOutput(migrationFilePath);
IncludeInCurrentProject(migrationFilePath)
    .Open(Constants.vsViewKindPrimary)
    .Activate();
#>
<#+
    private void SaveOutput(string filename)
    {
        File.WriteAllText(filename, GenerationEnvironment.ToString());
        GenerationEnvironment.Clear();
    }

    private ProjectItem IncludeInCurrentProject(string filename)
    {
        ProjectItem item = GetCurrentProjectItem().ProjectItems.AddFromFile(filename);
        item.Properties.Item("BuildAction").Value = "None";
        return item;
    }

    private ProjectItem GetCurrentProjectItem()
    {

        var serviceProvider = (IServiceProvider)this.Host;
        var dte = serviceProvider.GetService(typeof(DTE)) as DTE;

        string templateDirectory = Path.GetDirectoryName(Host.TemplateFile);
        string foldersString = templateDirectory.Replace(Path.GetDirectoryName(GetCurrentProject().FullName), string.Empty);
        string[] foldersArray = foldersString.Split(new [] {"\\"}, StringSplitOptions.RemoveEmptyEntries);

        foreach(Project project in dte.Solution.Projects) 
        {
            ProjectItems currentProjectItems = null;

            foreach(string folder in foldersArray)
            {
                if(currentProjectItems == null) 
                {
                    currentProjectItems = project.ProjectItems;
                }

                foreach(ProjectItem projectItem in currentProjectItems) 
                {
                    if(projectItem.Name == folder) 
                    {
                        if(Array.IndexOf(foldersArray, folder) == foldersArray.Length - 1)
                        {
                            return projectItem;
                        }
                        currentProjectItems = projectItem.ProjectItems;
                        break;
                    }
                }
            }
        }
        return null;
    }

    private Project GetCurrentProject()
    {
        var serviceProvider = (IServiceProvider)this.Host;
        var dte = serviceProvider.GetService(typeof(DTE)) as DTE;

        foreach(Project project in dte.Solution.Projects) 
        {
            // workaround. dte.Solution.FindProjectItem(Host.TemplateFile).ContainingProject didn't work
            string directory = Path.GetDirectoryName(project.FullName);
            if(Host.TemplateFile.Contains(directory))
            {
                return project;
            }
        }
        return null;
    }


    private ProjectItem _currentProjectItem;
#>

Upvotes: 1

Pedro Brito
Pedro Brito

Reputation: 263

When you call the IncludeInCurrentProject the input should be the path + filename.

Upvotes: 0

Related Questions