GenerateMsiVersion.cs 3.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using Microsoft.Build.Framework;

namespace Microsoft.DotNet.Build.Tasks
{
    // MSI versioning
    // Encode the CLI version to fit into the MSI versioning scheme - https://msdn.microsoft.com/en-us/library/windows/desktop/aa370859(v=vs.85).aspx
    // MSI versions are 3 part
    //                           major.minor.build
    // Size(bits) of each part     8     8    16
    // So we have 32 bits to encode the CLI version

    // For a CLI version based on commit count:
    //   Starting with most significant bit this how the CLI version is going to be encoded as MSI Version
    //   CLI major  -> 6 bits
    //   CLI minor  -> 6 bits
    //   CLI patch  -> 6 bits
    //   CLI commitcount -> 14 bits
    //
    // For a CLI version based on BuildTools versioning
    //   CLI major  -> 5 bits
    //   CLI minor  -> 5 bits
    //   CLI patch  -> 4 bits
    //   BuildNumber major -> 14 bits
    //   BuildNumber minor -> 4 bits
    public class GenerateMsiVersion : BuildTask
    {
        [Required]
        public string Major { get; set; }
        [Required]
        public string Minor { get; set; }
        [Required]
        public string Patch { get; set; }
        public string BuildNumber { get; set; }
        public string BuildNumberMajor { get; set; }
        public string BuildNumberMinor { get; set; }
        [Output]
        public string MsiVersion { get; set; }

        public override bool Execute()
        {
            if(BuildNumber == null && BuildNumberMajor == null)
            {
                Log.LogError("Either BuildNumber or BuildNumberMajor and BuildNumberMinor are required parameters.");
                return false;
            }
            if(BuildNumber != null && BuildNumberMajor != null)
            {
                Log.LogError("You must specify either BuildNumber or BuildNumberMajor and BuildNumberMinor, you cannot specify both parameters.");
                return false;
            }
            if (BuildNumberMajor != null && BuildNumberMinor == null)
            {
                Log.LogError("If you specify a BuildNumberMajor, you must also specify the BuildNumberMinor.");
                return false;
            }
            if(BuildNumber != null)
            {
                ParseBuildNumber();
            }
            else
            {
                ParseBuildNumberMajorMinor();
            }
            return true;
        }
        private void ParseBuildNumber()
        {
            var major = int.Parse(Major) << 26;
            var minor = int.Parse(Minor) << 20;
            var patch = int.Parse(Patch) << 14;
            var msiVersionNumber = major | minor | patch | int.Parse(BuildNumber);

            var msiMajor = (msiVersionNumber >> 24) & 0xFF;
            var msiMinor = (msiVersionNumber >> 16) & 0xFF;
            var msiBuild = msiVersionNumber & 0xFFFF;

            MsiVersion = $"{msiMajor}.{msiMinor}.{msiBuild}";
        }
        private void ParseBuildNumberMajorMinor()
        {
            var major = int.Parse(Major) << 27;
            var minor = int.Parse(Minor) << 22;
            var patch = int.Parse(Patch) << 18;

            var buildNumberMajor = int.Parse(BuildNumberMajor) & 0x3FFF << 4;
            var buildNumberMinor = int.Parse(BuildNumberMinor) & 0xF;

            var msiVersionNumber = major | minor | patch | buildNumberMajor | buildNumberMinor;

            var msiMajor = (msiVersionNumber >> 24) & 0xFF;
            var msiMinor = (msiVersionNumber >> 16) & 0xFF;
            var msiBuild = msiVersionNumber & 0xFFFF;

            MsiVersion = $"{msiMajor}.{msiMinor}.{msiBuild}";
        }

    }
}