ProGuide How to increment addin version number - Esri/arcgis-pro-sdk GitHub Wiki

Language:      C#
Subject:       Framework
Contributor:   ArcGIS Pro SDK Team <[email protected]>
Organization:  Esri, http://www.esri.com
Date:          04/25/2025
ArcGIS Pro:    3.5
Visual Studio: 2022

This guide will discuss 2 approaches for implementing a tool to auto-increment the build number component of your add-in or configuration version attribute within the Config.daml. Both approaches achieve the same result and your choice will mostly depend on preferences. For convenience, this guide will only refer to add-ins but could equally be applied to configurations.

In this Topic

Background

Versioning within an addin is controlled via the version attribute of the <AddInInfo ...> element within the add-in (or configuration) Config.daml. This guide describes how to "link" either an external, standalone exe or an MSBuild script to the Visual Studio .csproj build to auto-increment the last digit/number component of the add-in version number attribute in the Config.daml. Either of the standalone exe or msbuild script can be executed as post-build events in your .csproj. For more information on the structure of the add-in version attribute and its purpose, consult: ProConcepts Advanced Topics, Add-in Versioning

<!-- Config.daml -->
<ArcGIS defaultAssembly="..." defaultNamespace="..." ...>
  <AddInInfo id="{8cbb36b4...}" version="1.0" desktopVersion="3.5">
    <Name>AcmeTestAddin</Name>
    <Description>...

In both cases, the executable or the msbuild script within a ".targets" file will (auto) increment the last component of your add-in version attribute. So, for example, if you are using a 2-digit, 3-digit or 4-digit version number then after executing a build of your add-in project within visual studio (assuming you have configured the post-build event per this guide):

  • version="1.0" → version="1.1"
  • version="1.0.27" → version="1.0.28"
  • version="1.0.0.223" → version="1.0.0.224"

Modify the logic provided in either approach, as needed, if you wish to auto-increment other components of your add-in version number.

Executable Based (C#) Solution

With this approach, we will create a standalone c# console exe that auto-increments your add-In's version number.

Setup Instructions

Step 1: In Visual Studio 2022, create a new Console App project in C#. For purposes of this guide, it is assumed you name it "UpdateAddInVersion".

Step 2: Replace the content of the Program.cs with the following code:

using System;
using System.Xml;
using System.Xml.Linq;
using static System.Net.Mime.MediaTypeNames;

class Program {

 static void Main(string[] args) {

   try {
     // Find the config.daml file - the project folder is passed in as the
     //command line argument
     string filePath = System.IO.Path.Combine(args[0], "Config.daml");

     Console.WriteLine($"UpdateAddInVersion: Working directory: " +
       "{System.IO.Directory.GetCurrentDirectory()}...");
     if (!File.Exists(filePath))
     {
      string absolute = Path.GetFullPath(filePath);
      throw new Exception($@"File not found: {filePath} [{absolute}]");
     }

     // Parse the XML content of the config.daml
     XmlDocument xmlDoc = new();
     xmlDoc.Load(filePath);

     if (xmlDoc == null || xmlDoc.ChildNodes == null)
     {
      Console.WriteLine("Failed to load XML file.");
      return;
     }
            
     // Find the attribute we want to update ("version" on the "AddInInfo" node)
     var AddInInfoNode = xmlDoc.GetElementsByTagName("AddInInfo");
     if (AddInInfoNode == null || AddInInfoNode.Count > 1)
     {
      Console.WriteLine("AddInInfo node not compliant");
      return;
     }

     var version = AddInInfoNode[0]?.Attributes?.GetNamedItem("version");
     if (version == null) //This should never happen!!
     {
      Console.WriteLine("AddInInfo node doesn't have a version attribute.");
      return;
     }

     // Parse the content of the version attribute
     XmlAttribute versionAttribute = 
          AddInInfoNode[0]?.Attributes?.GetNamedItem("version") as XmlAttribute;
     if (versionAttribute != null)
     {
      string originalVersion = versionAttribute.Value;

      // Add 1 to the last component of the version
      int currentVersion = Convert.ToInt32(originalVersion.Substring(
                                              originalVersion.LastIndexOf('.') + 1));
      string newVersion = originalVersion.Replace(
	originalVersion.Substring(
	  originalVersion.LastIndexOf('.')), $".{currentVersion + 1}");

      // Update it
      versionAttribute.Value = newVersion;
      xmlDoc.Save(filePath);
      Console.WriteLine(
        $"UpdateAddInVersion: Version updated from {originalVersion} to {newVersion}");
     }
   }
   catch (Exception ex)
   {
     Console.WriteLine($"An error occurred: {ex.Message}");
   }
 }
}

(This implementation creates a standalone .NET 8 executable with no "special" dependencies - other than .NET 8)

Step 3: Build the Console project. Fix any compilation errors.

Step 4: Relocate, if needed, the console program to a folder of your choosing.

Step 5: Create a “Post-Build event” in your AddIn or Configuration project that references the Console program. It takes a single argument which is the project folder path to your add-in. You can use the convenient msbuild environment variable $(MSBuildProjectDirectory).

Follow these steps to create the post build event:

  • Right click the Add-in or Configuration project in the Solution Explorer and select "Properties" from the bottom of the context menu.
  • On the Properties page, expand the "Build" folder and select "Events".
  • In the “Post-Build event” text box on the Events page, enter in the full path to the UpdateAddInVersion.exe and a single argument that is the path to the add-in project directory. Your post-build event content should look similar to this:

E:\Data\SDK\UpdateAddInVersion\bin\Release\net8.0\UpdateAddinVersion.exe '$(MSBuildProjectDirectory)'

Obviously, the path to the location of UpdateAddinVersion.exe on your file system will be different.

Note: If you see an error message or an exception in the Build Output of Visual Studio check that the path being provided to UpdateAddinVersion.exe is the correct path to the project folder of your add-in containing the Config.daml file.

MSBuild script solution

Create a MSBuild script (AddinVersion.targets file) to auto-increment your add-in's version number. By default, we'll have the tool update the minor build number element of the version attribute in the config.daml file every time you build your project so version="1.0" becomes version="1.1".
In the text editor of your choice, create a new file (AddinVersion.targets) and paste the following content in it. Save the resulting file as AddinVersion.targets to c:\BuildTools (for example).

Code

<?xml version="1.0" encoding="utf-8" ?>
<Project>
  <PropertyGroup>
    <ConfigDAMLPath>$([System.IO.Path]::Combine($(MSBuildProjectDirectory), `config.daml`))</ConfigDAMLPath>
  </PropertyGroup>
 <UsingTask TaskName="UpdateAddInVersion" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll">
  <ParameterGroup>
    <!-- Defaults to string type -->
    <ConfigDAMLPath Required="True"/>
	  <IndexElement   Required="False"/>
  </ParameterGroup>		
  <Task>
    <Reference Include="System.Xml.Linq"/>
    <Reference Include="System.Xml"/>
    <Using Namespace="System.IO" />
    <Using Namespace="System.Xml" />
    <Code Type="Fragment" Language="cs">
    <![CDATA[
      string filePath = ConfigDAMLPath;
      Log.LogMessage("Reading file " + filePath);
      if (!string.IsNullOrEmpty(IndexElement))
      	Log.LogMessage("Processing " + IndexElement);
      else
      	Log.LogMessage("Processing build number in 'major.minor.build.revision' ");

      XmlDocument doc = new XmlDocument();
      doc.Load(filePath);

      if (doc == null || doc.ChildNodes == null)
      {
        Log.LogError("Failed to load XML file.");
        return false;
      }

      var AddInInfoNode = doc.GetElementsByTagName("AddInInfo");
      if (AddInInfoNode == null || AddInInfoNode.Count > 1)
      {
        Log.LogError("AddInInfo node not compliant");
        return false;
      }

      var version = AddInInfoNode[0].Attributes.GetNamedItem("version");
      if (version == null)
      {
        Log.LogError("AddInInfo node doesn't have a version attribute.");
        return false;
      }

      XmlAttribute versionAttribute = AddInInfoNode[0].Attributes.GetNamedItem("version") as XmlAttribute;
      if (versionAttribute != null)
      {
        string originalVersion = versionAttribute.Value;

        string[] versionParts = originalVersion.Split('.');

        // If no specific element is specified, increment the build number
        if (string.IsNullOrEmpty(IndexElement))
        {
          Log.LogMessage("No parameter specified, incrementing build number");
          if (versionParts.Length >= 3)
          {
            int buildNumber;
            if (int.TryParse(versionParts[2], out buildNumber))
            {
              buildNumber++;
              versionParts[2] = buildNumber.ToString();
              string versionNode = string.Join(".", versionParts);
              versionAttribute.Value = versionNode;
              doc.Save(filePath);
              Log.LogMessage("Version updated from " + originalVersion + " to " + versionNode);
              return true;
            }
            else
            {
              Log.LogError("Build number is not a valid integer.");
              return false;
            }
          }
          else
          {
            Log.LogError("Version format is incorrect. Expected format: major.minor.build or major.minor.build.revision - updating build.");
            return false;
          }
        }
        else
        {
          int elt = -1;
          switch (IndexElement.ToLower())
          {
            case "major":
              elt = 0;
              break;
            case "minor":
              elt = 1;
              break;
            case "build":
              elt = 2;
              break;
            case "revision":
              elt = 3;
              break;
            default:
              Log.LogError("Invalid element specified: " + IndexElement);
              return false;
          }

          if (elt > versionParts.Length - 1)
          {
            Log.LogError("Version format is incorrect. Expected format: major.minor.build.revision\nInstructed to update " + IndexElement);
            return false;
          }
          else
          {
            int elementNumber;
            if (int.TryParse(versionParts[elt], out elementNumber))
            {
              elementNumber++;
              versionParts[elt] = elementNumber.ToString();
              string newversion = string.Join(".", versionParts);
              versionAttribute.Value = newversion;
              doc.Save(filePath);
              Log.LogMessage("Version updated from" + originalVersion + " to " + newversion);
              return true;
            }
            else
            {
              Log.LogError(IndexElement + " is not a valid element index.");
              return false;
            }
          }
        }
      }
      else
      {
        Log.LogError("Version node not found in the configuration file: " + filePath);
        return false;
      }
    ]]>
    </Code>
  </Task>
 </UsingTask>
 <Target Name="PostBuildAddInVersionUpdate" AfterTargets="Build">
    <Message Text="ConfigDAMLPath Name: $(ConfigDAMLPath)..." Importance="High"/>
    <UpdateAddInVersion ConfigDAMLPath="$(ConfigDAMLPath)" IndexElement="minor"/>
  </Target>
</Project>

Now that the targets file is created, you need to edit your project to reference it:
right-click on your add-in project and select Edit Project File in the context menu: Context Menu entry
At the bottom of the project file, right before the ArcGIS Pro SDK targets import statement, insert the following line:

<Import Project="C:\BuildTools\AddInVersion.targets"/>

Project File Content

Notes:

  • As a result, your add-in will have its version tag in config.daml auto-incremented.
  • The script expects a version attribute in the form of major.minor.build.revision
  • By default, the script will update the minor element of the version (a.k.a. the 2nd digit)
  • The script can be changed to update any part of a version number as long as it exists in config.daml, by changing its (optional) IndexElement parameter:
    o revision for the 4th digit
    o build for the 3rd digit
    o minor for the 2nd digit
    o major for the 1st digit
    This can be achieved by editing the AddInVersion.targets file to add an IndexElement parameter with the appropriate target element (revision in this example): IndexElement change

Change the MSBuild script as needed for your individual workflows.

⚠️ **GitHub.com Fallback** ⚠️