Set git commit hash as dll version number in visual studio

I’m developing a project using visual studio 2013 and git.

I must distribute some libraries of the project so I’d like to set their version number with the current git commit hash, so I can be sure of which library version they are using.

  • How to recover from “git stash save --all”?
  • Git: Fetching latest commit on a branch
  • How can I prevent git push if local modifications are detected (including untracked files)?
  • Xcode delete working copies of source control
  • Git: committing a symlink to a folder
  • How can I get Posh-Git to launch using 'External Tools' in Visual Studio 2013?
  • Is there a way to put the hash as version number in automated way, I.e. with a pre-build event, instead of doing it manually every time?

  • How to concatenate two git histories?
  • How to turn a git branch into fork?
  • Git Pull without typing private key password
  • Why Does My BASH Script Fail When Adding Additional Arguments?
  • git push to remote repo, but seems nothing is uploaded on repo
  • Does using Git make sense for small internal teams?
  • 2 Solutions collect form web for “Set git commit hash as dll version number in visual studio”

    Here are some snippets of a possible implementation for native projects using resource files. The idea is to add a single property sheet to the project, which has a prebuild event which creates a .res file based on the git commit hash and which also adds this .res file as a resource. Here is the property sheet:

    <?xml version="1.0" encoding="utf-8"?>
    <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
      <ImportGroup Label="PropertySheets" />
      <PropertyGroup Label="UserMacros" />
      <PropertyGroup>
        <VersionResourceOut>$(MSBuildProjectDirectory)\version.res</VersionResourceOut>
      </PropertyGroup>
      <ItemGroup>
        <Resource Include="$(VersionResourceOut)" />
      </ItemGroup>
      <Import Project="$(BuildToolsDir)tools\versionrc.targets" />
      <Target Name="CreateGitVersionResource" BeforeTargets="BuildGenerateSources">
        <CallTarget Targets="CreateGitVersionResInBuild" />
        <MakeSameWriteTime SourceFile="$(OutDir)$(TargetName)$(TargetExt)" DestFile="$(VersionResourceOut)"/>
      </Target>
    </Project>
    

    The $(BuildToolsDir)tools\versionrc.targets file is where the actual creation of the resource file is done. The complete implementation is rather lengthy because it also works for svn and allows a bunch of customisation – a bit much to post here so I’ll just lay out the meat of it:

    • the commit hash is stored in an msbuild property, the command to get it is

      git --work-tree=$(GitVersionDir) --git-dir=$(GitVersionDir)\.git rev-parse --short HEAD
      

      where $(GitVersionDir) usually is set to $(MsbuildProjectDirectory) since we have most .vcxproj files in the source root.

    • I also like the build date to be included so the property which eventually goes into the FileDescription entry of the StringFileinfo block is

      <FileDesc>$(GitVersion) $([System.DateTime]::Now.ToString('HH:mm:ss dd/MM/yyyy'))</FileDesc>
      
    • the actual file/product version, company name and other fields are fetched from elsewhere. Usually we have a common header file defining all VRC_XXX macros needed by the RC file template (see below), and a per-project header file containing e.g. #define VRC_FILEDESC “Project Foo”, and those headers are merged using ReadLinesFromFile/WriteLinesToFile tasks. Anyway the idea is to end up with a header file like

      #define VRC_FILEVERSION 4,4,1,0
      #define VRC_PRODUCTVERSION 4.4.1.0
      #define VRC_COMPANYNAME MyCompany
      #define VRC_PRODUCTNAME VRC_COMPANYNAME Libraries
      #define VRC_FILEDESC Project Foo
      #define VRC_FILEDESCRIPTION VRC_FILEDESC VRC_FILEDESCGIT
      

      whos path is stored in a $(VersionMainInclude) property.

    • all of this is fed to rc.exe to create the .res file. The full command is something like

      rc /d VRC_INCLUDE=$(VersionMainInclude)
         /d VRC_ORIGINALFILENAME=$(TargetName)$(TargetExt)
         /d VRC_FILETYPE=$(FileType)
         /d VRC_FILEDESCGIT=$(FileDesc)
         /d VRC_COPYRIGHT=VRC_COMPANYNAME \251 $([System.DateTime]::Now.ToString(`yyyy`))
         /fo $(VersionResourceOut) $(MsBuildThisFileDirectory)version.rc
      
    • Note the MakeSameWriteTime trick to set the modified time of the .res file the same as the output file, to assure the prebuild event doesn’t trigger new builds each time it the .res file is generated. There might be better ways to do this, but this one works for me:

      <UsingTask TaskName="MakeSameWriteTime" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll" >
        <ParameterGroup>
          <SourceFile Required="true" ParameterType="System.String"/>
          <DestFile Required="true" ParameterType="System.String"/>
        </ParameterGroup>
        <Task>
          <Code Type="Fragment" Language="cs">
            <![CDATA[
      System.IO.File.SetLastWriteTime( DestFile, System.IO.File.GetLastWriteTime( SourceFile ) );]]>
          </Code>
        </Task>
      </UsingTask>
      

    This is the full .rc template used:

    #include <winver.h>
    
    #define stringize( x )        stringizei( x )
    #define stringizei( x )       #x
    
    #ifdef VRC_INCLUDE
      #include stringize( VRC_INCLUDE )
    #endif
    
    #ifdef _WIN32
      LANGUAGE 0x9,0x1
      #pragma code_page( 1252 )
    #endif
    
    1 VERSIONINFO
     FILEVERSION    VRC_FILEVERSION
     PRODUCTVERSION VRC_PRODUCTVERSION
     FILEFLAGSMASK  0x1L
     FILEFLAGS      VS_FF_DEBUG
     FILEOS         VOS__WINDOWS32
     FILETYPE       VRC_FILETYPE
    BEGIN
      BLOCK "StringFileInfo"
      BEGIN
        BLOCK "040904E4"
        BEGIN
          VALUE "CompanyName",      stringize( VRC_COMPANYNAME )
          VALUE "FileDescription",  stringize( VRC_FILEDESCRIPTION )
          VALUE "FileVersion",      stringize( VRC_FILEVERSION )
          VALUE "LegalCopyright",   stringize( VRC_COPYRIGHT )
          VALUE "InternalName",     stringize( VRC_ORIGINALFILENAME )
          VALUE "OriginalFilename", stringize( VRC_ORIGINALFILENAME )
          VALUE "ProductName",      stringize( VRC_PRODUCTNAME )
          VALUE "ProductVersion",   stringize( VRC_PRODUCTVERSION )
        END
      END
      BLOCK "VarFileInfo"
      BEGIN
        VALUE "Translation", 0x409, 1200
      END
    END
    

    Take a look at my modification of MSBuild Community Tasks project at https://github.com/Bitrete/msbuildtasks. I’ve added a task called SemanticVersionGitDescribe. See the example of usage below.

    <SemanticVersionGitDescribe LocalPath="$(MSBuildProjectDirectory)">
        <Output TaskParameter="SemanticVersion" PropertyName="Version"/>
        <Output TaskParameter="IsRelease" PropertyName="Release"/>
        <Output TaskParameter="AdditionalCommitsCount" PropertyName="AdditionalCommits"/>
        <Output TaskParameter="Hash" PropertyName="Hash"/>
    </SemanticVersionGitDescribe>
    

    And don’t forget to put version string with hast only in AssemblyInformalVersion property. Because AssemblyVersion accepts only digits delimited by periods.

     <Target Name="UpdateAssemblyInfoVersion" DependsOnTargets="Prepare; GetGitVersion">
        <Copy SourceFiles="$(EtcPath)\common\CommonAssemblyInfo.cs" DestinationFiles="$(OutputPath)\CommonAssemblyInfo.cs"/>
        <FileUpdate Files="$(EtcPath)\common\CommonAssemblyInfo.cs"
            Multiline="true"
            Singleline="false"
            Regex="(AssemblyVersion)\(&quot;([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)?&quot;\)"
            ReplacementText="$1(&quot;$(Version).$(AdditionalCommits)&quot;)" />
        <FileUpdate Files="$(EtcPath)\common\CommonAssemblyInfo.cs"
            Multiline="true"
            Singleline="false"
            Regex="(AssemblyFileVersion)\(&quot;([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)?&quot;\)"
            ReplacementText="$1(&quot;$(Version).$(AdditionalCommits)&quot;)" />
        <FileUpdate Condition=" $(Release) == False " 
            Files="$(EtcPath)\common\CommonAssemblyInfo.cs"
            Multiline="true"
            Singleline="false"
            Regex="(AssemblyInformationalVersion)\(&quot;([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)?&quot;\)"
            ReplacementText="$1(&quot;$(Version).$(AdditionalCommits)-g$(Hash)&quot;)" />
        <FileUpdate Condition=" $(Release) == True "
            Files="$(EtcPath)\common\CommonAssemblyInfo.cs"
            Multiline="true"
            Singleline="false"
            Regex="(AssemblyInformationalVersion)\(&quot;([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)?&quot;\)"
            ReplacementText="$1(&quot;$(Version)&quot;)" />
    </Target>
    
    Git Baby is a git and github fan, let's start git clone.