解决方案范围内的预构建事件?

时间:2010-02-19 10:13:34

标签: visual-studio projects-and-solutions pre-build-event

我在Visual Studio中有一个包含多个项目的解决方案。我想在每次构建的最初阶段运行一个命令 - 无论涉及哪个项目以及它们是否是最新的。

基本上我需要类似于解决方案范围的预构建事件,但不幸的是VS似乎不支持这些。有没有人知道实现我需要的另一种方式?

7 个答案:

答案 0 :(得分:41)

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

答案 1 :(得分:40)

以下我的变体的简短概述

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

summary

注意:

  • 1 - 不需要任何额外的扩展。但它可能只能通过项目级别工作,因此我们使用它来模拟我们的解决方案级别...对于通用解决方案来说很难和不方便,但它是变体。见下文。
  • 2 - vsSolutionBuildEvent的原始引擎提供了几种统一支持VS和msbuild.exe的方法。 targets mode调用仅适用于msbuild.exe的after.<name>.sln.targets的简单方法(这不需要额外的步骤,只需要操作)。但只有原始引擎(包括vsCommandEvent)可能允许额外的脚本支持,例如(7zip archiver,包含没有nuget.exe的nuget包,远程服务器等)。但是,它对我们的问题/问题并不重要,如果您在上方看到+,则可以使用任何可用选项来支持解决方案级别。

变体1:Microsoft.VisualStudio.Shell.Interop

此变体不适用于VS的简单用户。但是,它对您的完整解决方案等非常有用。

您应该实施,例如:

e.g:

public sealed class YourPackage: Package, IVsSolutionEvents, IVsUpdateSolutionEvents2
{
...
    public int UpdateSolution_Begin(ref int pfCancelUpdate)
    {
        //TODO:
    }
}

然后,注册处理程序&#39; Advise&#39;作为优先级侦听器的方法,即对于IVsUpdateSolutionEvents2,您应该使用AdviseUpdateSolutionEvents

这很重要,因为 BuildEvents (请参阅EnvDTE) - 可能无济于事,可能工作太晚 - {{3 }}

使用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:目标&amp;项目地图。

好的,你喜欢这样的东西 - Example,但是这个变种可能适用于msbuild.exe的构建过程而不是VS IDE ......

但是,当构建操作开始时,VS还在项目文件(* .csproj,* .vcxproj,..)中使用目标(Build,Rebuild,Clean,..)。所以我们也可以试试这个,但请记住:

  • VS也忽略了惊人的.sln文件。它使用EnvDTE等形成加载环境中的所有终端目标。
  • .sln应仅由msbuild.exe处理:自动生成.metaproj(默认情况下在内存中),其中包含&#39; what和when&#39;将建成。如果存在,则包括所有项目的公共目标,例如:
...
<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文件添加到您的所有项目中(可以使用任何工具自动执行,包括MSBuild: Extending the solution build等),例如:<Import Project="..\<SolutionFile>.targets" />
  • 然后,你应该使用一些限制:
    • &#34; only - 在所有项目之前&#34;
    • &#34;仅在所有项目&#34;
    • 之后

例如,是的,它可以是&#39;项目地图&#39;:

  • NuGet events说明了解决方案范围内的PRE / POST&#39;事件&#39;用于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>
...

一般情况下,我们会使用项目地图,现在我们知道了什么以及什么时候开始。应该发生。它对所有或大多数情况都是安全的(更改构建顺序或从解决方案中删除任何项目)。然而!您应该在第一个init中管理新项目的<Import>部分。这真的很不方便,但也是变种......

变体3:插件vsSolutionBuildEvent

今天,它是用于处理许多事件的最完整的解决方案,作为事件捕手,具有各种高级操作,用于维护项目和库,在运行时从Visual Studio和MSBuild工具构建流程和流程。

所有子项目在解决方案中同时作为解决方案事件或单独为每个子项目提供不同的操作类型。

'Map of projects'

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

如何在

内部工作

如果您想使用上面的plugin - vsSolutionBuildEvent或需要了解如何使用Shell.Interop,EnvDTE,IVsUpdateSolutionEvents2,MSBuild Engine等,请参阅Variant 1

here

变体4. EnvDTE.CommandEvents

此变体也不适用于VS的简单用户。但是,至于scheme,它对您的盒子解决方案等非常有用。

它不一样,但是也可以使用上面Variant 1中的EnvDTE.CommandEvents来实现。

对于当前类型的构建操作的优先级工作,您应该已经知道(见上文)Variant 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 | | |

this solution

此外,如果需要,您可以选择禁止此命令。 在下面的变体中,您将看到这种方式的完整解决方案。

变体5.插件vsCommandEvent

http://vsce.r-eg.net/doc/Features/Solution-wide/

它还提供了大多数事件的高级处理程序,但不像第一个专门用于MS Visual Studio的高级工作,所有命令和输出数据作为管理器。不仅适用于项目和解决方案,也适用于整个Visual Studio IDE。

通常,它是https://visualstudiogallery.msdn.microsoft.com/ad9f19b2-04c0-46fe-9637-9a52ce4ca661/的通用解决方案,您可以简单地覆盖上面的所有命令来解决此问题。

对于与vsSolutionBuildEvent相同的Event-Actions模型,它对大多数情况都很有用。

Variant 4

&#34;帮我解决变种&#34;

所有这些变体都有开放式实现。在这里看到并微笑

答案 2 :(得分:18)

您可以查看这篇文章:MSBuild: Extending the solution build.

似乎正是你所需要的。

答案 3 :(得分:9)

我们通过添加一个空项目并为该项目设置构建事件来实现此目的。然后,您必须将每个项目依赖项提供给此空项目,以确保每次都构建它。

答案 4 :(得分:0)

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

答案 5 :(得分:0)

另一个老帖子,但受@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
    

答案 6 :(得分:0)

对于使用MSBUILD 15构建的代码,最简单的方法是将Directory.Build.targets文件放在项目所在的路径(不一定是解决方案文件夹)的根目录中并对其进行自定义。 即使在Visual Studio中或使用命令提示符进行构建,此命令也可以运行。

https://docs.microsoft.com/en-us/visualstudio/msbuild/customize-your-build?view=vs-2019