解决方案范围内的预构建活动?


74

我在Visual Studio中有一个解决方案,其中包含几个项目。我想在每个构建的开始都运行一个命令-无论涉及哪个项目以及它们是否是最新的。

本质上,我需要与解决方案范围内的预构建事件类似的东西,但不幸的是,VS似乎不支持这些事件。有人知道实现我需要的替代方法吗?

Answers:


43

不寻常的要求。但这是可以完成的。将新项目添加到解决方案中,使用Visual C ++>常规> Makefile项目模板。将其NMake>构建命令行设置设置为要执行的命令。使用项目>项目依赖关系使所有其他项目都依赖它。


7
我不建议这样做,下面指向我的博客文章的答案是解决方法。张贴网址:sedodream.com/2010/10/22/MSBuildExtendingTheSolutionBuild.aspx
易卜拉欣·哈希米(Ibrahim

28
我不建议那样。我的解决方案可以完全在IDE中完成,而无需手工反复破解项目文件。它并没有产生一个“空DLL”。
汉斯·帕桑

5
IDE构建和系统构建使用不同的程序的事实完全令人发疯!Sayed的解决方案仅适用于MSBuild,不适用于IDE。但是,维护项目依赖项的开销太大,无法用于大型项目。
Jeff Kotula 2013年

5
这是一个可怕的骇客。我不建议这样做
本杰明·萨斯曼

7
@SayedIbrahimHashimi您的策略并没有在Visual Studio中工作。在以“我在Visual Studio中有解决方案...”开头的问题中,您如何推荐它?
julealgon

44

下面是我的变体的简短概述

只是一个注释:它是所有现有列表的不完整列表(另请参见其他答案等),我仅在实际状态下支持我的原始技巧...

概要

笔记:

  • 1-不需要任何其他扩展。但是它可能仅通过项目级别起作用,因此我们将其用于模拟我们的解决方案级别...对于通用解决方案而言,这既困难又不便,但这是变体。见下文。
  • 2 -vsSolutionBuildEvent的原始引擎提供了几种统一支持VS和msbuild.exe的方法。targets mode调用after.<name>.sln.targets仅适用于msbuild.exe的方法的一种简单方法(这不需要其他步骤,只需执行操作即可)。但是,只有原始引擎(包括vsCommandEvent)可以允许其他脚本支持,例如(7zip存档程序,无nuget.exe的nuget软件包打包,远程服务器等)。但是,对于我们的问题/问题并不重要,如果您看到+上面的内容,则可以使用任何可用的选项来支持解决方案级别。

变体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还使用项目文件(* .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" />
  • 是的,VS IDE也无法查看.metaproj。

因此,要使用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

如今,它是处理大量事件的最完整解决方案,它是事件捕捉器,具有各种高级操作,可用于维护项目和库,通过Visual Studio和MSBuild工具在运行时构建过程和过程。

在解决方案中,所有子项目都可以作为解决方案事件同时使用不同的操作类型,也可以针对每个子项目分别使用不同的操作类型。

https://visualstudiogallery.msdn.microsoft.com/0d1dbfd7-ed8a-40af-ae39-281bfeca2334/

插件-vsSolutionBuildEvent

内部如何运作

如果您想使用上面的Variant 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的常见解决方案,您可以简单地覆盖上面的所有命令来解决此问题。

对于与vsSolutionBuildEvent中相同的Event-Actions模型,它在大多数情况下可能很有用。

方案

“帮我变体”

所有这些变体都有开放的实现。看到这里,微笑


插件(vsSolutionBuildEvent)是我在VS内唯一可以找到的方法。自从我研究这几天以来,获得了+1,谢谢!
mpd

@mpd我添加了新的变体,如果需要,您也可以尝试新的vsCommandEvent。/很高兴它对您有所帮助。
丹尼斯·库兹敏

18

您可以看一下本文:MSBuild:扩展解决方案的构建。

似乎正是您所需要的。


1
这是一个了不起的发现!我不敢相信有人会创建一个空的dll并让所有项目都引用它来获取解决方案范围内的构建事件。
bryanbcook 2012年

17
似乎只有在从命令行使用MSBuild构建解决方案的情况下,这才有效?在Visual Studio中,after。*。sln.targets文件似乎没有任何作用。
BCran 2012年

这些构建扩展名将适用于“使用msbuild.exe在该计算机上构建的每个解决方案文件”。
Little Endian

虽然链接是共享知识的好方法,但是当链接断开时,它们不会真正回答问题。在回答中添加回答问题的链接的基本内容。如果内容太复杂或太大而无法容纳在此处,请描述所提出解决方案的总体思路。请参阅:我如何写一个好的答案?
sɐunıɔןɐqɐp

9

为此,我们添加一个空项目并为此项目设置构建事件。然后,您必须将每个项目依赖项赋予该空项目,以确保每次都生成它。


碰巧正是Hans Passant的答案,尽管您在他之前的回答是+1少一点。
sɐunıɔןɐqɐp

1

另一篇旧文章,但受到@reg解决方案的启发,我想运行一个简单的构建计时器,该计时器将记录解决方案构建所用的时间。我使用了Powershell模块来运行构建事件,该模块在Visual Studio IDE启动时通过包管理器控制台加载。

因此,创建一个类似于BuildEvents.psm1以下内容的powershell模块:

<#
.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

已有一段时间了,.Net基础结构中的某些内容自此发生了变化,提供了新的选择。现在,我解决这个问题之王的选择是nuget软件包。我将构建步骤放入软件包中,然后将其包含在每个项目中。有用的是,Visual Studio程序包管理器提供了解决方案级别的程序包概述,因此检查此规则非常容易。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.