交叉編譯實踐-Qt應用程序交叉編譯【用cmake工具編譯】

/****************************************************************************************/

/*           獲取更多乾貨技能,點擊 [這裏-小編文章列表] 主頁左側掃碼關注公衆號             */ 

/***************************************************************************************/


        上篇介紹了交叉編譯的基本原理,以及交叉編譯一個簡單的HelloWorld程序,這篇將介紹如何交叉編譯Qt程序。

由於Qt程序依賴的底層以及第三方的相關庫太多(比如libGL.so庫爲OpenGL庫,libX11.so庫爲系統圖像庫等等),因此在交叉編譯時會出現各種問題,下面將其主要的問題歸爲如下三類:
1. 編譯器所依賴的庫,在編譯鏈接過程中,找到了宿主機的庫了,導致文件格式不對,編譯鏈接不成功,比如Qt編譯是依賴於libGL.so庫,而這個庫在宿主機上的/usr/lib/目錄下也存在,導致交叉編譯器aarch64-linux-gnu-gcc在找庫時,找到了宿主機的x86_64的庫,所以肯定是鏈接不成功的。這裏要說明一點:交叉編譯所依賴的庫,必須是與目標機架構一致的庫,即libGL.so必須是ARM aarch64架構的庫,不能是宿主機上的庫,否則在目標機上運行時怎麼鏈接呢?

2. 編譯器在編譯鏈接時,沒有找到相應的頭文件和庫,即頭文件和對應庫沒有在交叉編譯器的尋庫路徑中,導致報錯找不到庫,如下圖所示:

解決方式是:一般將所依賴的相關庫,放到安裝交叉編譯器時生成的/usr/aarch64-linux-gnu/目錄中對應bin、include、lib的lib目錄中,這種方式就保證了交叉編譯器能萬無一失的找到庫和頭文件。還有一種方式是如果Qt用的是cmake編譯的,則可以通過如下方式設置其所使用的交叉編譯器和尋庫路徑:
# this is required
SET(CMAKE_SYSTEM_NAME Linux)

# specify the cross compiler
SET(CMAKE_C_COMPILER   /opt/arm/usr/bin/aarch64-linux-gnu-gcc)
SET(CMAKE_CXX_COMPILER /opt/arm/usr/bin/aarch64-linux-gnu-g++)

# where is the target environment 
SET(CMAKE_FIND_ROOT_PATH  /opt/arm/lib /home/joven/lib)

# search for programs in the build host directories (not necessary)
SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
# for libraries and headers in the target directories
SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)

# configure Boost and Qt
SET(QT_QMAKE_EXECUTABLE /usr/local/Qt5.6/bin/qmake)
以上牽扯到相關變量的設置,簡單介紹解釋如下:
    1.CMAKE_SYSTEM_NAME: 即你目標機target所在的操作系統名稱,比如ARM或者Linux你就需要寫"Linux",如果Windows平臺你就寫"Windows",如果你的嵌入式平臺沒有相關OS你即需要寫成"Generic",只有當CMAKE_SYSTEM_NAME這個變量被設置了,CMake才認爲此時正在交叉編譯,它會額外設置一個變量CMAKE_CROSSCOMPILING爲TRUE.

    2. CMAKE_C_COMPILER: 顧名思義,即C語言編譯器,這裏可以將變量設置成完整路徑或者文件名,設置成完整路徑有一個好處就是CMake會去這個路徑下去尋找編譯相關的其他工具比如linker,binutils等,如果你寫的文件名帶有arm-elf等等前綴,CMake會識別到並且去尋找相關的交叉編譯器。

    3. CMAKE_CXX_COMPILER: 同上,此時代表的是C++編譯器。

    4. CMAKE_FIND_ROOT_PATH: 代表了一系列的相關文件夾路徑的根路徑的變更,比如你設置了/opt/arm/,所有的Find_xxx.cmake都會優先根據這個路徑下的/usr/lib,/lib等進行查找,然後纔會去你自己的/usr/lib和/lib進行查找,如果你有一些庫是不被包含在/opt/arm裏面的,你也可以顯示指定多個值給CMAKE_FIND_ROOT_PATH,比如

    5.  CMAKE_FIND_ROOT_PATH_MODE_PROGRAM: 對FIND_PROGRAM()起作用,有三種取值,NEVER,ONLY,BOTH,第一個表示不在你CMAKE_FIND_ROOT_PATH下進行查找,第二個表示只在這個路徑下查找,第三個表示先查找這個路徑,再查找全局路徑,對於這個變量來說,一般都是調用宿主機的程序,所以一般都設置成NEVER

    6. CMAKE_FIND_ROOT_PATH_MODE_LIBRARY: 對FIND_LIBRARY()起作用,表示在鏈接的時候的庫的相關選項,因此這裏需要設置成ONLY來保證我們的庫是在交叉環境中找的.

    7. CMAKE_FIND_ROOT_PATH_MODE_INCLUDE: 對FIND_PATH()和FIND_FILE()起作用,一般來說也是ONLY,如果你想改變,一般也是在相關的FIND命令中增加option來改變局部設置,有NO_CMAKE_FIND_ROOT_PATH,ONLY_CMAKE_FIND_ROOT_PATH,BOTH_CMAKE_FIND_ROOT_PATH

    8. QT_QMAKE_EXECUTABLE: 指定相應的qmake路徑
以上也可以將設置相關變量的代碼寫到一個toolChain.cmake文件中,這樣在執行cmake是帶上參數 -DCMAKE_TOOLCHAIN_FILE=./toolChain.cmake 即可(cmake -DCMAKE_TOOLCHAIN_FILE=./toolChain.cmake -DCMAKE_BUILD_TYPE=Release ./)。

3. 報錯“/usr/aarch64-linux-gnu/lib/libGL.so.1:對‘XGetVisualInfo’未定義的引用” 等一系列未定義的引用,如下圖所示:

從整體報錯來看,報錯歸結爲如下圖所示:

這些未定義的引用就是上面第一種或第二種錯誤導致的,找到正確的引用庫後,就不會報錯了。

搞定以上問題後,基本上編譯、鏈接完成,可執行文件就會生成,交叉編譯Qt程序就算大功告成了!!!
編譯鏈接成功截圖如下:


但是,好多人可能都沒有到編譯階段就已經報錯了。。。。。。,可能原因如下:
1. 交叉編譯Qt程序的原理沒理清
2. Qt的庫和Qt的qmake、rcc、moc等工具沒有設置好
3. Qt的版本宿主機和目標機不一致
針對以上問題1:Qt應用程序的交叉編譯,其原理是通過宿主機上可執行的Qt相關工具程序(比如moc、rcc等),在編譯時生成中間文件和資源文件,最終打包成Qt應用程序,但是該應用程序所依賴的Qt動態庫又必須是目標機的(動態庫必須是ARM aarch64架構的)。
針對以上問題2:給環境變量PATH中必須設置本地Qt可執行文件的路徑(也就是Qt的bin目錄),這樣才能找到moc、rcc等執行相關的操作,也就是說qmake、moc、rcc等在交叉編譯是用的工具,必須是x86_64架構的。
針對以上問題3:由於在交叉編譯Qt應用程序是,用到了本地的Qt相關工具程序,所以在鏈接時,最好保持本地的Qt版本和目標機的Qt版本一直(也就是編譯時的Qt版本和運行時的Qt版本保持一致)。
總結以上問題:如果在cmake中設置了找庫路徑,則需要將目標機的Qt的lib庫拷貝到設置的路徑中,如果在cmake中沒有設置找庫路徑,則有個粗暴的方法,就是將本地的Qt中lib目錄換成目標機的lib,確保本地的Qt的bin目錄中可執行文件都是x86_64架構,lib目錄中的庫文件都是ARM aarch64架構的。

這樣就基本搞定了Qt應用程序的交叉編譯了^_^.



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章