2

次の MSBuild プロジェクト ファイルがあります。

<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Deploy" ToolsVersion="4.0">
  <ItemGroup>
    <Base Include="$(MSBuildProjectDirectory)\.." />
  </ItemGroup>

  <PropertyGroup>
    <BaseDirectory>@(Base->'%(FullPath)')</BaseDirectory>
    <DeployDirectory>$(BaseDirectory)\Deploy</DeployDirectory>
    <Configuration>Release</Configuration>
  </PropertyGroup>

  <Target Name="Deploy" DependsOnTargets="Hello;Clean;Build" />

  <Target Name="Hello">
    <Message Text="Hello world. BaseDirectory=$(BaseDirectory), DeployDirectory=$(DeployDirectory)" />
  </Target>

  <Target Name="Clean">
    <RemoveDir Directories="$(DeployDirectory)" />
  </Target>

  <Target Name="Build">
    <MSBuild Projects="$(BaseDirectory)\DebugConsoleApp\DebugConsoleApp.csproj" Properties="Configuration=$(Configuration);OutputPath=$(DeployDirectory)" ContinueOnError="false" />
  </Target>

</Project>

そして、実行するとエラーが発生します:

C:\Repositories\Project\Build\Build.proj(22,16): エラー MSB4012: 式 "@(Base->'%(FullPath)')\Deploy" は、このコンテキストでは使用できません。アイテム リストは、アイテム リストが期待される他の文字列と連結できません。複数の項目リストを区切るには、セミコロンを使用します。

このエラーが発生する理由と回避方法を教えてください。パス内を取り除く必要があるため、Base内でアイテムを使用し、メタデータを介してそれを実行できるようにします。ちょうどそれを使用すると、すべてが正常に機能しますが、これはすべてのパスにあります。ItemGroup..Items%FullPathPropertyGroup..

4

2 に答える 2

3

ボンネットの下で何が起こっているのかを正確に伝えるのは困難です。私は MSBuild を使用していないため、実際の実装についてはあまり詳しくありません。100% 正しい答えを得るには、MSBuild 開発者が必要です。しかし、これが私が想定していることです(読んでください:これの残りの部分には私の推測が含まれています)。

ステートメントを使用するときに対象とする内部

Projects="$(BaseDirectory)\DebugConsoleApp\DebugConsoleApp.csproj"

MSBuild は、プロパティ展開 $(BaseDirectory) が使用されていること、および MSBuild のプロジェクトのパラメーター タイプが配列であることを認識します。また、MSBuild は、BaseDirectory が項目を含むプロパティであることを認識します。これらのプロパティは、通常のプロパティとは異なります。それらを「仮想プロパティ」と考えることができます (はい、私はその用語を作りました)。値を検索する代わりにこれらのプロパティを使用すると、インラインで置換が行われます。したがって、 Projects 属性は次のように変更されます。

Projects="@(Base->'%(FullPath)')\DebugConsoleApp\DebugConsoleApp.csproj" 

Projects は配列であるため、MSBuild は指定された式に対して変換を実行しようとします。これは有効な変換ではないため、エラーが発生します。あなたが受け取っているエラーはどれですか。

これを回避するには、ビルド ターゲットを次のように変更します。

<Target Name="Build">
  <PropertyGroup>
    <_BaseDir>$(BaseDirectory)</_BaseDir>
    <_DeployDir>@(Base->'%(FullPath)')</_DeployDir>
  </PropertyGroup>

  <Message Text="_BaseDir: $(_BaseDir)"/>
  <Message Text="DeployDirectory: $(DeployDirectory)"/>

  <MSBuild Projects="$(_BaseDir)\DebugConsoleApp\DebugConsoleApp.csproj"
            Properties="Configuration=$(Configuration);OutputPath=$(_Tmp2)"
            ContinueOnError="false" />
  <!--<MSBuild Projects="$(BaseDirectory)\DebugConsoleApp\DebugConsoleApp.csproj" 
            Properties="Configuration=$(Configuration);OutputPath=$(DeployDirectory)" 
            ContinueOnError="false" />-->
</Target>

このアプローチでは、ターゲット自体にプロパティ グループを作成し、それらの「仮想プロパティ」の値を新しいプロパティに割り当てました。これらの新しいプロパティは仮想プロパティではなく、実際のプロパティであるため、問題なく期待どおりに使用できます。

「なぜメッセージタスクはWTFで動作するのですか?!!!」という質問に進みましょう。 Hello ターゲット内には、次のものがあります。

<Message Text="Hello world. BaseDirectory=$(BaseDirectory), DeployDirectory=$(DeployDirectory)" />

これは問題なく動作します。前に、これらの仮想プロパティは本質的に、それらを裏付ける定義にインラインで置き換えられると述べたので、実際にはそうなるでしょう。

<Message Text="Hello world. BaseDirectory=@(Base->'%(FullPath)'), DeployDirectory=@(Base->'%(FullPath)')\Deploy" />

OK、その考えを保持します。

MSBuild タスクのTextプロパティは、スカラー値である文字列として定義されます。MSBuild タスクの Projects プロパティが ITaskItem[] として定義されていることを思い出してください。これは配列であり、ベクトル値であるためです。ベクトル値プロパティ内でa@(...)が見つかった場合、式全体が項目変換として使用されます。この場合、ステートメント@(Base->'%(FullPath)')\DebugConsoleApp\DebugConsoleApp.csprojは有効な変換式ではありません。「@(..)」がスカラー値プロパティ宣言内で見つかった場合、値は文字列にフラット化されます。したがって、「@(...)」の各インスタンスが処理され、単一の文字列値にフラット化されます。複数の値がある場合は、区切り記号が使用されます。

うまくいけば、それはあなたが見ている動作を説明するものであり、実際にはバグである可能性があります. http://connect.microsoft.com/でログに記録すると、MSBuild チームがトリアージします。

仮想プロパティの詳細について は前述しましたが、これらの仮想プロパティは、値が検索されないという意味で通常のプロパティのようには動作しませんが、代わりに $(...) の使用がプロパティ式に置き換えられます。私の言葉を鵜呑みにしないで、自分の目で確かめてください。これが私が作成したサンプルファイルです

<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
  <ItemGroup>
    <MyItem Include="C:\temp\01.txt"></MyItem>
  </ItemGroup>

  <PropertyGroup>
    <MyProperty>@(MyItem->'%(FullPath)')</MyProperty>
  </PropertyGroup>

  <Target Name="Demo">
    <Message Text="MyProperty: $(MyProperty)" />
    <!-- Add to the item -->
    <ItemGroup>
      <MyItem Include="C:\temp\01.txt"></MyItem>
    </ItemGroup>
    <Message Text="MyProperty: $(MyProperty)" />
  </Target>

</Project>

ここでは、宣言された項目リストMyItemと依存プロパティMyPropertyがあります。Demo ターゲット内で、MyProperty の値を出力してから、別の値を MyItem アイテム リストに追加し、MyProperty の値を再度出力します。これが結果です。

PS C:\temp\MSBuild\SO> msbuild .\Build.proj /nologo
Build started 4/26/2011 10:17:08 PM.
Project "C:\temp\MSBuild\SO\Build.proj" on node 1 (default targets).
First:
  MyProperty: C:\temp\01.txt
  MyProperty: C:\temp\01.txt;C:\temp\01.txt
Done Building Project "C:\temp\MSBuild\SO\Build.proj" (default targets).

ご覧のとおり、私が述べた方法で動作します。

于 2011-04-27T05:18:23.567 に答える
2

あなたは評価の順序と戦っています。プロパティ グループ宣言を「Hello」ターゲット内に移動すると、期待どおりに動作します。さらに良いのは、それを独自のターゲットに移動し、実行前に評価を実行する必要がある他のターゲットの DependsOnTargets にそのターゲットを設定するか、逆に、それらのターゲットを新しいターゲットの「BeforeTargets」として設定することです。

(編集)

これはすべてのターゲットで機能します。

<ItemGroup>
  <Base Include="$(MSBuildProjectDirectory)\.." />
</ItemGroup>

<Target Name="Deploy" DependsOnTargets="Hello;Clean;Build" />

<Target Name="CalcProps">
  <PropertyGroup>
    <BaseDirectory>@(Base->'%(FullPath)')</BaseDirectory>
    <DeployDirectory>$(BaseDirectory)\Deploy</DeployDirectory>
    <Configuration>Release</Configuration>
  </PropertyGroup>
</Target>

<Target Name="Hello" DependsOnTargets="CalcProps">
  <Message
    Text="Hello world. BaseDirectory=$(BaseDirectory), DeployDirectory=$(DeployDirectory)"
    />
</Target>

<Target Name="Clean" DependsOnTargets="CalcProps">
  <RemoveDir Directories="$(DeployDirectory)" />
</Target>

<Target Name="Build" DependsOnTargets="CalcProps">
  <MSBuild 
    Projects="$(BaseDirectory)\DebugConsoleApp\DebugConsoleApp.csproj" 
    Properties="Configuration=$(Configuration);OutputPath=$(DeployDirectory)" 
    ContinueOnError="false"
    />  
</Target>

MSBuild タスクへの Projects 引数の評価は、ITaskItem[] 型であるため、$(BaseDirectory) で未評価の文字列を使用している可能性があり、それは項目変換であるため、変換されるアイテムに複数のメンバーがある場合 (この場合はありませんが)。メッセージ タスクで同じプロパティを使用すると、System.String 型の引数に渡されます。この引数の評価シーケンスは異なる場合があります。

于 2011-04-16T22:42:57.583 に答える