Specifing different versions of the same Nuget package in one .csproj file

Specifing different versions of the same Nuget package in one .csproj file
Photo by Arnold Francisca / Unsplash

If you are developing AutoCAD plugins, you may need to build different versions of your project for different AutoCAD versions. In this blog post, we will explore how to design a csproj file that allows building different versions of an AutoCAD plugin for different AutoCAD versions from one .csproj file (or .vbproj file, respectively).

The problem with building different versions of an AutoCAD plugin

Each version of AutoCAD comes with its own API version, so plugins that are built for one version of AutoCAD may not be compatible with another version. In this case, you need to build separate versions of your plugin for each AutoCAD version that you want to support. However, building separate versions of your plugin means having separate .csproj files which share the same code base but use different AutoCAD APIs. This is a maintenance hell as the number of versions grows.

The solution: using different build configurations to specify AutoCAD versions

One way to solve this problem is to use different build configurations in your .csproj file to specify the AutoCAD version for which you are building the plugin. You can create different build configurations for each AutoCAD version, and then use the Choose element to specify different options based on the build configuration.

Here's part of the .csproj file from Linq2Acad:

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug_2022|AnyCPU'">
    <DebugSymbols>true</DebugSymbols>
    <OutputPath>bin\Debug_2022\</OutputPath>
    <DefineConstants>DEBUG;TRACE;AutoCAD_2022</DefineConstants>
    <DocumentationFile>bin\Debug_2022\Linq2Acad.xml</DocumentationFile>
    <NoWarn>1591</NoWarn>
    <DebugType>full</DebugType>
    <PlatformTarget>AnyCPU</PlatformTarget>
    <LangVersion>7.3</LangVersion>
    <ErrorReport>prompt</ErrorReport>
    <TargetFrameworkVersion>v4.7</TargetFrameworkVersion>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug_2023|AnyCPU'">
    <DebugSymbols>true</DebugSymbols>
    <OutputPath>bin\Debug_2023\</OutputPath>
    <DefineConstants>DEBUG;TRACE;AutoCAD_2023</DefineConstants>
    <DocumentationFile>bin\Debug_2023\Linq2Acad.xml</DocumentationFile>
    <NoWarn>1591</NoWarn>
    <DebugType>full</DebugType>
    <PlatformTarget>AnyCPU</PlatformTarget>
    <LangVersion>7.3</LangVersion>
    <ErrorReport>prompt</ErrorReport>
    <TargetFrameworkVersion>v4.7</TargetFrameworkVersion>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release_2022|AnyCPU' ">
    <DebugType>pdbonly</DebugType>
    <Optimize>true</Optimize>
    <OutputPath>bin\Release_2022\</OutputPath>
    <DefineConstants>TRACE;AutoCAD_2022</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
    <Prefer32Bit>false</Prefer32Bit>
    <NoWarn>1591</NoWarn>
    <DocumentationFile>bin\Release_2022\Linq2Acad.xml</DocumentationFile>
    <TargetFrameworkVersion>v4.7</TargetFrameworkVersion>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release_2023|AnyCPU' ">
    <DebugType>pdbonly</DebugType>
    <Optimize>true</Optimize>
    <OutputPath>bin\Release_2023\</OutputPath>
    <DefineConstants>TRACE;AutoCAD_2023</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
    <Prefer32Bit>false</Prefer32Bit>
    <NoWarn>1591</NoWarn>
    <DocumentationFile>bin\Release_2023\Linq2Acad.xml</DocumentationFile>
    <TargetFrameworkVersion>v4.7</TargetFrameworkVersion>
  </PropertyGroup>
  <ItemGroup>
    <!-- Reference items -->
  </ItemGroup>
  <ItemGroup>
    <!-- Compile items -->
  </ItemGroup>
  <Choose>
    <When Condition="$(DefineConstants.Contains('AutoCAD_2022'))">
      <ItemGroup>
        <PackageReference Include="AutoCAD.NET.Core">
          <Version>24.1.51000</Version>
          <ExcludeAssets>runtime</ExcludeAssets>
        </PackageReference>
      </ItemGroup>
    </When>
    <When Condition="$(DefineConstants.Contains('AutoCAD_2023'))">
      <ItemGroup>
        <PackageReference Include="AutoCAD.NET.Core">
          <Version>24.2.0</Version>
          <ExcludeAssets>runtime</ExcludeAssets>
          <IncludeAssets>compile; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
        </PackageReference>
      </ItemGroup>
    </When>
  </Choose>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

In this example, we have defined four different build configurations, Debug_2022, Debug_2023, Release_2022 and Release_2023, for AutoCAD 2022 and AutoCAD 2023, respectively. Each build configuration defines a constant AutoCAD_XXXX to specify the AutoCAD version. As an example, for AutoCAD 2022 we defined Debug_2022 and Release_2022 which both define the constant AutoCAD_2022.

The important part is the Choose element in line 58. Each When-branch of the Choose element checks for the existence of a constant and selects the Nuget package if the condition is true.

As a result, when you build the project with the Debug_2022 configuration, the project will reference the AutoCAD 2022 API, and when you build the project with the Debug_2023 configuration, the project will reference the AutoCAD 2023 API.