Posts Tagged ‘MSBuild’

Change the default printing-output of an item @item

Normally, @(Item) is translated as a concatenation of the identity metadata of the elements in the item list Item – these values are delimitated by comma ‘,’  . We can customize this by using:

@(item->’…%(metaData)…’, ‘delim’ )

E.g., I often use the delimiter is %0A stands for a newline.

The difference between XmlRead vs XmlPeek ?

XmlPeek:
Read inner text.
Built-in support by MSBuild in dotNet Framework.

XmlRead:
Do not read inner text.
Need MSBuild Community Task.

Example:

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="MAIN" ToolsVersion="4.0">
  <!--<Import Project="$(MSBuildExtensionsPath)\MSBuildContrib\MSBuildContrib.Tasks"/>-->
  <!--<Import Project="$(MSBuildExtensionsPath)\ExtensionPack\MSBuild.ExtensionPack.tasks"/>-->
  <Import Project="$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets" />

  <Target Name="MAIN">
    <XmlPeek Namespaces="&lt;Namespace Prefix='msb' Uri='http://schemas.microsoft.com/developer/msbuild/2003'/&gt;"
             XmlInputPath="$(MSBuildProjectFile)"
             Query="/msb:Project/msb:Import">
      <Output TaskParameter="Result" ItemName="Peeked" />
    </XmlPeek>
    <Message Text="111: @(Peeked)"/>

    <XmlRead Prefix='msb' Namespace='http://schemas.microsoft.com/developer/msbuild/2003'
             XmlFileName="$(MSBuildProjectFile)"
             XPath="/msb:Project/msb:Import" >
      <Output TaskParameter="Value" ItemName="Peeked2" />
    </XmlRead>
    <Message Text="222: @(Peeked2)"/>
  </Target>
</Project>

Target Batching notes

When batching a target, we can not batch a series of calltarget since it will batch each calltarget one by one. See below sample script to see it.

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="MAIN" >
  <Target Name="MAIN">
    <CallTarget Targets="t" />
    <CallTarget Targets="tt" />
    <CallTarget Targets="ttB" />
  </Target>

  <Target Name="t">
    <ItemGroup>
      <aa Include="1;22;333" />
    </ItemGroup>
  </Target>

  <Target Name="tt" Outputs="%(aa.Identity)">
    <Message Text="This batching won't work!" />
    <CallTarget Targets="ttt" />
    <CallTarget Targets="tttt" />
  </Target>

  <Target Name="ttt">
    <Message Text="1:%(aa.Identity)" />
  </Target>

  <Target Name="tttt">
    <Message Text="2:%(aa.Identity)" />
  </Target>

  <Target Name="ttB" Outputs="%(aa.Identity)">
    <Message Text="Batch it this way will work!" />
    <Message Text="1B:%(aa.Identity)" />
    <Message Text="2B:%(aa.Identity)" />
  </Target>
</Project>

For those who insist on calling a target in batching mode, then is the handy tool. See below sample script.

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="MAIN" >
  <!--<Import Project="$(MSBuildExtensionsPath)\MSBuildContrib\MSBuildContrib.Tasks"/>-->
  <!--<Import Project="$(MSBuildExtensionsPath)\ExtensionPack\MSBuild.ExtensionPack.tasks"/>-->
  <!--<Import Project="$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets" />-->

  <Target Name="MAIN">
    <CallTarget Targets="t" />
    <CallTarget Targets="tt" />
    <CallTarget Targets="ttB" />
  </Target>

  <Target Name="t">
    <ItemGroup>
      <aa Include="1;22;333" />
    </ItemGroup>
  </Target>

  <Target Name="tt" Outputs="%(aa.Identity)">
    <Message Text="This batching won't work!" />
    <CallTarget Targets="ttt" />
    <CallTarget Targets="tttt" />
  </Target>

  <Target Name="ttt">
    <Message Text="1:%(aa.Identity)" />
  </Target>

  <Target Name="tttt">
    <Message Text="2:%(aa.Identity)" />
  </Target>

  <Target Name="ttB" Outputs="%(aa.Identity)">
    <Message Text="Batch it this way will work!" />
    <Message Text="1B:%(aa.Identity)" />
    <Message Text="2B:%(aa.Identity)" />

    <Message Text="Still need to call another target? Belowings are the work-around." />
    <PropertyGroup>
      <aaList>@(aa)</aaList>
    </PropertyGroup>
    <MSBuild Projects="$(MSBuildProjectFile)"
             Targets="tttB"
             Properties="aaList=$(aaList)"
             />
  </Target>
  <Target Name="tttB">
    <CallTarget Targets="tttB1" />
    <CallTarget Targets="tttB2" />
  </Target>
  <Target Name="tttB1">
    <ItemGroup>
      <aa Include="$(aaList.Split(';'))" />
    </ItemGroup>
  </Target>
  <Target Name="tttB2" Outputs="%(aa.Identity)">
    <Message Text="3B:%(aa.Identity)" />
  </Target>

</Project>

Enjoy!

[MSBuild] How to implement nested loops

Let’s say we have nested loops through two list aa={1; 22; 333} and bb={a; bb; ccc} like:

foreach (i in aa) {
  foreach (j in bb) {
    print "i=? j=??";
  }
}

The solution is quite complicated and as below.

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="MAIN" >
  <!--<Import Project="$(MSBuildExtensionsPath)\MSBuildContrib\MSBuildContrib.Tasks"/>-->
  <!--<Import Project="$(MSBuildExtensionsPath)\ExtensionPack\MSBuild.ExtensionPack.tasks"/>-->

  <Target Name="MAIN" >
    <CallTarget Targets="t" />
    <CallTarget Targets="tt" />
  </Target>

  <Target Name="t">
    <ItemGroup>
      <aa Include="1;22;333" />
    </ItemGroup>
  </Target>

  <Target Name="tt" Outputs="%(aa.Identity)">
    <Message Text="aa %(aa.Identity)" />

    <MSBuild Projects="$(MSBuildProjectFile)" Targets="ttt" Properties="aa=%(aa.Identity)" />
  </Target>

  <Target Name="ttt">
    <CallTarget Targets="ttt_1" />
    <CallTarget Targets="tttt" />
  </Target>

  <Target Name="ttt_1">
    <ItemGroup>
      <bb Include="a;bb;ccc" />
    </ItemGroup>
  </Target>

  <Target Name="tttt" Outputs="%(bb.Identity)">
    <Message Text="aa $(aa)" />
    <Message Text="bb %(bb.Identity)" />
  </Target>

</Project>

And for an even more complex nested loops (3 nested), see sample script below.

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="MAIN" >
  <Target Name="MAIN">
    <CallTarget Targets="t"/>
    <CallTarget Targets="tt"/>
  </Target>

  <Target Name="t" Outputs="%(aaList.Identity)">
    <ItemGroup>
      <aaList Include="1; 22; 333;" />
    </ItemGroup>
    <Message Text="Foreach aa in aaList:" />
  </Target>

  <Target Name="tt">
    <MSBuild Projects="$(MSBuildProjectFile)"
             Targets="ttt"
             Properties="aa=%(aaList.Identity)"
    />
  </Target>

  <Target Name="ttt">
    <CallTarget Targets="tttt" />
    <CallTarget Targets="ttttt" />
  </Target>

  <Target Name="tttt">
    <ItemGroup>
      <bbList Include="a; bb; ccc;" />
    </ItemGroup>
  </Target>

  <Target Name="ttttt" Outputs="%(bbList.Identity)">
    <Message Text="aa:$(aa)" />
    <MSBuild Projects="$(MSBuildProjectFile)"
             Targets="t2"
             Properties="bb=%(bbList.Identity); aa=$(aa)"
    />
  </Target>
  <Target Name="t2">
    <CallTarget Targets="tt2" />
    <CallTarget Targets="ttt2" />
  </Target>

  <Target Name="tt2">
    <ItemGroup>
      <ccList Include="x; yy; zzz;" />
    </ItemGroup>
  </Target>
  <Target Name="ttt2" Outputs="%(ccList.Identity)">
    <Message Text="aa:$(aa) bb:$(bb)" />
    <Message Text="cc:%(ccList.Identity)" />
  </Target>

</Project>

[MSBuild] Note when working with Condition

The < and > comparision may be tricky!

Example:

<Target Name="ttt">
<Message Text="1" Condition="2>1" />
<Message Text="22" Condition="2&gt;1" />
<Message Text="a" Condition="2>=2" />
<Message Text="bb" Condition="2&gt;=2" />

<Message Text="1x" Condition="1<2" /> <!--error-->
<Message Text="22x" Condition="1&lt;2" />
<Message Text="ax" Condition="2<=2" /> <!--error-->
<Message Text="bbx" Condition="2&lt;=2" />
</Target>

[MSBuild] Custom Tasks

MSBuild Community Tasks

  1. Download at msbuildtasks.tigris.org
  2. Usage:
<Import Project="$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets"/>

MSBuild Extension Pack

  1. Download at MSBuild Extension Pack
  2. Usage:
<Import Project="$(MSBuildExtensionsPath)\ExtensionPack\MSBuild.ExtensionPack.tasks"/>

MSBuild SDC Tasks

  1. Download at SDC Tasks Library
  2. Usage: You need to copy the task manually.

[MSBuild] Caution on properties's value when calling another target

Cautions:

  1. Property values updated in a called target can be visible in another called target but not in the current target.
  2. Programmatic properties & items are not emitted (can not be passed) to CallTarget invocation down the stream until the target that created these properties & items completes its execution. In order to pass a newly created property to subsequent targets, you need to create it outside the context of the current target.  In other words, you need to create a new target and place the creation in that target.