Python3 交叉编译 numpy pandas scipy scikit-learn

1. 概述

由于需要将Python3.7 和一些软件包交叉编译到 armv7 平台硬件,如果是arm64位的系统,很多包都有预编译好的版本,可直接下载。本文主要在基于 crossenv(https://github.com/benfogle/crossenv)环境下交叉编译。

2. 编译环境搭建

  • 创建编译环境路径 /home/ym/python-build,创建 /home/ym/python-build/install路径用于安装主机编译后的python, 创建路径/home/ym/python-build/install-arm用于安装交叉编译的python。

  • 下载python3 源码到 home/ym/python-build, 并解压,下载路径https://www.python.org/ftp/python/3.7.2/Python-3.7.2.tar.xz

  • 编译主机安装 Docker 环境,不同的linux发行版本安装方式不同, Debian -> apt, Redhat -> yum,我安装的版本是

  • 在编译路径下创建一个 Dockerfile 文件:

    FROM ubuntu:18.04
    ENV TZ=Asia/Shanghai
    ENV LANG=en_US.UTF-8

    RUN sed -i -e 's|archive.ubuntu.com|mirrors.tuna.tsinghua.edu.cn|g' \
    -e 's|security.ubuntu.com|mirrors.tuna.tsinghua.edu.cn|g' \
    /etc/apt/sources.list

    RUN apt update && apt install -y vim curl proxchains gcc \
    build-essential crossbuild-essential-armhf \
    libsdl1.2-dev xterm mesa-common-dev zstd liblz4-tool libffi-dev \
    cmake libssl-dev bc device-tree-compiler flex bison libncurses-dev
    lzma liblzma-dev libbz2-dev gfortran libopenblas-dev liblapack-dev
    gfortran-arm-linux-gnueabihf

    RUN rm -rf /etc/apt/apt.conf.d/docker-clean

    RUN mkdir -p /root/project
    WORKDIR /root
    CMD ["/bin/bash"]

  • 基于 Dockerfile 构建容器

    docker build -f Dockerfile -t ubuntu:python .

  • 运行容器,将编译路径python-build映射至容器路径/root/project路径下:

    docker run -v /home/ym/python-build/:/root/project -it ubuntu:python

3. 编译主机版本 python

  • 进入python源码目录

    ./configure --prefix=/root/project/install && make && make install

4. 交叉编译目标板 python

  • 交叉编译 openssl

    Adapted from https://github.com/japaric/cross

    set -ex

    INSTALL_DIR=/root/project/openssl
    mkdir -p $INSTALL_DIR
    main() {

      local version=1.1.1l
      local os=linux-armv4
      local triple=arm-linux-gnueabihf-
      local sysroot=$INSTALL_DIR
    
      local dependencies=(
          ca-certificates
          curl
          m4
          make
          perl
      )
    
      # NOTE cross toolchain must be already installed
      apt-get update
      for dep in ${dependencies[@]}; do
          if ! dpkg -L $dep; then
              apt-get install --no-install-recommends -y $dep
          fi
      done
    
      td=$(mktemp -d)
    
      pushd $td
      [ -e ./openssl-$version.tar.gz ] || {
          curl -L https://www.openssl.org/source/openssl-$version.tar.gz -o ./openssl-$version.tar.gz
      }
      tar --strip-components 1 -xz -f ./openssl-$version.tar.gz
    
      AR=${triple}ar CC=${triple}gcc ./Configure \
        --prefix=${sysroot}/usr \
        --openssldir=${sysroot}/usr \
        shared \
        no-asm \
        $os \
        -fPIC \
        ${@:4}
      make -j$(nproc)
      make install_sw
      
      # clean up
    
      popd
    
      rm -rf $td
      #rm $0
      #cp /usr/local/arm/usr/lib/pkgconfig/* /usr/share/pkgconfig/
    

    }

    main "${@}"

  • 交叉编译 bzip2, zlib, lzma 这些包会被python内建模块识别调用,并被 pandas 所依赖,同时需要把 zlib, lzma的动态库拷贝到目标板内核库的路径下。

  • lzma 下载地址https://xz.tukaani.org/xz-utils/#releases

  • 交叉编译libffi, 该包与 python ctypes 模块关联,注意交叉编译完成后需要将安装后的内容拷贝到交叉工具链的路径下(cp -rfp libffi/* /usr/arm-linux-gnueabihf/),并在 Python 配置选项中指定--with-system-ffi参数,交叉编译时 Python 自动构建 ctypes 模块,测试发现通过 LIBS 变量指定不会生效。

  • 创建一个 config.site 文件:

    ac_cv_file__dev_ptc=no
    ac_cv_buggy_getaddrinfo=no
    ac_cv_file__dev_ptmx=no

  • 清除 python 源码相关环境(make distclean),运行下面脚本交叉编译目标版本 python-target(armv7),安装到路径/root/project/install-arm

    #!/bin/sh
    CROSS_COMPILE=arm-linux-gnueabihf
    export CC=CROSS_COMPILE-gcc export CXX=CROSS_COMPILE-g++
    export AR=CROSS_COMPILE-ar export STRIP=CROSS_COMPILE-strip
    export LD=CROSS_COMPILE-ld export RANLIB=CROSS_COMPILE-ranlib
    export READELF=CROSS_COMPILE-readelf export PATH=PATH:/root/project/install/bin
    export CONFIG_SITE=/root/project/config.site

    cd Python-3.7.2 && ./configure --enable-optimizations --with-openssl=/root/project/openssl/usr --with-system-ffi
    LDFLAGS="-L/root/project/bzip2-1.0.8 -L/root/project/zlib/lib -L/root/project/lzma/lib"
    LIBS="-lbz2 -lz -llzma"
    --host=arm-linux-gnueabihf
    --build=x86_64-linux-gnu
    --target=arm-linux-gnueabihf
    --disable-ipv6
    --without-pydebug
    --without-dtrace
    --prefix=/root/project/install-arm
    make -j4 && make install

到此如果编译过程中没有出错的话,交叉编译后的 python 应该可以直接在 目标板上工作了,在目标板上创建一个 python 目录,将 install-arm 中的文件全部拷贝至该路径中,构建一个软连接 ln -sf /python/bin/python3.7 /usr/bin/python,然后在控制运行 python 查看是否正常:

5. 通过 crossenv 交叉编译 numpy,pandas

  • 如果只是想交叉编译 numpy 或者 pandas, 看到这一小节就行了,其中pandas 依赖 numpy。如果还要编译其他包请用第六节脚本编译的方法交叉编译 numpy.

    1. 在主机环境下安装 crossenv
      cd /root/project/install/bin
      ./pip3 install crossenv
    2. 使用crossenv创建 python-target 编译的虚拟环境
      ./python3 -m crossenv /root/project/install-arm/bin/python3.7 cross_venv
    3. 激活虚拟环境
      . cross_venv/bin/activate
      注意没有代理可使用国内镜像如 pip install xx -i http://pypi.douban.com/simple --trusted-host pypi.douban.com
    4. 编译安装 numpy
      build-pip -v install numpy
      pip -v install numpy
    5. 编译安装 pandas
      build-pip -v install pandas
      pip -v install pandas
      注意pandas 依赖 dateutil,dateutil会使用目标板的时区信息,请检查目标板是否有 /usr/share/zoneinfo 和 /etc/localtime 文件,没有可能在导入 pandas 包时会报错,可将交叉编译后的 dateutil/zoneinfo/dateutil-zoneinfo.tar.gz 解压至 /usr/share/zoneinfo中,并创建软连接 ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
      编译成功后可在 cross_venv/cross/lib/pythonVERSION/site-packages 路径下查看编译完后的包
      其中包主要分两类:
      1). package -> 对应包的实际文件
      2). package-VERSION-dist-info -> 包的信息
    6. 将对应的 package 拷贝到目标板 path/to/python/lib/pythonVERSION/site-package 路径下
  • 这里导入包的时候,可能会报一些库文件缺失,需要把对应的库拷贝到目标板库/lib的路径下。

6. 通过 crossenv 交叉编译 scikit-learn

  • 这里先列一下依赖项

  • scikit-learn:

    • NumPy
    • SciPy :
      • NumPy
      • openblas
    • joblib
    • threadpoolctl
  • 由于 SciPy 强依赖 numpy 中 openblas 包,openblas 是一个数学运算库,可用于加速 numpy 运算,如果只是单纯编译 numpy,那么 openblas 是一个可选项,但是如果需要编译 SciPy,openblas 就是一个强依赖项了,但是 openblas 是由 Fortran 语言编写,直接用 crossenv 环境编译会报错,下面用一个脚本来交叉编译 numpy 和 SciPy,编译之前,先用交叉工具链编译 openblas,并在虚拟环境中 build-pip/pip安装 Cython < 3 版本:

    1. 确认以下几个包已安装
      apt-get install gfortran libopenblas-dev liblapack-dev -y
    2. 安装 fortran 交叉编译工具
      apt-get install gfortran-arm-linux-gnueabihf
    3. 交叉编译 OpenBLAS-0.3.22
      cd OpenBLAS-0.3.22 && make TARGET=ARMV7 HOSTCC=gcc BINARY=32 CC=arm-linux-gnueabihf-gcc FC=arm-linux-gnueabihf-gfortran
      make TARGET=ARMV7 PREFIX=/root/project/openblas install
  • 交叉编译 scipy 脚本

    #!/bin/bash

    ####################

    Script to build numpy and scipy wheels for ARM.

    set -ex

    OUTPUT=PWD/output WORKING=OUTPUT/build
    if [ ! -d "$WORKING" ];then
    mkdir -p $WORKING
    fi

    GFORTRAN=arm-linux-gnueabihf-gfortran
    OPENBLAS_INSTALL_DIR=$PWD/openblas

    BUILD_PYTHON=PWD/install/bin/python3 HOST_PYTHON=PWD/install-arm/bin/python3

    NUMPY_URL=https://files.pythonhosted.org/packages/45/b7/de7b8e67f2232c26af57c205aaad29fe17754f793404f59c8a730c7a191a/numpy-1.21.6.zip
    SCIPY_URL=https://files.pythonhosted.org/packages/a7/5c/495190b8c7cc71977c3d3fafe788d99d43eeb4740ac56856095df6a23fbd/scipy-1.3.3.tar.gz
    NUMPY_VERSION=${NUMPY_URL##/}
    if [[ NUMPY_VERSION =~ .zip* ]];then NUMPY_DIR={NUMPY_VERSION%%.zip
    }
    elif [[ NUMPY_VERSION =~ .tar.gz* ]];then NUMPY_DIR={NUMPY_VERSION%%.tar.gz*}
    fi
    SCIPY_VERSION={SCIPY_URL##*/} SCIPY_DIR={SCIPY_VERSION%%.tar.gz*}

    PYPI_MIRROR="-i http://pypi.douban.com/simple --trusted-host pypi.douban.com"
    ################################################################

    Set up crossenv

    BUILD_PYTHON -m pip install crossenv CROSS_VENV=PWD/install/bin/cross_venv
    if [ ! -d "$CROSS_VENV" ];then
    $BUILD_PYTHON -m crossenv $HOST_PYTHON $CROSS_VENV
    fi
    . $CROSS_VENV/bin/activate
    pip install wheel $PYPI_MIRROR

    BUILD_SITE=PWD/install/bin/cross_venv/build/lib/python3.7/site-packages CROSS_SITE=PWD/install/bin/cross_venv/cross/lib/python3.7/site-packages

    ################################################################

    Host-numpy

    Install so we get the libnpymath.a in the right place.

    if [ ! -f "$NUMPY_VERSION" ];then
    curl -OL NUMPY_URL fi if [ ! -d "NUMPY_DIR" ];then
    if [[ $NUMPY_VERSION =~ .zip* ]];then
    unzip $NUMPY_VERSION
    elif [[ $NUMPY_VERSION =~ .tar.gz* ]];then
    tar xf $NUMPY_VERSION
    fi
    fi
    cd $NUMPY_DIR
    cat > site.cfg <<EOF
    [openblas]
    libraries = openblas
    library_dirs = $OPENBLAS_INSTALL_DIR/lib
    include_dirs = OPENBLAS_INSTALL_DIR/include extra_link_args = -lgfortran EOF F90=GFORTRAN cross-python setup.py install
    F90=$GFORTRAN cross-python setup.py bdist_wheel
    cd ..

    ################################################################

    Build-numpy. Need to patch after install.

    NUMPY_PIP_VERSION={NUMPY_DIR##*-} build-pip install -v numpy==NUMPY_PIP_VERSION PYPI_MIRROR INI=(find BUILD_SITE -name 'npymath.ini') LIBDIR=(find CROSS_SITE -path '*/numpy/core/lib') INCDIR=(find $CROSS_SITE -path '*/numpy/core/include')

    cat > $INI <<EOF
    [meta]
    Name=npymath
    Description=Portable, core math library implementing C99 standard
    Version=0.1

    [variables]

    Force it to find cross-build libs when we build scipy

    libdir=LIBDIR includedir=INCDIR

    [default]
    Libs=-L${libdir} -lnpymath
    Cflags=-I${includedir}
    Requires=mlib

    [msvc]
    Libs=/LIBPATH:${libdir} npymath.lib
    Cflags=/INCLUDE:${includedir}
    Requires=mlib
    EOF

    #################################################################

    host-scipy

    if [ ! -f "$SCIPY_VERSION" ];then
    curl -OL SCIPY_URL fi if [ ! -d "SCIPY_DIR" ];then
    tar xf $SCIPY_VERSION
    fi
    cd $SCIPY_DIR
    cat > site.cfg <<EOF
    [openblas]
    libraries = openblas
    library_dirs = $OPENBLAS_INSTALL_DIR/lib
    include_dirs = $OPENBLAS_INSTALL_DIR/include
    extra_link_args = -lgfortran
    EOF

    F90=GFORTRAN python setup.py bdist_wheel F90=GFORTRAN cross-python setup.py install
    cd ..

  • 交叉编译 scikit-learn: :

  • SciPy -> 先前利用脚本已经完成了交叉编译,现在使用 build-pip 安装 build 版本:

    build-pip install -v scipy=1.3.3 -i http://pypi.douban.com/simple --trusted-host pypi.douban.com

  • joblib -> build-pip/pip 直接安装没有其他依赖

    build-pip install joblib==0.11 -i http://pypi.douban.com/simple --trusted-host pypi.douban.com
    pip install joblib==0.11 -i http://pypi.douban.com/simple --trusted-host pypi.douban.com

  • threadpoolctl -> build-pip/pip 直接安装没有其他依赖

    build-pip install threadpoolctl==2.0.0 -i http://pypi.douban.com/simple --trusted-host pypi.douban.com
    pip install threadpoolctl==2.0.0 -i http://pypi.douban.com/simple --trusted-host pypi.douban.com

  • 安装 scikit-learn:

    build-pip install -v scikit-learn==1.0.2 --no-build-isolation -i http://pypi.douban.com/simple --trusted-host pypi.douban.com
    pip install -v scikit-learn==1.0.2 --no-build-isolation -i http://pypi.douban.com/simple --trusted-host pypi.douban.com

至此交叉编译全部完成,将对应的包拷贝到目标板查看

相关推荐
肖永威27 分钟前
CentOS环境上离线安装python3及相关包
linux·运维·机器学习·centos
SelectDB技术团队40 分钟前
兼顾高性能与低成本,浅析 Apache Doris 异步物化视图原理及典型场景
大数据·数据库·数据仓库·数据分析·doris
nuclear20111 小时前
使用Python 在Excel中创建和取消数据分组 - 详解
python·excel数据分组·创建excel分组·excel分类汇总·excel嵌套分组·excel大纲级别·取消excel分组
Lucky小小吴1 小时前
有关django、python版本、sqlite3版本冲突问题
python·django·sqlite
GIS 数据栈1 小时前
每日一书 《基于ArcGIS的Python编程秘笈》
开发语言·python·arcgis
爱分享的码瑞哥2 小时前
Python爬虫中的IP封禁问题及其解决方案
爬虫·python·tcp/ip
statistican_ABin2 小时前
R语言数据分析案例45-全国汽车销售数据分析(可视化与回归分析)
数据挖掘·数据分析
傻啦嘿哟3 小时前
如何使用 Python 开发一个简单的文本数据转换为 Excel 工具
开发语言·python·excel
B站计算机毕业设计超人3 小时前
计算机毕业设计SparkStreaming+Kafka旅游推荐系统 旅游景点客流量预测 旅游可视化 旅游大数据 Hive数据仓库 机器学习 深度学习
大数据·数据仓库·hadoop·python·kafka·课程设计·数据可视化
IT古董3 小时前
【人工智能】Python在机器学习与人工智能中的应用
开发语言·人工智能·python·机器学习