Including Git tags in Visual Studio application

371 Views Asked by At

I'm looking for a simple way to include the output of git describe --t --dirty in the title bar of a WPF program.

I've looked at MSBuild.Community.Tasks but I don't understand the build process too well. I haven't been able to find much recent information or any tutorial that put all the pieces together for me.

I tried writing a batch file to add a property to AssemblyInfo.cs:


@echo off
echo "Append Git Version Property"

for /F "tokens=* USEBACKQ" %%F IN (`git describe --tags --dirty`) DO (
set var=%%F
)
echo Version: %var%
set statement=[assembly: AssemblyInformationalVersion(" %var% ")]
echo Property: %statement%
echo %statement% >> AssemblyInfo.cs

This seems to work if I run it from explorer. Adding this to my project file should run it after the build completes, I think.

<PropertyGroup>
    <PostBuildEvent>cmd  (SolutionDir)\Properties\gitVersion.bat</PostBuildEvent>
</PropertyGroup>

It doesn't seem to change the file at all, when I do a build. I need to get it to actually add the property.

I don't know if the build creates a new file with each build. If not what I have will append a new property to the end of the file each time. That will not be good.

Update-- Whatever other issues there might be there must at least be something wrong with the way I am running it from the project file. I changed the output to a file I created, and changing it to a prebuild event. The bat file just doesn't seem to run. There are no errors.

Update 2: I'm not sure if I should continue this or start a new question. Let me know if I am breaking etiquette.

per mister Dodd's comment I added a target to the project.

  <Target Name="GitVersionAttribute" BeforeTargets="BeforeBuild">
    <Exec Command="git describe --tags --dirty &gt; $(IntermediateOutputPath)version.txt" />
    <ReadLinesFromFile File="$(IntermediateOutputPath)version.txt">
      <Output TaskParameter="Lines" PropertyName="VersionAttribute" />
    </ReadLinesFromFile>
    <WriteCodeFragment AssemblyAttributes="GitVersionAttribute" Language="C#" OutputDirectory="Properties" OutputFile="GitVersionAttribute.cs" />
    <ItemGroup>
      <Compile Include="Properties\GitVersionAttribute.cs" />
    </ItemGroup>
  </Target>

I added a RunCommand to to bring in the return value from git describe. I still can't figure out how to set the value for GitVersionAttribute.

1

There are 1 best solutions below

3
Jonathan Dodds On

The batch file is run in the PostBuildEvent which is after the build. i.e. it doesn't appear to do anything because it runs late in the process and, most importantly, after compilation.

From discussion in the comments it would appear that the project is a 'Legacy' style project targeting .Net Framework.

C# project files (.csproj) are MSBuild XML files. The top level document root element in an msbuild file is Project. An 'Sdk' style project will have an 'Sdk' attribute on the Project element. A 'Legacy' project will not.

'Sdk' projects support a set of properties for specifying many of the common assembly attributes. Since properties can be set via environment variables and the command line, it is very easy for a build system to inject the appropriate values.

'Legacy' projects don't have the properties. (But we are not stuck.)

The code

[assembly: AssemblyInformationalVersion("myversionstring")]

is the syntax for an Attribute. assembly: is specifying the target of the attribute.

This line of code can appear in any source file. There is nothing special about the AssemblyInfo.cs file or name.

A given assembly attribute can only be specified once for an assembly. Repeating the same assembly attribute anywhere in the source is an error.

e.g. The following code is a compile time error. Putting these two lines in separate files in the same project is still an error.

[assembly: AssemblyInformationalVersion("foo")]
[assembly: AssemblyInformationalVersion("bar")]

The AssemblyInfo.cs from the project template doesn't include AssemblyInformationalVersion so there shouldn't be a duplication issue. But it is something to be aware of, if you decide to dynamically update other attributes that do appear in the AssemblyInfo.cs file.

With Visual Studio 2015 a task was added to MSBuild named WriteCodeFragment. Despite the name this task is not (presently) capable of writing any generic code fragment. But that's okay because the one and only type of code fragment it is capable of writing is assembly attribute code fragments. This task was added to support 'Sdk' style projects and, thankfully, it was added to MSBuild itself as a general use task.

To dynamically inject an AssemblyInformationalVersion:

  1. Pass the value into the msbuild process
  2. Create a target that is set to be executed BeforeCompile
  3. In the target
    1. Use WriteCodeFragment to create a new file that defines the attribute
    2. Include the new file in the Compile items

Update with Example Code

Example code to add an AssemblyInformationalVersion:

<!-- Directory.Build.targets -->
<Project>
  <!--<Import Project="$([MSBuild]::GetPathOfFileAbove('$(MSBuildThisFile)', '$(MSBuildThisFileDirectory)../'))" />-->

  <Target Name="AddAssemblyInformationalVersion" BeforeTargets="BeforeCompile">
    <!-- The 'InfoVersionFile' property shoud be set with the value to use for the AssemblyInformationalVersion. -->

    <!-- Path and name of of the file to generate. -->
    <PropertyGroup>
      <InfoVersionFilename Condition="'$(InfoVersionFilename)' == ''">InfoVersion.cs</InfoVersionFilename>
      <InfoVersionFile Condition="'$(InfoVersionFile)' == ''">$(IntermediateOutputPath)$(InfoVersionFilename)</InfoVersionFile>
    </PropertyGroup>
    <!-- Delete the file if it exists from a priopr build. -->
    <Delete Files="$(InfoVersionFile)" Condition="Exists('$(InfoVersionFile)')" />

    <!-- Generate the source file. -->
    <ItemGroup>
      <InfoVersionAttribute Include="AssemblyInformationalVersionAttribute"  Condition="'$(InformationalVersion)' != ''">
        <value>$(InformationalVersion)</value>
      </InfoVersionAttribute>
    </ItemGroup>
    <WriteCodeFragment Language="C#" AssemblyAttributes="@(InfoVersionAttribute)" OutputFile="$(InfoVersionFile)"  Condition="'@(InfoVersionAttribute)' != ''">
      <Output TaskParameter="OutputFile" PropertyName="$(InfoVersionSourceFile)" />
    </WriteCodeFragment>

    <!-- Add the generated source file to the items to compile. -->
    <ItemGroup>
      <Compile Include="$(InfoVersionSourceFile)" Condition="Exists('$(InfoVersionSourceFile)')" />
    </ItemGroup>
  </Target>
</Project>

In a CI build, a property value can be passed on the command line with the -property switch. -p is the short form.

e.g.

msbuild ... -p:InformationalVersion=foobar

or

dotnet build ... -p:InformationalVersion=foobar

As a design issue I would avoid coupling the build to a specific version control tool, i.e. I would not call git from within the build code.