Вышла новая версия Revit — время пересобирать плагины для поддержки новой версии!
Revit API от версии к версии меняется, и что делать, если вы написали какой-то плагин и хотите, чтобы он работал под разными версиями Revit? Конечно, можно просто сделать копию всего решения и подправить под новую версию, но как тогда вносить изменения — в каждую копию отдельно? Ну уж нет!
Решить задачу можно разными способами, опишу решение, к которому сам пришел.
К сожалению, в стандартном интерфейсе Visual Studio задачу не решить: там можно вводить несколько «конфигураций», но нам этого мало — ведь ещё и нужно подключать разные версии библиотек Revit API. К счастью, возможность всё-таки есть, но нужно будет подредактировать файлы вручную! Я буду использовать Notepad++ и собирать под версии Revit 2017-2022.
Для начала, конечно, надо закрыть Visual Studio. Далее открываем sln-файл в блокноте (предварительно сделайте резервную копию!). Нас интересуют вот эти две секции:

Здесь мы как раз вводим «Конфигурации сборки». В первой секции меняем текст на следующий:
R2017|Any CPU = R2017|Any CPU R2018|Any CPU = R2018|Any CPU R2019|Any CPU = R2019|Any CPU R2020|Any CPU = R2020|Any CPU R2021|Any CPU = R2021|Any CPU R2022|Any CPU = R2022|Any CPU R2023|Any CPU = R2023|Any CPU R2024|Any CPU = R2024|Any CPU R2025|Any CPU = R2025|Any CPU
Во второй секции обратите внимание на GUID в фигурных скобках. Сохраните его где-нибудь, он сейчас пригодится. Вставляем этот текст, но заменяем guid на свой:
{замените здесь на свой guid}.R2017|Any CPU.ActiveCfg = R2017|Any CPU
{замените здесь на свой guid}.R2017|Any CPU.Build.0 = R2017|Any CPU
{замените здесь на свой guid}.R2018|Any CPU.ActiveCfg = R2018|Any CPU
{замените здесь на свой guid}.R2018|Any CPU.Build.0 = R2018|Any CPU
{замените здесь на свой guid}.R2019|Any CPU.ActiveCfg = R2019|Any CPU
{замените здесь на свой guid}.R2019|Any CPU.Build.0 = R2019|Any CPU
{замените здесь на свой guid}.R2020|Any CPU.ActiveCfg = R2020|Any CPU
{замените здесь на свой guid}.R2020|Any CPU.Build.0 = R2020|Any CPU
{замените здесь на свой guid}.R2021|Any CPU.ActiveCfg = R2021|Any CPU
{замените здесь на свой guid}.R2021|Any CPU.Build.0 = R2021|Any CPU
{замените здесь на свой guid}.R2022|Any CPU.ActiveCfg = R2022|Any CPU
{замените здесь на свой guid}.R2022|Any CPU.Build.0 = R2022|Any CPU
{замените здесь на свой guid}.R2023|Any CPU.ActiveCfg = R2023|Any CPU
{замените здесь на свой guid}.R2023|Any CPU.Build.0 = R2023|Any CPU
{замените здесь на свой guid}.R2024|Any CPU.ActiveCfg = R2024|Any CPU
{замените здесь на свой guid}.R2024|Any CPU.Build.0 = R2024|Any CPU
{замените здесь на свой guid}.R2025|Any CPU.ActiveCfg = R2025|Any CPU
{замените здесь на свой guid}.R2025|Any CPU.Build.0 = R2025|Any CPU
Результат будет примерно такой:

Готово, сохраняем и закрываем этот файл.
Далее будет посложнее. Заходим в папку с решением и открываем csproj-файл (для него тоже не забудьте сделать резервную копию!). В нём нам предстоит задать, что именно будет делать Студия в зависимости от выбранной конфигурации. Этот файл имеет xml-синтаксис: <Свойство> Содержимое </Свойство>. Надо не забывать, что у каждого открывающего тэга должен быть закрывающий с таким же именем, помеченный значком слэш /. В Notepad++ есть подсветка синтаксиса и работать достаточно удобно.
Структура файла будет примерно следующая:
<Project>
//начальные настройки не трогаем
<PropertyGroup>
Общие настройки не трогаем
</PropertyGroup>
<PropertyGroup>
Добавляем особые настройки под R2017
</PropertyGroup>
<PropertyGroup>
Особые настройки под R2018 и т.д.
</PropertyGroup>
<ItemGroup>
Общие подключаемые библиотеки
</ItemGroup>
<Choose>
<When Condition = 2017>
Подключаемые библиотеки для R2017
</When>
<When Condition = 2017>
Подключаемые библиотеки для R2018, и т.д.
</When>
</Choose>
//остальные настройки не трогаем
</Project>
Главная магия будет происходить в блоке Choose/When: как раз здесь мы можем указать, что в зависимости от конфигурации будут подключаться разные библиотеки. Намечаем план корректировок:

Смотрим блок конфигурации под конкретную версию. Содержимое его будет примерно такое:
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'R2017|AnyCPU' "> <DebugSymbols>true</DebugSymbols> <DebugType>full</DebugType> <Optimize>false</Optimize> <OutputPath>bin\R2017\</OutputPath> <DefineConstants>DEBUG;R2017</DefineConstants> <TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion> <AssemblyName>$(AssemblyName)_2017</AssemblyName> </PropertyGroup>
Опции DebugSymbols, Optimise и DebugType нужны, чтобы работала отладка и присоединение к процессу Revit в VisualStudio.
OutputPath — папка, куда будет складироваться dll-ка. У меня сделано, что каждая версия будет складываться в папку с номером версии. AssemblyName, соответственно, — шаблон имени dll-файла, у меня будет как ИмяРешения_НомерВерсии.dll.
DefineConstants — важная вещь, это «ключи для компиляции», которые можно будет использовать в коде (далее покажу).
TargetFramework — какую версию .Net Framework использовать при сборке:
- Revit 2017: v4.5.2
- Revit 2018: v4.6
- Revit 2019, 2020: v4.7
- Revit 2021, 2022: v4.8
.NET Framework устанавливается вместе с Ревитом, но только «облегченная» версия — для запуска готовых приложений, не для их сборки. Нужно установить именно версии Developer Pack:

Итак, копируем PropertyGroup столько раз, сколько у нас конфигураций, и внимательно меняем значения на нужные нам:

Далее в секции ItemGroups удаляем блоки Reference со ссылками на библиотеки Revit и после окончания этой ItemGroup добавляем секцию Choose/When:

«When», соответственно, дублируем столько же раз, сколько у нас конфигураций.
Готово! Сохраняем файл и открываем решение в VisualStudio. В списке сверху должны появиться все заданные конфигурации, и если их переключать — будет видно, что меняется путь в свойствах подключенных RevitAPI и RevitAPIUI:

Но для чего мы всем этим занимались? Для того, чтобы можно было использовать символы условной компиляции! Изменения в Revit API обычно вносятся небольшие, и весь код остается неизменный, но где-то в одном месте выдает ошибку из-за изменений в API. Например, свойство Rebar.Normal работает в Revit 2017, но в 2018 уже помечено как «Устаревшее», а в 2019 просто будет выдавать ошибку:

Мы можем просто «обернуть» этот блок кода и сказать, чтобы он выполнялся только для одной конфигурации, а для другой — игнорировался! Это делается синтаксисом #if #endif. Обратите внимание, как при переключении «текущей конфигурации» код «отключается» и вообще не будет компилироваться.

R2017 после #if — это как раз то, что мы ранее указывали в DefineConstants. Можно вводить несколько через ||.
Если в тот момент, когда выбрана какая-то конфигурация, запустить «Пересобрать проект», то соберется dll именно с нужным кодом и подключенными библиотеками и сохранится в нужной папке.
Но можно сделать ещё круче — использовать Пакетную сборку! Зайдите Build — Batch Build, просто отметьте все флажки, нажмите «Пересобрать» и разом получите набор dll-ок под каждую версию Revit:


Что-то не получилось? Вот примеры файлов на Github: sln, csproj.
Успехов в изучении C# и Revit API!