记一次调试

这是我最近几个月来遇到的最棘手的一个问题:
* 昨天花了4个小时找出第一层次的缘由
这个纠结啊,原本和老婆说好准时下班回家吃饭的,结果被这个问题拖了老久。

这是一个gradle的plugin,用来resolve公司内部的dependency的,弄完了跑测试项目的,抛一个NPE,并且NPE还不在本身的代码里面。好吧,把gradle的源代码翻出来看,若是是本身传进去的一个值是null,解决起来也还ok了。结果到里面一看,那是一个loop里面,迭代map的时候出的错 - 能够算是NPE中的最坏状况了把。仔细查看一下代码,好吧,是迭代环境变量的。那行,在本身的代码里把环境变量都打出来谁是null吧。结果全ok,看来是顺序问题,在我打出来的时候,那个null的仍是设上呢。既然是环境变量被设,那就在本身的代码里搜搜看有没有可疑点吧,还真找到个地方,一print仍是真是在那个地方把PATH设成了null。

照理犯罪现场找到了,解决也就三下两下的事了,因而我打算解决完,发完code review再走。结果发现那个null是被这么设上的:
env_path = plugin_ext.getCompileTimeJNI(jniPaths)

这是groovy语言,plugin_ext是一个gradle的plugin的extension,getCompileTimeJNI是定义在extension中的一个closure,我死活检查getCompileTimeJNI,他也绝对不可能返回null,若是有exception的话,也应该抛出来,而不是返回null。

仔细检查代码,理清逻辑,打印结果,仍是毫无头绪,无奈已经7:30多了,仍是先回去吧。

* 今天早上大概也两个小时吧,找出的根本缘由
周六早上起来, 多少还惦记着这件事,再看看吧。
终于,在观察打印出来的结果时,我注意到一个细节:在打印plugin_ext.getCompileTimeJNI的时候:
第一次是closure的地址
而后调用closure:plugin_ext.getCompileTimeJNI(jniPaths)
第二次就是一个Set了

这说明这个closure的调用有点蹊跷,我已经检查过了closure的实现自己没有问题,那么问题就在这简简单单的一句closure的调用上。 这花了我很长的时间去发现并相信着其实不是一个函数调用,而是一个赋值:
plugin_ext.getCompileTimeJNI(jniPaths)
就是
plugin_ext.getCompileTimeJNI = jniPaths
我不知道为何gradle要发明这么坑爹的语法 - 这绝对是编码质量与效率的杀手,可是无论怎样,根源问题是找到了:
env_path = plugin_ext.getCompileTimeJNI(jniPaths)
这个赋值语句永远返回null

* 暂时有了一个workaround,尚未比较official的解决方案(if there is one)
你固然能够plugin_ext.getCompileTimeJNI,call,这是这改变了原有api的调用方式,是个breaking change,并且巨丑无比。

我以为这是设计有点问题,也在咨询gradle官方:http://forums.gradle.org/gradle/topics/call_plugin_extension_property_becomes_an_assignment

目前的workaround,仍是基于gradle对extension的奇葩设计:
* 若是你的closure是定义在extension里面的,调用即赋值,挂
* 若是你的closure没在extension定义中,而是在后面使用时加上去的,调用仍是调用,ok
全部workaround就是把getCompileTimeJNI移出extension的定义,在外面加。api

相关文章
相关标签/搜索