Here are some notes I took when building the cryptography 36.0.2 python module in Chaquopy, which was needed to run mitmproxy 8.0.0. I hope this will be useful to integrate it in the officially supported packages.
Overview
mitmproxy 8.0.0 depends on the new cryptography 36.x module, for which rust is now mandatory.
cryptography uses setuptools_rust to build native shared objects and pyo3 to invoke them from python.
Here the relevant part of the dependency tree:
mitmproxy
cryptography
setuptools_rust
rust
pyo3
python
Other than rust setup for cross-compilation, the problematic part is that cryptography uses pyo3 v0.15.1, which requires a python interpreter to be cross-compiled for the target architecture (and it looks for _sysconfigdata* in the python lib folder). In theory, both pyo3 and cryptography support building abi3 modules, which work regardless of the specific python version and could
be built without an existing python interpreter. However, this possibility is only added in pyo3 0.16.4, which is not currently supported by cryptography. See PyO3/pyo3#2310 for more details.
So, in order to build the cryptography module, we currently need:
- Install rust and configure it for the cross compilation
- Cross compile python
With some tricks, it's possible to compile only the core of python without the need to also cross-compile its dependencies. In fact, the pyo3 shared modules of cryptography will be linked against the soname used by python library shipped with chaquopy.
Preparing the build environment
The following instructions are meant for building on an archlinux host without docker. Install requirements (most requirements not listed here, see target/Dockerfile)
Copy the Android toolchains used by chaquopy:
docker build -t chaquopy-base -f base.dockerfile .
docker build -t chaquopy-target target
# replace 62b7d2f98872 with the chaquopy-target container ID
cd target
docker cp 62b7d2f98872:/root/target/toolchains toolchains
To avoid polluting the build machine, use an overlayfs to install all the stuff:
cd /home/emanuele/src/build-wheel
mkdir build
cd build
rm -rf sysroot
mkdir -p sysroot overlay workdir
# NOTE: to refresh the lowerdir when mounted, run "sudo mount -o remount overlay"
sudo mount -t overlay overlay -o lowerdir=/,upperdir=./sysroot,workdir=./workdir ./overlay
sudo arch-chroot ./overlay
Build and install the exact python version required by Chaquopy:
version=3.8.7
cd /home/emanuele/src/build-wheel/build
wget https://www.python.org/ftp/python/$version/Python-$version.tgz
tar -xf Python-$version.tgz
cd Python-$version
./configure --prefix=/usr
make -j $(nproc)
make install
# verify
python3.8 --version
Install rust and libraries required for cross-compilation in rust
pacman -Rs rustup rust
curl https://sh.rustup.rs -sSf | sh -s -- -y
source "$HOME/.cargo/env"
# required for cross-compilation
rustup target add aarch64-linux-android
rustup target add arm-linux-androideabi
rustup target add i686-linux-android
rustup target add x86_64-linux-android
# Verify installed cross-compilation libraries
rustc --print target-list | grep android
# install requirements, they will be needed to build cryptography
cd /home/emanuele/src/build-wheel/server/pypi
pip3.8 install -r requirements.txt
Building for an ABI
These steps must be performed for each Android ABI.
First select the target ABI:
# only pick the target ABI of choice
export ARCH="armeabi-v7a" CPU="arm" TOOL_PREFIX="arm-linux-androideabi" TOOL_CFLAGS="-march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16 -mthumb" TOOL_LDFLAGS="-march=armv7-a -Wl,--fix-cortex-a8"
export ARCH="arm64-v8a" CPU="aarch64" TOOL_PREFIX="aarch64-linux-android" TOOL_CFLAGS="" TOOL_LDFLAGS=""
export ARCH="x86" CPU="x86" TOOL_PREFIX="i686-linux-android" TOOL_CFLAGS="" TOOL_LDFLAGS=""
export ARCH="x86_64" CPU="x86_64" TOOL_PREFIX="x86_64-linux-android" TOOL_CFLAGS="" TOOL_LDFLAGS=""
Setup the environment for cross compilation:
version=3.8.7
cd /home/emanuele/src/build-wheel/build
# cleanup and setup
rm -rf $ARCH/sysroot/usr $ARCH/Python-$version
mkdir -p $ARCH/sysroot/usr/lib $ARCH/sysroot/usr/include
tar -C $ARCH -xf Python-$version.tgz
TOOLCHAIN=`readlink -f ../target/toolchains`
SYSROOT=`readlink -f $ARCH/sysroot`
# copy headers from the toolchain to the sysroot
cp -r $TOOLCHAIN/$ARCH/sysroot/usr/* $SYSROOT/usr
# (OPTIONAL) extend sysroot with python deps, not actually needed by pyo3
#cp -r deps/OpenSSL-for-Android-Prebuilt/openssl-1.1.1k-clang/include/* $ARCH/sysroot/usr/include
#cp -r deps/libcrypt_0.2-5_aarch64/usr/* $ARCH/sysroot/usr
#...
# Needed to cross compile python (some may not be needed)
export CC="$TOOLCHAIN/$ARCH/bin/$TOOL_PREFIX-gcc"
export CXX="$TOOLCHAIN/$ARCH/bin/$TOOL_PREFIX-g++"
export AR="$TOOLCHAIN/$ARCH/bin/$TOOL_PREFIX-ar"
export AS="$TOOLCHAIN/$ARCH/bin/$TOOL_PREFIX-as"
export LD="$TOOLCHAIN/$ARCH/bin/$TOOL_PREFIX-ld"
export LDSHARED="$TOOLCHAIN/$ARCH/bin/$TOOL_PREFIX-gcc -shared"
export RANLIB="$TOOLCHAIN/$ARCH/bin/$TOOL_PREFIX-ranlib"
export STRIP="$TOOLCHAIN/$ARCH/bin/$TOOL_PREFIX-strip --strip-unneeded"
export NM="$TOOLCHAIN/$ARCH/bin/$TOOL_PREFIX-nm"
export READELF="$TOOLCHAIN/$ARCH/$TOOL_PREFIX/bin/readelf"
export CROSS_COMPILE_TARGET=yes
export CFLAGS="-fPIC -DANDROID --sysroot=$SYSROOT $TOOL_CFLAGS"
export CXXFLAGS="$CFLAGS"
# Flags taken from build-wheel.py
export LDFLAGS="--sysroot=$SYSROOT -Wl,--no-undefined -Wl,--exclude-libs,libgcc.a -Wl,--exclude-libs,libgcc_real.a -Wl,--exclude-libs,libunwind.a $TOOL_LDFLAGS"
Cross compile python
cd $ARCH/Python-$version
./configure --build=x86_64-unknown-linux-gnu --host=$CPU-linux-android --enable-shared --prefix=`readlink -f ../sysroot/usr` \
ac_cv_file__dev_ptmx=no ac_cv_file__dev_ptc=no ac_cv_have_long_long_format=yes ac_cv_buggy_getaddrinfo=no
# patch to support Android API 19
# chaquopy targets API 19 on armv7a/x86, which is not fully supported by python https://bugs.python.org/issue36162
# the API level is specified via the "--target=" arg inside the $TOOL_PREFIX-gcc script
sed -i 's/^#define HAVE_SENDFILE 1$/\/* #undef HAVE_SENDFILE *\//g' pyconfig.h
sed -i 's/^#define HAVE_TRUNCATE 1$/\/* #undef HAVE_TRUNCATE *\//g' pyconfig.h
sed -i 's/^#define HAVE_WCSFTIME 1$/\/* #undef HAVE_WCSFTIME *\//g' pyconfig.h
# build (ignore errors, they occur because of missing dependencies, but we are only interested in libpython and related _sysconfigdata)
make -j $(nproc)
make install
# Patch SONAME to correspond to the chaquopy "libpython3.8.so"
# check with x86_64-linux-android-readelf -Wa $SYSROOT/usr/lib/libpython3.8.so | grep SONAME
mv $SYSROOT/usr/lib/libpython3.8.so{.1.0,}
patchelf --set-soname libpython3.8.so $SYSROOT/usr/lib/libpython3.8.so
Finally, build cryptography (assume rust_cross_compile.patch is applied (see below), which sets PYO3_CROSS_LIB_DIR):
cd /home/emanuele/src/build-wheel/server/pypi
python3.8 ./build-wheel.py --toolchain ../../target/toolchains/$ARCH cryptography
If build successful, you can retrieve the built wheel from outside the overlayfs in the build-wheel/sysroot folder.
build-wheel patches
rust_cross_compile.patch:
--- src-original/setup.py
+++ src/setup.py
@@ -10,6 +10,16 @@ import sys
from setuptools import setup
+# https://doc.rust-lang.org/rustc/codegen-options/index.html
+os.environ["RUSTFLAGS"] = f"-C linker={os.environ['CC']}"
+os.environ["CARGO_BUILD_TARGET"] = os.environ['CHAQUOPY_TRIPLET']
+
+# https://pyo3.rs/v0.15.2/building_and_distribution.html
+os.environ["PYO3_PYTHON"] = f"python{os.environ['CHAQUOPY_PYTHON']}"
+os.environ["PYO3_CROSS_PYTHON_VERSION"] = os.environ['CHAQUOPY_PYTHON']
+os.environ["PYO3_CROSS_LIB_DIR"] = f"{os.environ['RECIPE_DIR']}/../../../../build/{os.environ['CHAQUOPY_ABI']}/sysroot/usr/lib"
+#print(os.environ)
+
try:
from setuptools_rust import RustExtension
except ImportError:
cryptography meta.yaml:
package:
name: cryptography
version: 36.0.2
requirements:
build:
- cffi 1.13.2
- setuptools-rust 1.2.0
host:
- openssl
Add rust to the docker build script
# Install rust and rustup
RUN curl https://sh.rustup.rs -sSf | sh -s -- -y
# Install crates needed for cross compilation (note you need to re-run all the build commands)
# https://rust-lang.github.io/rustup/cross-compilation.html
Run source $HOME/.cargo/env && \
rustup target add x86_64-linux-android && \
rustup target add i686-linux-android && \
rustup target add aarch64-linux-android && \
rustup target add armv7-linux-androideabi
Here are some notes I took when building the cryptography 36.0.2 python module in Chaquopy, which was needed to run mitmproxy 8.0.0. I hope this will be useful to integrate it in the officially supported packages.
Overview
mitmproxy 8.0.0 depends on the new cryptography 36.x module, for which
rustis now mandatory.cryptography uses
setuptools_rustto build native shared objects andpyo3to invoke them from python.Here the relevant part of the dependency tree:
Other than rust setup for cross-compilation, the problematic part is that cryptography uses
pyo3 v0.15.1, which requires a python interpreter to be cross-compiled for the target architecture (and it looks for_sysconfigdata*in the python lib folder). In theory, both pyo3 and cryptography support building abi3 modules, which work regardless of the specific python version and couldbe built without an existing python interpreter. However, this possibility is only added in pyo3 0.16.4, which is not currently supported by cryptography. See PyO3/pyo3#2310 for more details.
So, in order to build the cryptography module, we currently need:
With some tricks, it's possible to compile only the core of python without the need to also cross-compile its dependencies. In fact, the pyo3 shared modules of cryptography will be linked against the soname used by python library shipped with chaquopy.
Preparing the build environment
The following instructions are meant for building on an archlinux host without docker. Install requirements (most requirements not listed here, see
target/Dockerfile)Copy the Android toolchains used by chaquopy:
To avoid polluting the build machine, use an overlayfs to install all the stuff:
Build and install the exact python version required by Chaquopy:
Install rust and libraries required for cross-compilation in rust
Building for an ABI
These steps must be performed for each Android ABI.
First select the target ABI:
Setup the environment for cross compilation:
Cross compile python
Finally, build cryptography (assume
rust_cross_compile.patchis applied (see below), which setsPYO3_CROSS_LIB_DIR):If build successful, you can retrieve the built wheel from outside the overlayfs in the
build-wheel/sysrootfolder.build-wheel patches
rust_cross_compile.patch:cryptography meta.yaml:Add rust to the docker build script