【问题标题】:Solution-wide pre-build event?解决方案范围的预构建事件?
【发布时间】:2011-01-18 17:20:23
【问题描述】:

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

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

【问题讨论】:

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


    【解决方案1】:

    不寻常的要求。但这是可以完成的。向您的解决方案添加一个新项目,使用 Visual C++ > General > Makefile Project 模板。将其 NMake > Build Command Line 设置设置为您要执行的命令。使用 Project > Project Dependencies 使所有其他项目都依赖它。

    【讨论】:

    • 我不推荐这个,下面的答案指向我的博客文章,这是要走的路。发帖网址:sedodream.com/2010/10/22/MSBuildExtendingTheSolutionBuild.aspx
    • 我不建议这样做。我的解决方案可以完全在 IDE 中完成,而无需手动反复破解项目文件。它确实生成“空 dll”。
    • IDE 构建和系统构建使用不同程序的事实完全令人抓狂! Sayed 的解决方案仅适用于 MSBuild,不适用于 IDE。但是,维护项目依赖关系的开销太大,对于大型项目来说并不实用。
    • 这是一个可怕的黑客攻击。我不推荐这个
    • @SayedIbrahimHashimi 您的策略在 Visual Studio 中起作用。您如何在以“我在 Visual Studio 中有解决方案...”开头的问题中推荐它?
    【解决方案2】:

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

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

    注意事项:

    • 1 - 不需要任何额外的扩展。但它可能只能通过项目级别工作,所以我们用它来模拟我们的解决方案级别......对于常见的解决方案来说很难和不方便,但是是变体的。见下文。
    • 2 - vsSolutionBuildEvent的原始引擎提供了几种统一支持VS和msbuild.exe的方式。 targets mode 调用 after.<name>.sln.targets 的一种简单方法,仅适用于 msbuild.exe(这不需要额外的步骤,只需操作)。但只有原始引擎(inc. 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) - 可能无济于事,而且可能为时已晚 - Example

    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 保护类的一部分。
    • 使用 ServiceProvider 获取 SVsSolutionBuildManager 服务,但它可以根据您的需要。见 msdn

    现在我们可以同时处理所有项目 - 解决方案级别。

    变体 2:项目的目标和地图。

    好的,你喜欢这样的东西 - MSBuild: Extending the solution build,但这个变体可能适用于来自 msbuild.exe 而不是来自 VS IDE 的构建过程...

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

    • VS 也忽略了惊人的 .sln 文件。它使用 EnvDTE 等从加载环境中形成所有最终目标。
    • msbuild.exe 应仅将 .sln 处理为:自动生成 .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 events 等),例如:&lt;Import Project="..\&lt;SolutionFile&gt;.targets" /&gt;
    • 然后,您应该对以下情况使用一些限制:
      • “仅 - 在所有项目之前”
      • “仅 - 在所有项目之后”

    例如,是的,它可以是“项目地图”:

    • 'Map of projects' 说明了解决方案范围内的 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>
    ...
    

    一般来说,我们将使用项目图,现在我们知道“什么和什么时候”应该发生。对于所有或大多数情况(更改构建顺序或从解决方案中删除任何项目)都是安全的。然而!您应该在第一次初始化时为新项目管理 &lt;Import&gt; 部分。这确实很不方便,但也是变体......

    变体 3:插件 vsSolutionBuildEvent

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

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

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

    它是如何工作的

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

    变体 4.EnvDTE.CommandEvents

    这个变种也不适合简单的 VS 用户。但是,对于Variant 1,它可能对您的盒子解决方案等有用。

    这是不一样的,但是是的,EnvDTE.CommandEvents 也可以,就像上面的Variant 1 一样。

    您应该已经知道(见上文)this solution 以优先使用当前类型的构建操作...那么为什么不将其用作当前问题的主要解决方案呢?

    _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。

    一般来说是Variant 4的常用解决方案,你可以简单地覆盖上面的所有命令来解决这个问题。

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

    “帮我处理变体”

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

    【讨论】:

    • 插件 (vsSolutionBuildEvent) 是我能在 VS 中找到的唯一可以工作的东西。巨大的 +1,因为我已经研究了好几天了,谢谢!
    • @mpd 我添加了新的变体,如果需要,您也可以尝试新的 vsCommandEvent。 /很高兴它帮助了你。
    【解决方案3】:

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

    似乎正是您所需要的。

    【讨论】:

    • 这是一个了不起的发现!我不敢相信有人会创建一个空的 dll 并让所有项目都引用它以获取解决方案范围的构建事件。
    • 这似乎只有在解决方案是从命令行使用 MSBuild 构建时才有效?在 Visual Studio 中,after.*.sln.targets 文件似乎没有做任何事情。
    • 这些构建扩展将适用于“使用 msbuild.exe 在该计算机上构建的每个解决方案文件。”
    • 虽然链接是分享知识的好方法,但当它们被破坏时,它们不会真正回答问题。将回答问题的链接的基本内容添加到您的答案中。如果内容太复杂或太大而无法在此处放置,请描述所提出解决方案的总体思路。见:How do I write a good answer?
    【解决方案4】:

    我们通过添加一个空项目并为此项目设置构建事件来做到这一点。然后,您必须将每个项目依赖项赋予这个空项目,以确保它每次都被构建。

    【讨论】:

    【解决方案5】:

    另一个旧帖子,但受到@reg 解决方案的启发,我想运行一个简单的构建计时器,该计时器将记录解决方案构建的经过时间。当 Visual Studio IDE 启动时,我通过包管理器控制台加载的 powershell 模块使构建事件工作。

    所以创建一个类似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】:

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

      【讨论】:

        【解决方案7】:

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

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-07-06
          • 2018-06-04
          • 1970-01-01
          • 2011-03-10
          • 2011-12-24
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多