﻿# Как обновить библиотеку и утонуть в задаче\. Обновление Roslyn и PVS\-Studio 7\.34

Ежегодно Microsoft выпускает новую версию \.NET\. Это большое событие, к которому мы выпускаем версию PVS\-Studio с поддержкой нововведений\. Сегодня речь пойдёт про боль PVS\-Studio при обновлении Roslyn — неотъемлемой части \.NET\.

![1206_RoslynUpdateAndPVS-Stduio7_34_ru/image1.png](https://import.viva64.com/docx/blog/1206_RoslynUpdateAndPVS-Stduio7_34_ru/image1.png)

Для удобства я поделил статью на две части: краткую \(для тех, кто хочет быстро найти обоснование изменений, произошедших в PVS\-Studio 7\.34\) и длинную \(для тех, кто хочет узнать ещё и историю\)\.

## Краткая версия

В [релизе PVS\-Studio 7\.34](https://pvs-studio.ru/ru/blog/posts/1200/) была добавлена поддержка \.NET 9 и всех новых функций языка C\#\. Для этого нам требовалось обновить платформу Roslyn, которая используется PVS\-Studio для синтаксического анализа языка C\#, а также обновить взаимозависимые библиотеки MSBuild, используемые для построения модели и парсинга проектов на C\# и C\+\+ \(\.csproj, \.vcxproj\)\.

Начиная с версии PVS\-Studio 7\.34, для анализа проектов C\# \.NET, \.NET Standard и \.NET Framework в стиле SDK требуется дополнительная установка \.NET 9 SDK в системе, выполняющей анализ\.

Классические проекты \.NET Framework остаются без изменений, если в системе, выполняющей анализ, установлен экземпляр MSBuild или Visual Studio версий 2017 и выше\. Если в системе установлены только MSBuild или Visual Studio версий 2015 и ниже, то для анализа классических проектов \.NET Framework также потребуется наличие \.NET 9 SDK\.

Причиной этого изменения стала потеря обратной совместимости библиотек Roslyn, используемых для анализа проектов из Visual Studio\\MSBuild версий 2015 и ниже\. Теперь для этого Roslyn использует собственный серверный компонент на базе \.NET SDK, который, в свою очередь, требует наличия в системе \.NET SDK\.

Краткая версия — всё\.

## Длинная версия

### Обновление PVS\-Studio C\#

В [релизе PVS\-Studio 7\.34](https://pvs-studio.ru/ru/blog/posts/1200/) была добавлена поддержка \.NET 9 и всех новых функций языка C\#\. 

Кстати, предлагаю вам прочитать две наши статьи про нововведения:

* [Что нового в \.NET 9?](https://pvs-studio.ru/ru/blog/posts/csharp/1184/)
* [Обзор нововведений в C\# 13](https://pvs-studio.ru/ru/blog/posts/csharp/1173/)

Для поддержки \.NET 9 нам потребовалось обновить библиотеки Roslyn и MSBuild\. PVS\-Studio и Roslyn используют MSBuild для построения модели проекта \(Design\-Time Build\)\. Это достаточно сложный процесс, который требует определённых знаний от сотрудника \(или его ментора\)\. Вы могли бы подумать: а что сложного нажать update на соответствующих NuGet пакетах из IDE? До поддержки нами Visual Studio 2017 так и было\. Я бы мог начать рассказывать, что изменилось, и почему недостаточно обновить пакеты, но лучше поделюсь [ссылкой на статью](https://pvs-studio.ru/ru/blog/posts/csharp/0503/), где об этом написано\.

Но краткую вводную всё равно придётся рассказать\. Что приходится делать для поддержки нового \.NET и почему? MSBuild, начиная с версии 15 \(VS 2017\), не умеет находить собственную установочную директорию\. Предполагаю, это связано с тем, что MSBuild с версии 15 и выше не прописывается в реестре\. Чтобы обойти эту проблему, было предложено решение:

1. Мы создаём собственный кастомный toolset\. Для этого нам нужно тащить с собой изменённый BuildTools, который содержит большое количество ИЗМЕНЁННЫХ \.target и \.props файлов;
1. Ищем установочные директории MSBuild и на основе этой информации изменяем значение свойств в кастомном toolset;
1. Подсовываем MSBuild путь до нашего BuildTools, чтобы он считал кастомный toolset;
1. Profit\!

Исходя из этого, для поддержки нового \.NET нужно:

1. Обновить библиотеки Roslyn, MSBuild и необходимые им;
1. Справиться с некоторым [Dependency hell](https://en.wikipedia.org/wiki/Dependency_hell);
1. Обновить наш BuildTools до последней версии, сохраняя обратную совместимость с прошлыми версиями в некоторых местах;
1. Исправить проблемы, которые возникнут при обновлении \(а они точно возникнут, иначе не было бы этой статьи\)\.

Это очень упрощённая схема, и лучше бы вам прочитать статью по ссылке выше\. Вдобавок есть ещё статьи про поддержку [Visual Studio 2019](https://pvs-studio.ru/ru/blog/posts/csharp/0630/) и [Visual Studio 2022](https://pvs-studio.ru/ru/blog/posts/0920/)\. В них также описываются возникающие проблемы\. Прочитав всё это вы могли подумать: мммм, вкусное legacy\.

![1206_RoslynUpdateAndPVS-Stduio7_34_ru/image2.png](https://import.viva64.com/docx/blog/1206_RoslynUpdateAndPVS-Stduio7_34_ru/image2.png)

Да, legacy\.\.\. Теперь уже есть решение для поиска MSBuild — [MSBuildLocator](https://github.com/microsoft/MSBuildLocator)\. Но у него тоже есть свои ограничения: он разделён на сборки для \.NET Framework и \.NET Core\. Версия для \.NET Framework может искать и регистрировать MSBuild только для \.NET Framework, версия \.NET Core —только версию MSBuild для \.NET Core\. Предположу, что это связано с тем, что MSBuild также разделён на MSBuild для \.NET Framework и на MSBuild для \.NET Core\.

### Проблемы и решения

Теперь, когда вы немного лучше понимаете, с чем приходится иметь дело, начнём\. 

Вышел \.NET 9 RC 1, и мы приступили к поддержке\. Первым делом обновили библиотеки и наш BuildTools\. Во время обновления мы заметили, что в пакете Microsoft\.CodeAnalysis \(Roslyn\) появились две новые папки: BuildHost\-net472 и BuildHost\-netcore\. Сначала я даже обрадовался и подумал, что теперь у нас будет меньше проблем\. Но, как оказалось, это не так\.

Обновление библиотек и BuildTools прошло довольно быстро, поскольку у нас уже был такой опыт\. После этого этапа разработчик запускает локальный набор тестов, чтобы убедиться в корректности анализа\. Эти тесты содержат большое количество Open Source проектов, на которых запускается анализатор\. После их запуска возникли проблемы: в некоторых фрагментах кода анализатор начал сообщать \(с помощью внутренних логов\) об ошибках компиляции в полностью компилируемом коде, где\-то были выброшены исключения\. Мы приступили к правкам и успешно довели локальные тесты до полного прохождения\.

Следующий этап — запуск обновлённой версии на тестах, запускающих анализатор на шаблонных проектах под разные версии Visual Studio и \.NET\. Эти тесты запускаются в контейнерах с разным окружением\. И тут мы видим, что у нас сломался анализ на машинах, где установлена только VS 2010, 2012, 2013, 2015, и сломался анализ \.NET Framework проектов SDK стиля c VS 2017, 2019, 2022, но без \.NET SDK\.

Почему предыдущие тесты прошли и не выявили проблем? Они запускались локально на машине разработчика, где установлены, по сути, все версии Visual Studio и множество версий \.NET SDK\.

Мы начали разбираться и нашли проблему: обновлённый Roslyn сделал не лучше, а хуже\.

![1206_RoslynUpdateAndPVS-Stduio7_34_ru/image3.png](https://import.viva64.com/docx/blog/1206_RoslynUpdateAndPVS-Stduio7_34_ru/image3.png)

Раньше Roslyn ожидал, что пользователи будут использовать MSBuildLocator или что\-то похожее\. У нас же был свой BuildTools и свои toolset\-ы, которые Roslyn использовал\. Это позволяло нам поддерживать C\# проекты даже для Visual Studio 2010\. И в целом оставалась только сложность с поддержкой нового \.NET\. Ранее я писал, что Microsoft\.CodeAnalysis теперь включает две дополнительные папки\. Roslyn перешёл на новую архитектуру с Build серверами для самостоятельного поиска и Design\-Time сборки проектов\. Теперь он тянет с собой два BuildHost: для \.NET Framework и для \.NET Core\. В зависимости от типа проекта Roslyn вызывает соответствующий процесс BuildHost для сборки проекта\. Для поиска MSBuild используется MSBuildLocator\. И здесь есть несколько проблемных моментов\. 

#### Первая проблема

Для работы BuildHost для \.NET Core требуется минимум \.NET 6 Runtime\. И так как PVS\-Studio использует Roslyn, пользователь должен иметь установленный \.NET 6\+ Runtime\. В целом это можно понять и принять\. Вы можете сказать: а почему бы не публиковать BuildHost для \.NET Core как self\-contained приложение или даже использовать NativeAOT? К сожалению, сильно вырастет вес, который будет занимать \.exe файл\. Но основное, это что для регистрации MSBuild под капотом используется Assembly\.Load, который просто не работает в подобных приложениях\.

#### Вторая проблема

MSBuildLocator для \.NET Framework может искать только MSBuild 15, 16, 17 \(Visual Studio 2017, 2019, 2022\)\. Соответственно, если у пользователя достаточно старый проект и он использует Visual Studio 2015, то Roslyn не может найти подходящий MSBuild и просто не будет работать\. И это учитывая тот факт, что проект полностью собирается на машине\. Мы создали [issue](https://github.com/dotnet/roslyn/issues/75886) на GitHub об этом\. Если кратко, ответ состоит в том, что для них это не приоритет, так как VS 2015 и прошлые версии слишком старые\. Но оказалось, что если у пользователя имеется \.NET SDK, то Roslyn начинает использовать запасной план\. Если у вас старый \.NET Framework проект, то Roslyn попробует использовать BuildHost для \.NET Core\. Чаще всего это работает нормально, но возможны проблемы, если встречается что\-то, что не поддерживается MSBuild для \.NET Core\. Про это вы узнаете в описании третьей проблемы\.

Из всего этого мы приходим к тому, что, если мы хотим оставить поддержку старых проектов, нам нужно поставлять \.NET SDK\. Для целостности продукта мы решили поставлять с собой на Windows \.NET 9 SDK, как уже делаем на Linux и macOS\. На этих операционных системах анализатору для работы нужен \.NET SDK той версии, под которую он был собран\. Зачем нам \.NET 9 SDK на Linux и macOS — это отдельная тема для разговора, но поверьте, он нужен\.

#### Третья проблема

Roslyn для любых проектов SDK стиля начинает использовать BuildHost для \.NET Core, и неважно, может ли MSBuild для \.NET Core собрать его\. MSBuild для \.NET Core поддерживает не все вещи, которые поддерживает MSBuild для \.NET Framework\. Например, `COMReference` не поддерживается в MSBuild для \.NET Core\. По этой проблеме тоже было создано [issue](https://github.com/dotnet/roslyn/issues/75558)\. Как это решается с нашей стороны? Да в общем и целом никак\. Подобные точечные ошибки выливаются в мелкие недостатки полученной от Roslyn семантической модели\. И это никак не влияет на качество анализа\.

Это только самые запомнившиеся проблемы\. По ходу обновления мы сталкивались ещё с ворохом маленьких проблем\. 

### Мысли

Что же мы имеем по итогу улучшений в Roslyn от Microsoft? Усложнение дистрибуции анализатора, необходимость для клиента ставить ненужные ему \.NET SDK и десятки дней, потраченных на то, чтобы всё заработало\.

Зачем же эта статья? Хотелось поделиться небольшой историей о том, как обновление и улучшение инструмента делает жизнь не лучше, а хуже\. И на сам деле многое можно было избежать, если бы Roslyn более правильно выбирал BuildHost\. А ещё хотелось иметь быстрый ответ на клиентский вопрос: "Почему так?" Для этого была написана краткая версия\.

Во время чтения вам могло показаться, что я достаточно негативно высказываюсь о Roslyn\. Может, так и было, но только чуть\-чуть и из\-за того, что мне сильно прибавили работы :\) Я всё ещё считаю Roslyn отличным инструментом, который во многом облегчает разбор и анализ кода\.

Также скажу, что в будущем мы бы хотели избавиться от наших BuildTools и перейти к похожей на Roslyn архитектуре\. Как обычно и бывает, это достаточно сложная задача, так как много старого кода нужно переписать или выкинуть и написать заново\. И при этом нужно сохранить совместимости со старыми проектами\. Короче, та ещё работа\.

А вам большое спасибо за прочтение :\)