Now that we have the new project system, and we can define common assembly info in our
.csproj, we can say good-bye to
AssemblyInfo.cs, well, I still see these left around for general assembly attributes.
For example it is quite common to see an
InternalsVisibileTo.cs file these days, instead of
AssemblyInfo.cs, see here for an example.
However what if you want to add your own assembly attribute with a value you want to pass in as an MSBuild property? For example, an
AssemblyMetadataAttribute that contains a git commit hash?
Well, we are in luck, there is an
AssemblyAttribute item we can add to our ItemGroup! If we had a property named
CommitHash we could write the following:
<AssemblyAttribute Include="System.Reflection.AssemblyMetadataAttribute" Condition="$(CommitHash) != ''" > <_Parameter1>CommitHash</_Parameter1> <_Parameter2>$(CommitHash)</_Parameter2> </AssemblyAttribute>
Great! One last thing, we need to say when this is going to happen, and that is before
<Target Name="AddGitMetadaAssemblyAttributes" BeforeTargets="CoreGenerateAssemblyInfo"> <ItemGroup> <AssemblyAttribute Include="System.Reflection.AssemblyMetadataAttribute" Condition="$(CommitHash) != ''" > <_Parameter1>CommitHash</_Parameter1> <_Parameter2>$(CommitHash)</_Parameter2> </AssemblyAttribute> </ItemGroup> </Target>
Cool. But wait! It doesn't always work. Sometimes when I do a build, the value doesn't change. What gives?
So it turns out in .NET SDK 2 and up, there are lots of measures to improve performance, one of which is the
<AssemblyName>.AssemblyInfoInputs.cache file, which appears in the
obj folder, and just contains a hash of the
AssemblyInfo inputs as you'd expect.
The hash is calculated internally only by the
_Parameter1 values, so in our example where we put the varying data in
_Parameter2, this doesn't bust the cache. We can see the logic for this here in
As a hacky workaround, we can temporarily add our values to
_Parameter1 then remove them after the hash is generated. Like this:
<Target Name="GitMetadataAssemblyInfoCacheFileFix" BeforeTargets="CreateGeneratedAssemblyInfoInputsCacheFile" > <ItemGroup> <AssemblyAttribute Include="TemporaryAdditionalHashItem" Condition=" $(CommitHash) != '' "> <_Parameter1>$(CommitHash)</_Parameter1> </AssemblyAttribute> </ItemGroup> </Target> <Target Name="CleanupTemporaryAdditionalHashItem" AfterTargets="CreateGeneratedAssemblyInfoInputsCacheFile;AddGitMetadaAssemblyAttributes" > <ItemGroup> <AssemblyAttribute Remove="TemporaryAdditionalHashItem" /> </ItemGroup> </Target>
Now we recompile whenever there is a change to our added value. For the end result, see here.
UPDATE: In the upcoming SDK at the time of writing (2.1.300), this hack is no longer required as @natemcmaster has fixed it here. Hurray!