Automatic Version Numbering

Sample of class

You can also use the Wizard. Easy steps for quick results.

The easiest manual way here.

Before reading, please note: You can achieve this (or similar) result with our different modes.

For simplicity, we'll consider Script Mode. But you can also try with Targets Mode or C# Mode, or even with simple caller and others.

Synopsis

This method will generate automatically the Version class like this ↘

public struct EvMSBuildVersion
{
    public static readonly Version number = new Version(S_NUM_REV);

    public const string S_NUM = "1.14.1";
    public const string S_REV = "0";

    public const string S_NUM_REV = S_NUM + "." + S_REV;

    public const string B_SHA1 = "";

    internal const string S_PRODUCT = S_NUM_REV + "";
    internal const string S_INFO    = S_PRODUCT + "+" + B_SHA1;
}

For C++ you can also use preprocessor directives #define (macro definitions):

#pragma once

#ifndef VSSBE_VERSION_H_
#define VSSBE_VERSION_H_

#define VER_BRANCH_NAME     "develop";
#define VER_BRANCH_SHA1     "e3de826";
#define VER_BRANCH_REVCOUNT "296";
#define VER_INFORMATIONAL   "0.12.4.17639+e3de826";

#endif

or C++ struct similar to C# above:

struct RXWVersion
{
    struct TNum
    {
        const int major;
        const int minor;
        const int patch;
        const int build;

        TNum(int major, int minor, int patch, int build = 0)
            : major(major), minor(minor), patch(patch), build(build) { }

        TNum() : TNum(1, 3, 0, 3411) { }

    } number;

    const TCHAR* bSha1      = _T("f3bd722");
    const TCHAR* config     = _T("PublicRelease");
    const TCHAR* product    = _T("1.3.0");
};

Then, you can easily use this in various places or even provide as API.

Attributes:

[InstalledProductRegistration("#110", "#112", Version.numberWithRevString, IconResourceID = 400)]

AssemblyInfo:

[assembly: AssemblyVersion(Version.numberString)]

Other places, for example:

toolVersion.Text = $"v{Version.numberString}+{Version.branchSha1}";

What about static files?

For example, .vsixmanifest is a xml-based file used in VSPackages (VSIX).

Since we can't use this directly as above, we can just update this 'as is', for example, using regex or simple placeholders. For example:

#[IO replace.Regexp("source.extension.vsixmanifest", "<Version>[0-9.]+</Version>", "<Version>$(number)</Version>")]

Where, $(number) it's your version number (see MSBuild and UserVariableComponent). You can manage this value with the following options:

  • Getting from MSBuild Property #[var number = $(name)] or $(number = $(name))
  • Getting from file: #[var number = #[File get(".version")]]
  • Getting from your external tool(stdout): #[var number = #[IO sout("updv.exe", "-s new")]]
  • Manually set: #[var number = 1.2.3] or $(number = "1.2.3")
  • Other with MSBuild & SBE-Scripts
  • etc.

Note: The <Version> in .vsixmanifest follows the CLR assembly versioning format: Major.Minor.Build.Revision (1.2.40308.00). see MSDN:

Generating the Version class with build/revision number

Create template of Version class or struct.

  1. Either use external file such as Version.tpl or define data inside script, etc.
  2. Either use placeholders or evaluate actual values.

For example,

#[var tpl = // This code was generated by a vsSolutionBuildEvent. 
// Changes to this file may cause incorrect behavior and will be lost if the code is regenerated.
namespace net.r_eg.EvMSBuild
{
    using System;

    public struct EvMSBuildVersion
    {
        public static readonly Version number = new Version(S_NUM_REV);

        public const string S_NUM = "#[$(pVer)]";
        public const string S_REV = "#[$(revBuild)]";

        public const string S_NUM_REV = S_NUM + "." + S_REV;

        public const string B_SHA1 = "#[$(bSha1)]";

        internal const string S_PRODUCT = S_NUM_REV + "$(postfixRel)";
        internal const string S_INFO    = S_PRODUCT + "+" + B_SHA1;
    }
}]

You can also use some external file to store a common version in your repo like .version. Or just use some variables like $(Version) etc.

  • Select event type - "Pre-Build".
  • Change "Processing mode" to 'Script Mode'
  • Activate SBE-Scripts support
  • Activate MSBuild support
  • Write the following script, for example:
#[$(revDeltaBase  = "2015/12/02")]
#[$(revDeltaMin   = $([System.Math]::Pow(10, 3)))]
#[$(revDeltaMax   = 65534)] #[" limit of AssemblyVersion (2^16 - 2) "]

#[var tBase     = $([System.DateTime]::Parse('$(revDeltaBase)').ToBinary())]
#[var tNow      = $([System.DateTime]::UtcNow.Ticks)]
#[var revBuild  = #[$(
    [System.TimeSpan]::FromTicks('$(
        [MSBuild]::Subtract($(tNow), $(tBase))
    )')
    .TotalMinutes.ToString('0')    
)]]
                        
#[var revBuild  = #[$(

    [MSBuild]::Add(
        $(revDeltaMin), 
        $([MSBuild]::Modulo(
            $(revBuild), 
            $([MSBuild]::Subtract(
                $(revDeltaMax), $(revDeltaMin)
            ))
        ))
    )
    
)]]

#[" 
    Checking of the git to define sha1, branch name, etc.
"]
#[var isGit = #[IO cmd("git rev-parse 2>&1")]]
#[( $(isGit) == "" )
{
    #[var bSha1 = #[IO sout("git", "rev-parse --short HEAD")]]
}
else {
    #[$(bSha1 = '')]
}]


#[var tpl = // This code was generated by a vsSolutionBuildEvent. 
// Changes to this file may cause incorrect behavior and will be lost if the code is regenerated.
namespace net.r_eg.EvMSBuild
{
    using System;

    public struct EvMSBuildVersion
    {
        public static readonly Version number = new Version(S_NUM_REV);

        public const string S_NUM = "#[$(pVer)]";
        public const string S_REV = "#[$(revBuild)]";

        public const string S_NUM_REV = S_NUM + "." + S_REV;

        public const string B_SHA1 = "#[$(bSha1)]";

        internal const string S_PRODUCT = S_NUM_REV + "$(postfixRel)";
        internal const string S_INFO    = S_PRODUCT + "+" + B_SHA1;
    }
}]

#[$(pVerPub = "$(pVer).$(revBuild)$(postfixRel)")]
#[$(pVerPrint = "$(pVerPub)+$(bSha1)")]
#[$(pVerProd = "$(pVer)$(postfixRel)")]

#[" Finalize your data in file system "]

#[File write("src/EvMSBuildVersion.cs"):#[$(tpl.Replace('\n', '\r\n'))]]

#[IO replace.Regex("src/E-MSBuild.csproj", "<Version>.+?</Version>", "<Version>$(pVerProd)</Version>")]

Activate event and click apply. Enjoy.

See also - Custom counters & Date & Time features for details about limitations for revBuild if you work in team.

As result you will see EvMSBuildVersion.cs as above (must be included in main project with Build Action as Compile) and E-MSBuild.csproj file with updated number.

Also you can configure revision number only for specific configurations, for example:

#[( $(Configuration) == "PublicRelease" )
{
    #[$(pVerPub = "$(pVer).$(revBuild)+$(bSha1)")]
}]

In example above should be like 1.14.1.34073+014f49a only for PublicRelease configuration and 1.14.1 for others.

  • 1.14.1.30337
  • 1.14.1.30337+014f49a API 1.4

To get revBuild in range (see Math), you can use for example:

  • 0 - 99999:
#[var revBuild  = ...]
#[var revBuild  = $([MSBuild]::Modulo(#[var revBuild], 100000))]
  • n - m (e.g. 1000 - 99999):

Same as above, only use limit like:

 = (val % (max - min)) + min
#[$(
    [MSBuild]::Add(
        $(minrev), 
        $([MSBuild]::Modulo(
            $(rev), 
            $([MSBuild]::Subtract(
                $(maxrev), 
                $(minrev)
             ))
        ))
    )
)]

You can test/debug all scripts with our testing tools, find it in Settings - Tools

Wizard or "help me, it's so hard"

ok, try the Wizard for the most common solutions.

Examples of real projects

It is actively used for the following projects:

Fully Automatic Versions and Sequential numbers

Any generating of the numbers can be whatever you want, see for example Increment & Decrement Numbers

Some projects and CI can use basic numbering such as 1, 2, 3, 4 .. 99, 100, 101 … 500, 501 etc.

That's why you need understand where to store the common number between environments. Moreover, most CI servers already provides special environment variables like $(appveyor_build_version), $(BUILD_NUMBER), etc. You can combine this or Increment this as you want !

$(buildNumber += 1)
or
$(buildNumber = $([MSBuild]::Add($(buildNumber), 1)))

References