BUCK 與 RetroLambda 兼容性解決方案

aori59oe 8年前發布 | 13K 次閱讀 Java 安卓開發 Android開發 移動開發

 

從最初 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/ 

 本文由用戶 aori59oe 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!