T4 Template は、一つのテンプレートファイルにつき一つのファイルを出力します。しかし複数のファイルを出力したい場合もあります。そこで、複数のファイルを出力する方法を紹介します。

 尚、ここで紹介する内容は Oleg Sych さんのブログの「How to generate multiple outputs from single T4 template」を参考にしています。

GenerationEnvironment プロパティを使用

 T4 Template の機能だけでは、一つのファイルしか出力できないので、標準のクラスライブラリ(File.WriteAllText メソッドなど)を使用して複数のファイルを出力するようにします。ファイルの出力に必要な情報は、出力先のパスとファイルの内容です。

 ファイルの出力先は、テンプレートファイルと同じ場所に出力したいと思います。Host.TemplateFile プロパティからテンプレートファイルのパスを取得できます。Host プロパティを有効にするために、Template ディレクティブで hostspecific パラメータを True にします。

 ファイルの内容は、T4 Template が標準で出力する文字列を GenerationEnvironment プロパティから取得することができます。テキストブロックなどで Write メソッドが呼び出されるたびに GenerationEnvironment に出力文字列が蓄積されていきます。一つのファイルの内容分が GenerationEnvironment に蓄積されたときに、それをファイルの内容として使用します。

 ファイルを出力したら、次のファイルを出力するために GenerationEnvironment プロパティを空にします。

テンプレートファイル(SaveOutput.tt)
<#@ template language="C#v3.5" hostspecific="true" #>
<#@ import namespace="System.IO" #>
<#+
  void SaveOutput(string outputFileName) {
    string templateDirectory = Path.GetDirectoryName(Host.TemplateFile);
    string outputFilePath = Path.Combine(templateDirectory, outputFileName);
    File.WriteAllText(outputFilePath, this.GenerationEnvironment.ToString()); 

    this.GenerationEnvironment.Length = 0;
  }
#>

 このテンプレートファイルを使用すれば、次のようにファイルを出力することができます。

テンプレートファイル(TestTemplate.tt)
<#@ include file="SaveOutput.tt" #>
<#
    GenerateFile1();
    SaveOutput("File1.txt");  

    GenerateFile2();
    SaveOutput("File2.txt");
#>

<#+ void GenerateFile1() { #>
File1 を作成しました。
<#+ } #>

<#+ void GenerateFile2() { #>
File2 を作成しました。
<#+ } #>
作成されたファイル(File1.txt)

File1 を作成しました。
 
作成されたファイル(File2.txt)
File2 を作成しました。
 

 作成されたファイルは、プロジェクトへ自動で追加されません。手動で追加する必要があります。

 GenerateFile1 メソッドを別のテンプレートファイルに定義すれば、一つのテンプレートファイルから一つのファイルを出力しているように見せることもできます。

Engine クラスを使用

 T4 Template を動作させるエンジンである Engine クラスを、テンプレートファイルから使用することができます。Engine クラスを使用すれば、指定したテンプレートファイルの内容から出力する文字列を取得することができます。

テンプレートファイル(ProcessTemplate.tt)
<#@ template language="C#v3.5" hostSpecific="true" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="Microsoft.VisualStudio.TextTemplating" #>
<#+
  void ProcessTemplate(string templateFileName, string outputFileName) {
    string templateDirectory = Path.GetDirectoryName(Host.TemplateFile);
    string outputFilePath = Path.Combine(templateDirectory, outputFileName);  

    string template = File.ReadAllText(Host.ResolvePath(templateFileName));
    Engine engine = new Engine();
    string output = engine.ProcessTemplate(template, Host);  

    File.WriteAllText(outputFilePath, output);
  }
#>

 このテンプレートファイルを使用すれば、次のようにファイルを出力することができます。

テンプレートファイル(TestTemplate.tt)
<#@ include file="ProcessTemplate.tt" #>
<#
    ProcessTemplate("File1.tt", "File1.txt");
    ProcessTemplate("File2.tt", "File2.txt");
#>
テンプレートファイル(File1.tt)
File1 を作成しました。
テンプレートファイル(File2.tt)
File2 を作成しました。
作成されたファイル(File1.txt)

File1 を作成しました。
 
作成されたファイル(File2.txt)
File2 を作成しました。
 

 この方法でも作成されたファイルは、プロジェクトへ自動で追加されません。手動で追加する必要があります。

 この方法では、GenerationEnvironment プロパティの状態を気にする必要がないので、処理がスッキリします。

作成したファイルをプロジェクトに追加する

 紹介した二つの方法では、作成したファイルを手動でプロジェクトに追加する必要がありました。ファイルを自動でプロジェクトに追加するには、DTE オブジェクトを使用します。これは IDE の操作に使用し、主にアドインなどを作成する時に使用します。説明するより使ってみたほうがわかると思いますので、ソースをこちらからダウンロードしてください。

 ソースの "MultiOutput.tt" ファイルには、紹介した複数のファイルを出力する処理が全てまとめられています。GenerationEnvironment プロパティを使う SaveOutput メソッドでも、Engine クラスを使う ProcessTemplate メソッドでも、ファイルを出力すれば自動でファイルがプロジェクトに追加されるようになっています。

 このテンプレートファイルを使用すれば、次のようにファイルを出力することができます。

テンプレートファイル(TestTemplate.tt)
<#@ include file="MultiOutput.tt" #>
<#@ include file="File1.tt" #>
<#
    GenerateFile1("パラメータ 1");
    SaveOutput("File1.txt");

    ProcessTemplate("File2.tt","File2.txt");

    DeleteOldOutputs();
#>
テンプレートファイル(File1.tt)
<#+ void GenerateFile1(string parameter) { #>
File1 を作成しました。 <#= parameter #>
<#+ } #>
テンプレートファイル(File2.tt)
File2 を作成しました。
作成されたファイル(File1.txt)

File1 を作成しました。 パラメータ 1
 
作成されたファイル(File2.txt)
File2 を作成しました。
 
T4 Template 画像6

 DeleteOldOutputs メソッドを呼び出していますが、これは File1.tt などが標準で出力するファイルを削除する処理です。File1.tt が出力する内容は Engine クラスを使用して別に作成しているので、標準で出力するファイルは不要になります。