March 27th, 2023

Using the contents of a file to define an MSBuild property

Say you want to define a property in your MSBuild project file (vcxproj, csproj, etc.) and have the property value come from a file. You might try using the Read­Lines­From­File task to get the contents:

  <Target Name="DefineMagic">
    <ReadLinesFromFile File=".\magic.txt">
      <Output TaskParameter="Lines" PropertyName="Magic" />
    </ReadLinesFromFile>
  </Target>
  <ItemDefinitionGroup>
    <ClCompile>
      <PreprocessorDefinitions>MAGIC="$(Magic)";%(PreprocessorDefinitions)</PreprocessorDefinitions>
    </ClCompile>
  </ItemDefinitionGroup>

This doesn’t work because the Target doesn’t run until after the Item­Definition­Group is already defined. MSBuild evaluates Property­Group and Item­Group elements before running any Targets, By the time you execute the Read­Lines­From­File task, it’s too late.

Instead, you can use the Read­All­Text MSBuild property function to read the text into a property.

  <PropertyGroup>
    <Magic>$([System.IO.File]::ReadAllText('.\magic.txt').TrimEnd())</Magic>
  </PropertyGroup>
  <ItemDefinitionGroup>
    <ClCompile>
      <PreprocessorDefinitions>MAGIC="$(Magic)";%(PreprocessorDefinitions)</PreprocessorDefinitions>
    </ClCompile>
  </ItemDefinitionGroup>

You can see an example of this trick in the WindowsAppSDK build properties.

Topics
Code

Author

Raymond has been involved in the evolution of Windows for more than 30 years. In 2003, he began a Web site known as The Old New Thing which has grown in popularity far beyond his wildest imagination, a development which still gives him the heebie-jeebies. The Web site spawned a book, coincidentally also titled The Old New Thing (Addison Wesley 2007). He occasionally appears on the Windows Dev Docs Twitter account to tell stories which convey no useful information.

2 comments

Discussion is closed. Login to edit/delete existing comments.

  • Joshua Hudson

    MSBuild is so bad I’d love to cut it out of the build process, but trying to run the dotnet runtime any other way is its own pain.

    Most of the actual build code is C# code inside dotnet.exe with no other entry point.

  • Vijay Anand E G · Edited

    I’ve been using this approach for quite a long, especially for the NuGet package release notes.

    The only downside is the file needs to be physically present in the path mentioned. So relative path needs to be carefully managed.

    <PackageReleaseNotes>$([System.IO.File]::ReadAllText("$(MSBuildProjectDirectory)\release-notes.txt"))</PackageReleaseNotes>