How to reference ItemGroup Identity within the definition of the var

232 views Asked by At

I have a target like below. It needs to replace content of a file with new content. I have multiple files I am matching with ItemGroup. I couldn't figure out a way to get this working.

Here is my target definition.

  <ItemGroup>
      <PRSetting Include="$(settings_root)\**\settings_config_*.xml">
        <NewContent>$([System.IO.File]::ReadAllText('%(Identity)')).Replace('[config.version]', '$(PR_Version)'))</NewContent>
      </PRSetting>
  </ItemGroup>
  <Target Name="PrepSettings" Inputs="@(PRSetting)" 
                              Outputs="@(PRSetting->'$out\$Filename.xml')" >

    <Message Text="%(PRSetting.Identity) new contents:" />
    <Message Text="%(PRSetting.NewContent)"/>
  </Target>

I hope I explained it right what I am trying to do. When the target is built, I am getting an error that the path to File::ReadFile() can't be empty string. I am using VS 2019. This is work in progress. I am yet to figure out how to save the new content in destination file.

Update
I have the Itemgroup outside. I updated the question. The reason it is outside is because the target inputs parameter needs it.

1

There are 1 answers

4
Jonathan Dodds On

Try the following and see if it works:

  <Target Name="PrepSettings">
    <ItemGroup>
      <PRSetting Include="$(settings_root)\**\settings_config_*.xml" />
      <PRSetting>
        <NewContent Condition="%(Identity) != ''">$([System.IO.File]::ReadAllText('%(Identity)')).Replace('[config.version]', '$(PR_Version)'))</NewContent>
      </PRSetting>
    </ItemGroup>
    <Message Text="@(PRSetting.Identity) new contents:" />
    <Message Text="%(PRSetting.NewContent)"/>
  </Target>

There are two changes:

  1. There seems to be an issue with an Include that doesn't use an existing ItemGroup and metadata that is self-referencing. So, setting up PRSetting is split in two.
    • First, establish PRSetting with the Include.
    • Second, revisit to add the NewContent metadata item.
  2. Add Condition="%(Identity) != ''" on the NewContent metadata.

I'm not able to fully test your exact scenario at present but I tested an analogue.

Here is my test analogue:

  <Target Name="PrepSettings">
    <ItemGroup>
      <PRSetting Include="1;4;2;3"/>
      <PRSetting>
        <NewContent Condition="%(Identity) != ''">$([MSBuild]::Add('%(Identity)', '1'))</NewContent>
      </PRSetting>
    </ItemGroup>
    <Message Text="PRSetting is @(PRSetting->'(%(Identity),%(NewContent))');" />
  </Target>

The output is

PrepSettings:
  PRSetting is (1,2);(4,5);(2,3);(3,4);

Regarding your code change to move the ItemGroup outside the target:

  • The technique of splitting the ItemGroup as written won't work, but if you are using VS2017 or later and working with .NET Core/.NET 5+ you can use Update.
  • As shown in the question, the Outputs attribute has syntax errors. I assume Outputs="@(PRSetting->'$(OutputPath)\%(Filename).xml')" (or something close) is intended.
  • As shown in the question, the Outputs attribute will never be satisfied because PrepSettings doesn't create the files. I assume PrepSettings as shown is not complete.