views:

526

answers:

2

I am trying to get a list of all unit test assemblies under the root of my project. I can do this as follows:

<CreateItem Include="**\bin\**\*.UnitTest.*.dll">
   <Output TaskParameter="Include" ItemName="Items"/>
</CreateItem>

However, this will find the same DLLs multiple times since they exist in multiple sub-directories. Is there an easy way for me to normalize based on item metadata (ie. the file name and extension) so that I get a list of unique unit test DLLs? Or do I have to resort to writing my own task?

Thanks

+1  A: 

I had a good search online and couldn't find any way of doing this. If anyone knows a clean built-in way then please let me know. In the meantime, I wrote a simple task to do the job. The usage looks like this:

<NormalizeByMetadata Items="@(ItemsToNormalize)" MetadataName="Filename">
    <Output TaskParameter="NormalizedItems" ItemName="MyNormalizedItems"/>
</NormalizeByMetadata>

After the above task has executed, MyNormalizedItems will contain only those items from ItemsToNormalize that have a unique value for the Filename metadata. If two or more items have the same value for their Filename metadata, the first match will be included in the output.

The code for the MSBuild task is:

public class NormalizeByMetadata : Task
{
    [Required]
    public ITaskItem[] Items
    {
     get;
     set;
    }

    [Required]
    public string MetadataName
    {
     get;
     set;
    }

    [Output]
    public ITaskItem[] NormalizedItems
    {
     get;
     private set;
    }

    public override bool Execute()
    {
     NormalizedItems = Items.Distinct(new ItemEqualityComparer(MetadataName)).ToArray();
     return true;
    }

    private sealed class ItemEqualityComparer : IEqualityComparer<ITaskItem>
    {
     private readonly string _metadataName;

     public ItemEqualityComparer(string metadataName)
     {
      Debug.Assert(metadataName != null);
      _metadataName = metadataName;
     }

     public bool Equals(ITaskItem x, ITaskItem y)
     {
      if (x == null || y == null)
      {
       return x == y;
      }

      var xMetadata = x.GetMetadata(_metadataName);
      var yMetadata = y.GetMetadata(_metadataName);
      return string.Equals(xMetadata, yMetadata);
     }

     public int GetHashCode(ITaskItem obj)
     {
      if (obj == null)
      {
       return 0;
      }

      var objMetadata = obj.GetMetadata(_metadataName);
      return objMetadata.GetHashCode();
     }
    }
}

HTH, Kent

Kent Boogaart
+1  A: 

The MSBuild Extension Pack contains the task MSBuildHelper, supporting the command RemoveDuplicateFiles.

<CreateItem Include="**\bin\**\*.UnitTest.*.dll">
 <Output TaskParameter="Include" ItemName="Items"/>
</CreateItem>
<MSBuild.ExtensionPack.Framework.MsBuildHelper TaskAction="RemoveDuplicateFiles" InputItems1="@(Items)">
 <Output TaskParameter="OutputItems" ItemName="Items"/>
</MSBuild.ExtensionPack.Framework.MsBuildHelper>
Thomas Freudenberg
Sounds useful - thanks. Will try this next time I need it.
Kent Boogaart