次の方法で共有


MSBuild インライン タスク

MSBuild タスクは、通常、 ITask インターフェイスを実装するクラスをコンパイルすることによって作成されます。 詳細については、「タスクの」を参照してください。

コンパイル済みタスクを作成するオーバーヘッドを回避する場合は、プロジェクト ファイルまたはインポートされたファイル内にタスクをインラインで作成できます。 タスクをホストするために別のアセンブリを作成する必要はありません。 インライン タスクを使用すると、ソース コードを追跡しやすくなり、タスクのデプロイも簡単になります。 ソース コードは、MSBuild プロジェクト ファイルまたはインポートされたファイル (通常は .targets ファイル) に統合されます。

インライン タスクは、 コード タスク ファクトリを使用して作成します。 現在の開発では、ではなく CodeTaskFactory を使用してください。 CodeTaskFactory は、最大 4.0 の C# バージョンのみをサポートします。

インライン タスクは、複雑な依存関係を必要としない小規模なタスクの利便性を目的としています。 インライン タスクのデバッグサポートは制限されています。 より複雑なコードを記述する場合、NuGet パッケージを参照する場合、外部ツールを実行する場合、またはエラー状態を発生させる可能性がある操作を実行する場合は、インライン タスクではなくコンパイル済みタスクを作成することをお勧めします。 また、インライン タスクはビルドのたびにコンパイルされるため、ビルドのパフォーマンスに大きな影響を与える可能性があります。

インライン タスクの構造

インライン タスクは UsingTask 要素に含まれています。 インライン タスクとそれを含む UsingTask 要素は、通常、 .targets ファイルに含まれており、必要に応じて他のプロジェクト ファイルにインポートされます。 ここでは、何も行わないが、構文を示す基本的なインライン タスクを示します。

 <!-- This simple inline task does nothing. -->
  <UsingTask
    TaskName="DoNothing"
    TaskFactory="RoslynCodeTaskFactory"
    AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll" >
    <ParameterGroup />
    <Task>
      <Reference Include="" />
      <Using Namespace="" />
      <Code Type="Fragment" Language="cs">
      </Code>
    </Task>
  </UsingTask>

この例の UsingTask 要素には、タスクを記述する 3 つの属性と、それをコンパイルするインライン タスク ファクトリがあります。

  • TaskName属性は、タスクにDoNothingという名前を付けます。

  • TaskFactory属性は、インライン タスク ファクトリを実装するクラスに名前を付けます。

  • AssemblyFile属性は、インライン タスク ファクトリの場所を指定します。 または、 AssemblyName 属性を使用して、インライン タスク ファクトリ クラスの完全修飾名 (通常は $(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll にあります) を指定することもできます。

DoNothing タスクの残りの要素は空であり、インライン タスクの順序と構造を示すために提供されます。 完全な例については、この記事の後半で説明します。

  • ParameterGroup 要素は省略可能です。 指定すると、タスクのパラメーターが宣言されます。 入力パラメーターと出力パラメーターの詳細については、この記事で後述する 「入力パラメーターと出力パラメーター 」を参照してください。

  • Task要素は、タスクのソース コードを記述し、含みます。

  • Reference要素は、コードで使用している .NET アセンブリへの参照を指定します。 この要素の使用は、Visual Studio でプロジェクトへの参照を追加することと同じです。 Include属性は、参照されるアセンブリのパスを指定します。 mscorlib、.NET Standard、 Microsoft.Build.FrameworkMicrosoft.Build.Utilities.Core のアセンブリと、依存関係として推移的に参照されるアセンブリは、 Referenceなしで使用できます。

  • Using要素には、アクセスする名前空間が一覧表示されます。 この要素は、C# の using ディレクティブと同じです。 Namespace属性は、含める名前空間を指定します。 インライン コードに using ディレクティブを配置することはできません。これは、そのコードがメソッド本体に配置され、 using ディレクティブが許可されないためです。

Reference および Using 要素は言語に依存しません。 インライン タスクは、Visual Basic または C# で記述できます。

Task要素に含まれる要素は、タスク ファクトリ (この場合はコード タスク ファクトリ) に固有です。

コード要素

Task要素内に表示される最後の子要素は、Code要素です。 Code要素は、タスクにコンパイルするコードを格納または検索します。 Code要素に含める内容は、タスクの記述方法によって異なります。

Language属性は、コードを記述する言語を指定します。 C# の場合は cs 、Visual Basic の場合は vb 値を使用できます。

Type属性は、Code要素で見つかったコードの種類を指定します。

  • Typeの値がClassの場合、Code要素には、ITask インターフェイスから派生するクラスのコードが含まれます。

  • Typeの値がMethod場合、コードは、Execute インターフェイスのITask メソッドのオーバーライドを定義します。

  • Typeの値がFragment場合、コードはExecute メソッドの内容を定義しますが、シグネチャやreturnステートメントは定義しません。

通常、コード自体は、 <![CDATA[ マーカーと ]]> マーカーの間に表示されます。 コードは CDATA セクションにあるため、予約文字 ("<" や ">" など) のエスケープについて心配する必要はありません。

または、Source要素のCode属性を使用して、タスクのコードを含むファイルの場所を指定することもできます。 ソース ファイル内のコードは、 Type 属性で指定された型である必要があります。 Source属性が存在する場合、Typeの既定値はClassSourceが存在しない場合、既定値はFragment

ソース ファイルでタスク クラスを定義する場合、クラス名は、対応する TaskName 要素の属性と一致する必要があります。

HelloWorld

単純なインライン タスクの例を次に示します。 HelloWorld タスクは、既定のエラー ログ デバイス (通常はシステム コンソールまたは Visual Studio 出力 ウィンドウ) に "Hello, world!" と表示されます。

<Project>
  <!-- This simple inline task displays "Hello, world!" -->
  <UsingTask
    TaskName="HelloWorld"
    TaskFactory="RoslynCodeTaskFactory"
    AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll" >
    <ParameterGroup />
    <Task>
      <Using Namespace="System"/>
      <Using Namespace="System.IO"/>
      <Code Type="Fragment" Language="cs">
<![CDATA[
// Display "Hello, world!"
Log.LogError("Hello, world!");
]]>
      </Code>
    </Task>
  </UsingTask>
</Project>

HelloWorld タスクを HelloWorld.targets という名前のファイルに保存し、次のようにプロジェクトから呼び出すことができます。

<Project>
  <Import Project="HelloWorld.targets" />
  <Target Name="Hello">
    <HelloWorld />
  </Target>
</Project>

入力パラメーターと出力パラメーター

インライン タスク パラメーターは、 ParameterGroup 要素の子要素です。 すべてのパラメーターは、それを定義する要素の名前を受け取ります。 次のコードでは、パラメーター Textを定義します。

<ParameterGroup>
  <Text />
</ParameterGroup>

パラメーターには、次の属性が 1 つ以上含まれる場合があります。

  • Required は、既定で false される省略可能な属性です。 true場合、パラメーターは必須であり、タスクを呼び出す前に値を指定する必要があります。
  • ParameterType は、既定で System.String される省略可能な属性です。 ChangeTypeを使用して文字列との間で変換できる項目または値である任意の完全修飾型に設定できます。 (つまり、外部タスクとの間で渡すことができる任意の型)。
  • Output は、既定で false される省略可能な属性です。 true場合は、Execute メソッドから戻る前にパラメーターに値を指定する必要があります。

たとえば、

<ParameterGroup>
  <Expression Required="true" />
  <Files ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true" />
  <Tally ParameterType="System.Int32" Output="true" />
</ParameterGroup>

では、次の 3 つのパラメーターが定義されています。

  • Expression は System.String 型の必須の入力パラメーターです。

  • Files は必須の項目リスト入力パラメーターです。

  • Tally は System.Int32 型の出力パラメーターです。

Code要素にTypeまたはFragmentMethod属性がある場合、すべてのパラメーターに対してプロパティが自動的に作成されます。 それ以外の場合、プロパティはタスクのソース コードで明示的に宣言する必要があり、パラメーター定義と完全に一致する必要があります。

インライン タスクをデバッグする

MSBuild は、インライン タスクのソース ファイルを生成し、一時ファイル フォルダー AppData\Local\Temp\MSBuildTemp に GUID ファイル名を含むテキスト ファイルに出力を書き込みます。 出力は通常削除されますが、この出力ファイルを保持するために、環境変数 MSBUILDLOGCODETASKFACTORYOUTPUT を 1 に設定できます。

例 1

次のインライン タスクは、指定されたファイル内のトークンが出現するたびに、指定された値に置き換えられます。

<Project>

  <UsingTask TaskName="TokenReplace" TaskFactory="RoslynCodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll">
    <ParameterGroup>
      <Path ParameterType="System.String" Required="true" />
      <Token ParameterType="System.String" Required="true" />
      <Replacement ParameterType="System.String" Required="true" />
    </ParameterGroup>
    <Task>
      <Code Type="Fragment" Language="cs"><![CDATA[
string content = File.ReadAllText(Path);
content = content.Replace(Token, Replacement);
File.WriteAllText(Path, content);

]]></Code>
    </Task>
  </UsingTask>

  <Target Name='Demo' >
    <TokenReplace Path="Target.config" Token="$MyToken$" Replacement="MyValue"/>
  </Target>
</Project>

例 2

次のインライン タスクでは、シリアル化された出力が生成されます。 この例では、出力パラメーターと参照の使用を示します。

<Project>
  <PropertyGroup>
    <RoslynCodeTaskFactoryAssembly Condition="$(RoslynCodeTaskFactoryAssembly) == ''">$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll</RoslynCodeTaskFactoryAssembly>
  </PropertyGroup>

    <UsingTask 
    TaskName="MyInlineTask" 
    TaskFactory="RoslynCodeTaskFactory" 
    AssemblyFile="$(RoslynCodeTaskFactoryAssembly)">
    <ParameterGroup>
      <Input ParameterType="System.String" Required="true" />
      <Output ParameterType="System.String" Output="true" />
    </ParameterGroup>
    <Task>
      <Reference Include="System.Text.Json" /> <!-- Reference an assembly -->
      <Using Namespace="System.Text.Json" />   <!-- Use a namespace -->
      <Code Type="Fragment" Language="cs">
        <![CDATA[
          Output = JsonSerializer.Serialize(new { Message = Input });
        ]]>
      </Code>
    </Task>
  </UsingTask>

  <Target Name="RunInlineTask">
    <MyInlineTask Input="Hello, Roslyn!" >
      <Output TaskParameter="Output" PropertyName="SerializedOutput" />
    </MyInlineTask>
    <Message Text="Serialized Output: $(SerializedOutput)" />
  </Target>
</Project>