Комплексное событие перед сборкой? - PullRequest
64 голосов
/ 19 февраля 2010

У меня есть решение в Visual Studio, которое содержит несколько проектов. Я хотел бы запускать команду в самом начале каждой сборки - независимо от того, какие проекты задействованы и являются ли они актуальными.

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

Ответы [ 6 ]

41 голосов
/ 19 августа 2013

Краткий обзор моих вариантов ниже

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

summary

Примечания:

  • 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 ) - вероятно, не поможет и может работать слишком поздно - Пример * +1064 *

Образец с 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/

plugin - vsSolutionBuildEvent

Как это работает внутри

Если вы хотите использовать вариант 1 выше или хотите узнать, как работать с Shell.Interop, EnvDTE, IVsUpdateSolutionEvents2, MSBuild Engine и т. Д., См. здесь :

scheme

Вариант 4. EnvDTE.CommandEvents

Этот вариант также не для простых пользователей VS. Однако, что касается Variant 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 / документ / Свойства / Решение шириной /

Кроме того, по желанию вы можете подавить эти команды, если вам нужно. В приведенном ниже варианте вы увидите полное решение для этого пути.

Вариант 5. Плагин vsCommandEvent

https://visualstudiogallery.msdn.microsoft.com/ad9f19b2-04c0-46fe-9637-9a52ce4ca661/

Он также представляет расширенный обработчик большинства событий, но, в отличие от первого, он специализировался на MS Visual Studio для расширенной работы со всеми командами и вывода данных в качестве менеджера этого. Не только для проектов и решений, но и для всей Visual Studio IDE.

В общем, это общее решение Вариант 4 , и вы можете просто переопределить все команды выше для решения этой проблемы.

И для той же модели Event-Actions, что и в vsSolutionBuildEvent, она может быть полезна в большинстве случаев.

scheme

"Помоги мне с вариантами"

Есть открытая реализация для всех этих вариантов. Смотрите здесь и улыбайтесь :

41 голосов
/ 19 февраля 2010

Необычное требование. Но это может быть сделано. Добавьте новый проект в свое решение, используйте шаблон Visual C ++> General> Makefile Project. Установите его NMake> Build Command Line для команд, которые вы хотите выполнить. Используйте «Проект»> «Зависимости проекта», чтобы все остальные проекты зависели от него.

18 голосов
/ 19 апреля 2011

Вы можете взглянуть на эту статью: MSBuild: расширение сборки решения.

Кажется, именно то, что вам нужно.

9 голосов
/ 19 февраля 2010

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

0 голосов
/ 23 мая 2018

Еще один старый пост, но вдохновленный решением @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

Импорт модуля при инициализации хоста менеджера пакетов:

  1. В консоли диспетчера пакетов введите $ profile, чтобы получить местоположение вашего профиля powershell
  2. Перейдите в этот каталог на диске, если там нет файла, создайте один с именем, возвращенным вышеуказанной командой (например, NuGet_profile.ps1)
  3. Откройте файл в блокноте и добавьте следующие строки

    Import-Module -Name <Path to your ps module>\BuildEvents -Force
    RegisterBuildEvents
    
0 голосов
/ 05 января 2018

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...