我在Visual Studio中有一个解决方案,其中包含几个项目。我想在每个构建的开始都运行一个命令-无论涉及哪个项目以及它们是否是最新的。
本质上,我需要与解决方案范围内的预构建事件类似的东西,但不幸的是,VS似乎不支持这些事件。有人知道实现我需要的替代方法吗?
Answers:
不寻常的要求。但这是可以完成的。将新项目添加到解决方案中,使用Visual C ++>常规> Makefile项目模板。将其NMake>构建命令行设置设置为要执行的命令。使用项目>项目依赖关系使所有其他项目都依赖它。
下面是我的变体的简短概述
只是一个注释:它是所有现有列表的不完整列表(另请参见其他答案等),我仅在实际状态下支持我的原始技巧...
笔记:
targets mode
调用after.<name>.sln.targets
仅适用于msbuild.exe的方法的一种简单方法(这不需要其他步骤,只需执行操作即可)。但是,只有原始引擎(包括vsCommandEvent)可以允许其他脚本支持,例如(7zip存档程序,无nuget.exe的nuget软件包打包,远程服务器等)。但是,对于我们的问题/问题并不重要,如果您看到+
上面的内容,则可以使用任何可用的选项来支持解决方案级别。此变体不适用于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保护的类的一部分。现在,我们可以同时处理所有项目-解决方案级别。
好的,您喜欢这样的东西-MSBuild:扩展解决方案的构建,但是此变体可能适用于msbuild.exe而不是VS IDE的生成过程...
但是,在开始构建操作时,VS还使用项目文件(* .csproj,*。vcxproj,..)中的目标(构建,重建,清理,..)。因此我们也可以尝试一下,但请记住:
...
<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中的通用目标,您只能使用有一定限制的项目文件(这意味着无需修改/扩展VS)。
因此,如果您需要通用的解决方案(即您可能不了解项目等,例如对于某些盒式解决方案或类似的解决方案):
<Import Project="..\<SolutionFile>.targets" />
例如,是的,它可以是“项目地图”:
...
<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中管理新项目的部分。这确实很不方便,但也有不同之处...
如今,它是处理大量事件的最完整解决方案,它是事件捕捉器,具有各种高级操作,可用于维护项目和库,通过Visual Studio和MSBuild工具在运行时构建过程和过程。
在解决方案中,所有子项目都可以作为解决方案事件同时使用不同的操作类型,也可以针对每个子项目分别使用不同的操作类型。
https://visualstudiogallery.msdn.microsoft.com/0d1dbfd7-ed8a-40af-ae39-281bfeca2334/
如果您想使用上面的Variant 1或需要了解如何使用Shell.Interop,EnvDTE,IVsUpdateSolutionEvents2,MSBuild Engine等,请参见此处:
该变体也不适合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/
而且,可选的,如果需要,您可以取消显示此命令。在下面的变体中,您将看到这种方式的完整解决方案。
https://visualstudiogallery.msdn.microsoft.com/ad9f19b2-04c0-46fe-9637-9a52ce4ca661/
它还提供了大多数事件的高级处理程序,但与第一个事件处理程序不同的是,它专门为MS Visual Studio进行高级处理,使用所有命令并作为此管理器输出数据。不仅适用于项目和解决方案,而且适用于整个Visual Studio IDE。
通常,这是变体4的常见解决方案,您可以简单地覆盖上面的所有命令来解决此问题。
对于与vsSolutionBuildEvent中相同的Event-Actions模型,它在大多数情况下可能很有用。
所有这些变体都有开放的实现。看到这里,微笑:
您可以看一下本文:MSBuild:扩展解决方案的构建。
似乎正是您所需要的。
为此,我们添加一个空项目并为此项目设置构建事件。然后,您必须将每个项目依赖项赋予该空项目,以确保每次都生成它。
另一篇旧文章,但受到@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
程序包管理器主机初始化时,导入模块:
NuGet_profile.ps1
)在记事本中打开文件并添加以下行
Import-Module -Name <Path to your ps module>\BuildEvents -Force
RegisterBuildEvents
对于使用MSBUILD 15构建的代码,最简单的方法是将Directory.Build.targets文件放在项目所在的路径(不一定是解决方案文件夹)的根目录中并对其进行自定义。即使在Visual Studio中或使用命令提示符进行构建,此命令也可以运行。
https://docs.microsoft.com/zh-cn/visualstudio/msbuild/customize-your-build?view=vs-2019