Linux下Clang交叉編譯protobuffer庫供Qt on Android使用

緣由

筆者在一個由Qt開發(fā)的桌面應(yīng)用程序中,使用到了protobuffer消息傳輸協(xié)議,而今需要移植到安卓手機上,為了節(jié)省開發(fā)時間,不打算修改服務(wù)器程序(如果不在手機應(yīng)用上繼續(xù)采用protobuffer協(xié)議,則面臨修改服務(wù)器程序,提供json消息格式供手機端使用)。為發(fā)揮Qt的跨平臺特性,在Qt for Android下延用桌面程序的通信模塊,決定編譯arm版的probuffer庫供安卓手機使用。筆者打算在Unbuntu下交叉編譯protobuffer動態(tài)鏈接庫,然后在Windows下的Qt for Android開發(fā)環(huán)境中使用。

一次彎路

本段記錄筆者最初編譯protobuffer庫時,采用GNU GCC編譯所遇到的庫函數(shù)調(diào)用問題,不感興趣的讀者可以直接跳過本段,從“正式編譯”章節(jié)開始看。最初筆者的開發(fā)和編譯環(huán)境如下:

Android開發(fā)環(huán)境:

  • Windows 10 64位
  • Qt for Android 5.12
  • android-ndk-r16b-windows-x86

protobuffer庫交叉編譯環(huán)境:

  • Unbuntu 14.04 LTS
  • android-ndk-r16b-linux-x86_64
  • GNU GCC
  • protobuffer 3.3.0源代碼

相關(guān)下載

protobuf下載地址:
https://github.com/protocolbuffers/protobuf
NDK下載地址:
https://developer.android.google.cn/ndk/downloads/

后來發(fā)現(xiàn)這樣的交叉編譯環(huán)境下所編譯出來的protobuffer庫在開發(fā)環(huán)境中并不支持,因為兩者所使用的編譯器前者Qt for Android SDK采用的是Clang,后者的庫編譯環(huán)境采用的是GNU GCC。于是將開發(fā)環(huán)境降低到Qt 5.9+ndk rc15(因為Qt for Android 5.9還是采用的GNU GCC)。但是最終還是遇到問題,在調(diào)用一個庫函數(shù)AppendPartialToString()會導(dǎo)致程序崩潰。最后在一篇國外網(wǎng)站上找到了修正方法,說需要切換到Clang編譯器。請看下面章節(jié)。(腳本文件中所涉及的路徑,請修改為和自己的編譯環(huán)境一致。)

正式編譯

開發(fā)和編譯環(huán)境和上述一致,但沒有使用GNU GCC了,使用的是Clang編譯工具鏈(通過NDK生成)
注意:讀者可以嘗試使用nkd r18版本的ndk,因為我在使用ndk r16b時,開發(fā)環(huán)境中鏈接遇到找不到llvm-readobj.exe及l(fā)lvm-strip.exe的問題,最后從ndk r18中將這兩個文件拷貝到了指定位置;讀者還可以嘗試更高版本的protobuffer庫。
1. 首先編譯Linux下的protobuffer庫
這里我們主要是為了得到編譯所生成的protoc執(zhí)行文件,用于今后將protobuffer消息定義文件.proto生成c++文件。

  • 編譯Linux版的protobuffer庫腳本如下:
#!/bin/sh
echo "here"
cd $HOME/Linux/works/protobuff/protobuf-3.3.0
make clean
echo "end"
./autogen.sh
./configure --enable-shared --prefix=$HOME/Linux/works/protobuff_linux
make -j4
make install

如果腳本執(zhí)行沒有問題,則會生成下面三個目錄:


image.png
  • 將.proto文件生成c++文件命令的格式:

protoc -I=<導(dǎo)入文件的搜索路徑,可指定為當前路徑> --cpp_out=<c++文件的生成路徑> <.proto文件的路徑,包含文件名>
例如:
protoc -I=‘home/user/Linux/works/protobuffer_linux/bin’ --cpp_out=‘home/user/Linux/works/protobuffer_linux/bin’ ‘home/user/Linux/works/protobuffer_linux/protomessage.proto’

最后將生成的protomessage.pb.h和protomessage.pb.cc文件保存好,共后面在你的Qt for Android應(yīng)用工程中使用。

2. 利用ndk生成Clang工具鏈
ndk自帶生成clang工具鏈的腳本make-standalone-toolchain.sh,我將執(zhí)行該腳本需要的參數(shù)寫入了新腳本文件make_clang_toolchain.sh,其內(nèi)容如下:

#!/bin/sh
cd $HOME/Linux/works/android/ndk/android-ndk-r16b/build/tools
./make-standalone-toolchain.sh --arch=arm --platform=android-26 --toolchain=arm-linux-android-clang5.0 --install-dir=$HOME/Linux/works/arm-26-toolchain-clang --use-llvm --stl=libc++

腳本執(zhí)行完之后,會在定義的install-dir目錄下自動生成包含clang編譯工具鏈的arm-26-toolchain-clang目錄,將在下面的腳本中使用。
3. 交叉編譯protobuffer庫
腳本arm_combile_clang_ndk_r16.sh內(nèi)容如下:

export PREFIX=$HOME/Linux/works/protobuf_arm_3.0.0_clang/
export PATH=$HOME/Linux/works/arm-26-toolchain-clang/bin:$PATH
export SYSROOT=$HOME/Linux/works/arm-26-toolchain-clang/sysroot
export CC="arm-linux-androideabi-clang --sysroot $SYSROOT"
export CXX="arm-linux-androideabi-clang++ --sysroot $SYSROOT"
cd $HOME/Linux/works/protobuff/protobuf-3.3.0
make clean
./autogen.sh
./configure --prefix=$PREFIX \
--host=arm-linux-androideabi \
--with-sysroot="${SYSROOT}" \
--enable-shared \
--enable-cross-compile \
--with-protoc=$HOME/Linux/works/protobuff_linux/bin/protoc \
CFLAGS="-march=armv7-a -D__ANDROID_API__=26" \
CXXFLAGS="-frtti -fexceptions -march=armv7-a -D__ANDROID_API__=26" \
LIBS="-llog -lz -lc++_static"
make -j 4
make install
  1. 如果上述兩個腳本執(zhí)行沒有問題,將在上述定義的protobuf_arm_3.0.0_clang目錄下產(chǎn)生如下protobuffer庫文件:
    image.png

    lib目錄中的內(nèi)容:


    image.png

    bin目錄中的protoc文件不用理會,這是arm版的用于把protobuffer消息文件生成c++文件的工具。我們要使用的是前面所編譯的Linux版本。
  2. 文件名修改
  • 將生成的include目錄拷貝到你的Qt for Android工程目錄中,并配置到pro文件中的INCLUDEPATH
  • 將下面紅框中的文件拷貝到你的Qt for Android SDK目錄下,同時在你的工程文件下創(chuàng)建lib目錄,里面也復(fù)制一份
    如拷貝到:D:\Qt\Qt5.12.1\5.12.1\android_armv7\lib,并將.so.xx.x.x修改為.so


    image.png
  1. Qt工程文件.pro的配置


    image.png

心得

  1. 編譯高于rc15版本的ndk可能也需要clang編譯器,經(jīng)測不支持Qt 5.9,所以一般較低版本Qt會搭配較低版本的ndk,反則反之;
  2. Qt也在與時俱進,Qt for Android 5.9還在使用gnu gcc編譯器,到了Qt for Android 5.12中使用了clang編譯器。(具體從哪個版本開始變化的,筆者沒有查閱)
  3. Android NDK大約從rc 15版本之后,也要使用clang編譯器編譯,否則在configure檢查過程中,會提示c編譯器無法啟動;
  4. 當編譯出來的動態(tài)庫在開發(fā)環(huán)境中使用遇到錯誤的時候,查看錯誤日志,如果是鏈接問題,可以看到它在編譯過程中所連接的C庫,到底是gnu的還是clang的,是動態(tài)庫還是靜態(tài)庫,這樣可以重新調(diào)整自己的環(huán)境,讓開發(fā)環(huán)境和編譯環(huán)境互相匹配。

由于筆者能力有限,文中若有疏漏或不妥之處,歡迎大家指正。

參考鏈接

https://github.com/protocolbuffers/protobuf/issues/5279
https://blog.csdn.net/cqchengdan/article/details/75578934

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容