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.
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?
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With