Pre build что это
prebuild
1 prebuild
2 prebuild
См. также в других словарях:
prebuild — verb To build ahead of time. We need to prebuild these cabinets before the installers arrive … Wiktionary
prebuild — v.t., prebuilt, building. * * * … Universalium
prebuild — pre•build′ v. t. built, build•ing … From formal English to slang
prebuild — v.t., prebuilt, building … Useful english dictionary
Alaska Gas Pipeline — The Alaskan Natural Gas Pipeline is a proposal to transport natural gas from the Alaska North Slope natural gas reserves to the U.S. Midwest. There are two competing projects: one by BP and ConocoPhillips, and another by TransCanada Corp.cite… … Wikipedia
Alaska gas pipeline — For the proposed pipeline from the Mackenzie Valley, see Mackenzie Valley Pipeline. Alaska gas pipeline Location Country United States, Canada General direction north–south From … Wikipedia
808 State — Infobox musical artist Name = 808 State Img capt = Img size = Landscape = Background = group or band Origin = Manchester, England, UK Genre = House Techno Ambient Acid house Years active = 1988–present Label = ZTT Records (UK) Tommy Boy/Warner… … Wikipedia
808State — 808 State 808 State est un groupe de musique électronique britannique créé en 1988 à Manchester (Angleterre). Sommaire 1 Biographie 2 Histoire du nom 3 Discographie 4 … Wikipédia en Français
808 State — Pays d’origine Royaume Uni Genre musical Musique électronique House Acid house Techno … Wikipédia en Français
808 state — est un groupe de musique électronique britannique créé en 1988 à Manchester (Angleterre). Sommaire 1 Biographie 2 Histoire du nom 3 Discographie 4 … Wikipédia en Français
Outpost Transmission — Studio album by 808 State Released 2 November 2002 (Japan) 13 Novembe … Wikipedia
prebuild
Смотреть что такое «prebuild» в других словарях:
prebuild — verb To build ahead of time. We need to prebuild these cabinets before the installers arrive … Wiktionary
prebuild — v.t., prebuilt, building. * * * … Universalium
prebuild — pre•build′ v. t. built, build•ing … From formal English to slang
prebuild — v.t., prebuilt, building … Useful english dictionary
Alaska Gas Pipeline — The Alaskan Natural Gas Pipeline is a proposal to transport natural gas from the Alaska North Slope natural gas reserves to the U.S. Midwest. There are two competing projects: one by BP and ConocoPhillips, and another by TransCanada Corp.cite… … Wikipedia
Alaska gas pipeline — For the proposed pipeline from the Mackenzie Valley, see Mackenzie Valley Pipeline. Alaska gas pipeline Location Country United States, Canada General direction north–south From … Wikipedia
808 State — Infobox musical artist Name = 808 State Img capt = Img size = Landscape = Background = group or band Origin = Manchester, England, UK Genre = House Techno Ambient Acid house Years active = 1988–present Label = ZTT Records (UK) Tommy Boy/Warner… … Wikipedia
808State — 808 State 808 State est un groupe de musique électronique britannique créé en 1988 à Manchester (Angleterre). Sommaire 1 Biographie 2 Histoire du nom 3 Discographie 4 … Wikipédia en Français
808 State — Pays d’origine Royaume Uni Genre musical Musique électronique House Acid house Techno … Wikipédia en Français
808 state — est un groupe de musique électronique britannique créé en 1988 à Manchester (Angleterre). Sommaire 1 Biographie 2 Histoire du nom 3 Discographie 4 … Wikipédia en Français
Outpost Transmission — Studio album by 808 State Released 2 November 2002 (Japan) 13 Novembe … Wikipedia
Вторая жизнь вместе с Maven
Думаю, для большинства хабражителей не будет откровением, что среди крупных софтварных разработок нередко встречаются технологии и языки значительно отстающие от передовых. К сожалению, это неизбежность, т. к. невозможно в какой-то момент взять и переписать несколько миллионов строк кода на другой язык или с применением более современного фреймворка.
Но в какой то момент отставание становится настолько существенным, что продукт перестает быть конкурентоспособен и постепенно исчезает с рынка. Именно такую ситуацию постепенного затухания мне и довелось наблюдать.
Здесь я хочу рассказать о том, как в конкретном случае я постарался оттянуть неизбежное и оживить разработку применив для этого сборщик проектов Maven.
Главным достоинством разработанной мною системы, пожалуй, можно назвать то, что небольшим количеством трудозатрат, можно при помощи Maven научить проект, реализованный на любом языке, собираться из отдельных Пакетов, соблюдающих свою версионность и автоматически обновляющихся при сборке проекта. Все что для этого нужно, это утилита командной строки, которая может скомпилировать исходник, а в случае скриптовых языков была бы полезна утилита валидации.
Итак дано:
— Система, «ядро» которой состоит из набора dll и отдается прикладным разработчикам без исходников.
— Прикладной язык программирования, который подобен Паскалю, но кроме стандартных возможностей Паскаля(из которых доступно далеко не все) позволяет использовать объекты, реализованные в dll «ядра». Данный прикладной язык компилируется в подобие байт кода и исполняется отдельной dll «ядра».
Таким образом, основная разработка ведется на описанном выше волшебном прикладном языке, все прелести которого, пожалуй, могли бы ужаснуть уважаемое сообщество, а «ядро» периодически(новая версия раз в пару месяцев) поставляется из головного офиса и не подлежит модификации.
Очень вероятно, что разработка и поддержание собственного языка программирования похожего на Паскаль и сделанного на базе Delphi возможно была плохой идеей, но 15-17 лет назад, когда все это начиналось, об этом никто не думал. Преследовались другие цели, которые в общем-то с успехом были достигнуты.
Сама система представляет из себя продаваемый заказчику продукт с сервером, клиентом и тонким клиентом. Естественно, большинство клиентов не удовлетворяются стандартным функционалом системы, поэтому существует несколько отделов прикладной разработки, “допиливающих”систему под их нужды. Каждым проектом занимается обособленная группа программистов. Взаимодействие и обмен опытом между группами катастрофически малы. Таким образом, каждая группа получает свой шанс переизобрести велосипед и наступить по десятому разу на одни и те же грабли.
Проведя анализ, стало понятно, что такое отсутствие взаимодействия отделов объясняется прежде всего нехваткой механизмов использования общего кода. Иными словами, если все-таки, происходил обмен опытом и исходниками между группами, то библиотека(исходник) просто копировалась в другой проект(продукт для друго клиента) и начинала жить самостоятельной жизнью, сохранив в себе все глюки и недоработки, которые присутствовали на момент копирования.
Для исправления ситуации было принято решение организовать репозиторий (хранилише) и систему сбора проекта с использованием набора библиотек общего кода(Пакетов).
В качестве инструмента для этого был выбран Maven, т. к. он по умолчанию выполняет схожие функции, имеет гибкий жизненный цикл и огромную гибкость плагинной системы.
Но обо всем по порядку
Главной проблемой, о которой я и не подозревал в начале своих изысканий, оказалось то, что Пакеты должны содержать в себе не скомпилированные в байт код файлы, а именно исходники, т.к. компиляция должна происходить именно под той версией «ядра», для которой они будут использоваться. Для простоты назовем наши прикладные исходники bpas. Это немного противоречит стандартному ЖЦ в Maven.
Немного о том как работает Maven
Жизненный цикл Maven достаточно полный, и набор фаз(phases) учитывает практически все этапы сборки проекта, которые могут потребоваться.
Причем пытаясь запустить какую-то фазу ЖЦ, например «mvn compile», я на самом деле запускаю всю цепочку фаз от validate (валидация проекта) до compile, не пропуская ни одной. Для каких-то фаз существуют так называемые плагины по умолчанию, которые будут вызваны несмотря на то, что в pom.xml(основной файл Maven проекта) существует только заголовок и ни одного указания на запуск плагинов.
Здесь стоит отдельно отметить тот факт, что Maven — это полностью плагинная система. Иными словами, он не умеет практически ничего, кроме запуска плагинов, а вот они уже умеют делать потрясающе много. Получается, что когда мы хотим научить Maven каким- то особенностям сборки проекта, мы должны добавить в pom.xml указание на запуск нужного плагина в нужную фазу и с нужными параметрами.
Вот такой абсолютно валидный пустой pom.xml, несмотря на свою пустоту, при получении команды mvn deploy запустит Плагин инициализации, компиляции, упаковки и деплоя Java исходников из папки src/main.
Основной политикой использования в Maven является то, что для любого действия есть свои параметры по умолчанию и дополнительные настройки требуются только в том случае, когда этих умолчаний не хватает или они грубо нарушаются. В моем случае пришлось отказаться от очень многих умолчаний, поэтому pom.xml уже не выглядит так скромно.
Построение нового ЖЦ в pom.xml
Для реализации пакетов был подобран следующий жизненный цикл.
initialize (инциализация) – Читаем настройки из конфиг(property или key = value) файла и добавляем их в тег properties. О теге properties поговорим чуть позже.
generate-sources (генерация исходного кода) – Загружаем и распаковываем из zip все Пакеты, которые являются зависимостями данного пакета/проекта, в отдельную директорию для последующей компиляции вместе с исходниками текущего пакета/проекта.
compile (компиляция) – Запускаем свой плагин компиляции для наших bpas, который определяет правильный порядок компиляции и запускает компилятор командной строки для нашего языка. Кратко я расскажу о собственном плагине ниже, но гайд по его написанию предлагаю вынести за пределы данной статьи.
assembly (сборка) – запаковываем пакет, состоящий из исходников bpas в zip с сохранением структуры подкаталогов исходных файлов.
deploy (в нашем случае выгрузка в репозиторий) – Собранный на фазе assembly zip отправляется в локальный репозиторий Maven с добавлением к нему pom.xml и другой информации по пакету. Данная процедура почти идентична нормальному deploy jar файла, но с особыми параметрами.
clean (очистка) – Данная фаза не входит в общий ЖЦ фаз сборки, а стоит несколько особняком, но поскольку она также была модернизирована, ее тоже стоит упомянуть. Кроме стандартного удаления директории, в которой лежат скомпилированные файлы. (targetDirectory), потребовалось удалять тот мусор, который образуется в процессе скачивания и распаковки пакетов-зависимостей.
Общая структура pom.xml
Я условно делю pom.xml на две части: заголовок и сборка.
Заголовок представляет из себя идентификацию пакета (groupId, artifactId, version), свойства (properties, которые выполняют роль внутренних констант), указание локального репозитория (distributionManagement), указание локального репозитория плагинов (pluginRepositories), указание локального репозитория пакетов (repositories) и указание зависимостей этого пакета (dependencies). При этом все три репозитория могут указывать на одно и то же хранилище, но принципиально это три разные сущности, каждую из которых нужно указывать отдельно. Так например, мы можем решить хранить плагины отдельно от остального кода, а для доступа к пакетам в хранилише использовать http доступ, тогда как «деплоить» туда мы будет как в файловое хранилище.
Сборка (тег build) — это вторая часть pom.xml, в которой настраиваются особенности обработки той или иной фазы жизненного цикла различными плагинами с недефолтными настройками. Кроме этого там настраиваются директории и параметры, которые будут участвовать в сборке проекта:
sourceDirectory – директория, в которой находятся исходники для компиляции.
finalName – конечное имя файла после запаковки в архив.
directory – рабочая директория, в которую будут положены скомпилированные файлы.
Кроме этого еще раз хочу напомнить, что для всех этих параметров, существуют значения по умолчанию, и их отдельное указание в нашем случае требуется только потому, что они должны отличаться от этих самых умолчаний.
Реализация ЖЦ в теге build
Теперь вернемся к определенному нами ЖЦ и посмотрим, как каждая из фаз жизненного цикла реализована при помощи вызова нужного плагина с той конфигурацией, которая нам нужна.
initialize
Тут опять давайте немного отвлечемся и отдельно упомянем тег properties. Объясним, зачем он нужен и как используется.
Если говорить очень грубо, то этот тег похож на объявление констант, которые в дальнейшем будут использоваться в нашей программе (pom.xml). Но попадать туда значения могут тремя разными способами. И у каждого способа есть приоритет, определяющий, какое в итоге значение будет в тот момент, когда оно реально потребуется.
Низший приоритет у прямой записи свойств в тег properties, например так:
Более высокий приоритет у прямого задания параметров при запуске Maven, примерно так «mvn –DhelloText=Hi initialize»
При запуске Maven с таким параметром исходное значение тега helloText будет заменено на переданное в строке запуска для текущего сеанса, т.е. в xml оно не сохранится. Если такой константы вообще не существовало, то на этот сеанс она будет существовать и может быть использована. Значения всех несуществующих констант — пустая строка.
Наивысшим приоритетом обладает добавление значений в тег proprties плагинами в текущей сессии. Они так же не сохраняться в pom.xml. Именно этот механизм и будет использован нами для вынесения отдельных настроек сборки в properties файл, содержащий “имя=значение”
Для этого используется плагин properties-maven-plugin
generate-sources
На стадии генерации исходников мы рекурсивно загружаем из репозитория и распаковываем все необходимые нам Пакеты, которые указаны в зависимостях. Опять же самим практически ничего делать не нужно. Все за нас сделает плагин после указания ему правильных настроек.
Конструкция $ означает, что нужно взять значение из тега “/project/properties/packagesPath”.
Отдельно хочу отметить, что использования плагина maven-assembly-plugin для распаковки считается deprecated и не рекомендуется к использованию в Maven 3. Вместо него используется maven-dependency-plugin с настройками аналогичными указанным выше. Я же использую более старую версию плагина, чтобы еще раз наглядно показать, как один и тот же плагин настраивается на выполнение нескольких задач из его ассортимента.
compile
Со стадией компиляции пришлось изрядно повозиться, но основные трудности возникли с написанием собственного плагина компиляции. Пошаговая инструкция по написанию собственного плагина для Maven — это тема для отдельной статьи, поэтому сейчас мы не будем заострять на этом внимание. В конце-концов изложенный здесь материал может быть использован и для скриптовых языков, компиляция которым вообще не требуется.
Одно можно сказать наверняка, как бы вы не старались, не удастся отключить запуск родного плагина maven-compile-plugin, призвание которого компилировать исходники на Java( Не рассматривая возможности редактирования superPom.xml). Итак настройки моего плагина компиляции выглядят следующим образом:
используемые параметры:
protectionServer — сервер защиты, без которого невозможен запуск компилятора командной строки.
protectionAlias — секция используемой лицензии сервера защиты.
bpasccPath — полный или относительный путь к компилятору командной строки.
binaryVersion — Версия, которая будет «вмонтирована» в скомпилированную библиотеку.
Это далеко не все настройки моего плагина, но как я и говорил, это тема для отдельной большой статьи. В принципе секцию configuration можно было вообще не указывать и тогда все параметры, которые необходимы плагину, были бы проинициализированы плагином значениями по умолчанию, что соотвествует основной концепции Maven.
assembly
При прохождении фазы assembly у Maven по умолчанию настроен запуск maven-assembly-plugin, явно указав его запуск в фазе assembly в теге build, мы можем переопределить его настройки и заставить работать на нас. Этот же плагин мы использовали для распаковки пакетов перед компиляцией, поэтому теперь я приведу полную версию настроек этого плагина, включающую и запаковку и распаковку.
packagesDirName — это константа из /project/properties файла pom.xml
Отдельно хочу отметить, что такое вынесение настроек запаковки в отдельный файл, позволило мне создать один конфиг запаковки на все Пакеты, что крайне удобно.
deploy
Фаза deploy так же запускается Maven’ом независимо от того, указали ли мы настройки этого плагина или нет. Переопределив их, мы и этот плагин заставили работать на себя.
С такими ручными настройками maven-deploy-plugin позволяет любой файл(или даже группу файлов) выложить в репозиторий Maven как валидную библиотеку(Пакет). Теперь разберем настройки по порядку.
file — файл, который будет отправлен в репозиторий Maven как Пакет.
url — путь к репозиторию Maven
repositoryId — идентификатор репозитория, в который будет производиться депллой.
groupId, artifactId, version — стандартная идентификация пакета в репозитории Maven. Именно по этим трем параметрам можно однозначно идентифицировать библиотеку. packaging – функционал деплой так же включает в себя запаковку файлов, которые будут отправлены в репозиторий, поэтому необходимо снова указать ему zip, чтобы он ничего не делал с пакетом, иначе распакует и запакует как jar :-).
pomFile – если данный параметр указан, то в комплект к Пакету будет добавлен тот файл, который мы укажем как pom.xml, при этом его изначальное имя может быть другим. Сохранять оригинальный pom.xml выгодно по многим причинам, поэтому мы не будем брезговать данной возможностью.
clean
Фаза clean, как я уже говорил, не входит в стандартный ЖЦ. Изначальная ее задача состоит в том, чтобы после выполнения команды mvn clean в проекте не осталось никаких следов жизнедеятельности. В нашем случае кроме стандартной папки targetSource(указана в теге build) требуется так же удалить все Пакеты, которые были «слиты» как зависимости для успешной компиляции пакета/проекта.
directory — собственно указание директории, которую необходимо удалить. Надо отметить, что тут создатели плагина отошли от общепринятой концепции, и явное указание настроек плагина не отменяет его действий по умолчанию. Но в данном случае, это очень хорошо, т. к. избавляет нас от лишний настроек.
Памятуя о том, как поначалу тяжело разбираться с отдельными ветками настроек, ниже приведу полный текст pom.xml одного из пакетов.
Итоги
Несмотря на то, что я сам остался чертовски доволен своей работой, я готов признать, что в данном виде система пакетной сборки проекта еще далека от совершенства, однако, приведенное описание достаточно иллюстрирует реализацию полного жиненного цикла пакета/проекта. Фактически это работоспособный каркас, поверх которого можно почти неограниченно расширять возможности.
Настраиваем удобную сборку проектов в Visual Studio
Эта статья является руководством по настройке сборки C++ проектов Visual Studio. Частично она сводилась из материалов разрозненных статей на эту тему, частично является результатом реверс-инжениринга стандартных конфигурационных файлов Студии. Я написал ее в основном потому что полезность документации от самой Microsoft на эту тему стремится к нулю и мне хотелось иметь под рукой удобный референс к которому в дальнейшем можно будет обращаться и отсылать других разработчиков. Visual Studio имеет удобные и широкие возможности для настройки по-настоящему удобной работы со сложными проектами и мне досадно видеть что из-за отвратительной документации эти возможности очень редко сейчас используются.
В качестве примера попробуем сделать так чтобы в Студию можно было добавлять flatbuffer schema, а Студия автоматически вызывала flatc в тех случаях когда это нужно (и не вызывала — когда изменений не было) и позволяла задавать настройки напрямую через File Properties
Оглавление
ЗАМЕЧАНИЕ: все приведенные в статье примеры проверялись в VS 2017. В рамках моего понимания они должны работать и в более ранних версиях студии начиная по крайней мере с VS 2012, но обещать я этого не могу.
Довольно нечитаемое месиво, не правда ли? И это ведь еще очень небольшой и практически тривиальный файл. Попробуем превратить его во что-то более читабельное и удобное для восприятия.
Перезагружаем проект в студии, проверяем сборку — все работает.
Делаем настройку проекта читабельнее
Делаем удобным подключение сторонних библиотек
Обычный процесс включения зависимости от thirdparty-библиотеки в Visual Studio частенько выглядит примерно вот так:
Процесс соответствующей настройки включает в себя редактирование сразу нескольких параметров находящихся на разных вкладках настроек проекта и потому довольно зануден. Вдобавок его обычно приходится проделывать по нескольку раз для каждой отдельно взятой конфигурации в проекте, так что нередко в результате подобных манипуляций оказывается что проект в Release-сборке собирается, а в Debug-сборке — нет. Так что это неудобный и ненадежный подход. Но как Вы наверное уже догадываетесь, те же самые настройки можно «упаковать» в props-файл. К примеру для библиотеки ZeroMQ подобный файл может выглядеть примерно так:
Обратите внимание что если мы просто определим тэг типа AdditionalLibraryDirectories в props-файле, то он перекроет все более ранние определения. Поэтому здесь используется чуть более сложная конструкция в которой тэг завершается последовательностью символов ;%(AdditionalLibraryDirectories) образующих ссылку тэга самого на себя. В семантике MSBuild этот макрос раскрывается в предыдущее значение тэга, так что подобная конструкция дописывает параметры в начало строки хранящейся в парамере AdditionalLibraryDirectories.
И на этом манипуляции с проектом заканчиваются — MSBuild автоматически подключит необходимые заголовочные файлы и библиотеки и в Release и в Debug сборках. Таким образом потратив немного времени на написание zeromq.props мы получаем возможность надежно и безошибочно подключать ZeroMQ к любому проекту всего в одну строчку. Создатели Студии даже предусмотрели для этого специальный GUI который называется Property Manager, так что любители мышки могут проделать ту же операцию в несколько кликов.
Project Templates — автоматизируем создание проектов
Обратите внимание на параметр ReplaceParameters=«true». В данном случае он применяется только к vcxproj-файлу который выглядит следующим образом:
Для создания VSIX нам понадобится установить опциональный компонент Студии который так и называется — средства для разработки Visual Studio Extensions:
После этого в разделе Visual C# \ Extensibility появится вариант «VSIX project». Обратите внимание, что несмотря на свое расположение (C#), он используется для создания любых расширений, в том числе и наборов шаблонов проектов на C++.
Подобный подход позволяет легко унифицировать работу большого количества человек над проектом. Для установки и использования шаблонов от пользователя не требуется никакой квалификации кроме пары кликов мышкой. Установленное расширение можно посмотреть (и удалить) в диалоге Tools \ Extensions and Updates
Level 2: настраиваем кастомную компиляцию
Традиционный подход
Знакомимся с MSBuild targets
«Стандартный» билд-план предлагаемый Студией довольно обширен и предлагает большой набор правил для компиляции C++-кода и «стандартных» таргетов типа Build, Clean и Rebuild к которым умеет «цепляться» Студия. Этот набор часто известен под собирательным названием toolset и заменяя в импорте toolset можно заставить Студию компилировать один и тот же проект с помощью другой версии Студии или, к примеру, интеловским компилятором или Clang. Кроме того при желании от стандартного toolset-а можно вообще отказаться и написать свой собственный toolset с нуля. Но мы будем рассматривать в этой статье более простой вариант в котором мы ничего не будем заменять, а лишь дополним стандартные правила необходимыми нам дополнениями.
то мы добавляем (Include) файл protobuf_tests.cpp в список входов (inputs) данного таргета, а когда пишем
то присваем значение «Use» настройке ClCompile.PrecompiledHeader которую target затем превратит в флаг /Yu переданный компилятору cl.exe.
Добавление нового target-а реализуется с помощью тэга target:
созданный нами target необходимо добавить в список AvailableItemName
Нам также понадобится описать что же конкретно мы хотим сделать с нашими входными файлами и что должно получиться на выходе. Для этого в MSBuild используется сущность которая называется «task». Таска — это какое-то простое действие которое нужно сделать в ходе сборки проекта. К примеру «создать директорию», «скомпилировать файл», «запустить команду», «скопировать что-то». В нашем случае мы воспользуемся таской Exec чтобы запустить protoc.exe и таской Message чтобы отобразить этот шаг в логе компиляции. Укажем так же что запуск данного target-а следует провести сразу после стандартного таргета PrepareForBuild. В результате у нас получится примерно вот такой файлик protobuf.targets
Мы использовали здесь довольно нетривиальный оператор «%» (batching operator) который означает «для каждого элемента из списка» и автоматически добавляемые метаданные. Идея тут в следующем: когда мы записываем код вида
и проверяем сборку
Перезагружаем проект — файлы снова видны.
Доводим наш модельный пример до ума
Впрочем на самом деле я немного схитрил. Если не создать вручную перед началом билда папку generated, то билд на самом деле падает
Чтобы это исправить добавим вспомогательный target который создаст необходимую папку
С помощью свойства DependsOnTargets мы указываем что перед тем как запускать любую из задач GenerateProtobuf следует запустить PrepareToGenerateProtobuf, а запись @(ProtobufSchema) ссылается на список ProtobufSchema целиком, как единую сущность используемую как вход для этой задачи, так что запущена она будет лишь один раз.
Перезапускам сборку — работает! Давайте попробуем сделать теперь еще раз Rebuild, чтобы уж на этот раз точно во всем убедиться
Проверяем — работает. Clean очищает созданные нами файлы, Rebuild пересоздает их заново, повторный вызов Build не запускает без нужды пересборку еще раз.
========== Build: 0 succeeded, 0 failed, 1 up-to-date, 0 skipped ==========
Вносим правку в один из C++ файлов, пробуем сделать Build еще раз
========== Build: 0 succeeded, 0 failed, 1 up-to-date, 0 skipped ==========
U2DCheck и tlog файлы
Оказывается что создатели Visual Studio посчитали что вызывать MSBuild на каждый чих слишком накладно и… реализовали собственную «быструю проверку» на то нужно ли собирать проект или нет. Она называется U2DCheck и если по ее мнению проект не менялся, то Студия просто не станет запускать MSBuild для этого проекта. Обычно U2DCheck работает настолько «тихо» что про ее существование мало кто догадывается но в реестре можно включить полезный флажок который заставит U2DCheck выводить более подробные отчеты.
Чертыхнувшись, возвращаемся к соответствующей правке нашего target-а
При желании можно так же полностью отключить U2DCheck для любого проекта добавив одну строчку в поле ProjectCapability
Однако в этом случае Студия будет гонять MSBuild для этого проекта и всех зависящих от него при каждой сборке и да, U2DCheck добавляли не просто так — это работает не так быстро как мне хотелось бы.
Получившийся у нас результат вполне работоспособен, но его есть еще куда совершенствовать. К примеру в MSBuild существует режим «выборочной сборки» когда в командной строке указывается что требуется собрать не весь проект в целом, а лишь отдельные конкретно выбранные в нем файлы. Поддержка этого режима требует чтобы таргет проверял содержимое списка @(SelectedFiles).
Помимо этого в нашем исходном коде встречается довольно много повторяющих друг друга строк которые должны совпадать. Правила хорошего тона рекомендуют дать всем этим сущностям читабельные имена и адресовать по этим именам. Для этого часто создается отдельный специальный таргет который создает и заполняет вспомогательный список всеми именами которые понадобятся в дальнейшем.
Наконец мы все еще не реализовали обещанную в самом начале задумку — автоматическое включение сгенерированных файлов в проект. Мы уже можем #include-ить сгенерированные protobuf-ом заголовочные файлы зная что они будут автоматически созданы до того как дело дойдет до компиляции, но с линковщиком этот номер не проходит :). Поэтому просто дописываем сгенерированные файлы в список ClCompile.
Общие настройки здесь были вынесены в PropertyGroup, а списки входных и выходных файлов заполняет новый target ComputeProtobufInput. Попутно (чтобы продемонстрировать работу со списками выходных файлов) была добавлена генерация кода из схемы для интеграции с python. Запускаем и проверяем что все работает правильно
А что насчет CustomBuildStep?
Затем следовало бы указать необходимые шаги по сборке во вкладке Custom Build Step
Эта конструкция работает за счет того что введенные таким образом данные подставляются в недрах Microsoft.CppCommon.targets в специальный таргет CustomBuildStep который делает, в общем-то, все то же самое что я описал выше. Но работает все через GUI и не надо задумываться о реализации clean и tlog-ах :). При желании этот механизм вполне можно использовать, но я бы не рекомендовал этого делать в силу следующих соображений:
Правильное копирование файлов
Очень часто встречающейся разновидностью build target является копирование каких-нибудь файлов из одного места в другое. Например копирование файлов ресурсов в папку с собранным проектом или копирование thirdparty DLL к собранному бинарнику. И очень часто эту операцию реализуют «в лоб» через запуск консольной утилиты xcopy в Post-Build Targets. К примеру,
Так делать не надо по тем же самым причинам по которым не надо пытаться запихивать в Post-build steps другие build targets. Вместо этого мы можем напрямую указать Студии что ей необходимо скопировать тот или иной файл. К примеру если файл напрямую входит в проект, то ему достаточно указать ItemType=Copy
Всё заработает «из коробки», включая правильную поддержку tlog-файлов. Внутри это реализовано по все тому же принципу «специальной стандартной таски для копирования файлов» что и Custom Build Step которую я критиковал буквально в предыдущем разделе, но поскольку копирование файлов — довольно тривиальная операция и мы не переопределяем саму операцию (копирование) а лишь меняем список входных и выходных файлов для нее то работает это неплохо.
Замечу что при формировании списков файлов CopyFilesToFolder можно использовать wildcards. К примеру
Однако работа с стандартным CopyFileToFolders обычно будет намного проще.
Level 3: интегрируемся с GUI от Visual Studio
Все то чем мы до сих пор занимались со стороны может показаться довольно унылой попыткой реализовать в не слишком подходящем для этого инструменте функциональность нормального make. Ручная правка XML-файлов, неочевидные конструкции для решения простых задач, костыльные tlog-файлы… Однако у билд-системы Студии есть и плюсы — к примеру после первоначальной настройки она обеспечивает получившимуся билд-плану неплохой графический интерфейс. Для его реализации используется тэг PropertyPageSchema о котором мы сейчас и поговорим.
Для того чтобы наши правки вступили в силу перезапускаем Студию, загружаем наш проект, открываем project properties и видим…
Как можно видеть по традиционному условию Condition, по умолчанию настройки ассоциированы с конкретной конфигурацией билда. Но при желании это можно перекрыть с помощью установки флага DataSource HasConfigurationCondition=«false». Правда в 2017 студии присутствует баг из-за которого настройки проекта могут не показываться если среди них нет хотя бы одной настройки ассоциированной с какой-то конфигурацией. К счастью эта настройка может быть невидимой.
Редактируем настройку, сохраняем проект — все работает как ожидалось
Объясняем Студии про новые типы файлов
Ассоциируем настройки с индивидуальными файлами
Помимо настроек «для проекта в целом» совершенно аналогичным образом можно сделать «настройки для отдельного файла». Достаточно указать в тэге DataSource аттрибут ItemType.
Все работает как ожидалось.
Level 4: расширяем функциональность MSBuild
У меня никогда не возникало необходимости залезать в процесс сборки настолько глубоко, но раз уж статья так и так получилась немаленькой, то коротенько упомяну о последней возможности для кастомизации: расширение самого MSBuild. Помимо довольно обширной коллекции «стандартных» тасков, в MSBuild таски можно «импортировать» из разных источников с помощью тэга UsingTask. К примеру мы можем написать свое расширение для MSBuild, скомпилировать его в DLL-библиотеку и импортировать как-то вот так:
Если кто-то подобной функциональностью пользовался — отпишитесь об интересных use-case в комментариях.
Спасибо всем кто дочитал до конца, надеюсь что получилось интересно :).
Делитесь своими рецептами для msbuild в комментариях — я постараюсь обновлять пост чтобы он служил исчерпывающим гайдом по конфигурированию solution в Студии.