Как открыть свойства проекта visual studio
Перейти к содержимому

Как открыть свойства проекта visual studio

  • автор:

Как открыть свойства проекта в Visual Studio 2017?

в visual studio 2017 во вкладке проект нет свойств проекта, как еще можно открыть свойства проекта?

94731 / 64177 / 26122

Регистрация: 12.04.2006

Сообщений: 116,782

Ответы с готовыми решениями:

Как открыть Team Explorer в Visual Studio 2017?
Случайно закрыл Team Explorer в студии. Как теперь его открыть?что нужно сделать.

Не корректно создаётся шаблон проекта в Visual Studio 2017
Раньше активно пользовался шаблонами проекта в VS 2013. Тогда они создавались корректно, но в VS.

Ошибка запуск проекта Visual Studio 2012 на Visual Studio 2017
Предупреждение CS0618 ‘ZipEntry.Extract(string, bool)" является устаревшим: ‘Please use method.

Как открыть средства диагностики при работе с отладчиком в visual studio 2017?
Изучаю работу c++ и работу с отладкой кода в среде visual studio 2017. Справа в окне, где.

Основы работы с Visual Studio

В этом документе я хочу познакомить вас с работой средой разработки Visual Studio 2008. Мы не будем изучать программирование как таковое, нас будет интересовать сама среда разработки и ее возможности. Вы познакомитесь с инструментами, которые предоставляет нам среда разработки, рассмотрим визуальный и текстовый редактор.

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

Среда разработки Visual Studio 2008 по сравнению с предыдущей версией 2005 изменилась не так сильно. Изменения можно назвать эволюционными, а не революционными. Но эти эволюционные изменения чувствительно повышают производительность работы программистов. В ведь от вашей производительности зависит и ваша доходность.

Введение в Visual Studio

Среда разработки Visual Studio 2008 позволяет разрабатывать программы на нескольких языках программирования: C++, C#, Visual Basic и ASP .NET. При первом старте вы можете увидеть окно, в котором среда попросит вас указать предпочитаемый вами язык программирования. От вашего выбора зависит, какие типы файлов и проектов будут предлагаться вам при создании нового приложения. Я чаще работаю с C# поэтому в моем случае среда разработки настроена на предпочтения этого языка и его типа проектов будут первыми.

Выбрав предпочтения, загрузится непосредственно главное окно среды разработки. В центре окна располагается рабочая область. По умолчанию в ней загружается WEB страничка, состоящая из четырех разделов:

•Recent projects – в этом разделе расположен список последних проектов, с которыми вы работали. При первом запуске этот список будет пуст. Внизу списка есть две ссылки для открытия существующего проекта и создания нового проекта.

•Get started – в этом разделе находятся ссылки на разделы файла помощи с информацией для тех, кто впервые работает с Visual Studio или впервые начинает разрабатывать программы.

•Getting started – в этом разделе находятся ссылки на последние документы, связанные со средой разработки и программированием.

•MSDN: Visual C# Headlines – занимает основную часть окна и содержит последние новости из мира предпочитаемого вами языка программирования. В моем случае это Visual C#.

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

— эта кнопка вызывает меню настройки панели.

— если эта кнопка нажата, то панель будет закреплена на поверхности главного окна. Если кнопка. Если копка отжата, то панель будет автоматически прятаться, когда она теряет фокус ввода, как это показано на рисунке выше.

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

— закрыть текущую панель. Ненужные панели, которые вы используете очень редко в своей работе можно закрыть. Если вы пока не знаете, что будет нужно или не нужно, то лучше оставить окна по умолчанию, потому что панели по умолчанию действительно нужны практически постоянно. Если вы закрыли какое-то окно, то впоследствии его можно будет открыть, выбрав название окна в меню View.

Для просмотра всех доступных панелей, желательно сейчас создать какой-либо проект в среде разработке, чтобы вам проще было читать этот документ. В этом случае, вы одновременно сможете читать и видеть результат в собственном окне. Для создания проекта выберите меню File -> New -> Project. Перед вами откроется следующее окно:

С левой стороны представлены различные категории проектов, которые вы можете создавать. Первым в дереве категорий будет раздел с языком программирования, который вы выбрали по умолчанию. Я работаю в основном с C#, поэтому у меня первым идет раздел Visual C#. Выберите его и в основном списке окна вы увидите иконки различных проектов. Для тестирования нам достаточно будет Windows Forms Application. Выделите соответствующую иконку.

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

Работа с решениями (Solution)

Основной панелью, с которой мы познакомимся — панель Solution Explorer – эксплорер решения. Вариант панели для одного из тестовых проектов можно увидеть справа от этого текста. Если у вас эта панель не отображается, то ее можно включить, выбрав меню View –> Solution Explorer.

Все проекты, которые вы будете создавать в Visual Studio, заключаются в решение. Одно решение – это как папка для проектов и может содержать несколько проектов. Это очень удобно, потому что очень часто решения действительно должны состоять из нескольких проектов и благодаря объединению в одном виртуальном пространстве, мы можем работать со всеми этими проектами, как с одним целым.

Например, программа может состоять из исполняемого файла и из библиотеки кода Class Library. Для реализации этого вам придется создать два проекта, каждый из которых будет компилировать свой файл. Объединив оба проекта в одно решение, вы сможете держать их под рукой в окне Solution Explorer и одновременно работать с обоими проектами.

Итак, взглянем на окно Solution Explorer моего тестового примера. В нем содержимое представлено в виде дерева. Корнем дерева выступает имя решения. В данном случае, решение имеет имя FindRecordProject. По умолчанию решения получают такое же имя, как и проект. Чтобы переименовать решение, нужно щелкнуть правой кнопкой по имени решения (по корневому элементу дерева) и в контекстном меню выбрать пункт меню Rename. На месте имени решения появится встроенное поле для редактирования, в котором вы можете ввести новое имя для решения. Ввод имени нужно завершить нажатием клавиши Enter.

Конфигурация решения сохраняется в файле с расширением .sln. Впоследствии, если вы захотите открыть все решение в целом, нужно открывать именно этот файл, а не файлы проектов, такие как csproj. Если же у вас в решении только один проект, то можно открывать файл проекта, решение будет открыто автоматически.

Давайте посмотрим, какие еще команды есть в контекстном меню решения (пример этого меню можно увидеть на рисунке):

•Build Solution – собрать решение. Будут полностью собраны все проекты решения;

•Rebuild Solution – пересобрать решение;

•Clean Solution – очистить решение;

•Batch build – настройка проектов, которые нужно будет пересобрать;

•Configuration Manager – вызывает окно, в котором можно управлять конфигурациями решения;

•Add – этот пункт содержит подпункты, с помощью которых в решение можно добавить дополнительные проекты. Это может быть:

oNew Project – создать новый проект и добавить его в решение. Выбрав это меню, вы увидите окно создания нового проекта, как при выборе меню File -> New -> Project.

oExisting Project – отобразит стандартное окно открытия файла, с помощью которого вы можете найти уже существующий проект в вашей файловой системе и добавить его в решение;

oNew Web Site – позволяет создать проект нового WEB сайта;

oExisting Web Site – добавить в решении существующий сайт;

oNew Item – добавить в решение новый элемент;

oExisting Item – добавить в решение существующий элемент;

oNew Solution Folder – создать новую папку в решении.

•Set StartUp Projects – вызывает окно конфигурации решения, в котором вы можете указать, какой из проектов и как должен запускаться при нажатии клавиши F5 во время разработки;

•Paste – вставить из буфера обмена;

•Rename – переименовать решение;

•Open Folder in Windows Explorer – открыть папку решения в окне проводника Windows;

•Properties – отобразить окно свойств решения.

Рассмотрим некоторые из команд, которые могут представлять для вас наибольший интерес во время разработки. Большинство из рассмотренных команд не требуют дополнительных пояснений, потому что они просто выполняют какое-то простое действие. Например, с первыми тремя пунктами все понятно, они просто вызываются и выполняют свои действия. Интерес вызывает меню Batch Build. Что это такое? Вызвав это меню, перед вами откроется окно настройки сборки, как показано на рисунке справа. Здесь перечислены все проекты решения и указаны возможные конфигурации (вторая колонка списка).

В последней колонке Build вы можете указать галкой, какие конфигурации и проекты вас интересуют. Нажав кнопку Build, Rebuild или Clean в этом окне, вы можете запустить процесс сборки, пересборки или очистки всех выбранных проектов. Получается эффект пакетной компиляции.

Следующий пункт пеню, который вас может заинтересовать – это Configuration Manager. Выберите сейчас его, и перед вами откроется окно.

Слева наверху находится выпадающий список, в котором перечислены конфигурации решения. По умолчанию существует две конфигурации (если только у вас не Visual Studio Express Edition, который не позволяет работать с конфигурациями): Debug и Run. В выпадающем списке есть еще пунктыис помощью которых вы можете создать новые конфигурации или вызвать окно редактирования уже существующих конфигураций.

Следующая команда, которая может заинтересовать вас – Set StartUp Project. Выбрав ее, перед вами откроется окно, в котором вы можете выбрать проект, который должен запускаться при нажатии клавиши F5 во время разработки. Итак, выбрав команду Set StartUp Project, перед вами появиться окно, как показано на рисунке ниже:

Это окно свойств решения, просто оно открыто в разделе Startup Project. Какой это раздел, можно определить по дереву, находящемуся слева. В основной части окна находится три переключателя, с помощью которых вы можете задать, какой проект должен запускаться при нажатии F5:

oCurrent Project – текущий проект;

oSingle startup project – вне зависимости от того, какой сейчас текущий проект, всегда будет запускаться тот, который выбран в выпадающем списке после этого пункта;

oMultiple startup projects – запускаться будут несколько проектов и в списке после пункта вы можете указать, как они должны запускаться. Щелкнув по этой колонке можно выбрать для каждого проекта один из трех вариантов:

None – проект не будет запускаться;

Start – проект будет запущен на выполнение в режиме отладки;

Start without debugging – запустить без отладки.

Если ваше решение состоит из одной библиотеки кода и одного исполняемого файла, то логично было бы выбрать второй пункт (Single startup project) и явно указать проект в решении, который должен запускаться.

Окно свойств решения можно вызвать, если щелкнуть по решению правой кнопкой и выбрать в контекстном меню пункт Properties. Раздел Startup Project, который является наиболее интересным, мы уже рассмотрели. Вторым по значимости, на мой взгляд, является раздел Project Dependencies. В этом разделе вы можете выбрать в выпадающем списке проект, а в основном списке (он на рисунке пуст, потому что решение состоит только из одного проекта) можно указать, от чего зависит наш проект. Например, исполняемый файл может зависеть от библиотеки кода. Это значит, что библиотека кода должна компилироваться раньше, чем исполняемый файл. С помощью зависимостей мы можем указать эту проблему, и среда разработки послушно будет компилировать проекты в соответствии с их зависимостями.

При работе с тестовыми примерами и с маленькими программами программисты не обращают внимания на решения и работают в основном только с проектами. Это не является ошибкой, потому что файл решения – создается просто для вашего удобства. Используйте его там, где необходимо, а если вы не используете его, то можете даже не сохранять или удалять, чтобы он не занимал лишнее место на диске. Хотя, файлы sln настолько маленькие, что проще не обращать на них никакого внимания.

Работа с проектами

Работа с проектом начинается с его создания. Для этого выбираем меню File -> New -> Project. Перед вами откроется окно, как показано на следующем рисунке.

Начнем с правого верхнего угла. Здесь располагается выпадающий список, в котором вы можете выбрать версию платформы .NET, под которую вы создаете проект. Этот выпадающий список имеет смысл только при создании .NET приложения. Если вы создаете MFC или другое Win32 приложение, то выбор .NET платформы не повлияет на проект.

Слева окна находится дерево различных категорий проектов. Я при первом запуске Visual Studio выбрал языком по умолчанию C#, поэтому первым в этом дереве у меня стоит раздел Visual C#. Выбирая разделы, в основной части окна в представлении списка будут появляться иконки шаблонов, которые установлены у вас на диске для данного раздела. Помимо этого, есть еще пункт Search Online Templates…, с помощью которого можно поискать шаблоны в интернете.

Внизу окна располагаются следующие элементы управления:

•Name – в этом поле вы должны указать имя будущего проекта. Таким именем будет назван файл проекта, и если вы создадите новое решение, то и файл решения. По умолчанию, такое же имя будет получать и результирующий файл после компиляции;

•Location – расположение проекта;

•Solution – здесь в выпадающем списке вы можете выбрать один из вариантов:

oCreate new Solution – создать новое решение;

oAdd to Solution – добавить в существующее решение, которое на данный момент открыто в среде разработки.

•Create directory for solution – если выбран этот пункт, то по указанному пути будет создана отдельная папка для решения, а в нее уже будет добавлена папка проекта;

•Solution name – позволяет указать явно имя решения. Этот пункт доступен, если вы выберите Create directory for solution.

Как мы уже знаем, проекты добавляются в решение. Щелкнув правой кнопкой мышки по имени проекта в окне Solution Explorer, мы можем увидеть большое контекстное меню с командами управления проектами. Давайте посмотрим, какие команды нам доступны в контекстном меню:

•Build – собрать проект. При этом будут компилироваться только измененные и новые файлы;

•Rebuild – пересобрать проект. При этом, будут компилироваться все файлы проекта;

•Clean – очистить проект;

•Publish – опубликовать проект;

•Add – добавить в проект. При наведении на этот пункт, перед нами появляется дополнительное контекстное меню:

oNew Item – добавить новый элемент. При выборе этого пункта, перед нами появится окно выбора нового типа файла;

oExisting Item – добавить в проект уже существующий у вас на диске файл. При выборе этого пункта, вы увидите стандартное окно открытия файла, с помощью которого нужно выбрать файл, который должен быть добавлен в проект;

oNew Folder – создать в проекте новую папку. Папки позволяют организовывать файлы в проекте по смыслу и по типу использования;

oWindows Form… — добавить в проект новую визуальную форму ;

oUser Control… — добавить в проект новый элемент управления;

oComponent – добавить в проект новый компонент;

oClass – добавить новый класс.

•Add Reference – добавить в проект ссылку на библиотеку;

•Add Web Reference – добавить Web ссылку;

•View Class Diagram – просмотреть диаграмму классов проекта в виде визуального представления;

•Set as SrartUp Project – назначить текущий проект как запускаемый при нажатии F5. Этот пункт удоен для быстрой смены запускаемого проекта по умолчанию, если у вас в решении находится несколько проектов;

•Debug – управлении отладкой проекта.

•Cut – вырезать проект в буфер обмена;

•Paste – вставить из буфера обмена;

•Remove – удалить проект из решения;

•Rename – переименовать проект;

•Unload project – выгрузить проект;

•Open Folder in Windows Explorer – открыть папку проекта в окне проводника;

•Properties – отобразить свойства проекта.

Наиболее часто используемыми пунктами в этом меню по моей практике являются пункт Add, Add Reference и Properties. Очень редко приходится работать с проектами, которые состоят только из одного файла, очень часто все же приходится что-то добавлять в проект. Это могут быть файлы ресурсов, компоненты или просто файлы классов. Поэтому я достаточно часто обращаюсь к подпунктам меню Add.

При работе с библиотеками кода, чтобы среда разработки узнала о существовании нужной вам библиотеки, нужно добавить к проекту ссылку на эту библиотеку. К большинству стандартных и часто используемых библиотек, ссылки уже наведены. Вы можете открыть в проекте папку References и здесь увидите список библиотек, которые уже добавлены в ваш проект. Если вы создаете проект для платформы .NET Framework 3.5, то в этом списке могут находиться так же ссылки, необходимые для работы с Linq.

Если нужной вам библиотеки нет, то ее можно добавить. Для этого можно сделать одно из двух:

•Щелкнуть правой кнопкой по имени проекта, которому нужно добавить новую ссылку, и из контекстного меню выбрать Add Reference…;

•Щелкнуть правой кнопкой по папке Reference и здесь так же появиться контекстное меню, в котором есть пункт Add Reference….

Выбрав пункт меню Add Reference…, перед нами открывается окно, с помощью которого можно выбрать непосредственно библиотеку, которую вы хотите добавить к проекту. Окно состоит из следующих закладок:

•.NET – содержит список .NET библиотек, установленных на данном компьютере, в глобальном кеше сборок;

•COM – здесь находятся библиотеки COM, установленные на компьютере;

•Projects – вы можете выбрать проект, ссылка к которому будет добавлена;

•Browse – позволяет найти библиотеку кода на диске компьютера и добавить явно файл. Этот метод удобен, когда нужно использовать локальную сборку;

•Recent – содержит список недавно использованных или добавляемых вами сборок.

Последний из важных пунктов управления проектами, который мы сейчас рассмотрим – это свойства проекта. Свойства проекта находятся под пунктом меню Properties. Выбрав это меню, вы не увидите классического Windows окна со свойствами, в Visual Studio 2008 окно свойств было переработано, и теперь появляется как закладка в основной области окна. Это очень удобно, потому что теперь вы можете держать окно со свойствами проекта постоянно открытыми. Изменяя свойства, можно тут же перекомпилировать проект и запустить его на выполнение, чтобы увидеть результат. Если результат оказался неудовлетворительным, то можно все вернуть на родину или попробовать другие сочетания свойств или параметров проекта.

На мой взгляд, новое окно свойств Visual Studio 2008 стало намного удобнее и интеллектуальнее. Все свойства разбиты по категориям и с ними удобно работать. В зависимости от типа открытого проекта, вам будут доступны те или иные возможности и закладки. Количество свойств может отличаться и от версии среды разработки. Например, Visual Studio Express Edition обладает небольшими возможностями по настройке проекта.

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

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

•Application – основные настройки приложения;

•Build – параметры сборки проекта;

•Build Events – события сборки. На разные события вы можете повесить собственный код;

•Debug – параметры отладки;

•Resources – ресурсы проекта;

•Services – использование сервисов в вашем приложении;

•Settings – параметры проекта;

•Reference Paths – настройки путей;

•Signing – подписывание файла;

•Public – параметры публикации проекта.

Теперь рассмотрим каждую из закладок в отдельности и наиболее интересные параметры в этих закладках. Начнем сверху вниз, а самой верхней закладкой является Application, на которой находятся базовые настройки приложения.

Работа с формами

Попробуйте сейчас открыть в проекте окно Form1.cs. У вас должна быть такая форма в окне Soulution Explorer. Дважды щелкните по этому файлу, и он должен открыться в виде закладки в основной части окна среды разработки. Внутри окна будет видно визуальное представление формы.

Чтобы переключиться в режим редактирования кода формы, можно нажать F7 или щелкнуть правой кнопкой мыши по имени формы в окне Solution Explorer и из контекстного меню выбрать View Code.

Для того чтобы вернуться обратно в режим визуальной работы с формой, нужно нажать Shift+F7 или щелкнуть правой кнопкой мыши по имени формы в окне Solution Explorer и из контекстного меню выбрать View Designer.

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

Обратите внимание, что справа, снизу, а так же в правом нижнем углу визуального представления формы находятся квадратики. Это указатели на то, что вы можете изменять размеры компонента (в данном случае формы) в указанных направлениях.

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

Слева от кода вы можете увидеть полосы и квадратики. Таким образом, редактор кода автоматически нашел код, который можно сворачивать или наоборот раскрывать. Щелкнув по квадратику напротив имени метода, вы можете свернуть код этого метода, чтобы он не занимал рабочее пространство. Вы можете сворачивать целые классы или регионы.

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

Панель инструментов

Окно Toolbox по умолчанию располагается вдоль левого края формы. В этом окне располагаются инструменты визуального проектирования приложения. Все инструменты разбиты по категориям, а каждая категория отражает определенное назначение входящих в них компонентов. В первой категории под именем All Windows Forms можно увидеть все элементы управления, установленные в различных категориях панели Toolbox.

Чтобы установить элемент управления на визуальную форму, можно взять мышкой и перетащить элемент управления на форму. Попробуйте сейчас открыть в проекте окно Form1.cs. Если у вас открыт проект по умолчанию, который мы создавали, и вы ничего в нем не меняли, то у вас должна быть такая форма в окне Soulution Explorer. Дважды щелкните по этому файлу, и он должен открыться в виде закладки в основной части окна среды разработки. Внутри окна будет видно визуальное представление формы. Теперь рассмотрим методы установки компонентов на форме:

1.Нажмите мышкой на имени компонента, который вы хотите установить на форме и перетащите его в нужную позицию. В этом случае компонент будет создан с размерами по умолчанию;

2.Щелкните на имени компонента, который вы хотите установить в панели инструментов. В результате он будет выделен. Теперь щелкните на визуальной форме. В позиции щелчка на форме будет создан выделенный в панели инструментов компонент с параметрами по умолчанию.

3.Щелкните на имени компонента, который вы хотите установить в панели инструментов. В результате он будет выделен. Теперь на визуальной форме установить курсор в позицию, где вы предполагаете должен находиться левый верхний угол компонента. Нажмите кнопку мышки и растяните ее (удерживая левую кнопку) до того места, где по вашему мнению, должен находиться правый нижний угол компонента. В результате будет создан выбранный элемент управления с указанными размерами.

Вы можете выбирать любой из этих методов, а можете использовать тот или иной, в зависимости от ситуации и компонента. Иногда необходимо создать компонент сразу с определенными размерами, а иногда тот же элемент управления нужно поместить на форму с размерами по умолчанию.

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

Настраиваем удобную сборку проектов в Visual Studio

Эта статья является руководством по настройке сборки C++ проектов Visual Studio. Частично она сводилась из материалов разрозненных статей на эту тему, частично является результатом реверс-инжениринга стандартных конфигурационных файлов Студии. Я написал ее в основном потому что полезность документации от самой Microsoft на эту тему стремится к нулю и мне хотелось иметь под рукой удобный референс к которому в дальнейшем можно будет обращаться и отсылать других разработчиков. Visual Studio имеет удобные и широкие возможности для настройки по-настоящему удобной работы со сложными проектами и мне досадно видеть что из-за отвратительной документации эти возможности очень редко сейчас используются.

В качестве примера попробуем сделать так чтобы в Студию можно было добавлять flatbuffer schema, а Студия автоматически вызывала flatc в тех случаях когда это нужно (и не вызывала — когда изменений не было) и позволяла задавать настройки напрямую через File Properties

Оглавление

ЗАМЕЧАНИЕ: все приведенные в статье примеры проверялись в VS 2017. В рамках моего понимания они должны работать и в более ранних версиях студии начиная по крайней мере с VS 2012, но обещать я этого не могу.

Level 1: лезем внутрь .vcxproj файлов

Давайте взглянем внутрь типичного .vcxproj автоматически сгенеренного Visual Studio.

Он будет выглядеть как-то примерно так

    Debug Win32  Release Win32  Debug x64  Release x64   15.0 Win32Proj protobuftest 10.0.17134.0   Application true v141 Unicode  Application false v141 true Unicode  Application true v141 Unicode  Application false v141 true Unicode            true  true  false  false   Use Level3 Disabled true WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) true pch.h  Console true    Use Level3 Disabled true _DEBUG;_CONSOLE;%(PreprocessorDefinitions) true pch.h  Console true    Use Level3 MaxSpeed true true true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true pch.h  Console true true true    Use Level3 MaxSpeed true true true NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true pch.h  Console true true true      Create Create Create Create     

Довольно нечитаемое месиво, не правда ли? И это ведь еще очень небольшой и практически тривиальный файл. Попробуем превратить его во что-то более читабельное и удобное для восприятия.

Поговорим о .props файлах

Для этого обратим пока внимание на то что взятый нами файл — это обычный XML-документ и его можно логически разделить на две части, в первой из которых перечисляются настройки проекта, а во второй — входящие в него файлы. Давайте эти половинки разделим физически. Для этого нам понадобится уже встречающийся в коде тэг Import который является аналогом сишного #include и позволяет включить один файл в другой. Скопируем наш .vcxproj в какой-нибудь другой файл и уберем из него все объявления относящиеся к файлам входящим в проект, а из .vcxproj-а в свою очередь наоборот уберем все кроме объявлений относящихся к файлам собственно входящим в проект. Получившийся у нас файл с настройками проекта но без файлов в Visual Studio принято называть Property Sheets и сохранять с расширением .props. В свою очередь в .vcxproj мы поставим соответствующий Import

Теперь .vcxproj описывает только файлы входящие в проект и читается намного легче

        Create Create Create Create     

Его можно упростить еще больше, убрав лишние XML-элементы. К примеру свойство «PrecompiledHeader» объявляется сейчас 4 раза для разных вариантов конфигурации (release / debug) и платформы (win32 / x64) но каждый раз это объявление одно и то же. Кроме того у нас здесь используется несколько разных ItemGroup тогда как в реальности вполне достаточно одного элемента. В результате приходим к компактному и понятному .vcxproj который просто перечисляет 1) входящие в проект файлы, 2) то чем является каждый из них (плюс настройки специфичные для конкретных отдельных файлов) и 3) содержит в себе ссылку на хранящиеся отдельно настройки проекта.

      Create    

Перезагружаем проект в студии, проверяем сборку — все работает.

Но зачем вообще разделять .vcxproj и .props?

Поскольку в сборке ничего не поменялось, то на первый взгляд может показаться что мы поменяли шило на мыло, сделав бессмысленный «рефакторинг» файла в который нам до этого собственно и не было никакой нужды заглядывать. Однако допустим на минутку что в наш solution входит более одного проекта. Тогда, как несложно заметить, несколько разных .vcxproj-файлов от разных проектов могут использовать один и тот же .props файл с настройками. Мы отделили правила сборки используемые в solution от исходного кода и можем теперь менять настройки сборки для всех однотипных проектов в одном месте. В подавляющем большинстве случаев подобная унификация сборки — это хорошая идея. К примеру добавляя в solution новый проект мы в одно действие тривиально перенесем в него подобным образом все настройки из уже существующих в solution проектов.

Но что если нам все же нужны разные настройки для разных проектов? В этом случае мы можем просто создать несколько разных .props-файлов для разных типов проектов. Поскольку .props-файлы могут совершенно аналогичным образом Import-ить другие .props-файлы, то довольно легко и естественно можно выстроить «иерархию» из нескольких .props-файлов, от файлов описывающих общие настройки для всех проектов в solution до узкоспециализированных версий задающих специальные правила для всего одного-двух проектов в solution. В MSBuild действует правило что если одна и та же настройка объявляется во входном файле дважды (скажем вначале импортится в base.props а затем объявляется повторно в derived.props который import-ит в своем начале base.props) то более позднее объявление перекрывает более раннее. Это позволяет легко и удобно задавать произвольные иерархии настроек просто перекрывая в каждом .props файле все необходимые для данного .props-а настройки не заботясь о том что они могли быть где-то уже объявлены ранее. В числе прочего где-нибудь в .props-ах разумно импортировать стандартные настройки окружения Студии которые для C++-проекта будут выгледеть вот так:

Отмечу что на практике весьма удобно класть собственные .props файлы в ту же папку что и .sln файл

Поскольку это позволяет удобно импортировать .props независимо от местоположения .vcxproj

Делаем настройку проекта читабельнее

Теперь когда нам больше не требуется возиться с каждым проектом по отдельности мы можем уделить больше внимания настройке процесса сборки. И для начала я рекомендую дать с помощью .props-файлов вменяемые имена большинству интересных объектов в файловой системе относящихся к solution. Для этого нам следует создать тэг PropertyGroup с пометкой UserMacros:

 $(SolutionDir)\.. $(RepositoryRoot)\projects $(RepositoryRoot)\..\ThirdParty $(ThirdPartyDir)\protobuf\src  

Тогда в настройках проектов вместо конструкций вида «..\..\..\ThirdParty\protobuf\src\protoc.exe» мы сможем написать просто «$(ProtoBufRoot)\protoc.exe». Помимо большей читабельности это делает код намного мобильнее — мы можем свободно перемещать .vcxproj не боясь что у него слетят настройки и можем перемещать (или обновлять) Protobuf изменив всего одну строчку в одном из .props файлов.

При последовательном объявлении нескольких PropertyGroups их содержимое будет объединено — перезапишутся только макросы имена которых совпадают с ранее объявлявшимися. Это позволяет легко дополнять объявления во вложенных .props файлах не боясь потерять макросы уже объявленные ранее.

Делаем удобным подключение сторонних библиотек

Обычный процесс включения зависимости от thirdparty-библиотеки в Visual Studio частенько выглядит примерно вот так:

Процесс соответствующей настройки включает в себя редактирование сразу нескольких параметров находящихся на разных вкладках настроек проекта и потому довольно зануден. Вдобавок его обычно приходится проделывать по нескольку раз для каждой отдельно взятой конфигурации в проекте, так что нередко в результате подобных манипуляций оказывается что проект в Release-сборке собирается, а в Debug-сборке — нет. Так что это неудобный и ненадежный подход. Но как Вы наверное уже догадываетесь, те же самые настройки можно «упаковать» в props-файл. К примеру для библиотеки ZeroMQ подобный файл может выглядеть примерно так:

    $(ThirdPartyDir)\libzmq\include;%(AdditionalIncludeDirectories) ZMQ_STATIC;%(PreprocessorDefinitions)  libzmq-v120-mt-sgd-4_3_1.lib;Ws2_32.Lib;%(AdditionalDependencies) libzmq-v120-mt-s-4_3_1.lib;Ws2_32.Lib;%(AdditionalDependencies) $(ThirdPartyDir)\libzmq\lib\x64\$(Configuration);%(AdditionalLibraryDirectories)   

Обратите внимание что если мы просто определим тэг типа AdditionalLibraryDirectories в props-файле, то он перекроет все более ранние определения. Поэтому здесь используется чуть более сложная конструкция в которой тэг завершается последовательностью символов ;%(AdditionalLibraryDirectories) образующих ссылку тэга самого на себя. В семантике MSBuild этот макрос раскрывается в предыдущее значение тэга, так что подобная конструкция дописывает параметры в начало строки хранящейся в парамере AdditionalLibraryDirectories.

Для подключения ZeroMQ теперь достаточно просто импортировать данный .props файл.

И на этом манипуляции с проектом заканчиваются — MSBuild автоматически подключит необходимые заголовочные файлы и библиотеки и в Release и в Debug сборках. Таким образом потратив немного времени на написание zeromq.props мы получаем возможность надежно и безошибочно подключать ZeroMQ к любому проекту всего в одну строчку. Создатели Студии даже предусмотрели для этого специальный GUI который называется Property Manager, так что любители мышки могут проделать ту же операцию в несколько кликов.

Правда как и остальные инструменты Студии этот GUI вместо читабельного однострочника добавит в код .vcxproj что-то вроде

вот такого кода

Так что я предпочитаю добавлять ссылки на сторонние библиотеки в .vcxproj файлы вручную.

Аналогично тому что уже обсуждалось ранее, работа с ThirdParty-компонентами через .props файлы позволяет так же легко в дальнейшем обновлять используемые библиотеки. Достаточно отредактировать единственный файл zeromq.props — и сборка всего solution синхронно переключится на новую версию. К примеру в наших проектах сборка проекта через этот механизм увязана с менеджером зависимостей Conan который собирает необходимый набор thirdparty-библиотек по манифесту зависимостей и автоматически генерирует соответствующие .props-файлы.

Project Templates — автоматизируем создание проектов

Править вручную .vcxproj-файлы созданные Студией конечно довольно скучно (хотя при наличии навыка и недолго). Поэтому в Студии предусмотрена удобная возможность по созданию собственных шаблонов для новых проектов, которые позволяют провести ручную работу по настройке .vcxproj лишь один раз, после чего повторно использовать ее одним кликом в любом новом проекте. В простейшем случае для этого даже не надо ничего править вручную — достаточно открыть проект который нужно превратить в шаблон и выбрать в меню Project \ Export Template. В открывшемся диалоговом окне можно задать несколько тривиальных параметров вроде имени для шаблона или строки которая будет показываться в его описании, а так же выбрать, будет ли вновь созданный шаблон сразу добавлен в диалоговое окно «New Project». Созданный таким способом шаблон создает копию использованного для его создания проекта (включая все файлы входящие в проект), заменяя в нем только имя проекта и его GUID. В довольно большом проценте случаев этого более чем достаточно.

При более детальном рассмотрении сгенерированного Студией шаблона можно легко убедиться в том что это просто zip архив в котором лежат все файлы использованные в шаблоне и один дополнительный конфигурационный файл с расширением .vstemplate. В этом файле хранится список метаданных проекта (вроде использованной иконки или строки с описанием) и список файлов которые необходимо создать при создании нового проекта. Например,

  C++ console application C++ console application for our project VC 1000 true ` OurCppConsoleApp true Enabled true ng.ico   console_app.vcxproj.filters main.cpp stdafx.cpp stdafx.h   

Обратите внимание на параметр ReplaceParameters=«true». В данном случае он применяется только к vcxproj-файлу который выглядит следующим образом:

     $safeprojectname$    Create     

На месте GUID и RootNamespace как видите стоят не конкретные значения, а «заглушки» $guid1$ и $safeprojectname$. При использовании шаблона, Студия проходит по файлам помеченным ReplaceParamters=«true», ищет в них заглушки вида $name$ и заменяет их на вычисляемые значения по специальному словарю. По умолчанию Студия поддерживает не очень много параметров, но при написании Visual Studio Extensions (о чем мы поговорим чуть позже) туда нетрудно добавить сколько угодно своих собственные параметров вычисляемых (или вводимых пользователем) при запуске диалога по созданию нового проекта из шаблона. Как можно увидеть в файле .vstemplate, тот же словарь может использоваться и для формирования имени файла что позволяет, в частности, сформировать шаблону уникальные имена .vcxproj-файлов для разных проектов. При задании ReplaceParameters=false файл указанный в шаблоне будет просто скопирован без дополнительной обработки.

Полученный ZIP-архив с шаблоном можно добавить в список шаблонов известных Студии одним из нескольких способов. Проще всего просто скопировать этот файл в папку %USERPROFILE%\Documents\Visual Studio XX\Templates\ProjectTemplates. Стоит заметить, что несмотря на то что в этой папке Вы найдете множество разных подпапок совпадающих по названиям с папками в окне создания нового проекта, по факту шаблон следует положить просто в корневую папку поскольку положение шаблона в дереве новых проектов определяется Студией из тэгов ProjectType и ProjectSubType в .vstemplate-файле. Этот способ удобнее всего подходит для создания «персональных» шаблонов уникальных только для Вас и если Вы выберете в диалоге Export Template галочку «Automatically import template into Visual Studio» то Студия именно это и сделает, поместив созданный при экспорте zip-архив в эту папку с шаблонами. Однако делиться такими шаблонами с коллегами путем их ручного копирования конечно не очень удобно. Поэтому давайте познакомимся с чуть более продвинутым вариантом — создадим Visual Studio Extension (.vsix)

Для создания VSIX нам понадобится установить опциональный компонент Студии который так и называется — средства для разработки Visual Studio Extensions:

После этого в разделе Visual C# \ Extensibility появится вариант «VSIX project». Обратите внимание, что несмотря на свое расположение (C#), он используется для создания любых расширений, в том числе и наборов шаблонов проектов на C++.

В созданном VSIX проекте можно делать массу самых разных вещей — к примеру, создать свое собственное диалоговое окно которое будет использоваться для настройки создаваемых по шаблону проектов. Но это отдельная огромная тема для обсуждения которую я не буду в этой статье затрагивать. Для создания же шаблонов в VSIX все устроено предельно просто: создаем пустой VSIX проект, открываем файл .vsixmanifest и прямо в GUI задаем все данные для проекта. Вписываем метаданные (название расширения, описание, лицензия) на вкладке Metadata. Обратите внимание на расположенное в правом верхнем углу поле «Version» — его желательно указать правильно, поскольку Студия впоследствии использует именно его для определения того какая версия расширения установлена на компьютере. Затем идем на вкладку Assets и выбираем «Add new Asset», с Type: Microsoft.VisualStudio.ProjectTemplate, Source: File on filesystem, Path: (имя к zip-архиву с шаблоном). Нажимаем OK, повторяем процесс пока не добавим в VSIX все желаемые шаблоны.

После этого остается выбрать Configuration: Release и скомандовать Build Solution. Код писать не требуется, править конфигурационные файлы вручную — тоже. На выходе получается переносимый файл с расширением .vsix, который является, по сути, инсталлятором для созданного нами расширения. Созданный файл будет «запускаться» на любом компьютере с установленной Студией, показывать диалог с описанием расширения и лицензией и предлагать установить его содержимое. Разрешив установку — получаем добавление наших шаблонов в диалоговое окно «Создать новый проект»

Подобный подход позволяет легко унифицировать работу большого количества человек над проектом. Для установки и использования шаблонов от пользователя не требуется никакой квалификации кроме пары кликов мышкой. Установленное расширение можно посмотреть (и удалить) в диалоге Tools \ Extensions and Updates

Level 2: настраиваем кастомную компиляцию

ОК, на этом этапе мы разобрались как организованы vcxproj и props файлы и научились их организовывать. Давайте теперь предположим что мы хотим добавить в наш проект парочку .proto схем для сериализации объектов на основе замечательной библиотеки Google Protocol Buffers. Напомню основную идею этой библиотеки: Вы пишите описание объекта («схему») на специальном платформонезависимом мета-языке (.proto-файл) которая компилируется специальным компилятором (protoc.exe) в набор .cpp / .cs / .py / .java / etc. файлов которые реализуют сериализацию / десериализацию объектов по этой схеме в нужном языке программирования и которые Вы можете использовать в своём проекте. Таким образом при компиляции проекта нам нужно первым делом позвать protoc который создаст для нас набор .cpp файлов которые мы в дальнейшем будем использовать.

Традиционный подход

Классическая реализация «в лоб» прямолинейна и состоит в том чтобы просто добавить вызов protoc в pre-build step для проекта которому нужны .proto-файлы. Примерно вот так:

  • Требуется явно указывать список обрабатываемых файлов в команде
  • При изменении этих файлов билд НЕ будет пересобран автоматически
  • При изменении ДРУГИХ файлов в проекте которые Студия распознает как исходные коды, напротив, без нужды будет выполнен pre-build step
  • Сгенерированные файлы не входят по умолчанию в сборку проекта
  • Если мы включим сгенерированные файлы в проект вручную, то проект будет выдавать ошибку когда мы его будем открывать в первый раз (поскольку файлы еще не сгенерированы первой сборкой).
Знакомимся с MSBuild targets

С точки зрения MSBuild, сборка любого проекта состоит из последовательности сборки сущностей которые называются build targets, сокращенно targets. К примеру сборка проекта может включать в себя выполнение таргета Clean который удалит оставшиеся от предыдущих билдов временные файлы, затем выполнение таргета Compile который скомпилирует проект, затем таргета Link и наконец таргета Deploy. Все эти таргеты вместе с правилами по их сборке не фиксированы заранее а определяются в самом .vcxproj файле. Если Вы знакомы с nix-овой утилитой make и Вам на ум в этот момент приходит слово «makefile», то Вы совершенно правы: .vcxproj является XML-вариацией на тему makefile.

Но стоп-стоп-стоп скажет тут сбитый с толку читатель. Как это так? Мы просмотрели до этого .vcxproj в простом проекте и там не было ни target-ов ни какого-либо сходства с классическим makefile. О каких target-ах тогда может идти речь? Оказывается что они просто «спрятаны» вот в этой строчке включающей в .vcxproj набор стандартных target-ов для сборки C++ — кода.

«Стандартный» билд-план предлагаемый Студией довольно обширен и предлагает большой набор правил для компиляции C++-кода и «стандартных» таргетов типа Build, Clean и Rebuild к которым умеет «цепляться» Студия. Этот набор часто известен под собирательным названием toolset и заменяя в импорте toolset можно заставить Студию компилировать один и тот же проект с помощью другой версии Студии или, к примеру, интеловским компилятором или Clang. Кроме того при желании от стандартного toolset-а можно вообще отказаться и написать свой собственный toolset с нуля. Но мы будем рассматривать в этой статье более простой вариант в котором мы ничего не будем заменять, а лишь дополним стандартные правила необходимыми нам дополнениями.

  • Список входов (inputs)
  • Список выходов (outputs)
  • Зависимости от других targets (dependencies)
  • Настройки target-а
  • Последовательность фактических шагов выполняемых target-ом (tasks)

то мы добавляем (Include) файл protobuf_tests.cpp в список входов (inputs) данного таргета, а когда пишем

то присваем значение «Use» настройке ClCompile.PrecompiledHeader которую target затем превратит в флаг /Yu переданный компилятору cl.exe.

Попробуем создать target для сборки .proto файлов

Добавление нового target-а реализуется с помощью тэга target:

 . steps to take. 

Традиционно target-ы выносят в подключаемый файл с расширением .targets. Не то чтобы это было строго необходимо (и vcxproj и targets и props файлы внутри являются равнозначным XML-ем), но это стандартная схема именования и мы будем ее придерживаться. Чтобы в коде .vcxproj файла теперь можно было писать что-то вроде

созданный нами target необходимо добавить в список AvailableItemName

  GenerateProtobuf  

Нам также понадобится описать что же конкретно мы хотим сделать с нашими входными файлами и что должно получиться на выходе. Для этого в MSBuild используется сущность которая называется «task». Таска — это какое-то простое действие которое нужно сделать в ходе сборки проекта. К примеру «создать директорию», «скомпилировать файл», «запустить команду», «скопировать что-то». В нашем случае мы воспользуемся таской Exec чтобы запустить protoc.exe и таской Message чтобы отобразить этот шаг в логе компиляции. Укажем так же что запуск данного target-а следует провести сразу после стандартного таргета PrepareForBuild. В результате у нас получится примерно вот такой файлик protobuf.targets

   GenerateProtobuf      

Мы использовали здесь довольно нетривиальный оператор «%» (batching operator) который означает «для каждого элемента из списка» и автоматически добавляемые метаданные. Идея тут в следующем: когда мы записываем код вида

  Test  

то мы этой записью добавляем в список с названием «ProtobufSchema» дочерний элемент «test.proto» у которого есть дочерний элемент (метадата) AdditionalData содержащая строку «Test». Если мы напишем «ProtobufSchema.AdditionalData» то мы получим доступ к записи «Test». При этом помимо явно объявленных нами метаданных AdditionalData, хитрый MSBuild ради нашего удобства автоматически добавляет к записи еще добрый десяток полезных часто используемых дочерних элементов описанных вот здесь из числа которых мы использовали Identity (исходная строка), Filename (имя файла без расширения) и FullPath (полный путь к файлу). Запись же со знаком % заставляет MSBuild применить описанную нами операцию к каждому элементу из списка — т.е. к каждому .proto файлу по отдельности.

в protobuf.props, переписываем наши proto-файлы в .vcxproj-е на тэг ProtobufSchema

и проверяем сборку

1>—— Rebuild All started: Project: protobuf_test, Configuration: Debug x64 ——
1>Compiling schema test.proto
1>Compiling schema test2.proto
1>pch.cpp
1>protobuf_test.cpp
1>protobuf_test.vcxproj -> S:\Temp\msbuild\protobuf_msbuild_integration\x64\Debug\protobuf_test.exe
========== Rebuild All: 1 succeeded, 0 failed, 0 skipped ==========

Ура! Заработало! Правда наши .proto файлы теперь стали не видны в проекте. Лезем в .vcxproj.filters и вписываем там по аналогии

.  Resource Files  Resource Files   . 

Перезагружаем проект — файлы снова видны.

Доводим наш модельный пример до ума

Впрочем на самом деле я немного схитрил. Если не создать вручную перед началом билда папку generated, то билд на самом деле падает

1>. \protobuf_test\protobuf.targets(13,6): error MSB3073: The command «. \ThirdParty\protobuf\bin\protoc.exe —cpp_out=.\generated test.proto» exited with code 1.

Чтобы это исправить добавим вспомогательный target который создаст необходимую папку

С помощью свойства DependsOnTargets мы указываем что перед тем как запускать любую из задач GenerateProtobuf следует запустить PrepareToGenerateProtobuf, а запись @(ProtobufSchema) ссылается на список ProtobufSchema целиком, как единую сущность используемую как вход для этой задачи, так что запущена она будет лишь один раз.

Перезапускам сборку — работает! Давайте попробуем сделать теперь еще раз Rebuild, чтобы уж на этот раз точно во всем убедиться

1>—— Rebuild All started: Project: protobuf_test, Configuration: Debug x64 ——
1>pch.cpp
1>protobuf_test.cpp
1>protobuf_test.vcxproj -> S:\Temp\msbuild\protobuf_msbuild_integration\x64\Debug\protobuf_test.exe
========== Rebuild All: 1 succeeded, 0 failed, 0 skipped ==========

Эм, а куда же пропали наши новые таски? Небольшая отладка — и мы видим что таски на самом деле запускаются MSBuild, но не выполняются поскольку в указанной нами выходной папке уже есть сгенерированные файлы. Проще говоря в Rebuild у нас не работает Clean для .\generated файлов. Исправим это, добавив еще один таргет

Проверяем — работает. Clean очищает созданные нами файлы, Rebuild пересоздает их заново, повторный вызов Build не запускает без нужды пересборку еще раз.

========== Build: 0 succeeded, 0 failed, 1 up-to-date, 0 skipped ==========

Вносим правку в один из C++ файлов, пробуем сделать Build еще раз

1>—— Build started: Project: protobuf_test, Configuration: Debug x64 ——
1>protobuf_test.cpp
1>protobuf_test.vcxproj -> S:\Temp\msbuild\protobuf_msbuild_integration\x64\Debug\protobuf_test.exe
========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========

.proto-файл не менялся, поэтому protoc не перезапускался, все ожидаемо. Пробуем теперь изменить .proto файл.

========== Build: 0 succeeded, 0 failed, 1 up-to-date, 0 skipped ==========

Интересно что если запустить сборку MSBuild через командную строку вручную, а не через UI из Студии то такой проблемы не будет — MSBuild корректно пересоберет необходимые .pp.cc файлы. Если мы поменяем какой-нибудь .cpp то запустившийся в студии MSBuild пересоберет не только его, но и .props файл который мы меняли раньше

1>—— Build started: Project: protobuf_test, Configuration: Debug x64 ——
1>Compiling schema test.proto
1>protobuf_test.cpp
1>protobuf_test.vcxproj -> S:\Temp\msbuild\protobuf_msbuild_integration\x64\Debug\protobuf_test.exe
========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========

U2DCheck и tlog файлы

Оказывается что создатели Visual Studio посчитали что вызывать MSBuild на каждый чих слишком накладно и… реализовали собственную «быструю проверку» на то нужно ли собирать проект или нет. Она называется U2DCheck и если по ее мнению проект не менялся, то Студия просто не станет запускать MSBuild для этого проекта. Обычно U2DCheck работает настолько «тихо» что про ее существование мало кто догадывается но в реестре можно включить полезный флажок который заставит U2DCheck выводить более подробные отчеты.

В своей работе U2DCheck опирается на специальные .tlog файлы. Их легко можно найти в intermediate-output папке (имя_проекта).tlog и чтобы U2DCheck корректно реагировал на изменения в исходных файлах нам надо сделать в этой папке запись в один из read tlog — файлов, а чтобы U2DCheck корректно реагировал на удаление выходных файлов — запись в одном из write tlog — файлов.

Чертыхнувшись, возвращаемся к соответствующей правке нашего target-а

Проверяем — работает: правка .props файла триггерит необходимый ребилд, сборка в отсутствие правки показывает что проект up-to-date. В данном примере для простоты я не стал писать write tlog отслеживающий удаление созданных при компиляции файлов, но он добавляется в target аналогичным образом.

Начиная с Visual Studio 2017 update 15.8 в MSBuild была добавлена новая стандартная таска GetOutOfDateItems которая автоматизирует эту черную магию, но поскольку это произошло совсем недавно то практически все кастомные .target-ы продолжают работать с .tlog файлами вручную.

При желании можно так же полностью отключить U2DCheck для любого проекта добавив одну строчку в поле ProjectCapability

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

Финализуем наш кастомный .target

Получившийся у нас результат вполне работоспособен, но его есть еще куда совершенствовать. К примеру в MSBuild существует режим «выборочной сборки» когда в командной строке указывается что требуется собрать не весь проект в целом, а лишь отдельные конкретно выбранные в нем файлы. Поддержка этого режима требует чтобы таргет проверял содержимое списка @(SelectedFiles).

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

Наконец мы все еще не реализовали обещанную в самом начале задумку — автоматическое включение сгенерированных файлов в проект. Мы уже можем #include-ить сгенерированные protobuf-ом заголовочные файлы зная что они будут автоматически созданы до того как дело дойдет до компиляции, но с линковщиком этот номер не проходит :). Поэтому просто дописываем сгенерированные файлы в список ClCompile.

Пример подобной причесанной реализации protobuf.targets

    GenerateProtobuf   .\generated    $(ProtobufOutputFolder)\%(ProtobufSchema.Filename).pb.cc $(ProtobufOutputFolder)\%(ProtobufSchema.Filename)_pb2.py %(ProtobufCompilerData.OutputCppFile);%(ProtobufCompilerData.OutputPythonFile)  NotUsing             

Общие настройки здесь были вынесены в PropertyGroup, а списки входных и выходных файлов заполняет новый target ComputeProtobufInput. Попутно (чтобы продемонстрировать работу со списками выходных файлов) была добавлена генерация кода из схемы для интеграции с python. Запускаем и проверяем что все работает правильно

1>------ Rebuild All started: Project: protobuf_test, Configuration: Debug x64 ------ 1>Compiling schema test.proto 1>Compiling schema test2.proto 1>pch.cpp 1>protobuf_test.cpp 1>test.pb.cc 1>test2.pb.cc 1>Generating Code. 1>protobuf_test.vcxproj -> S:\Temp\msbuild\protobuf_msbuild_integration\x64\Debug\protobuf_test.exe 1>Done building project "protobuf_test.vcxproj". ========== Rebuild All: 1 succeeded, 0 failed, 0 skipped ========== 
А что насчет CustomBuildStep?

Надо сказать что разработчики из Майкрософт вполне здраво оценивали что все вышеописанное, хм, несколько нетривиально и плохо задокументировано и попытались облегчить жизнь программистов введя специальный таргет CustomBuildStep. В рамках этой концепции мы должны были бы в настройках файла отметить что наши .props файлы относятся к типу Custom Build Step

Затем следовало бы указать необходимые шаги по сборке во вкладке Custom Build Step

В .vcxproj-е это выглядит примерно вот так

   $(Protoc) --cpp_out=.\generated\%(FileName).pb.cc %(FullPath) Generate protobuf files .\generated\%(FileName).pb.cc   .  . 

Эта конструкция работает за счет того что введенные таким образом данные подставляются в недрах Microsoft.CppCommon.targets в специальный таргет CustomBuildStep который делает, в общем-то, все то же самое что я описал выше. Но работает все через GUI и не надо задумываться о реализации clean и tlog-ах :). При желании этот механизм вполне можно использовать, но я бы не рекомендовал этого делать в силу следующих соображений:

  • CustomBuildStep может быть только один на весь проект
    • Соответственно так обработать можно лишь 1 тип файлов на весь проект
    • Включать такой step в .props файл используемый для подключения ThirdParty библиотеки нецелесообразно, т.к. разные библиотеки могут его перекрывать друг у друга
    Правильное копирование файлов

    Очень часто встречающейся разновидностью build target является копирование каких-нибудь файлов из одного места в другое. Например копирование файлов ресурсов в папку с собранным проектом или копирование thirdparty DLL к собранному бинарнику. И очень часто эту операцию реализуют «в лоб» через запуск консольной утилиты xcopy в Post-Build Targets. К примеру,

    Так делать не надо по тем же самым причинам по которым не надо пытаться запихивать в Post-build steps другие build targets. Вместо этого мы можем напрямую указать Студии что ей необходимо скопировать тот или иной файл. К примеру если файл напрямую входит в проект, то ему достаточно указать ItemType=Copy

    После нажатия кнопки apply появится дополнительная вкладка на которой можно настроить куда и как следует копировать выбранный файл. В коде .vcxproj-файла это будет выглядеть примерно так:

      .  $(OutDir)  

    Всё заработает «из коробки», включая правильную поддержку tlog-файлов. Внутри это реализовано по все тому же принципу «специальной стандартной таски для копирования файлов» что и Custom Build Step которую я критиковал буквально в предыдущем разделе, но поскольку копирование файлов — довольно тривиальная операция и мы не переопределяем саму операцию (копирование) а лишь меняем список входных и выходных файлов для нее то работает это неплохо.

    Замечу что при формировании списков файлов CopyFileToFolder можно использовать wildcards. К примеру

     $(OutDir) 

    Добавление файлов в список CopyFileToFolders — пожалуй самый простой способ реализовать копирование при сборке проекта, в том числе в .props-файлах подключающих thirdparty-библиотеки. Однако если хочется получить больше контроля над происходящим, то еще одним вариантом является добавление в свои build target специализированной таски Copy. К примеру

    Небольшое лирическое отступление

    Вообще набор разнообразных стандартных task-ов у MS весьма обширен и включает в себя такие таски как DownloadFile, VerifyFileHash, Unzip и многие другие полезные примитивы. А стандартная таска Copy умеет делать Retry, пропускать не менявшиеся файлы и создавать hard-link вместо тупого копирования если это поддерживается файловой системой.

    R сожалению таска Copy не поддерживает wildcards и не заполняет .tlog файлы. При желании это можно реализовать вручную,

    к примеру так

         $(TargetFolder)\%(MyFilesToCopy.Filename)%(MyFilesToCopy.Extension)        

    Однако работа с стандартным CopyFileToFolders обычно будет намного проще.

    Level 3: интегрируемся с GUI от Visual Studio

    Все то чем мы до сих пор занимались со стороны может показаться довольно унылой попыткой реализовать в не слишком подходящем для этого инструменте функциональность нормального make. Ручная правка XML-файлов, неочевидные конструкции для решения простых задач, костыльные tlog-файлы… Однако у билд-системы Студии есть и плюсы — к примеру после первоначальной настройки она обеспечивает получившимуся билд-плану неплохой графический интерфейс. Для его реализации используется тэг PropertyPageSchema о котором мы сейчас и поговорим.

    Вытаскиваем настройки из недр .vcxproj в Configuration Properties

    Давайте попробуем сделать так чтобы мы могли бы редактировать свойство $(ProtobufOutputFolder) из «причесанной реализации protobuf.targets» не вручную в файле, а с комфортом прямо из IDE. Для этого нам потребуется написать специальный XAML-файл с описанием настроек. Открываем текстовый редактор и создаем файл с названием, к примеру, custom_settings.xml

    Помимо собственно тэга StringProperty который указывает Студии на существование настройки «ProtobufOutputFolder» с типом String и Subtype=Folder и объясняет то как ее следует показывать в GUI, данный XML-ник указывает что хранить эту информацию следует в project file. Помимо ProjectFile можно использовать еще UserFile — тогда данные будут записаны в отдельный файлик .vcxproj.user который по задумке создателей Студии предназначается для приватных (не сохраняемых в VCS) настроек. Подключаем описанную нами схему к проекту, дописав в наш protobuf.targets тэг PropertyPageSchema

      GenerateProtobuf  

    Для того чтобы наши правки вступили в силу перезапускаем Студию, загружаем наш проект, открываем project properties и видим…

    Да! Появилась наша страничка с нашей настройкой и ее значение по умолчанию было верно прочитано Студией. Пробуем ее изменить, сохраняем проект, смотрим .vcxproj…

      .\generated_custom  

    Как можно видеть по традиционному условию Condition, по умолчанию настройки ассоциированы с конкретной конфигурацией билда. Но при желании это можно перекрыть с помощью установки флага DataSource HasConfigurationCondition=«false». Правда в 2017 студии присутствует баг из-за которого настройки проекта могут не показываться если среди них нет хотя бы одной настройки ассоциированной с какой-то конфигурацией. К счастью эта настройка может быть невидимой.

    Вариант без привязки к configuration

    Настроек можно добавлять сколько угодно. Возможные типы включают BoolProperty, StringProperty (с опциональными подтипами «folder» и «file»), StringListProperty, IntProperty, EnumProperty и DynamicEnumProperty причем последний может заполняться на лету из любого списка доступного в .vcxproj. Подробнее об этом можно почитать здесь. Можно так же группировать настройки в разделы. Попробуем к примеру добавить еще одну настройку типа Bool

    Редактируем настройку, сохраняем проект — все работает как ожидалось

     true  .\generated_сustom 
    Объясняем Студии про новые типы файлов

    До сих пор чтобы добавить в проект protobuf-файл нам необходимо было вручную прописывать в .vcxproj что это . Это легко исправить дописав к упомянутому выше .xml три тэга

    Перезапускаем студию, смотрим свойства у наших .proto файлов

    Как легко видеть файлы теперь верно распознаются как «Google Protobuf Schema». К сожалению соответствующий пункт не добавляется автоматически в диалог «Add new item», но если мы добавим в проект уже существующий .proto-файл (контекстное меню проекта \ Add \ Existing item… ) то он распознается и добавится правильно. Кроме того наш новый «тип файлов» можно будет выбрать в выпадающем списке Item type:

    Ассоциируем настройки с индивидуальными файлами

    Помимо настроек «для проекта в целом» совершенно аналогичным образом можно сделать «настройки для отдельного файла». Достаточно указать в тэге DataSource аттрибут ItemType.

    Сохраняем, смотрим содержимое .vcxproj

      MYLIB_EXPORT 

    Все работает как ожидалось.

    Level 4: расширяем функциональность MSBuild

    У меня никогда не возникало необходимости залезать в процесс сборки настолько глубоко, но раз уж статья так и так получилась немаленькой, то коротенько упомяну о последней возможности для кастомизации: расширение самого MSBuild. Помимо довольно обширной коллекции «стандартных» тасков, в MSBuild таски можно «импортировать» из разных источников с помощью тэга UsingTask. К примеру мы можем написать свое расширение для MSBuild, скомпилировать его в DLL-библиотеку и импортировать как-то вот так:

    Именно так реализовано большинство «стандартных» тасков предоставляемых Студией. Но таскать с собою кастомную DLL для сборки по очевидным причинам частенько неудобно. Поэтому в тэге UsingTask поддерживается штука которая называется TaskFactory. TaskFactory можно считать «компилятором для task-ов» — мы передаем ей на вход некий исходный «мета-код», а она по нему генерирует реализующий его объект типа Task. К примеру с помощью CodeTaskFactory можно воткнуть код написанной на C# таски прямо внутрь .props-файла.

    Подобный подход использует, к примеру Qt VS Tools

                 x) .Select(x => string.Format("[=]", x, Item.GetMetadata(x)))) .ToUpper()); using (var dataZipped = new MemoryStream()) < using (var zip = new DeflateStream(dataZipped, CompressionLevel.Fastest)) zip.Write(data, 0, data.Length); Hash = Convert.ToBase64String(dataZipped.ToArray()); >]]>   

    Если кто-то подобной функциональностью пользовался — отпишитесь об интересных use-case в комментариях.

    На этом всё. Надеюсь что мне удалось показать как при настройке MSBuild работу с крупным проектом в Visual Studio можно сделать простой и удобной. Если Вы соберетесь внедрять у себя что-то из описанного выше, то дам небольшой совет: для отладки .props, .targets и .vcxproj удобно выставить MSBuild «отладочный» уровень логгирования в котором он весьма подробно пошагово расписывает свои действия с входными и выходными файлами

    Спасибо всем кто дочитал до конца, надеюсь что получилось интересно :).

    Делитесь своими рецептами для msbuild в комментариях — я постараюсь обновлять пост чтобы он служил исчерпывающим гайдом по конфигурированию solution в Студии.

    Как открыть свойства проекта visual studio

    Участник клуба

    Регистрация: 28.07.2009

    Сообщений: 1,510

    Не могу открыть свойства проекта. Faile to create PropertyGrid

    Пару недель назад ставить DirectX SDK, а сегодня заметил, что не могу открыть свойства проекта (может это и не из-за SDK). При попытке открыть свойства, VS 2008 пишет:
    «Failed to create .NET Framework PropertyGrid component. Please verify that the CLR and .NET Frameworks are properly installed.»

    Обновил .Net Framework (скачал 4й с сайта и сказал ему обновить мой старый Framework). Не помогло.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *