visual-studiomsbuilditemgroup

How can I prepend items in an MSBuild ItemGroup I've created by splitting a string?


I'm parsing a number of items that we're getting from our Visual Studio project file and need to run a command line utility on a number of these files. The parameters I need to use are based on a collection of ancillary files that are often stored as semicolon delimited lists, stored as item metadata within the project file:

<ItemGroup>
  <Content Include="js\main.js">
    <Concatenate>True</Concatenate>
    <MoreFiles>path\to\file-1.js;path\to\file-2.js;path\to-another\file-3.js;path\to-yet-another\file-4.js</MoreFiles>
  </Content>
  <Content Include="js\another.js">
    <Concatenate>True</Concatenate>
    <MoreFiles>path\to\file-5.js;path\to\file-6.js;path\to\file-7.js;path\to-another\file-8.js</MoreFiles>
  </Content>
</ItemGroup>

I'm currently retrieving these using a property, JSFiles, which I build from @(Content), thus:

<ItemGroup>
  <JSFiles Include="@(Content)" KeepMetadata="Concatenate;MoreFiles" Condition="'%(Content.Extension)' = '.js' AND '%(Content.Concatenate)' == 'true'" />
</ItemGroup>

I then use a secondary target, which uses @(JSFiles) as its input:

<Target Name="ConcatenateJS" Inputs="@(JSFiles)" Outputs="%(JSFiles.Identity).concatenatetemp">
  <Message Importance="high" Text="  %(JSFiles.Identity):" />
  <PropertyGroup>
    <MoreFiles>%(JSFiles.MoreFiles)</MoreFiles>
  </PropertyGroup>
  <ItemGroup>
    <MoreFilesArray Include="$(MoreFiles.Split(';'))" />
  </ItemGroup>

  <Message Importance="high" Text="    MoreFiles: %(MoreFilesArray.Identity)" />
</Target>

So far, so good. By this point, I can use a <Message /> task to output the contents of the Split operation, which gives me what I would expect:

ConcatenateJS:
  js\main.js:
    MoreFiles: path\to\file-1.js
    MoreFiles: path\to\file-2.js
    MoreFiles: path\to-another\file-3.js
    MoreFiles: path\to-yet-another\file-4.js
ConcatenateJS:
  js\another.js:
    MoreFiles: path\to\file-5.js
    MoreFiles: path\to\file-6.js
    MoreFiles: path\to\file-7.js
    MoreFiles: path\to-another\file-8.js

However, in order to pass these files to the command line utility correctly, they need to be full paths, so I need to prepend all the items in $(MoreFiles) with $(MSBuildProjectDirectory).

I've tried using a batching operation (using $(MSBuildProjectDirectory)\%(MoreFilesArray.Identity) and even $([System.IO.Path]::Combine($(MSBuildProjectDirectory), %(MoreFilesArray.Identity)) to no avail), and I've tried using <CreateItem> using the AdditionalMetadata attribute, but that didn't seem to work too well for me (though I'm not sure I was using it correctly).

How can I do this so that the output of my build process looks like this:

ConcatenateJS:
  js\main.js:
    MoreFiles: C:\full\path\to\file-1.js
    MoreFiles: C:\full\path\to\file-2.js
    MoreFiles: C:\full\path\to-another\file-3.js
    MoreFiles: C:\full\path\to-yet-another\file-4.js
ConcatenateJS:
  js\another.js:
    MoreFiles: C:\full\path\to\file-5.js
    MoreFiles: C:\full\path\to\file-6.js
    MoreFiles: C:\full\path\to\file-7.js
    MoreFiles: C:\full\path\to-another\file-8.js

Thanks!


Solution

  • MsBuild items has a well-known metadata called 'FullPath' that would display the full path of an item.

    <Message Importance="high" Text="    MoreFiles: %(MoreFilesArray.FullPath)" />