如何在.csproj中检测项目是否由MSBuildWorkspace而非MSBuild直接构建?
解决MSBuildWorkspace触发循环构建(Fork Bomb)的问题
这个场景我碰到过不少——当你的自定义工具通过MSBuildWorkspace构建项目时,会触发.csproj里的所有构建目标,包括你用来启动工具的那个任务,直接形成无限循环。下面是两种可靠的解决方法:
方法1:自定义属性标记(最推荐)
核心思路是在工具的MSBuildWorkspace构建中添加一个专属标记属性,然后在.csproj里检测这个属性,只有当标记不存在时才执行工具。
第一步:在你的exe工具中添加全局属性
在使用MSBuildWorkspace构建项目的代码里,添加一个自定义全局属性,用来标识这是工具自身触发的构建:
var workspace = MSBuildWorkspace.Create(); // 添加自定义属性,标记当前构建来自你的生成工具 workspace.GlobalProperties["IsTriggeredByCustomGenerator"] = "true"; // 后续加载项目、执行构建的代码 var project = await workspace.OpenProjectAsync(yourProjectPath); var buildResult = await project.BuildAsync();
第二步:在.csproj中添加条件判断
修改你用来执行exe的Target,添加条件判断:只有当这个自定义属性**不等于true**时,才执行工具:
<Target Name="ExecuteCustomGenerator" AfterTargets="Build" Condition=" '$(IsTriggeredByCustomGenerator)' != 'true' "> <Exec Command="$(SolutionDir)path\to\your\generator.exe $(MSBuildProjectFullPath)" /> </Target>
这样一来,常规构建(VS内部、命令行MSBuild)时,这个属性不存在,任务会正常执行;而当工具自身通过MSBuildWorkspace构建时,因为设置了标记,任务会直接跳过,完美打破循环。
方法2:利用内置属性(可选,场景受限)
如果不想修改工具代码,也可以尝试利用MSBuild的内置属性来区分上下文,但这个方法可靠性稍低,因为不同环境下属性可能有变化:
BuildingInsideVisualStudio:仅在Visual Studio内部启动构建时会被设为true,命令行MSBuild和MSBuildWorkspace默认都不会设置它。如果你的工具只会在VS外部运行,这个属性可以用来判断。DesignTimeBuild:当MSBuildWorkspace执行设计时构建(比如获取项目元数据)时会被设为true,但如果你的工具是执行完整编译构建,这个属性可能不会被设置。
示例配置(根据你的场景调整条件):
<Target Name="ExecuteCustomGenerator" AfterTargets="Build" Condition=" '$(BuildingInsideVisualStudio)' == 'true' "> <Exec Command="$(SolutionDir)path\to\your\generator.exe $(MSBuildProjectFullPath)" /> </Target>
不过还是强烈推荐第一种方法,因为它完全由你控制,不会受环境或MSBuild版本变化的影响。
内容的提问来源于stack exchange,提问作者RB.
相关产品推荐
相关产品推荐

