記一次調試

這是我最近幾個月來遇到的最棘手的一個問題:
* 昨天花了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

相關文章
相關標籤/搜索