網上關於python的交叉編譯的文章很多,但是關於python第三庫的交叉編譯的文章就比較少了,而且很多標題是第三方庫的交叉編譯,但是實際上用到的都是不需要交叉編譯就能用的庫,可參考性不強,最近關於python及其第三方庫的交叉編譯也踩了不少坑,記錄一下!
1、什麼是交叉編譯:在一個平臺上生成另一個平臺上的可執行程式碼。
2、為什麼要交叉編譯:在進行嵌入式系統的開發時,執行程式的目標平臺通常具有有限的儲存空間和運算能力,比如常見的ARM 平臺,其一般的靜態儲存空間比較小,而CPU運算能力弱。這種情況下,在ARM平臺上進行本機編譯就不太可能了,為了解決這個問題,交叉編譯工具就應運而生了。通過交叉編譯工具,我們就可以在CPU能力很強、儲存控制元件足夠的主機平臺上(比如PC上)編譯出針對其他平臺的可執行程式。
1、交叉編譯鏈:rv1126-arm-buildroot-linux-gnueabihf-toolchain.tar.bz2
2、目標板子(target主機):armv7l
3、執行交叉編輯的主機(build主機):ubuntu18-x86_64
4、python版本:3.5.2
5、numpy==1.18.5
build主機是我新安裝的一個ubuntu18的新虛擬機器器,所以連gcc 都沒有的
1、安裝gcc: sudo apt-get install gcc-8 -y
2、將gcc-8指定成預設的gcc: sudo ln -s /usr/bin/gcc-8 /usr/bin/gcc
3、安裝cmake: sudo apt-get install make cmake -y
4、安裝libffi-dev 交叉編譯 python 需要的依賴: sudo apt-get install libffi-dev
5、安裝zip 解壓壓縮包使用:sudo apt-get install zip -y
1、在build主機上交叉編譯zlib庫,這個是python原始碼安裝必須的依賴庫
2、在build主機上交叉編譯openssl庫,這個雖然不是原始碼安裝必須的依賴庫,但是大部分其他庫都有可能使用到這個庫
3、在build主機上安裝build主機上的python版本,我們成為python-build
4、在build主機上交叉編譯target主機上的python版本,我們稱之為python-target
5、在build主機上通過crossenv搭建target-python的執行虛擬環境
6、在crossenv虛擬環境中通過pip打包交叉編譯第三方庫為.whl形式的
1、解壓交叉編譯鏈:說明不同的平臺的使用的交叉編譯鏈不同,但是思路和步驟是一樣的。
tar jxvf rv1126-arm-buildroot-linux-gnueabihf-toolchain.tar.bz2
解壓之後得到一個名為 host 的資料夾。
2、進入 host 目錄: cd host
3、執行 relocate-sdk.sh 指令: ./relocate-sdk.sh (不是所有交叉編譯鏈都需要這一步的)
4、將交叉編譯鏈新增到環境變數:vim /etc/profile
5、在最後新增:export PATH=$PATH:/home/host/bin 這裡的路徑根據自己實際的路徑進行修改即可。
6、重新載入環境變數:source /etc/profile
7、測試:arm-buildroot-linux-gnueabihf-gcc -v
這裡我已經準備好了openssl-1.0.2g.tar.gz的壓縮包,這裡我嘗試了openssl-1.1.1的版本,但是和python3.5.2不太合適,總是有問題,所以這裡我使用的是openssl-1.0.2的版本
1、 解壓原始碼包,這些原始碼包我都是放在/home路徑下的:tar -xzvf openssl-1.0.2g.tar.gz
2、對壓縮包進行重新命名,區分是在build主機上用的還是在target主機上用的,在build主機上用的我都統一在後面加上_build,在target主機上使用的統一在後面加上_target
mv openssl-1.0.2g openssl-1.0.2g-build
3、cd openssl-1.0.2g-build
4、設定編譯環境:./config --prefix=/home/openssl-1.0.2g-build/openssl-build
其中: --prefix是指定編譯後的安裝的路徑
5、執行編譯安裝:make && make install 此時在/home/openssl-1.0.2g-build裡面就會有openssl-build資料夾
6、因為安裝的ubuntu18中預設的openssl是1.1.1,我們需要換成我們的openssl-1.0.2g
把以前的備份:sudo mv /usr/bin/openssl /usr/bin/openssl.old
7、建立新的軟連線:sudo ln -s /home/openssl-1.0.2g-build/openssl-build/bin/openssl /usr/bin/openssl
8、編輯連結檔案:vim /etc/ld.so.conf.d/libc.conf
9、在libc.conf檔案中新增:/usr/openssl-1.0.2g-build/openssl-build/lib
10、重新載入設定:ldconfig
11、測試:openssl version ,已經變成1.0.2g版本了
1、同樣是再次解壓openssl原始碼包,這次解壓的原始碼包用來交叉編譯給target-python使用的:tar -xzvf openssl-1.0.2g.tar.gz
2、更改名字:mv openssl-1.0.2g openssl-1.0.2g-target
3、cd openssl-1.0.2g-target
4、設定編譯環境:./config no-asm --shared --cross-compile-prefix=arm-buildroot-linux-gnueabihf- --prefix=/home/openssl-1.0.2g-target/openssl-target
解釋: no-asm :加上 no-asm 表示不使用組合程式碼加速編譯,不然會報錯
--cross-compile: 指定交叉編譯鏈的字首,這樣在交叉編譯openssl就會使用我們的交叉編譯鏈進行交叉編譯了
--prefix: 已經是交叉編譯後的路徑
5、在編譯後生成的Makefile中有兩處是 -m64 的標記要刪除,因為交叉編譯後是在32位元的板子上執行,所以這一步也要改:sed -i 's/-m64//' Makefile
6、執行編譯安裝:make && make install
目前我們就把openssl-build和openssl-target都準備好了
1、解壓原始碼包:unzip zlib1211.zip
2、改名:mv zlib-1.2.11 zlib-1.2.11-build
3、cd zlib-1.2.11-build
4、設定編譯環境:./configure --prefix=/home/zlib-1.2.11-build/zlib-build
5、執行編譯安裝:make && make install
1、解壓原始碼包:unzip zlib1211.zip
2、改名:mv zlib-1.2.11 zlib-1.2.11-target
3、cd zlib-1.2.11-target
4、設定交叉編譯器:export CC=arm-buildroot-linux-gnueabihf-gcc 通過export 設定的環境變數都是臨時一次性的,當shell視窗關閉了就失效了
5、設定編譯環境:./configure --prefix=/home/zlib-1.2.11-target/zlib-target --enable-shared
6、執行編譯安裝:make && make install
目前我們也已經包zlib-build和zlib-target準備好了
這一步已經在準備工作中做了:sudo apt-get install libffi-dev
1、解壓原始碼包:tar -xzvf libffi-3.2.1.tar.gz
2、改名:mv libffi-3.2.1 libffi-3.2.1-target
3、cd libffi-3.2.1-target
4、設定交叉編譯器:export CC=arm-buildroot-linux-gnueabihf-gcc 如果這一步在準備zlib-target沒有關閉shell視窗的時候,可以不用設定,因為已經設定過了,但是如果關了視窗就要重新設定了
5、設定編譯環境:./configure CC=arm-buildroot-linux-gnueabihf-gcc --host=arm-buildroot-linux-gnueabihf --build=x86_64-linux-gnu target=arm-buildroot-linux-gnueabihf --enable-shared --prefix=/home/libffi-3.2.1-target/libffi-target
6、執行編譯安裝:make && make install
目前ctypes-build和ctypes-target也準備好了
1、解壓原始碼:tar xvf Python-3.5.2.tgz
2、改名:mv Python-3.5.2 python-3.5.2-build
3、cd /home/python-3.5.2-build
4、修改 Modules/Setup.dist檔案:vim Modules/Setup.dist
a、修改關於openssl部分
b、修改關於zlib部分
5、將之前設定的交叉編譯器改為預設的編譯器:export CC= 這裡=後面什麼都不賦值就表示設定為空,這樣就會去找預設的gcc了
6、設定編譯環境,./configure --prefix=/home/python-build --without-ensurepip
--without-ensurepip:不安裝pip,因為預設安裝的pip版本太低了,所以一會我們自己安裝pip
7、執行安裝編譯:make && make install
8、cd /home/python-build/bin
9、下載pip檔案:curl https://bootstrap.pypa.io/pip/3.5/get-pip.py -o get-pip.py -k
10、安裝pip: ./python3 get-pip.py
11、將該python-build新增到環境變數,設定為build主機上預設的python: export PATH=/home/python-build/bin:$PATH
12、安裝Cython: pip3 install Cython
13、測試:python3
1、解壓原始碼包:tar xvf Python-3.5.2.tgz
2、改名:mv Python-3.5.2 python-3.5.2-target
3、cd python-3.5.2-target
4、建立資料夾:mkdir /home/python-target
5、將之前準備的openssl-targer、zlib-targer、cytpes-targer的標頭檔案和連結庫複製到/home/python-targer
cp -rfp /home/zlib-1.2.11-target/zlib-target/* /home/python-target/
cp -rfp /home/libffi-3.2.1-target/libffi-target/* /home/python-target/
cp -rfp /home/openssl-1.0.2g-target/openssl-target/* /home/python-target/
6、設定CFLAGS: CFLAGS="-I/home/python-target/include -I/home/python-target/include/python3.5m -L/home/python-target/lib"
7、設定LDFLAGS: LDFLAGS="-L/home/python-target/lib"
8、vim Modules/Setup.dist
9、設定編譯環境:注意這裡我為了方便看,手動的給每個引數換行了,實際使用中不應該換行的
./configure CC=arm-buildroot-linux-gnueabihf-gcc
CXX=arm-buildroot-linux-gnueabihf-g++
AR=arm-buildroot-linux-gnueabihf-ar
RANLIB=arm-buildroot-linux-gnueabihf-ranlib
--host=arm-buildroot-linux-gnueabihf
--build=x86_64-linux-gnu
--target=arm-buildroot-linux-gnueabihf
--disable-ipv6
ac_cv_file__dev_ptmx=yes
ac_cv_file__dev_ptc=yes
--prefix=/home/python-target
--without-ensurepip
10、編譯:make HOSTPYTHON=/home/python-build/bin/python3 HOSTPGEN=/home/python-3.5.2-build/Parser/pgen
11、執行:make install HOSTPYTHON=/home/python-build/bin/python3
目前位置我們就在build主機上已經編譯好了python-build和python-target
1、在build主機上使用python-build搭建python-target的虛擬環境,然後再虛擬環境中打包python-target的第三方庫,這裡以numpy為例:因為numpy是需要經過交叉編譯才能使用的。
2、cd /home/python-build/bin
3、安裝crossenv: ./pip3 install crossenv
4、使用crossenv代表python-target的虛擬環境:./python3 -m crossenv --without-pip /home/python-target/bin/python3 cross_venv
5、cd cross_venv/cross/bin
6、啟用虛擬環境:source activate
7、curl https://bootstrap.pypa.io/pip/3.5/get-pip.py -o get-pip.py -k
8、./python3 get-pip.py
9、在cross_venv這個虛擬環境中的安裝Cython:./pip3 install Cython
10、建立資料夾用來存放編譯後的第三方:mkdir /home/target_lib
11、建立requestments.txt:vim requirements.txt 裡面寫上numpy
12、交叉編譯第三方庫成為.whl格式的安裝包:./pip3 wheel --wheel-dir /home/target_lib -r requirements.txt
13、驗證:cd /home/target_lib
14、注意,這裡我們使用crossenv交叉編譯後的numpy第三方庫的字尾是linux_arm,而我們的目標板子是armv7l的,所以這裡我們要手動的將
numpy-1.18.5-cp35-cp35m-linux_arm.whl改為numpy-1.18.5-cp35-cp35m-linux_armv7l.whl。不然會報錯。這個坑,一直坑了我一個月的時間,嘗試了很多方法,不知道是編譯鏈的問題,還是編譯過程的問題。將交叉編譯後的numpy的.whl檔案移植到目標板子的中,總是報錯,突然靈光一閃,就手動改個名字,居然可以了,這坑簡直是巨坑,坑了一個月的時間。
將編譯好的python-target打包 和 numpy-1.18.5-cp35-cp35m-linux_arm.whl(先不改名,移植到目標板子上在改名)移植到目標板子上
1、壓縮python-target: tar cvf python-target.tar python-target
2、通過ftp工具,將python-target.tar和numpy-1.18.5-cp35-cp35m-linux_arm.whl ,移植到目標板子的/home下
3、解壓python-target: tar xvf python-target.tar
4、cd /home/python-target/bin
5、驗證在目標板子上執行python3
6、驗證交叉編譯的第三方
1、先下載pip: curl https://bootstrap.pypa.io/pip/3.5/get-pip.py -o get-pip.py -k
2、安裝pip: ./python3 get-pip.py
3、設定pip源
a、mkdir ~/.pip
b、vi ~/.pip/pip.conf
c、新增如下程式碼
[global] index-url = https://pypi.tuna.tsinghua.edu.cn/simple
trusted-host = pypi.tuna.tsinghua.edu.cn
4、驗證pip
5、通過pip安裝未改名的numpy第三方庫:這是會報錯:numpy-1.18.5-cp35-cp35m-linux_arm.whl is not a supported wheel on this platform.
6、改名: mv /home/numpy-1.18.5-cp35-cp35m-linux_arm.whl /home/numpy-1.18.5-cp35-cp35m-linux_armv7l.whl
7、重新安裝驗證:
到此python3及python需要的第三方庫,類似numpy這樣需要交叉編譯的第三方庫就完成了!其中其他庫不一定都是完全一樣的,但是大致流程是一樣的可以參考借鑑。