Расширяемость Visual Studio. Часть Первая: Msbuild

Привет Хабр, в этих статьях я постараюсь осветить тему расширений Microsoft Visual Studio (а попутно и MSBuild), потому что… эта область крайне плохо документирована и вообще покрыта пеленой какой-то тайны.



Расширяемость Visual Studio. Часть первая: MSBuild



Пролог

Я профессиональный C++-разработчик с довольно большим опытом, а также большой поклонник продуктов Microsoft и, в первую очередь, моего основного инструмента разработки — Visual Studio. Не так давно в качестве хобби я начал программировать микроконтроллеры, и выбрал микроконтроллеры от Microchip. Единственное, что меня не устроило, это использование средств разработки, которые предоставляет сама Microchip. Ничего плохого об этих продуктах сказать не могу, просто не хотелось бы устанавливать на свой рабочий (или домашний) компьютер несколько IDE, поэтому родилась идея интегрировать компилятор XC8 от Microchip в Microsoft Visual Studio. Позже я увидел в этой идее еще один плюс — многие мои (и не только мои) проекты прямо или косвенно связаны с подключением их к компьютеру, поэтому мне предстоит разработать адаптивную программную часть — было бы здорово объединить их в одно решение с проектом прошивки.

Покопавшись немало времени, я понял, что тема интеграции чего-либо в Visual Studio — это своего рода слепое пятно: нормального описания нет, описания в блогах скудные, и что еще хуже — примеров практически нет. Собрав по крупицам информацию, кое-что поняв из описания и опираясь на метод научного тыка, я решил поделиться полученными знаниями.

Итак, начнем.



План Действий

Ну раз уж мы решили расширить функционал Visual Studio за счет подключения стороннего компилятора, то давайте расширим его максимально.

Для этого определимся со списком того, что мы хотим сделать:

  1. Чтобы при просмотре свойств проекта (Project Properties) в Visual Studio были свои свойства.

  2. Определите свой собственный набор расширений файлов, которые можно включить в проект.
  3. Естественно определить свою собственную систему сборки, чтобы вызывались наши компиляторы, компоновщики и т. д.
  4. Создайте свой тип проекта, чтобы студия могла открыть файл со своим расширением.

  5. Создайте мастер для создания проекта этого типа.

Что ж, попробуем реализовать хотя бы часть требований из этого списка.

Часть 1: MSBuild Начнем с теории:

  • Microsoft Visual Studio чуть менее чем полностью построена на технологии COM. Поэтому нужно быть готовым встретиться с этим лицом к лицу.

    Хотя мы все равно постараемся этого избежать.

  • Любой проект Microsoft Visual Studio представляет собой сценарий MSBuild.
Ввиду вышесказанного, для расширения возможностей студии в первую очередь нам придется изучить устройство системы сборки, называемой MSBuild. Сразу оговорюсь: у меня под рукой была достаточно старая VIsual Studio 2010 - так что все описания будут для нее, но я полагаю, что со студиями 2012 и 2013 года все будет аналогично.

Итак, мы открываем студию, создаем Пустое решение и добавить к этому Пустой проект (Мне больше нравится проект из категории C++, поэтому я выбрал его)

Расширяемость Visual Studio. Часть первая: MSBuild

И проект, и решение я назвал «test» и убедился, что они находятся в одном каталоге (это удобно для наших экспериментов, объясню позже) — получив таким образом файлы test.sln И test.vcxproj Теперь закрываем студию и берем какой-нибудь текстовый редактор (желательно с подсветкой синтаксиса XML - в принципе подойдет та же студия, только другая ее копия) и открываем test.vcxproj как текстовый файл.

Давайте посмотрим, что внутри test.vcxproj : test.vcxproj

  
  
  
  
   

<Эxml version="1.0" encoding="utf-8"?> <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns=" http://schemas.microsoft.com/developer/msbuild/2003 "> <ItemGroup Label="ProjectConfigurations"> <ProjectConfiguration Include="Debug|Win32"> <Configuration>Debug</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|Win32"> <Configuration>Release</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> <ProjectGuid>{E1064D79-B415-4EDC-9FAC-C50E4102268B}</ProjectGuid> <RootNamespace>test</RootNamespace> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <UseDebugLibraries>true</UseDebugLibraries> <CharacterSet>MultiByte</CharacterSet> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <UseDebugLibraries>false</UseDebugLibraries> <WholeProgramOptimization>true</WholeProgramOptimization> <CharacterSet>MultiByte</CharacterSet> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).

user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).

user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).

user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).

user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <PropertyGroup Label="UserMacros" /> <PropertyGroup /> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ClCompile> <WarningLevel>Level3</WarningLevel> <Optimization>Disabled</Optimization> </ClCompile> <Link> <GenerateDebugInformation>true</GenerateDebugInformation> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> <WarningLevel>Level3</WarningLevel> <Optimization>MaxSpeed</Optimization> <FunctionLevelLinking>true</FunctionLevelLinking> <IntrinsicFunctions>true</IntrinsicFunctions> </ClCompile> <Link> <GenerateDebugInformation>true</GenerateDebugInformation> <EnableCOMDATFolding>true</EnableCOMDATFolding> <OptimizeReferences>true</OptimizeReferences> </Link> </ItemDefinitionGroup> <ItemGroup> </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> </ImportGroup> </Project>

Самое главное, что стоит увидеть здесь:
  • ярлык Конфигурация проекта .

  • ярлык Руководство по проекту .

  • файлы, включенные в тег Импортировать .

Все остальное можно смело удалить.

Я также предлагаю удалить файл тест.фильтры , чтобы виртуальные каталоги в проекте нам не мешали.



<Эxml version="1.0" encoding="utf-8"?> <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns=" http://schemas.microsoft.com/developer/msbuild/2003 "> <ItemGroup Label="ProjectConfigurations"> <ProjectConfiguration Include="Debug|Win32"> <Configuration>Debug</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|Win32"> <Configuration>Release</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> <ProjectGuid>{E1064D79-B415-4EDC-9FAC-C50E4102268B}</ProjectGuid> <RootNamespace>test</RootNamespace> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> </Project>

Приведенный выше файл абсолютно действителен с точки зрения студии и MSBuild, его можно открывать и даже компилировать.

Также предлагаю сразу разобраться со сборкой нашего проекта из командной строки:

  1. Давайте запустим Командная строка Microsoft Visual Studio
  2. Перейдите в каталог, в котором он находится test.vcxproj
  3. Запустите msbuild test.vcxproj /p:Configuration=Debug /p:Platform=Win32.
Можно написать скрипт для автоматизации этого процесса, чтобы было удобнее проверять сборку на наличие ошибок и прочего.

Теперь мы начинаем понимать, что отвечает за свойства проекта и вообще, что делает наш проект проектом студии.

И все строки делают это:

<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />

Мои импортированные файлы находятся в каталоге C:\Program Files\MSBuild\Microsoft.Cpp\v4.0 Можно, конечно, попробовать их посмотреть, но боюсь, человек, никогда не разбиравшийся в MSBuild, вряд ли что-то поймет, а желание во всем разбираться моментально пропадет. Так что предлагаю пока туда не заглядывать, потому что.

можно потратить кучу времени (как это сделал автор) и так ничего и не понять.

Важно!!: Каждый раз, когда вы редактируете vcxproj и связанные с ним файлы, вам необходимо полностью перезапустить студию! Экспериментально установлено, что Visual Studio что-то кэширует (скорее всего, файлы *.

props и *.

targets) — поэтому простого Выгрузить проект/Перезагрузить проект недостаточно! Вот почему я изначально создал СЛН файл рядом с vcxproj , чтобы было удобно перезагружаться без смены каталогов.

Итак, давайте просто удалим строки с тегом Импортировать и посмотрим, что произойдет. Файл должен быть таким: test.vcxproj

<Эxml version="1.0" encoding="utf-8"?> <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns=" http://schemas.microsoft.com/developer/msbuild/2003 "> <ItemGroup Label="ProjectConfigurations"> <ProjectConfiguration Include="Debug|Win32"> <Configuration>Debug</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|Win32"> <Configuration>Release</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> <ProjectGuid>{E1064D79-B415-4EDC-9FAC-C50E4102268B}</ProjectGuid> <RootNamespace>test</RootNamespace> </PropertyGroup> </Project>

Открыв его в студии, мы с удивлением обнаруживаем, что файл все еще действителен.

Но собрать его уже не получится; мы получим сообщение: 1> Ошибка: в проекте отсутствует правило «Общая конфигурация».

Собирать проект пока не хотим, поэтому не обращаем на это внимание.

Посмотрим, что у нас есть в свойствах проекта — и видим следующую картину:

Расширяемость Visual Studio. Часть первая: MSBuild

Красота! Мы удалили всё ненужное, точнее, удалили всё вообще.

Если попытаться добавить файл в проект, то ничего не получится, студия выдаёт ошибку.



Расширяемость Visual Studio. Часть первая: MSBuild

Лирическое отступление : Читатель может задать вопрос, а как студия все-таки определяет, что это C++ проект - просто по расширению vcxproj , что явно указывает на Visual C++.

Возможно, этот вопрос покажется читателю довольно глупым, но когда вы долго экспериментируете с проектом, который на самом деле уже не является C++-проектом, а студия все еще пытается вести себя по правилам C++, вы совершенно забываете о расширение самого файла — и это имеет кардинальное значение.

От этого мы избавимся, но только в одной из следующих частей этой истории.



Проект пустой.

Приступаем к заполнению.

Читатель, наверное, уже догадался, что мы будем создавать свои файлы.

*.

реквизит И *.

targets , но сначала немного теории:

  • файлы *.

    реквизит И *.

    targets Совсем не обязательно иметь расширения *.

    props и *.

    targets — это фактически что-то вроде включаемых файлов (include).

    Но мы не будем нарушать гегемонию студии и оставим привычные всем расширения.

  • *.

    реквизит обычно отвечает за свойства проекта и переменные среды.

  • *.

    targets отвечают за сборку - описывают, что делать с файлами (а может и не с файлами), которые добавляются в проект, т.е.

    все возможные действия/задачи (Tasks) и для типов файлов проекта, определенных схемой (ProjectSchema) и правила (Правила).



*.

targets файл.

Для дальнейшего повествования предлагаю перейти от абстракции к конкретной задаче по прикручиванию компилятора XC8 от Microchip к Visual Studio. Компилятор XC8 необходимо скачать с сайта Микрочип и установить.

Отступление : В целом файлы *.

targets так же, как и *.

props, располагаются в определенных папках самого MSBuild, но сейчас мы не будем этого делать, поскольку это уже относится к задачам по распространению вновь созданного расширения и вообще к задачи установщика, и для наших экспериментов удобнее всего хранить все в одной директории рядом с проектом.

Давайте создадим файл XC8.targets в каталоге рядом с test.vcxproj .

Содержание XC8.targets :

<Project ToolsVersion="4.0" xmlns=" http://schemas.microsoft.com/developer/msbuild/2003 ">

Теги: #extensibility #msbuild #Visual Studio

Вместе с данным постом часто просматривают:

Автор Статьи


Зарегистрирован: 2019-12-10 15:07:06
Баллов опыта: 0
Всего постов на сайте: 0
Всего комментарий на сайте: 0
Dima Manisha

Dima Manisha

Эксперт Wmlog. Профессиональный веб-мастер, SEO-специалист, дизайнер, маркетолог и интернет-предприниматель.