2

Context:

I use MSBuild to build my projects. Currently I use a date of release version number that, unfortunately, lacks clarity when multiple releases occur in the same day. In Directory.Build.props:

  <PropertyGroup>
    <Version>
      $([System.DateTime]::Now.Year).
      $([System.DateTime]::Now.Month).
      $([System.DateTime]::Now.Day).
      $([System.Convert]::ToUInt16(
          $([MSBuild]::Divide(
              $([System.DateTime]::Now.TimeOfDay.TotalSeconds),
              1.32
          ))
      ))
    </Version>
  </PropertyGroup>

Goal:

Create a versioning scheme that looks something like this:

3/23/20:

  • Release Build: 2020.3.23.0

3/24/20:

  • Debug Build: 2020.3.24.0
  • Debug Build: 2020.3.24.1
  • Debug Build: 2020.3.24.2
  • Release Build: 2020.3.24.0
  • Debug Build: 2020.3.24.3
  • Release Build: 2020.3.24.1

Essentially: the first three numbers are year/month/day, because date of release is frequently important. Then use auto incrementing version numbers for releases within the same day. Incrementing on debug is useful so I can confirm the correct version of software is being loaded and run, but I don't want confusingly high numbers on release builds. I may play around with some additional indicator for debug builds, but I should be able to figure that out on my own.

Question:

How can I auto increment builds within the same day, having a separate version for debug and release? Ideally solutions that don't add additional dependencies are preferred, but if there is no way without, then it is acceptable.

8
  • Please edit your question to explicitly state, in objective terms, what your metrics for 'best way' are. Commented Mar 25, 2020 at 14:52
  • @TylerH Changed it to 'simplest' instead of 'best', and added the only stipulation I can think of. Though I feel that 'best' was fairly self explanatory here as "generally adhering to good coding practices and common sense". Commented Mar 25, 2020 at 18:08
  • The issue is good coding practices vary from person to person. Common sense is also, of course, a varying thing. Simplest is a little better, but still vague. Do you mean fewest lines of code? Fewest dependencies (additional packages, as you mentioned)? Etc. a good guide for objectivity is: if a total newbie and a 20-year pro both look at this question, would they come up with the same descriptors for what you're requesting? Would they both agree a given solution meets the needs of the problem statement? Etc. Commented Mar 25, 2020 at 18:12
  • @TylerH This feels nitpicky. Simplest is obviously impossible to define 100% objectively. Fewest lines of code could very well be super unclear and hacky. In light of this I'm just going to change it to remove all subjectivity - any functioning answer is fine. Commented Mar 25, 2020 at 18:15
  • "Simplest is obviously impossible to define 100% objective" Exactly! Which is why you should skip such terms as 'simplest' 'best' 'most idiomatic', etc. and just ask for what objectively-answerable terms you mean :-) Commented Mar 25, 2020 at 18:54

1 Answer 1

3

MSBuild auto increment build version differently for release/debug

In general, MSBuild did not have a function to see the version number of the obvious incremental build but only used the timestamp of the system build determines the build order as you used before.

In fact, if you create a custom property in msbuild to record the version number of the incremental build, it still needs to use an entity to store the record, and if it is not used, the parameter is reinitialized for each build (the msbuild attribute can only be identified in msbuild).

So the ideal way it that use textfile as an intermediate. You can follow my solution:

Solution

1) create a custom msbuild task which does increment the value of the record property.

--a) Create a class library project called MyCustomTask then Right-click on the project-->Add Reference-->reference Microsoft.Build.Framework dll and Microsoft.Build.Utilities.v4.0 dll.

enter image description here

--b) add these into CustomTask.cs(this is the name of the task which will be used in xxx.csproj file).

 public class CustomTask : Task
        {

            private int _number;
            [Required]
            public int number   //input taskitem
            {
                get { return _number; }

                set { _number = value; }

            }
            private int _lastnumber;
            [Output]
            public int LastNumber   //output value
            {
                get { return _lastnumber; }
                set { _lastnumber = value; }
             }

            public override bool Execute()   // Execution logic 
            {
                LastNumber = number + 1;
                return true;
            }
        }

--c) Then build the project and remember to store its MyCustomTask dll.

2) Aim to your main project and then create two txt files called Debug.txt,Release.txt and give each of them an initial value of 0.

enter image description here

3) add these into your Directory.Build.props file:

<Project>
  <UsingTask TaskName="CustomTask" AssemblyFile="xxxxxx\MyCustomTask\MyCustomTask\MyCustomTask\bin\Debug\MyCustomTask.dll(the local path of the dll)"> </UsingTask>    
  <PropertyGroup>
    <Record></Record>
  </PropertyGroup>

    <Target Name="WriteToFile1"  BeforeTargets="PrepareForBuild">
      <PropertyGroup>
      <Record Condition="'$(Configuration)'=='Debug' and !Exists('$(TargetPath)')">
        0
      </Record>
      <Record Condition="'$(Configuration)'=='Release'and !Exists('$(TargetPath)')">
        0
      </Record>
      </PropertyGroup>
      <ItemGroup Condition="'$(Configuration)'=='Debug'"> 
        <MyTextFile Include="Debug.txt">
        <Number>$(Record)</Number>
        </MyTextFile>
      </ItemGroup>
      <ItemGroup Condition="'$(Configuration)'=='Release'">
        <MyTextFile Include="Release.txt">
          <Number>$(Record)</Number>
        </MyTextFile>
      </ItemGroup>
      <WriteLinesToFile
        File="@(MyTextFile)"
        Lines="$(Record)"
        Overwrite="true"
        Encoding="Unicode" Condition="'$(Configuration)'=='Debug'"/>
      <WriteLinesToFile
        File="@(MyTextFile)"
        Lines="$(Record)"
        Overwrite="true"
        Encoding="Unicode" Condition="'$(Configuration)'=='Release'"/>
      <PropertyGroup>
        <Version>
          $([System.DateTime]::Now.Year).
          $([System.DateTime]::Now.Month).
          $([System.DateTime]::Now.Day).
          $(Record)
        </Version>
      </PropertyGroup>
    </Target>

  <Target Name="ReadLineFromFile" BeforeTargets="WriteToFile1">
    <ReadLinesFromFile File="Debug.txt" Condition="'$(Configuration)'=='Debug'">
      <Output TaskParameter="Lines" PropertyName="Record"/>
    </ReadLinesFromFile>
    <ReadLinesFromFile File="Release.txt" Condition="'$(Configuration)'=='Release'">
      <Output TaskParameter="Lines" PropertyName="Record"/>
    </ReadLinesFromFile>

    <CustomTask number="$(Record)">
      <Output TaskParameter="LastNumber" PropertyName="Record"/>
    </CustomTask>
    </Target>
</Project>

4) When you execute a task which depends on Build to show the property Version, it will work well as you hope.

Note that it will work for incremental build and if you click Rebuild(which execute Clean and then Build), it will set the version number to zero and start the rethrow.

Overall, this is an ideal solution which I try to realize it.

Sign up to request clarification or add additional context in comments.

3 Comments

Thank you, this served as a very useful start for me, and helped with several issues I ran into along the way. I made several changes for my personal implementation, notably: I used an inline task instead of a separate project and I stored the previous version in a JSON file in the $(IntermediateOutputPath). Thanks again for the effort you put into this.
@Aze it would have been nice if you'd posted your solution - give the level of detail passed to you
@ShhTot I can post it if desired, but there are still edge case bugs that I never ironed out, and it’s largely the same as the provided solution.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.