当我们开始4.0开发时,我对MSBuild博客的读者进行了民意调查,以找出对他们来说最重要的功能。调试是#1这让我们感到非常惊讶。仔细想想,这是有道理的。在我们的团队中,我们在阅读XML和理解日志方面变得非常熟练,以至于很容易忘记这有多困难——尤其是对新人来说。调试大师约翰·罗宾斯,还请求了Visual Studio集成调试器.
快进到今年早些时候的4.0版本,我们解决了我统计的16个请求中的7个。我们必须平衡这些请求与VisualStudio本身对MSBuild的需求。它对MSBuild有两个主要要求:启用VC++将移至MSBuild(#5请求),并帮助实现更强大和更细粒度的多目标定位.
事实证明,这两个功能反过来又需要许多其他功能,其中大多数都是博客投票中受欢迎的请求。我们添加了使用内联代码定义任务的功能(#7–请参阅powershell示例)一个新的综合对象模型(分为三部分,一,二,三),已改进性能在许多情况下具有可扩展性(#8-和在这里),财产和项目功能(#9–尽管目前不可扩展),通过执行文件系统截取进行准确的自动依赖项检查(#11),再加上一些小的语法添加(标签、,导入组,通过通配符导入)和更可配置的构建引擎(如参见在这里,在这里,以及在这里),加上更容易构建扩展性还有一些性能诊断.
我们没有不幸的是,我们有时间解决将解决方案文件转换为MSBuild(#3)的问题亲爱的喜欢这样做–也不喜欢添加Visual Studio集成调试器(#1)。
至少,不支持!
迈克·斯塔尔向我们展示了一个独创的反射极限思想,这使得创建具有真正托管代码调试器的许多功能的MSBuild特定调试器变得更加可行。在休假期间,我编写并签入了代码来完成这项工作。不幸的是,我们无法及时完成这项任务,以制定4.0的时间表。
因此,它位于产品中,但默认情况下处于禁用状态。它确实有效,只是没有得到支持或文档记录,并且有一些限制和错误:它可能很慢,并不总是很漂亮,至少在一种情况下,它有点不准确。这篇博文是关于如何使用它的“非官方”文档,希望它会有用。虽然它不受支持,但我们欢迎Connect反馈,但它可能会转移到我们的积压工作中,而不是立即修复。在这篇博文的评论中添加任何错误报告和反馈也是一个好主意。
调试演练
我将依次浏览每个调试场景。
在开始之前,请短暂打开Visual Studio,并确保已启用“仅我的代码”。这一点至关重要:
这里有很多截图,但这个博客很窄,所以有些截图被扭曲了——你可以点击它们查看完整的版本。
场景1–仅限命令行
首先,通过将HKEY_LOCAL_MACHINESOFTWAREMicrosoftMSBuild4.0键设置为debuggerenabled=true,在MSBuild.exe上启用未记录的“/debug”开关,就像我在提升的Visual Studio提示符中使用reg.exe所做的那样:
假设C:是您的系统驱动器,您现在应该有这些键。
运行MSBuild/?你会看到新的开关出现了。
我们现在可以调试了。
通常,您会调试一些自定义或编写的构建过程,但为了便于说明,我将调试一个全新的C#Windows窗体项目。我将使用/debug开关构建它,它将立即停止:
在我的情况下,我得到提示符提升,然后点击Yes:
然后我得到了标准的JIT调试提示。确保选中“手动选择调试引擎”。
这将导致出现一个对话框来选择调试引擎:您只需要“托管”。(混合会起作用,更笨重。)
现在您正在调试!
首先要注意的是,我们正好位于第一个项目文件的顶部,MSBuild的第一行就是求值。您在一开始就自动进入,就像您开始用“F11”调试常规应用程序一样。好了,几乎是最开始的:MSBuild已经在环境中读取了,以及它的其他初始设置:
现在按F10,您将逐行执行:
当您跨过属性时,您将看到局部变量窗口正在更新:
正如您可能知道的那样,MSBuild在通过。第一个过程仅计算属性,在遇到导入时将其拉入。现在尝试在item标记上设置断点(F9)-你不能!MSBuild此时不知道它们。
在底部的<Import>标记上设置一个断点并运行到它(F5):
现在进入(F11)。您将输入要导入的文件,在本例中是Microsoft。C竖琴目标。
Callstack窗口显示了像函数调用一样的跳转,包括文件中的位置:
当然,<Import>根本没有函数调用的语义。与C++中的#include类似,它只是在逻辑上插入另一个文件的内容。但我选择了这样做,以便您可以在Callstack窗口中看到导入链并了解上下文。
通过在Imports上设置更多断点并执行step-in,我可以更深入地说明:
为了通过属性传递,考虑到我还不能在项上设置断点,我将使用一个技巧。我将反复退出(Shift-F11),直到再次进入项目文件,然后再进入下一个步骤,即项目定义。C++构建过程经常使用项定义,但它们对C#来说不是很有趣,实际上只有一个:
使用相同的技巧进入物品通行证,我们将进入第一个物品。然后我设置了一个断点来说明我现在可以做到这一点。
条件断点也起作用,正如我认为的跟踪点一样。
再往前走一步,我可以看到局部变量窗口中的项,以及它们的元数据。这里有一个小错误-忽略红色消息,进入“非公共成员”查看名称和值:
有时,您会想知道当前条件的评估结果。为此,在即时窗口中,将条件传递给函数EvaluateCondition:
如果要计算(展开)表达式,这与实际情况基本相同,但函数名为EvaluateExpression:
这也是一种查看属性值或项目列表中内容的便捷方法,无需在局部变量窗口中导航。确保避开斜线,就像我在这里所做的那样。
“自动”窗口不工作,但“监视”可以:
在即时窗口中,您可以使用新的对象模型在构建期间更改几乎任何项目状态。例如,我将在此处停止时修改此属性:
你可以通过新的对象模型做很多事情,所以在这里调用它非常有用。
“我的手表”窗口更新为匹配:
这就是我要展示的调试MSBuild求值的内容的结尾。
它是如何工作的
高层发生的事情(您可以从Mike的博客中找到更多信息)是MSBuild假装脚本实际上是VB或C#。它通过动态发出IL来实现这一点,这在语义上等同于它在XML中的实际操作。MSBuild本身的代码当然是经过优化的,所以“仅我的代码”将其隐藏,但很方便,IL没有进行优化,所以它显示了出来。在IL MSBuild内部,会发出指向项目文件中正确位置的行指令,从而完成任务。至于“局部变量”,它们实际上是传递给IL中函数的参数,以便它们出现。EvaluateCondition和Evaluate Expression只是以相同的方式传递的委托。
因此,常规VB/C#调试器的大部分基本功能都能正常工作;你不能只在即时窗口中使用“?”语法;线程和进程窗口没有意义;我怀疑Intellitrace是否有效。另外,我们的一些内部构件到处都在窗户里漏出。但通过使用这个技巧,获得集成调试器的基础知识所需的工作量大大减少。我相信我花了一两天时间整理Mike的示例代码,又花了三天时间将其直接连接到MSBuild中。创建一个真正的调试器引擎将花费更多;与您从C#获得的东西相比,它的成本会非常高,所以我预计从长远来看,这将是MSBuild调试的故事。我希望您会同意,这比盯着XML和日志或添加<Message>标记要好得多。
在我的下一篇文章中,我将报道
- 在构建期间进行调试,即调试目标和项目引用内部发生的事情;
- 调试多处理器构建;
- 调试加载到Visual Studio中的项目的生成
到时见!
丹
Visual Studio项目和生成开发主管
帖子使用Visual Studio调试MSBuild脚本首次出现于Visual Studio博客.
]]>