Question

Here's an MSBuild script:

<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="AugmentItemGroup" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
 <ItemGroup> 
   <ItmGrp Include="File1.txt">
       <Dest>dest\%(FileName)%(Extension)</Dest> 
   </ItmGrp>
   <ItmGrp Include="File2.txt">
       <Dest>dest\%(FileName)%(Extension)</Dest> 
   </ItmGrp>
   <ItmGrp Include="File3.txt">
       <Dest>dest\%(FileName)%(Extension)</Dest> 
   </ItmGrp>
  </ItemGroup>

   <Target Name="AugmentItemGroup">
     <ItemGroup>
      <ItmGrp Include="File4.txt">
        <Dest>dest\%(FileName)%(Extension)</Dest> 
      </ItmGrp>          
     </ItemGroup>
     <Message Text="%(ItmGrp.FullPath) to %(ItmGrp.Dest)" />
   </Target>
</Project>

The output I would expect from it is:

  D:\t\File1.txt to dest\File1.txt
  D:\t\File2.txt to dest\File2.txt
  D:\t\File3.txt to dest\File3.txt
  D:\t\File4.txt to dest\File4.txt

But the result is:

  D:\t\File1.txt to dest\File1.txt
  D:\t\File2.txt to dest\File2.txt
  D:\t\File3.txt to dest\File3.txt
  D:\t\File4.txt to dest\File1.txt
  D:\t\File4.txt to dest\File2.txt
  D:\t\File4.txt to dest\File3.txt

Why is the behavior of the %(FileName)%(Extension) well-known metadata reference is different when an ItemGroup is inside a target?

Is it possible to get the "outside a target" behavior inside a target?

Was it helpful?

Solution

This will give the output you desire. Though it may not be the correct approach in the general case, it does avoid the batching that occurs with "File4" by making the custom metadata a part of the item definition that is calculated:

<Project
   xmlns="http://schemas.microsoft.com/developer/msbuild/2003"
   DefaultTargets="AugmentItemGroup" 
   ToolsVersion="4.0">  
   <ItemDefinitionGroup>
      <ItmGrp>
         <Dest>dest\%(FileName)%(Extension)</Dest>  
      </ItmGrp>
   </ItemDefinitionGroup>

   <ItemGroup>  
      <ItmGrp Include="File1.txt" />
      <ItmGrp Include="File2.txt" /> 
      <ItmGrp Include="File3.txt" /> 
   </ItemGroup> 

   <Target Name="AugmentItemGroup"> 
      <ItemGroup> 
         <ItmGrp Include="File4.txt" />
      </ItemGroup> 
      <Message Text="%(ItmGrp.FullPath) to %(ItmGrp.Dest)" /> 
   </Target> 
</Project>

edit:

If (as your comment below says) each item has a different value for %(Dest), you just need to make the final value calculated:

<Project ...>
    <ItemDefinitionGroup>
       <ItmGrp>
          <_Dest />
       </ItmGrp>
    </ItemDefinitionGroup>

    <ItemGroup>  
       <ItmGrp Include="File1.txt"><Dest>dest1</Dest></ItmGrp>
       <ItmGrp Include="File2.txt"><Dest>dest2</Dest></ItmGrp>
       <ItmGrp Include="File3.txt"><Dest>dest3</Dest></ItmGrp>
    </ItemGroup> 

    <Target Name="AugmentItemGroup"> 
       <ItemGroup> 
          <ItmGrp Include="File4.txt"><Dest>dest4</Dest></ItmGrp>
          <ItmGrp>
             <_Dest>%(Dest)\%(FileName)%(Extension)</_Dest>
          </ItmGrp>
       </ItemGroup> 
       <Message Text="%(ItmGrp.FullPath) to %(ItmGrp._Dest)" /> 
    </Target> 
</Project>

Excerpted from MSBuild Trickery tricks #70, 71

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top