Комплексное событие перед сборкой?
У меня есть решение в Visual Studio, которое содержит несколько проектов. Я хотел бы запускать команду в самом начале каждой сборки - независимо от того, какие проекты задействованы и являются ли они современными.
По сути, мне нужно что-то похожее на событие перед сборкой решения, но, к сожалению, VS не поддерживает их. Кто-нибудь знает альтернативный способ достижения того, что мне нужно?
8 ответов
Необычное требование. Но это может быть сделано. Добавьте новый проект в свое решение, используйте шаблон Visual C++ > General > Makefile Project. Установите его NMake > Build Command Line для команд, которые вы хотите выполнить. Используйте Project > Project Dependencies, чтобы все остальные проекты зависели от него.
Краткий обзор моих вариантов ниже
просто примечание: это неполный список всех существующих (см. также другие ответы и т. д.), я поддерживаю только мои оригинальные трюки в актуальном состоянии...
Заметки:
- 1 - не требует никаких дополнительных расширений. Но он может работать только на уровне проектов, поэтому мы используем его для эмуляции уровня нашего решения... Это сложно и неудобно для общего решения, но это вариант. Увидеть ниже.
- 2 - оригинальный движок vsSolutionBuildEvent предоставляет несколько способов унифицированной поддержки VS и msbuild.exe. Простой способ
targets mode
позвонитьafter.<name>.sln.targets
это доступно только для msbuild.exe (это не требует дополнительных шагов, просто действие). Но только оригинальный движок (в том числе vsCommandEvent) может разрешить дополнительные сценарии, которые поддерживают, например, (архиватор 7zip, упаковка пакета nuget без nuget.exe, удаленные серверы и т. Д.). Однако это не важно для нашего вопроса / проблемы, и вы можете использовать любую доступную опцию для поддержки уровня решения, если вы видите+
выше.
Вариант 1: Microsoft.VisualStudio.Shell.Interop
Этот вариант не для простых пользователей VS. Тем не менее, это может быть полезно для вашего полного решения и т. Д.
Вы должны реализовать, например:
например:
public sealed class YourPackage: Package, IVsSolutionEvents, IVsUpdateSolutionEvents2
{
...
public int UpdateSolution_Begin(ref int pfCancelUpdate)
{
//TODO:
}
}
Затем зарегистрируйте обработчик с помощью методов Advise в качестве приоритетного прослушивателя, т. Е. Для IVsUpdateSolutionEvents2 следует использовать AdviseUpdateSolutionEvents.
Это важно, потому что BuildEvents (см. EnvDTE) - вероятно, не поможет и может работать слишком поздно - Пример
Пример с AdviseUpdateSolutionEvents:
// http://msdn.microsoft.com/en-us/library/microsoft.visualstudio.shell.interop.ivssolutionbuildmanager2.aspx
private IVsSolutionBuildManager2 sbm;
// http://msdn.microsoft.com/en-us/library/bb141335.aspx
private uint _sbmCookie;
...
sbm = (IVsSolutionBuildManager2)ServiceProvider.GlobalProvider.GetService(typeof(SVsSolutionBuildManager));
sbm.AdviseUpdateSolutionEvents(this, out _sbmCookie);
Куда:
-
sbm
поле должно быть частью класса для защиты от GC. - Для получения сервиса SVsSolutionBuildManager используется ServiceProvider, но он может быть как вам нужно. См MSDN
Теперь мы можем работать со всеми проектами одновременно - на уровне решения.
Вариант 2: Цели и Карта проектов.
хорошо, вы любите что-то вроде этого - MSBuild: расширение сборки решения, но этот вариант может работать с процессами сборки из msbuild.exe, а не из VS IDE...
Но VS также использует цели (Build, Rebuild, Clean, ..) в файлах проекта (*.csproj, *.vcxproj, ..), когда запускаются операции сборки. Так что мы можем попробовать это, но помните:
- VS также игнорирует удивительный файл.sln. Он формирует все конечные цели из загруженной среды с EnvDTE и т. Д.
- Файл.sln должен обрабатываться msbuild.exe только следующим образом: автоматически генерировать.metaproj (в памяти по умолчанию), который содержит "что и когда" будет построено. Включая общие цели для всех проектов, если таковые существуют, например:
...
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\SolutionFile\ImportAfter\*" Condition="'$(ImportByWildcardBeforeSolution)' != 'false' and exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\SolutionFile\ImportAfter')" />
<Import Project="D:\tmp\p\after.name.sln.targets" Condition="exists('D:\tmp\p\after.name.sln.targets')" />
<Target Name="Build" />
<Target Name="Rebuild" />
<Target Name="Clean" />
<Target Name="Publish" />
- И да,.metaproj также не может быть просмотрен VS IDE.
Поэтому для работы с общими целями из VS IDE вы можете использовать только файлы проекта с некоторыми ограничениями (это означает, что без изменения / расширения VS).
И так, если вам нужно общее решение (т.е. вы можете не знать о проектах и т. Д. - это может быть, например, для некоторых коробочных решений и аналогичных):
- Добавьте ваш общий файл.targets во все ваши проекты (это может быть автоматически любым инструментом, в том числе событиями NuGet и т. Д.), Например:
<Import Project="..\<SolutionFile>.targets" />
- Затем вы должны использовать некоторые ограничения для:
- "только - до всех проектов"
- "только - после всех проектов"
И, например, да, это может быть "Карта проектов":
- "Карта проектов" иллюстрирует "события" PRE/POST для всего решения для операций сборки из Visual Studio IDE (то есть, первичной из VS IDE)
...
<Target Name="_Build" BeforeTargets="Build" DependsOnTargets="ProjectsMap">
<CallTarget Targets="_BuildPRE" Condition="$(ScopeDetectFirst)" />
<CallTarget Targets="_BuildPOST" Condition="$(ScopeDetectLast)" />
</Target>
<Target Name="_BuildPRE">
<!-- ... -->
</Target>
<Target Name="_BuildPOST">
<!-- ... -->
</Target>
...
В общем, мы будем использовать карту проектов и теперь знаем, что и когда должно произойти. Это безопасно для всех или большинства случаев (изменение порядка сборки или удаление любых проектов из решения). Тем не мение! ты должен управлять <Import>
раздел для новых проектов в первом init. Это действительно неудобно, но также вариант...
Вариант 3: Плагин vsSolutionBuildEvent
На сегодняшний день это наиболее полное решение для работы с большим количеством событий в качестве Event-Catcher с множеством расширенных действий для обслуживания ваших проектов и библиотек, построения процессов и процессов во время выполнения из вашей Visual Studio и MSBuild Tool.
Различные типы действий для всех подпроектов одновременно в решении как Solution-Events или индивидуально для каждого.
https://visualstudiogallery.msdn.microsoft.com/0d1dbfd7-ed8a-40af-ae39-281bfeca2334/
Как это работает внутри
Если вы хотите использовать вариант 1, описанный выше, или хотите узнать, как работать с Shell.Interop, EnvDTE, IVsUpdateSolutionEvents2, MSBuild Engine и т. Д., См. Здесь:
Вариант 4. EnvDTE.CommandEvents
Этот вариант также не для простых пользователей VS. Однако, что касается варианта 1, он может быть полезен для вашего коробочного решения и т. Д.
Это не то же самое, но да, это также возможно с EnvDTE.CommandEvents, как в варианте 1 выше.
Вы уже должны знать (см. Выше) об этом решении для приоритетной работы с текущим типом действия сборки... Так почему бы не использовать это в качестве основного решения для текущей проблемы?
_cmdEvents.BeforeExecute += (string guid, int id, object customIn, object customOut, ref bool cancelDefault) => {
if(UnifiedTypes.Build.VSCommand.existsById(id)) {
// ... your action
}
};
Куда:
Description | guid | id |In |Out|
--------------------------|---------------------------------------|-----|---|---|
Started: Build Solution |{5EFC7975-14BC-11CF-9B2B-00AA00573819} | 882 | | |
Started: Rebuild Solution |{5EFC7975-14BC-11CF-9B2B-00AA00573819} | 883 | | |
Started: Clean Solution |{5EFC7975-14BC-11CF-9B2B-00AA00573819} | 885 | | |
http://vsce.r-eg.net/doc/Features/Solution-wide/
Более того, по желанию вы можете отключить эту команду, если вам нужно. В приведенном ниже варианте вы увидите полное решение для этого пути.
Вариант 5. Плагин vsCommandEvent
https://visualstudiogallery.msdn.microsoft.com/ad9f19b2-04c0-46fe-9637-9a52ce4ca661/
Он также представляет расширенный обработчик большинства событий, но в отличие от первого он специализировался на MS Visual Studio для расширенной работы со всеми командами и выходными данными в качестве менеджера этого. Не только для проектов и решений, но и для всей Visual Studio IDE.
В общем, это общее решение варианта 4, и вы можете просто переопределить все команды выше для решения этой проблемы.
И для той же модели Event-Actions, как в vsSolutionBuildEvent, она может быть полезна в большинстве случаев.
"Помоги мне с вариантами"
Есть открытая реализация для всех этих вариантов. Смотрите здесь и улыбайтесь:
Вы можете взглянуть на эту статью: MSBuild: Расширение сборки решения.
Кажется, именно то, что вам нужно.
Мы делаем это, добавляя пустой проект и устанавливая события сборки для этого проекта. Затем вы должны задать каждому проекту зависимость от этого пустого проекта, чтобы убедиться, что он создается каждый раз.
Для кода, который создается с помощью MSBUILD 15, самый простой способ - поместить файл Directory.Build.targets в корень пути (не обязательно в папке решения), в котором находятся проекты, и настроить его. Это работает, даже если вы строите из Visual Studio или из командной строки.
https://docs.microsoft.com/en-us/visualstudio/msbuild/customize-your-build?view=vs-2019
Еще один старый пост, но вдохновленный решением @reg. Я хотел запустить простой таймер сборки, который бы регистрировал истекшее время для сборки решения. Я получил события сборки, работающие с использованием модуля powershell, который загружается через консоль диспетчера пакетов при запуске Visual Studio IDE.
Так что создайте модуль powershell, такой как BuildEvents.psm1
:
<#
.SYNOPSIS
Register solution build events
.DESCRIPTION
Registers the OnBuildBegin and OnBuildDone events for the entire solution
De-registers the events if called multiple times.
.EXAMPLE
RegisterBuildEvents
#>
function RegisterBuildEvents{
try {
Unregister-Event -SourceIdentifier "OnBuildBegin" -Force
} catch {
#we don't care if this doesn't work
}
try {
Unregister-Event -SourceIdentifier "OnBuildDone" -Force
} catch {
#we don't care if this doesn't work
}
$obj = [System.Runtime.InteropServices.Marshal]::CreateWrapperOfType($dte.Application.Events.BuildEvents, [EnvDTE.BuildEventsClass])
Register-ObjectEvent -InputObject $obj -EventName OnBuildBegin -Action {
# do stuff here on build begin
Write-Host "Solution build started!"
} -SourceIdentifier "OnBuildBegin"
Register-ObjectEvent -InputObject $obj -EventName OnBuildDone -Action {
# do stuff here on build done
Write-Host "Solution build done!"
} -SourceIdentifier "OnBuildDone"
}
# export the functions from the module
export-modulemember -function RegisterBuildEvents
Импортируйте модуль при инициализации хоста менеджера пакетов:
- В консоли диспетчера пакетов введите $ profile, чтобы получить местоположение вашего профиля powershell.
- Перейдите в этот каталог на диске, если там нет файла, создайте его с именем, возвращенным вышеуказанной командой (например,
NuGet_profile.ps1
) Откройте файл в блокноте и добавьте следующие строки
Import-Module -Name <Path to your ps module>\BuildEvents -Force RegisterBuildEvents
Прошло много времени, и с тех пор некоторые вещи в инфраструктуре.Net изменились, дав новые возможности. Теперь мой выбор для решения этой проблемы - пакеты nuget. Я поместил свои этапы сборки в пакет, который затем включался в каждый отдельный проект. Полезно, что менеджер пакетов Visual Studio дает обзор пакетов на уровне решения, поэтому проверить это правило довольно просто.
Я знаю, что это не "решение" для всего мира, но я пытался найти любой способ запустить скрипт в сборке проекта, поэтому может быть полезен для людей, которые хотели бы, чтобы любой скрипт С#, например, модифицировал файлы решения/проекта до/после сборки.
Итак, как написал @Hans Passant выше, мы можем создать новое консольное приложение и запустить этот .exe в событии сборки. Пожалуйста, прочитайте комментарии к его сообщению, так как это применимо только в том случае, если вам не нужно интегрировать эту логику в CI/CD и т. д. Так что использование несколько ограничено. И вообще, это не самое красивое решение.
Итак, например, у нас есть какой-то проект () и вам нужно изменить файл .env внутри на каждой сборке, чтобы обновить внутренние переменные.
- создать новую консоль приложения С# (в моем случае это имя:
PreBuildEventApp
) - создайте его и откройте свойства вашего целевого AwsLambdaProject
- найдите вкладку build->events в свойствах проекта. И введите там "$(SolutionDir)\PreBuild\PreBuildEventApp\bin\Debug\net6.0\PreBuildEventApp.exe -solutionPath=$(SolutionDir)"
Итак, здесь мы вызываем наш .exe и передаем туда один параметр (путь к папке решения). * Убедитесь, что этот путь содержит ваш действительный файл .exe консольного приложения.
Таким образом, ваш .exe будет выполняться перед каждой "" сборкой и изменением необходимых файлов.
Так, например, вы пытаетесь записать что-то в файл для его отладки:
И ваш файл будет создан в папке проекта, а не в папке консольного проекта/bin.
Примечания :
Чтобы использовать асинхронность внутри консольного приложения, убедитесь, что оно содержит 7.1 или выше (я использую 10.0).
Кроме того, вы можете столкнуться с проблемой, когда первая сборка не удалась, и только во второй раз она будет успешной. Это потому, что вам нужно сначала создать консольное приложение, чтобы .exe был в нужном месте. Вы можете скопировать его вручную в любую папку - все будет работать нормально. Но если вам нужно изменить код - вам нужно сделать это снова.
Второй способ - перейти к решению и нажать «Порядок сборки проекта», там вы можете найти свой «AwsLambdaProject
" и добавьте свое консольное приложение в качестве зависимости. Теперь ваше консольное приложение будет собираться первым, а ваш проект, в котором вам нужно изменить файлы, - вторым.
Кроме того, здесь вы можете столкнуться с еще одной проблемой - событие сборки будет работать только при первой сборке или при перестроении решения. Чтобы вызывать ваше консольное приложение при каждой сборке решения, вам нужно изменить два проекта и добавить туда:
<PropertyGroup>
<DisableFastUpToDateCheck>true</DisableFastUpToDateCheck>
</PropertyGroup>
Это означает, что ваши два проекта будут собираться каждый раз без кэширования. Будьте осторожны, это может увеличить обычное время сборки решения в зависимости от размера проекта.
Надеюсь, поможет кому-нибудь. Хорошего дня!