c++msbuildc-preprocessormsbuild-task

MSBuild: Update the C++ PreprocessorDefinitions with my custom target


I'm trying to create a custom MSBuild target to automatically fetching the SVN revision number into some define, say MY_VERSION. So, my target file (get-svn-info.targets):

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Target Name="GetSvnRevision" BeforeTargets="PrepareForBuild">
    <Exec Command="svn info --show-item revision" ConsoleToMSBuild="true">
      <Output TaskParameter="ConsoleOutput" PropertyName="SvnRevisionNum" />
    </Exec>
    <ItemGroup>
      <ItemDefinitionGroup>
        <ClCompile>
          <PreprocessorDefinitions>%(PreprocessorDefinitions);MY_VERSION=$(SvnRevisionNum);</PreprocessorDefinitions>
        </ClCompile>
      </ItemDefinitionGroup>
    </ItemGroup>
    <Message Text="MY_VERSION=$(SvnRevisionNum)" Importance="high"></Message>
  </Target>
</Project>

And finally, modified the project file:

<Project ...
  <Import Project="get-svn-info.targets" />
...
</Project>

But compiler says that the 'MY_VERSION': undeclared identifier here

std::cout << "Hello World!\n" << std::to_string(MY_VERSION) << std::endl;

What am I doing wrong? Why does PreprocessorDefinitions still have the old value? Thanks

Build log output (a lot of unimportant? lines skipped):

...
1>Target "GetSvnRevision" in file "D:\ConsoleApplication1\get-svn-info.targets":
1>  Using "Exec" task from assembly "Microsoft.Build.Tasks.Core, Version=15.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a".
1>  Task "Exec"
1>    Task Parameter:ConsoleToMSBuild=True
1>    Task Parameter:Command=svn info --show-item revision
1>    svn info --show-item revision
1>    30
1>    Output Property: SvnRevisionNum=30
1>  Done executing task "Exec".
1>  Task "Message"
1>    Task Parameter:Importance=high
1>    Task Parameter:Text=MY_VERSION=30
1>    MY_VERSION=30
1>  Done executing task "Message".
1>Done building target "GetSvnRevision" in project "ConsoleApplication1.vcxproj".
...
1>Target "ClCompile" in file "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Microsoft\VC\v160\Microsoft.CppCommon.targets":
1>  Task "CL"
...
1>    Task Parameter:
1>        Sources=
1>            ConsoleApplication1.cpp
1>                    PreprocessorDefinitions=WIN32;_DEBUG;_CONSOLE;_UNICODE;UNICODE;
...
1>    Task Parameter:
1>        PreprocessorDefinitions=
1>            WIN32
1>            _DEBUG
1>            _CONSOLE
1>            _UNICODE
1>            UNICODE
...
1>    D:\ConsoleApplication1\ConsoleApplication1.cpp(9,53): error C2065: 'MY_VERSION': undeclared identifier
1>    The command exited with code 2.
1>  Done executing task "CL" -- FAILED.
1>Done building target "ClCompile" in project "ConsoleApplication1.vcxproj" -- FAILED.
1>
1>Done building project "ConsoleApplication1.vcxproj" -- FAILED.
...

Solution

  • The syntax for the ItemDefinitionGroup is incorrect. An ItemDefinitionGroup should not be within an ItemGroup.

    The ItemDefinitionGroup will add the changed PreprocessorDefinitions as a default for items in the ClCompile ItemGroup.

    But ItemDefinitionGroup can't be used within a Target.

    But we can still alter the metadata of Items in the ClCompile ItemGroup.

    I would suggest making two targets.

      <Target Name="GetSvnRevision" BeforeTargets="PrepareForBuild">
        <Exec Command="svn info --show-item revision" ConsoleToMSBuild="true">
          <Output TaskParameter="ConsoleOutput" PropertyName="SvnRevisionNum" />
        </Exec>
        ...
      </Target>
    
      <Target Name="ApplySvnRevision" BeforeTargets="ClCompile">
        <ItemGroup>
          <ClCompile>
            <PreprocessorDefinitions>%(PreprocessorDefinitions);MY_VERSION=$(SvnRevisionNum);</PreprocessorDefinitions>
          </ClCompile>
        </ItemGroup>
      </Target>
    

    'GetSvnRevision' can be performed early. But 'ApplySvnRevision' should be done just before compiling.