Search code examples
visual-studiobuildmsbuildmsbuild-task

MSBuild: Copying a list of files into different locations based on file name using the Copy Task


I have a set of customer-specific configuration files that are located in a single Release folder at the time of the build. The file names are something like:

  • CustomerA_InstanceConfigurationX.config
  • CustomerA_InstanceConfigurationY.config
  • CustomerB_InstanceConfigurationX.config
  • CustomerB_InstanceConfigurationY.config

... etc.

During the build, I want to copy the customer-specific configuration files into a customer-specific Binaries folder:

  • $(BuildDirectory)\Binaries\Installers\CustomerA\ProductName\
  • $(BuildDirectory)\Binaries\Installers\CustomerB\ProductName\

So CustomerA_InstanceConfigurationX.config and CustomerA_InstanceConfigurationY.config would go into go into $(BuildDirectory)\Binaries\Installers\CustomerA\ProductName\ and so on.

How can I set the SourceFiles and the DestinationFolder properties to make this happen?

I have the list of Customers as a meta of an Instance property and set the SourceFiles and DestinationFiles around it:

<ItemGroup>
  <ConfigFilesToCopy Include="$(BuildDirectory)\stage\InstallerDev\%(Instance.Customer)\Setup\bin\Release\%(Instance.Customer)_*.*" />
  <DestionationsForConfigFiles Include="$(BuildDirectory)\Binaries\Installers\%(Instance.Customer)\InstallerDev\" />
</ItemGroup>

<Copy SourceFiles="@(ConfigFilesToCopy)" DestinationFolder="%(DestionationsForConfigFiles.FullPath)" />

That just copies all the customer .config files into all the customer-specific Binaries folder though.

Using the message task, %(Instance.Customer) outputs:

  • CustomerA
  • CustomerB
  • CustomerC.

%(ConfigFilesToCopy.Identity) outputs:

  • "C:\Builds\AgentX\YYY\INSTALLER_DEV Build\stage\InstallerDev\CustomerA\Setup\bin\Release\CustomerA_InstanceX.config"
  • "C:\Builds\AgentX\YYY\INSTALLER_DEV Build\stage\InstallerDev\CustomerA\Setup\bin\Release\CustomerA_InstanceY.config"
  • "C:\Builds\AgentX\YYY\INSTALLER_DEV Build\stage\InstallerDev\CustomerB\Setup\bin\Release\CustomerB_InstanceX.config"
  • "C:\Builds\AgentX\YYY\INSTALLER_DEV Build\stage\InstallerDev\CustomerB\Setup\bin\Release\CustomerB_InstanceY.config"
  • Etc.

%(DestionationsForConfigFiles.Identity) outputs:

  • C:\Builds\Agent8\Five0\INSTALLER_DEV Build\Binaries\Installers\CustomerA\InstallerDev\
  • C:\Builds\Agent8\Five0\INSTALLER_DEV Build\Binaries\Installers\CustomerB\InstallerDev\
  • C:\Builds\Agent8\Five0\INSTALLER_DEV Build\Binaries\Installers\CustomerC\InstallerDev\
  • Etc.

If someone could offer some help on achieving this or had a alternative approach for it, that'd be great. (E.g., I could re-organize the customer-specific configuration files into a customer-specific folders or something.) Thanks a lot in advance!

[** Update Note **: For now, I hard-coded each customer name into a ConfigFilesToCopy list item as well as a DestinationsForConfigFiles item.

  <ConfigFilesToCopy Include="$(BuildDirectory)\stage\InstallerDev\CustomerA\Setup\bin\Release\CustomerA_*.*">
    <DestionationsForConfigFiles>$(BuildDirectory)\Binaries\Installers\CustomerA\InstallerDev\</DestionationsForConfigFiles>
  </ConfigFilesToCopy>

  <ConfigFilesToCopy Include="$(BuildDirectory)\stage\InstallerDev\CustomerB\Setup\bin\Release\CustomerB_*.*">
    <DestionationsForConfigFiles>$(BuildDirectory)\Binaries\Installers\CustomerB\InstallerDev\</DestionationsForConfigFiles>
  </ConfigFilesToCopy>

This works, but I am basically wondering if it's possible to do the same thing without explicitly using the customer name so that I don't have to maintain this list every time we add a new customer.


Solution

  • Make the destination metadata of the files (I changed the paths so it better fits in SO's code box, but the idea is the same):

    <Target Name="Copy">
      <ItemGroup>
        <ConfigFilesToCopy Include="$(SomeDir)\%(Instance.Customer)\%(Instance.Customer)_*.*">
          <DestionationsForConfigFiles>$(SomeDir)\Binaries\%(Instance.Customer)</DestionationsForConfigFiles>
        </ConfigFilesToCopy>
      </ItemGroup>
    
      <Message Text="Source=%(ConfigFilesToCopy.Identity) Dest=%(ConfigFilesToCopy.DestionationsForConfigFiles)" />
    </Target>