.netmsbuildcopymsbuild-4.0msbuild-batching

MSBuild Copy task + batching on correlation metadata


I am trying to build an MSBuild target that is to take a certain file in a list of directories, and then copy that file with a different name into the same directory. The "destination" name is directly dependent upon the directory.

Let's illustrate with an example:

|-\Source\MySolution.ProjectFoo\
|    -- App.config.tpl
|    -- MySolution.ProjectFoo.exe
|    -- (Target) MySolution.ProjectFoo.exe.config.tpl
|-\Source\MySolution.ProjectBar\
|    -- App.config.tpl
|    -- MySolution.ProjectBar.exe
|    -- (Target) MySolution.ProjectBar.exe.config.tpl

I have began building my ItemGroup like this:

<ItemGroup>
    <AppConfigTemplates Include="Source\**\App.config.tpl">
        <Correlate>%(RecursiveDir)</Correlate>
    </AppConfigTemplates>
    <ExeFiles Include="Source*\**\*.exe">
        <Correlate>%(RecursiveDir)</Correlate>
    </ExeFiles>
</ItemGroup>

I was hoping to be able to batch on the Correlate metadata property. Something like:

<Copy Batch="%(Correlate)" SourceFiles="%(AppConfigTemplates.FullPath)"
                           DestinationFiles="%(ExeFiles.FullPath).config.tpl" />

How can I achieve this?


Solution

  • It might be possible to 'correlate' seperate itemgroups like that, but you'd still have to make sure they have the same number of items in the same order (eg what if there's an exe but no .tpl and vice-versa), which will likely get messy. A simpler solution is to just list the exe files and copy the tpl file (which has a fixed name) if it exists:

    <ItemGroup>
      <ExeFiles Include="Source\**\*.exe">
        <!-- expected location of the tpl: same directory -->
        <AppConfigTpl>%(RootDir)%(Directory)App.config.tpl</AppConfigTpl>
      </ExeFiles>
    </ItemGroup>
    
    <Target Name="CopyTpl">
      <Copy SourceFiles="%(ExeFiles.AppConfigTpl)"
            DestinationFiles="%(ExeFiles.FullPath).config.tpl"
            Condition="Exists('%(ExeFiles.AppConfigTpl)') />
      <!-- If needed you could raise an error like this -->
      <Error Text="oops no tpl" Condition="!Exists('%(ExeFiles.AppConfigTpl)')" />
    </Target>