Friday, October 13, 2023

Custom build properties and items

Common Properties

Microsoft msbuild processing provides a convenient way of factoring out common build properties that might be shared by many projects. I read about this feature years ago, then forgot about it, so I'm posting this as a reminder to myself and any other developers who might be interested. See Customize the build by folder for information on how you can place the file Directory.Build.props in a suitable parent folder of projects and it will silently be found and used by all child projects. Many projects in a large solution can share build properties by placing a .props file in the solution folder, with contents like this example:

<Project>
  <PropertyGroup>
    <Version>1.2.3</Version>
    <TargetFrameworks>netstandard2.0;net6.0</TargetFrameworks>
    <Authors>The Big Company</Authors>
    <Company>The Big Company</Company>
    <Copyright>Copyright © 1992-2023 The Big Company</Copyright>
    …etc…
  </PropertyGroup>
</Project>

Be careful though if you have other unrelated child projects that might inherit the properties. I had some test projects of mixed types that were spoiled by the common properties, so I had to either move them to a different non-child folder or manually put the correct local override values into their project files.

Common Items

Another common requirement is to put auto-calculated metadata values into the build process. In my case I wanted to put the build time and build machine name into the compiled assembly. After many experiments I discovered the easiest technique is to create a file named Directory.Build.targets in the solution folder next to the custom .props file, with contents like this example:

<Project>
  <ItemGroup>
    <AssemblyAttribute Include="System.Reflection.AssemblyMetadataAttribute">
      <_Parameter1>BuildMachine</_Parameter1>
      <_Parameter2>$(COMPUTERNAME)</_Parameter2>
    </AssemblyAttribute>
    <AssemblyAttribute Include="System.Reflection.AssemblyMetadataAttribute">
      <_Parameter1>BuildTime</_Parameter1>
      <_Parameter2>$([System.DateTime]::Now.ToString("yyyy-MM-dd HH:mm:ss K"))</_Parameter2>
    </AssemblyAttribute>
  </ItemGroup>
</Project>

In this case I'm generating a pair of AssemblyMetadata attributes into the build process. You can generate other attributes as needed and web searches will reveal similar techniques. I wasn't previously aware that a .targets file would be found the same way as the .props file, but I tried it, and it works (I presume it's documented somewhere).