BUCK 與 RetroLambda 兼容性解決方案
從最初 OkBuck 發布時宣稱 BUCK 與 RetroLambda 不兼容只能忍痛割愛(lambda),到 BUCK 維護者之一聯系我聲稱 BUCK 可以編譯 Java 8 結果遇到編譯錯誤未解,到昨晚終于成功讓 BUCK 與 RetroLambda 出雙入對,時隔大半年終于臻至完美,怎一個爽字了得!如果你還不了解什么是 BUCK,可以參考我的兩篇文章 OkBuck, underneath the hood , 手把手OkBuck教程:應用到AndroidTDDBootStrap項目(續) ,以及 BUCK 官方文檔 。
BUCK 編譯 Java 8
這一點 BUCK 確實已經支持了,只需要在 java_library 和 android_library 這兩種 rule 中加入以下配置即可:
source = '8', target = '8',
然而就是這一步中出現的攔路虎,擋住了我們前進的腳步半年之久,簡而言之,純 Java library module 這樣做沒有任何問題,但是 Android library module 的編譯卻報告了錯誤:
com.sun.tools.javac.code.Symbol$CompletionFailure: class file for java.lang.invoke.MethodType not found.
錯誤日志很明顯對不對?然而,搜索出來的結果絕大部分都是指向了 gradle-retrolambda 的一個 issue ,而這個 issue 的解決方案外部看來和這個問題沒有任何聯系。無奈之下只能再次向 BUCK 維護者求助:
但這個帥小伙卻從此失去了音信 :(
好了帥小伙的故事先暫時到這里,我們繼續。
對于 Java library module,BUCK 能夠成功使用 javac 編譯出 java 8 的字節碼,但是我們怎么把 RetroLambda 集成到 BUCK 的構建過程中呢?還是這個帥小伙出的主意(其實是 BUCK 的有些文檔嚴重缺乏,只能自己看源碼或者他們出主意): postprocess_classes_commands 。
BUCK 調用 RetroLambda
RetroLambda 只是一個命令行工具,大家通常使用的可能是另一個 gradle 插件: gradle-retrolambda ,利用上面提到的 postprocess_classes_commands 參數,我們可以在 java_library 和 android_library 這兩種 rule 中加一個 class 編譯完成之后的 hook,BUCK 會執行 postprocess_classes_commands 參數的命令,并把本次編譯的 class 路徑作為參數傳入。所以我們就可以在這里執行 RetroLambda 程序把 java 8 的字節碼編譯為 java 6 的字節碼了。這里因為需要為 shell 腳本傳入參數,所以我們需要把命令封裝到一個腳本文件中,腳本文件的內容如下:
java \ -Dretrolambda.inputDir=$1 \ -Dretrolambda.classpath=$1 \ -jar ./retrolambda-2.3.0.jar
這里 RetroLambda 會直接覆蓋 BUCK 編譯生成的 class 文件,命令執行完畢之后,BUCK 會繼續打包的后續步驟。
這個版本的腳本文件編譯 Java library module 時成功了,但是編譯 Android library module 時卻失敗了,報了上節提到的錯誤。
也就是這個錯誤,困擾了我們半年之久。當初的困境感興趣的朋友可以查看這個 Github issue 。
class file for java.lang.invoke.MethodType not found 問題的解決
這個問題之前 RetroLambda 也遇見過,雖然他們的解決方法看上去和我們遇見的問題沒有關系,但它畢竟解決了,所以深挖肯定有門路。我在之前的文章中曾總結過各種問題的解決思路,這次的問題還是通過“差異分析法”解決的。
RetroLambda 可以把同樣的代碼先編譯為 java 8 的字節碼,BUCK 的卻不可以,但他們用的至少都是相同的 javac 程序吧?那問題肯定就出現了 編譯選項 上。通過給兩種方式加上日志輸出選項,我拿到了它們各自的編譯選項,這里,我以我發布到 Github 的 demo 工程 為例。
執行 buck build -v 10 app/:src_release ,在控制臺看到了一段紅色的信息,就是出錯的命令,錯誤就是上面提到的 class file for java.lang.invoke.MethodType not found ,編譯選項如下:
javac \ -source 8 -target 8 \ -sourcepath -g \ -bootclasspath \ /Users/piasy/tools/android-sdk/platforms/android-23/android.jar:\ /Users/piasy/tools/android-sdk/platforms/android-23/optional/org.apache.http.legacy.jar \ -verbose \ -d /Users/piasy/src/BuckJava8RetroLambdaDemo/buck-out/bin/app/lib__src_release__classes \ -classpath /Users/piasy/src/BuckJava8RetroLambdaDemo/buck-out/bin/app/__src_release#dummy_r_dot_java_rdotjava_bin__:\ /Users/piasy/src/BuckJava8RetroLambdaDemo/buck-out/gen/.okbuck/AFFB34D18189F4D10144A341628B7C81/aar__animated-vector-drawable-23.3.0.aar#aar_prebuilt_jar.jar:\ /Users/piasy/src/BuckJava8RetroLambdaDemo/buck-out/gen/.okbuck/AFFB34D18189F4D10144A341628B7C81/aar__appcompat-v7-23.3.0.aar#aar_prebuilt_jar.jar:\ /Users/piasy/src/BuckJava8RetroLambdaDemo/buck-out/gen/.okbuck/AFFB34D18189F4D10144A341628B7C81/aar__support-v4-23.3.0.aar#aar_prebuilt_jar.jar:\ /Users/piasy/src/BuckJava8RetroLambdaDemo/buck-out/gen/.okbuck/AFFB34D18189F4D10144A341628B7C81/aar__support-vector-drawable-23.3.0.aar#aar_prebuilt_jar.jar:\ /Users/piasy/src/BuckJava8RetroLambdaDemo/buck-out/gen/.okbuck/AFFB34D18189F4D10144A341628B7C81/jar__support-annotations-23.3.0.jar.jar:\ /Users/piasy/src/BuckJava8RetroLambdaDemo/buck-out/gen/app/lib__build_config_release__output/build_config_release.jar \ @buck-out/gen/app/__src_release__srcs
執行 ./gradlew :app:compileDebugJavaWithJavac --debug ,查看輸出找到編譯選項:
javac \ -d /Users/piasy/src/BuckJava8RetroLambdaDemo/app/build/retrolambda/debug \ -g -encoding UTF-8 \ -bootclasspath /Users/piasy/tools/android-sdk/platforms/android-23/android.jar \ -sourcepath /Users/piasy/src/BuckJava8RetroLambdaDemo/app/build/tmp/compileDebugJavaWithJavac/emptySourcePathRef \ -classpath /Users/piasy/src/BuckJava8RetroLambdaDemo/app/build/intermediates/exploded-aar/com.android.support/support-v4/23.3.0/jars/classes.jar:\ /Users/piasy/tools/android-sdk/extras/android/m2repository/com/android/support/support-annotations/23.3.0/support-annotations-23.3.0.jar:\ /Users/piasy/src/BuckJava8RetroLambdaDemo/app/build/intermediates/exploded-aar/com.android.support/support-vector-drawable/23.3.0/jars/classes.jar:\ /Users/piasy/src/BuckJava8RetroLambdaDemo/app/build/intermediates/exploded-aar/com.android.support/appcompat-v7/23.3.0/jars/classes.jar:\ /Users/piasy/src/BuckJava8RetroLambdaDemo/app/build/intermediates/exploded-aar/com.android.support/animated-vector-drawable/23.3.0/jars/classes.jar:\ /Users/piasy/src/BuckJava8RetroLambdaDemo/app/build/intermediates/exploded-aar/com.android.support/support-v4/23.3.0/jars/libs/internal_impl-23.3.0.jar:\ /Library/Java/JavaVirtualMachines/jdk1.8.0_92.jdk/Contents/Home/jre/lib/rt.jar \ /Users/piasy/src/BuckJava8RetroLambdaDemo/app/src/main/java/com/github/piasy/buck/retrolambda/demo/MainActivity.java \ /Users/piasy/src/BuckJava8RetroLambdaDemo/app/build/generated/source/r/debug/android/support/v7/appcompat/R.java \ /Users/piasy/src/BuckJava8RetroLambdaDemo/app/build/generated/source/r/debug/com/github/piasy/buck/retrolambda/demo/R.java \ /Users/piasy/src/BuckJava8RetroLambdaDemo/app/build/generated/source/buildConfig/debug/com/github/piasy/buck/retrolambda/demo/BuildConfig.java \ -XDuseUnsharedTable=true
差異最大的是兩部分,一個是 classpath 選項,一個是最后一部分,BUCK 是一個 @ 加一個文件路徑,gradle 則是多個文件名,而查看 BUCK 命令中的那個文件內容, 基本上 就是 gradle 命令中傳入的那幾個文件名。那么很可能就是 classpath 部分了(當然,真實情況是我逐一替換了所有不同的部分,不幸的是最后才輪到 classpath )。
有趣的是,gradle 和 BUCK 都會先把 aar 文件解壓開來,然后把其中的 jar 包加入到 classpath 中,而 gradle 的 classpath 相較于 BUCK 的多了一個非常可疑的對象: jre/lib/rt.jar ,gotcha!不用試了,一看就是它了。
當然我還是 google 了一下它的淵源,詳見 這篇文章 ,慚愧的是我正如這篇文章開頭所描述的那幫程序員一樣,連 rt.jar 為何方神圣都不知道,罪過罪過。解壓開來之后確實發現 java.lang.invoke.MethodType 這個 class 端坐其中。
通過修改 BUCK 的源碼,把 rt.jar 加入到 javac 的默認 classpath 中,進一步的測試也驗證了問題的癥結所在,就是 classpath 少了 rt.jar!
完整的示例
示例工程代碼可以在 Github 獲取 。
上上節中的 RetroLambda 腳本對 Java library module 的 BUCK 與 RetroLambda 聯編可以通過,但對 Android library module 卻不行,因為還有許多 jar 包需要加入到 classpath 中,包括 android.jar。而這一點很好解決,把我們在上節中拿到的 BUCK 編譯的 javac classpath 加入到 RetroLambda 執行的 classpath 中即可,完整的 RetroLambda 腳本如下:
java \ -Dretrolambda.inputDir=$1 \ -Dretrolambda.classpath=$1:\ /Users/piasy/tools/android-sdk/platforms/android-23/android.jar:\ \ /Users/piasy/tools/android-sdk/platforms/android-23/optional/org.apache.http.legacy.jar:\ ./buck-out/bin/app/__src_release#dummy_r_dot_java_rdotjava_bin__:\ ./buck-out/gen/.okbuck/AFFB34D18189F4D10144A341628B7C81/aar__animated-vector-drawable-23.3.0.aar#aar_prebuilt_jar.jar:\ ./buck-out/gen/.okbuck/AFFB34D18189F4D10144A341628B7C81/aar__appcompat-v7-23.3.0.aar#aar_prebuilt_jar.jar:\ ./buck-out/gen/.okbuck/AFFB34D18189F4D10144A341628B7C81/aar__support-v4-23.3.0.aar#aar_prebuilt_jar.jar:\ ./buck-out/gen/.okbuck/AFFB34D18189F4D10144A341628B7C81/aar__support-vector-drawable-23.3.0.aar#aar_prebuilt_jar.jar:\ ./buck-out/gen/.okbuck/AFFB34D18189F4D10144A341628B7C81/jar__support-annotations-23.3.0.jar.jar:\ ./buck-out/gen/app/lib__build_config_release__output/build_config_release.jar \ -jar ./retrolambda-2.3.0.jar
運行修改過后的 BUCK 編譯,成功!
后續工作
這次對 BUCK 的修改量很少,只有一行代碼,但卻解決了困擾我們長達半年的一個問題,這段心酸歲月終于過去了。修改內容已經提交 pr 到 BUCK repo,希望能夠盡快合入主干,而這個 RetroLambda 的腳本也是有可能自動生成的,所以也加入到了 OkBuck 的開發日程中,近日就將完成。屆時歡迎大家使用,盡情體驗 lambda + BUCK 的暢快淋漓!
來自: http://blog.piasy.com/2016/05/03/BUCK-With-RetroLambda/