visual-studiovisual-c++msbuildpropertysheet

Using $(OutDir) in a property group of a property sheet


Using Visual Studio 2022 with a C++ solution.

The below property sheet is working fine:

<?xml version="1.0" encoding="utf-8"?> 
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  
  <ItemGroup>
    <CopyFileToFolders Include="$(MSBuildThisFileDirectory)somefile.bin">
      <DeploymentContent>true</DeploymentContent>
      <FileType>Document</FileType>
      <DestinationFolders>$(OutDir)</DestinationFolders>
    </CopyFileToFolders>
  </ItemGroup>

</Project>

But the below one is not working (when output directory is changed in project properties):

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

  <PropertyGroup>
    <CustomDestination>$(OutDir)</CustomDestination>
  </PropertyGroup>

  <ItemGroup>
    <CopyFileToFolders Include="$(MSBuildThisFileDirectory)somefile.bin">
      <DeploymentContent>true</DeploymentContent>
      <FileType>Document</FileType>
      <DestinationFolders>$(CustomDestination)</DestinationFolders>
    </CopyFileToFolders>
  </ItemGroup>

</Project>

This makes sense as "CustomDestination" property is evaluated before "OutDir" is well-defined from VS IDE, on the contrary to the "CopyFileToFolder"->"DestinationFolders" item metadata which is evaluated after all properties.

Is there an elegant way to predefine "CustomDestination" with any value including some $(X) before the "CopyFileToFolders" item, so I can move both on separate property sheets ?

_

Additional note:

  1. I came out with the below workaround consisting in managing $(OutDir) separately like this:
<PropertyGroup>
  <CustomDestination>$(ProjectDir);$(SolutionDir)SomeDirectory</CustomDestination>
  <UseOutDir>Yes</UseOutDir>
</PropertyGroup>

<ItemGroup>
  <CopyFileToFolders Include="$(MSBuildThisFileDirectory)somefile.bin">
    <DeploymentContent>true</DeploymentContent>
    <FileType>Document</FileType>
    <DestinationFolders Condition="'$(UseOutDir)' == ''">$(CustomDestination) </DestinationFolders>
    <DestinationFolders Condition="'$(UseOutDir)' != ''">$(OutDir);$(CustomDestination) </DestinationFolders>
  </CopyFileToFolders>
</ItemGroup>

But I was wondering if someone would come out with a more generic solution.

  1. I also tried something like this:
  <PropertyGroup>
    <CustomDestination>%24(OutDir);%24(ProjectDir)</CustomDestination>
  </PropertyGroup>

  <ItemGroup>
    <CopyFileToFolders Include="$(MSBuildThisFileDirectory)somefile.bin">
      <DeploymentContent>true</DeploymentContent>
      <FileType>Document</FileType>
      <DestinationFolders>$(CustomDestination)</DestinationFolders>
    </CopyFileToFolders>
  </ItemGroup>

But I don't know how to make VS/MSBuild to replace $(X) contents into $(CustomDestination) property value.


Solution

  • I found a solution !

    Defining "CustomDestination" as a custom item value instead of a property makes it evaluated after properties, then with the latest $(OutDir) value.

      <!--Define CustomDestination-->
      <ItemGroup>
        <CustomDestination Include="$(OutDir)" />
      </ItemGroup>
    
      <!--Print CustomDestination value in build output-->
      <Target Name="PrintDestTarget" BeforeTargets="Build">
        <Message Importance="high" Text="PrintDestTarget: CustomDestination = '@(CustomDestination)'" />
      </Target>
    
      <!--Use CustomDestination value to copy file-->
      <ItemGroup>
        <CopyFileToFolders Include="$(MSBuildThisFileDirectory)somefile.bin">
          <DeploymentContent>true</DeploymentContent>
          <FileType>Document</FileType>
          <DestinationFolders>@(CustomDestination)</DestinationFolders>
        </CopyFileToFolders>
      </ItemGroup>
    

    Note that the setup logic of items is different than properties:

    <ItemGroup>
      <CustomDestination Include="Smith" />
    </ItemGroup>
    <ItemGroup>
      <CustomDestination Include="Adams" />
    </ItemGroup>
    <Target Name="PrintDestTarget" BeforeTargets="Build">
      <Message Importance="high" Text="PrintDestTarget: CustomDestination = '@(CustomDestination)'" />
    </Target>
    

    Will output "Smith;Adams".

    I am not shure if it is possible to modify an item value, but one can still define new ones as needed.