10
votes

I’m trying to create a “Files” task item group with a metadata attribute called “TargetPath” populated with the relative path to a file.

Example:
For these paths:

D:\Test\Blah.exe
D:\Test\Config\fun.config
D:\Test\en-US\my.resources.dll

The output should be:

File Target = Blah.exe
File Target = Config\fun.config
File Target = en-US\my.resources.dll

Here is my best attempt... hopefully this makes my question clearer:

<ItemGroup>
     <Files Include="d:\test\**\*" >
        <TargetPath>%(RecursiveDir)%(Filename)%(Extension)</TargetPath>
     </Files>
 </ItemGroup>

<Message Text="File Target = @(Files->'%(TargetPath)')"/>

I'd like "TargetPath" populated correctly... currently it appears to be null or empty. Anyone know what I'm missing?

Edit:

Yes, I realize I can do this:

<Message Text="File Target = @(Files->'%(RecursiveDir)%(Filename)%(Extension)')"/>

However, I'm building up this ItemGroup to use the ResolveManifestFiles MSBuild task, which requires that I build up a TaskItem with the TargetPath metadata attribute to be able to customize that value.

3

3 Answers

8
votes

You're trying to assign dynamic metadata to an itemgroup before it's created. In your example there's no need to create custom metadata since this information is already part of the well-known metadata, so you can just do:

<ItemGroup>
   <Files Include="d:\test\**\*" ></Files>
</ItemGroup>

<Message Text="File Target = @(Files->'%(RecursiveDir)%(Filename)%(Extension)')"/>  

Or:

<Message Text="File Target = %(Files.RecursiveDir)%(Files.Filename)%(Files.Extension)"/>

EDIT:

This example uses CreateItem task to dynamically update the itemgroup:

<ItemGroup>
    <Files Include="d:\test\**\*" ></Files>
</ItemGroup>

<CreateItem
    Include="@(Files)"
    AdditionalMetadata="TargetPath=%(RecursiveDir)%(Filename)%(Extension)">
      <Output TaskParameter="Include" ItemName="Files"/>
</CreateItem>
3
votes

Modern MSBuild doesn't require CreateTask (since .NET 3.5).

You can do it like this:

<ItemGroup>
    <Files Include="d:\test\**\*" />
    <FilesWithMetadata Include="%(Files.Identity)" >
        <TargetPath>%(RecursiveDir)%(Filename)%(Extension)</TargetPath>
    </FilesWithMetadata>
</ItemGroup>
0
votes

I am liking the CreateItem method to use like this:

  <ItemGroup>
    <Reference Include="Newtonsoft.Json, Version=8.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
      <HintPath>..\packages\Newtonsoft.Json.8.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
       <Private>True</Private>
    </Reference>
    ...
  </ItemGroup>

  <CreateItem Include="@(Reference)" Condition="'%(Reference.Private)' == 'True'" AdditionalMetadata="TargetPath=$([System.IO.Path]::GetFileName(%(Reference.HintPath)))">
    <Output TaskParameter="Include" ItemName="DLLFiles"/>
  </CreateItem>

  <Message Text="HintPaths:  &quot;@(DLLFiles->'$(OutputPath)%(TargetPath)')&quot;" Importance="high" />

I am using Transforms to just get only the file name.

Output:

HintPaths: "bin\Release\log4net.dll;bin\Release\Newtonsoft.Json.dll;bin\Release\RabbitMQ.Client.dll;bin\Release\ReflectSoftware.Insight.dll"