解决方案范围的预生成事件?

我在 VisualStudio 中有一个解决方案,其中包含几个项目。我希望在每个构建的开始运行一个命令——不管涉及到哪个项目,也不管它们是否是最新的。

本质上,我需要一些类似于解决方案范围的预构建事件的东西,但不幸的是 VS 似乎不支持这些。有人知道我需要什么吗?

44099 次浏览

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

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

你可以看看这篇文章: MSBuild: 扩展解决方案生成。

看起来正是你需要的。

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

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

summary

备注:

  • 1 -不需要任何额外的扩展。但是它可能只通过项目级别工作,所以我们使用它来仿真我们的解决方案级别... 这是困难和不方便的通用解决方案,但是是不同的。请看下面。
  • 2 -vsSolutionBuildEvent 的原始引擎提供了几种统一支持 VS 和 msbuild.exe 的方法。targets mode调用仅对 msbuild.exe 可用的 after.<name>.sln.targets的简单方法(这不需要额外的步骤,只需要操作)。但是只有原始引擎(包括 vsCommandEvent)可以支持额外的脚本,例如(7zip 归档程序、不使用 nuget.exe 打包 nuget 包、远程服务器等)。但是,它对于我们的问题/问题并不重要,如果您看到上面的 +,您可以使用任何可用的选项来支持解决方案级别。

变体1: Microsoft。 VisualStudio。 Shell。 Interop

这个变体不适用于 VS 的简单用户,但是,它可以用于您的完整解决方案等。

例如,您应该实现:

  • IVsUpdateSolutionEvents2 /< a href = “ https://msdn.microsoft.com/en-us/library/microsoft.visalstudio.shell.interop.ivSupdatesolutionevents s.aspx”rel = “ noReferrer”> IVsUpdateSolutionEvents

例如:

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

然后,使用‘ Advise’方法作为优先级侦听器的注册处理程序,也就是说,对于 IVsUpdateSolutionEvents2,您应该使用 AdviseUpdateSolutionEvents

这很重要,因为 编译: pestwave(参见 EnvDTE)-可能不会有帮助,可能工作得太晚-< a href = “ https://stackoverflow. com/q/27018762”> 示例

使用 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 服务,但它可以是您所需要的

现在我们可以在一次解决方案级别处理所有项目。

变式2: 目标和项目地图。

好的,您喜欢这样的东西-MSBuild: 扩展解决方案生成,但是这个变体可以在 msbuild.exe 而不是 VS IDE 的构建过程中工作..。

但是 VS 也使用目标(Build,Rebuild,Clean,。.)在工程项目档案(* 。Csproj,* .Vcxproj,..)当构建操作开始时。所以我们也可以试试这个,但是记住:

  • VS 也忽略了令人惊奇的.sln 文件,它使用 EnvDTE 等来形成所有来自加载环境的最终目标。
  • 那个。Sln 应该仅由 msbuild.exe 处理为: 自动生成。Metaproj (默认情况下在内存中) ,其中包含将要构建的“ what 和 when”。包括所有项目的共同目标(如果存在的话) ,例如:
...
<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" />
  • 是的,VSIDE 也不能查看.metproj。

因此,对于使用 VS IDE 中的公共目标,您只能使用带有一定限制的项目文件(不需要修改/扩展 VS,这意味着)。

因此,如果你需要通用的解决方案(例如,你可能不知道项目等-这可以是,例如,对于一些盒子解决方案和类似的) :

  • 加上你的共同点。目标文件到您的所有项目(它可以自动与任何工具,包括 NuGet 事件等) ,例如: <Import Project="..\<SolutionFile>.targets" />
  • 然后,您应该对以下内容使用一些限制:
    • “只有在所有项目之前”
    • “只有-毕竟项目”

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

  • 「工程地图」说明了从 Visual Studio IDE (即 VS IDE 的主要部分)构建操作的解决方案范围的 PRE/POST“事件”
...
<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: Plugin vsSolutionBuildEvent

今天,它是处理大量事件的最完整的解决方案,比如 Events-Catcher,它提供了各种高级操作,用于维护项目和库,在运行时从 Visual Studio 和 MSBuild Tool 构建流程和流程。

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

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

plugin - vsSolutionBuildEvent

里面是怎么运作的

如果您想使用上面的 变种1或者需要了解如何使用 Shell。互操作,EnvDTE,IVsUpdateSolutionEvents2,MSBuild 引擎等,见 给你:

scheme

变体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
}


};

地点: 描述 | guid | id | In | Out | --------------------------|---------------------------------------|-----|---|---| 开始: 构建解决方案 | {5EFC7975-14BC-11CF-9B2B-00AA00573819} | 882 | | | | 开始: 重建解决方案 | {5EFC7975-14BC-11CF-9B2B-00AA00573819} | 883 | | | | 开始: 清洁解决方案 | {5EFC7975-14BC-11CF-9B2B-00AA00573819} | 885 | | | |

Http://vsce.r-eg.net/doc/features/solution-wide/

此外,如果需要,还可以选择禁止此命令

变体5. Plugin vsCommandEvent

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

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

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

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

scheme

“帮助我的变体”

所有这些变体都有开放的实现:

已经有一段时间了,有些事情。此后,网络基础设施发生了变化,提供了新的选择。现在我选择解决这个问题的方法是新奇套装。我把我的构建步骤打包,然后包括到每个单一的项目。有益的是,VisualStudio 包管理器提供了解决方案级别的包概述,因此检查此规则非常容易。

另一篇老文章,但受@reg 解决方案的启发,我想运行一个简单的构建计时器,记录一个解决方案构建的运行时间。我使用 Powershell 模块使构建事件正常工作,当 VisualStudioIDE 启动时,我通过包管理器控制台加载该模块。

因此,创建一个像 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

在 Package Manager 主机初始化时导入模块:

  1. 在包管理器控制台中输入 $profile 以获取您的 powershell 配置文件的位置
  2. 如果磁盘上没有文件,则浏览到该目录 具有上述命令返回的名称(例如。 NuGet_profile.ps1)
  3. 在记事本中打开该文件并添加以下行

    Import-Module -Name <Path to your ps module>\BuildEvents -Force
    RegisterBuildEvents
    

对于使用 MSBUILD15构建的代码,最简单的方法是放入一个目录。目标文件位于项目所在的路径(不一定是解决方案文件夹)的根目录下,并对其进行自定义。 即使在 VisualStudio 内部或使用命令提示符进行生成时,也会运行此命令。

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

我知道这不是一个“解决方案”的范围,但我试图找到任何方式在 项目建设上运行脚本,所以可能对人们有帮助,他们希望任何 C # 脚本,例如,在构建之前/之后修改解决方案/项目文件。

因此,正如@hansPassant 上面所说,我们可以创建新的控制台应用并运行它。在生成事件上执行。请阅读对他的帖子的评论,因为它只适用于你不需要将这个逻辑集成到 CI/CD 等等的情况。所以用量有限。一般来说,这不是最美妙的解决方案。

因此,例如,我们有任何项目(AwsLambdaProject) ,您需要修改。在每个构建中更新内部变量的 env 文件。

  1. 创建新的 C # 应用程序控制台(在我的例子中它的名字是 PreBuildEventApp)
  2. 构建它并打开目标 AwsLambdaProject 的属性
  3. 在项目属性中找到 build-> events 选项卡,然后键入“ $(SolutionDir) PreBuild PreBuildEventApp bin Debug net6.0 PreBuildEventApp.exe-solutionPath = $(SolutionDir)”

enter image description here 因此,我们在这里调用.exe 并向其传递一个参数(解决方案文件夹路径)。 * 检查,该路径包含有效的控制台 app.exe 文件。

因此,您的.exe 将在每次“ AwsLambdaProject”构建和修改所需文件之前执行。

例如,您尝试编写一些文件来调试它:

enter image description here

您的文件将在 AwsLambdaProject项目文件夹中创建,而不是在控制台项目/bin 文件夹中创建。

备注:

要在控制台应用程序中使用异步 ,请确保 AwsLambdaProject包含7.1或更高版本(我使用10.0)。

enter image description here

此外,您可能会面临问题,当第一次构建失败,只有第二次将成功。这是因为您需要首先构建您的控制台应用程序。在正确的地方执行。您可以手动复制到任何文件夹-它将工作正常。但是如果你需要改变代码-你需要再做一次。

第二种方法是点击解决方案和点击“项目建设订单”,在那里你可以找到你的“ AwsLambdaProject”,并添加你的控制台应用作为一个依赖项。现在,您的控制台应用程序将首先构建您需要修改文件的项目-其次。

此外,您可能还会面临另一个问题——生成事件只能在第一次生成或解决方案重新生成时起作用。要在 每个解决方案构建上调用控制台应用程序,您需要修改两个项目并在其中添加:

<PropertyGroup>
<DisableFastUpToDateCheck>true</DisableFastUpToDateCheck>
</PropertyGroup>

这意味着-您的两个项目将每次构建,而不需要缓存。请注意,它可能会增加解决方案生成的通常生成时间,具体取决于项目大小。

霍普,会帮助别人的,祝你今天愉快!