Notifications
Article
C#静态代码分析工具链

Published 11 days ago
326
0
价值专家经验分享。
作者:Unity价值专家 王棣

起因

近期项目终于告一段落,正逐步的对轩辕剑的研发过程展开一些反思。在研发过程中,由于编码规规范不断的更新完善,对整个项目组程序来说存在很大的同步困难,因此开发了一套基于Rolsyn的静态代码分析工具。但初版工具存在很多不完善的地方,包括无法与Unity同步编译检测结果,IDE需要VisualStudio,无法达到强制性的工具链特点,初版实现只能算作一种提醒的辅助工具。对于有项目经验的同学应该很容易想到,高强度的工作状态下,此工具很难保证规范的严格执行。

设计目标

对于历史存在问题,想要开发一套更加完善的针对C#的静态代码分析工具链,提出如下功能目标:
1.可自由定制的静态代码分析能力
2.能够与Unity实时交互分析结果
3.强制修正提醒
4.无需使用者进行任何环境配置

Rolsyn实现静态分析

对于自由定制的静态代码分析能力这一需求来说,依然选择沿用Rolsyn的Analyzers实现。本文将不对Roslyn Analyzers的开发做细致描述,读者可根据如下文档进行了解:
1.https://docs.microsoft.com/en-us/visualstudio/extensibility/getting-started-with-roslyn-analyzers?view=vs-2017 微软官方文档
2.https://github.com/meng-hui/UnityEngineAnalyzer Github上一个针对Unity的C#编码的简单规范实现项目
实际上实现分析逻辑并不复杂,麻烦的是做一份十分完善的规范规则。这其中包含基础C#实现时一些不良用法,项目编码中的硬性规则,代码编写的安全检测等。比如:boxing的检测,可变参提示,MonoBehavior脚本的创建,私有方法中的空指针判断,字符串操作,GetCommpoment的使用,事件定义的重复性检测,lambda表达式的使用……
推荐一本不错的书《UNITY 2017 GAME OPTIMIZATION SECOND EDITION》,可根据其中编码规范做一些基础检测规则。
在VisualStudio中添加静态分析库比较容易,但是由于Unity每次添加/删除脚本都会引发项目的csproj文件刷新,导致项目配置失效。幸好Visual Studio Tools for Unity工具提供了相关扩展,用来针对每次刷新事件进行自定义的修正,相关文档地址https://docs.microsoft.com/en-us/visualstudio/cross-platform/customize-project-files-created-by-vstu?view=vs-2017。最终使用方法请参考代码ProjectFileHook,项目中静态分析库放置在了Assets/Plugins/x86/目录下,可根据需求定制项目的静态代码分析库。

与Unity编译结合

想要解决该问题,首先需要确保编译器使用Rolsyn,其次想办法接管或者部分接管Unity的每次C#代码编译流程。
我的测试环境为2018.3版本,在该版本下使用.Net 4.6应该是一种必然选项(这是由于后续的很多Unity新特性都需要基于.Net 4.6)。该版本下在Windows平台对其进行编译使用Rolsyn,因此满足初步需求条件。
对于编译流程来说,由于在2017版本时Unity开源了Editor的C#部分代码,这提供了我们对其编译流程分析的可能性。经过分析相关代码,我找到了核心编译参数位置https://github.com/Unity-Technologies/UnityCsReference/blob/2018.3/Editor/Mono/Scripting/Compilers/MicrosoftCSharpCompiler.cs#L42-L64。这样我们只需要想办法对arguments列表中添加使用我们自定义静态分析库的参数即可。因此问题转移到如何对该列表添加编译参数。
C#在Rolsyn编译后实际上是以IL编码存在,根据其原理一个方法的调用实际上就是call一个指针,因此只需要我们通过一个自定义的方法指针代替原有函数指针被执行,就可以达到插入我们自定义逻辑的目的。具体的实现@Misaka同学分享了一个开源库https://github.com/easy66/MonoHooker,我直接使用其达到目的。
最终实现代码请参考CSharpCompilerHook实现。

强制修正提示

对于强制修正提示,我的目标是监听编译结果,对于存在问题的编译结果在Unity中进行定时Alert对话提醒,以达到醒目提示开发人员的作用。新版Unity提供了脚本编译完成的回调API,https://docs.unity3d.com/ScriptReference/Compilation.CompilationPipeline-assemblyCompilationFinished.html。因此只需要通过该API就可以轻松实现相关强制修正提示的逻辑,相关逻辑是现在EditorCompilerFinish中,同时在AnalyzerUtility中可以实现过滤器函数以确定是否进行弹窗提示。

总结

本次实现依然有一些苛刻的条件,比如说需要使用较新的Unity版本,但对于老版本也有相关第三方方案提供Rolsyn编译。总体来说新的工具链达到了我最初期望的目标,实现过程中围绕Unity提供的相关资源进行充分的利用。可以看出Unity的部分开源策略与工具链上的改进,为我们提供了足够的特性。充分了解Unity提供的各种特性能力有助于我们更加优雅的实现需求。
最后附上整套工具链地址:https://github.com/odiecc/UnityCodeAnalyzersPipeline
最终使用到的相关技术包Rolsyn Analyzer、C# unsafe形式代码及IL执行特点、Visual Studio Tools For Unity及Editor相关API

作者介绍:

王棣,目前就职于搜狐畅游,Unity技术专家
从事游戏研发10余年,对Unity及图形学有深入研究,目前主要探索将SRP应用于移动端领域。包括针对多光源的高效渲染、色彩管线的工作流设计与运用、高效的阴影方案等。时常活跃于各开发社区,解答相关问题。

Tags:
Comments