17

Is there a way to get all the metadata keys associated with a given item?

I want to do something like the following.

Given:

  <ItemGroup>
    <MyItems Include="item1">
      <key1>val1</key1>
      <key2>val2</key2>
      <key3>val3</key3>
    </MyItems>
    <MyItems Include="item2">
      <key4>val4</key4>
    </MyItems>
  </ItemGroup>

Be able to determine that item1 has metadata available for key1, key2, and key3, and that item2 has metadata available for key4, without knowing what the names of those keys actually are.

In effect, I'm trying to use the metadata to specify attributes that I have no idea about, and then trying to figure out a way to check to see what attributes have been specified.

Put another way, I believe the metadata of each item is just a hash containing key/value pairs and I'm trying to figure out what all the keys are.

Anyone know how to do this with msbuild?

Thanks

Zain Rizvi
  • 23,586
  • 22
  • 91
  • 133
  • 2
    As far as I know there is no option to list the metadata of an item in MSBuild, but you could write a custom BuildTask the iterates over the reflected Metadata and could list it. – MikeR Jul 15 '13 at 07:19
  • Yup, that was the route I ended up taking – Zain Rizvi Jul 26 '13 at 21:18

1 Answers1

34

I ended up solving this using a custom inline task similar to the following:

<UsingTask
  TaskName="GetMetadataTask"
  TaskFactory="CodeTaskFactory"
  AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll" >
  <ParameterGroup>
    <MyItemGroup ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true" />
    <MetadataString Output="true" />
  </ParameterGroup>
  <Task>
    <Using Namespace="System"/>
    <Code Type="Fragment" Language="cs">
      <![CDATA[
          StringBuilder command = new StringBuilder();
          foreach (ITaskItem item in MyItemGroup )
          {
              command.AppendFormat("ItemName={0}\r\n", item);
              foreach (string parameter in item.MetadataNames)
              {
                  command.AppendFormat("  {0}={1}\r\n", parameter, item.GetMetadata(parameter));
              }
              command.AppendFormat("\r\n");
          }
          MetadataString = command.ToString();
      ]]>
    </Code>
  </Task>
</UsingTask>

Note that the above will also include all the default metadata that MSBuild automatically adds to every item in an itemgroup (like FullPath, RootDir, Filename, etc). In my implementation I added an extra check to ignore those metadata items that I didn't care about

Sample usage:

<GetMetadataTask MyItemGroup="@(YourItemGroup)">
  <Output TaskParameter="MetadataString" PropertyName="YourMetadataString"/>
</GetMetadataTask>
<Message Text="$(YourMetadataString)" />

To see message output in Visual Studio's Output window, you may need to change your MSBuild output verbosity to at least Normal.

Community
  • 1
  • 1
Zain Rizvi
  • 23,586
  • 22
  • 91
  • 133
  • 2
    Could you please show a small example of how to use this task? – Ivan Feb 13 '15 at 13:16
  • 1
    Thank you, that's really helpful! I added sample code for calling it. – EM0 Oct 27 '15 at 13:19
  • 2
    @EM, I went and found your edit, which was rejected (incorrectly, IMO). Here it is for those @EM was trying to help: ` ` – weir Dec 03 '15 at 19:11
  • 3
    The CodeTaskFactory is not supported on dotnet core, but this still works with the new [RoslynCodeTaskFactory](https://github.com/microsoft/msbuild/pull/3175) by making a couple changes: `TaskFactory="RoslynCodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll"` – piedar Mar 05 '20 at 23:52
  • How to get it work for one item? Syntax `%(Item.Identity)` doesn't pass all metadata. – Denis535 Nov 11 '20 at 17:05