This is a brief guide about how to make a cross compilation of Python 3.8.10 on a generic ARM processor.
Cross compilation is the process of building an executable program for a platform other than the one on which the compiler is running. In our case, the cross-compiled program will be the python interpreter.
Python is a high-level, interpreted programming language that is widely used for a variety of purposes, including web development, data analysis, artificial intelligence, and scientific computing. It is known for its simplicity, readability, and flexibility, which make it a popular choice for beginners and experienced programmers alike.
In the field of embedded systems, Python can be used to write software for small, single-board computers that are used to control and monitor a variety of devices. Python is often used in embedded systems because it is easy to learn and use, and it has a large and active community of developers who contribute libraries and tools that can be used to interface with hardware and other devices.
2. Dependencies- NL668-LA Module
- Linux Operating System (can be a virtual machine or WSL)
- ADB (Follow the First steps with NL668)
- GCC Cross toolchain
- LIBC (source code)
- Python 3.8.10 (source code)
Let's create some folders to organize our workflow:
mkdir -pv $HOME/cross-compilation-tutorial/{source,python}
4. Installing software dependenciesIt's necessary to install some dependencies before we go and make sure that everthing is working fine.
4.1 ADB - Android Debugger BridgeThe full instructions for installing ADB are provided in Victor's article: NL668-LA OpenLinux Quickstart.
4.2. GCC Cross toolchainWe are going to use the gcc-arm-linux-gnueabi
toolchain, provided in apt repository.
sudo apt-get update
sudo apt-get install -y gcc-arm-linux-gnueabi g++-arm-linux-gnueabi libc6-armel-cross libc6-dev-armel-cross binutils-arm-linux-gnueabi
OBS: You can use other cross-toolchain like Linaro if you want too. It is only necessary to specify the latter where the components of the toolchain are.
4. Getting the source codeAs we're doing a installation for a generic Linux system (in case, the OpenLinux) we are going to cross compile our dependencies from source code.
4.1 LIBCThe libc (short for "library C") is a library that provides the functions and data structures required for C programs to run on a particular operating system. It is a fundamental component of any operating system and is responsible for providing a wide range of functionality, including input/output, memory management, and threading.
libc is typically implemented as a set of dynamic libraries that are loaded at runtime when a C program is executed. It provides a large number of functions that can be called from C programs, including functions for file manipulation, networking, and system calls.
In short, libc provides the basic API between the Kernel and the rest of OS.
On the NL668 OpenSDK module, we already have a libc, but it is outdated, so we have to use a different one in order to be able to run Python.
There are many options for libc, such as MUSL, UCLIBC-NG, and EGLIBC, but today our focus is on GLIBC (the "GNU C Library"). Let's donwload it and extract:
cd $HOME/cross-compilation-tutorial/source
wget https://ftp.gnu.org/gnu/glibc/glibc-2.31.tar.gz
tar -xvf glibc-2.31.tar.gz
4.2 Python 3.8.10Let's donwload Python source code from FTP server and extract:
cd $HOME/cross-compilation-tutorial/source
wget https://www.python.org/ftp/python/3.8.10/Python-3.8.10.tar.xz
tar -xvf Python-3.8.10.tar.xz
5. Cross compilingGenerally, the process of compiling some application from source code contains three steps: the configure
process, compilation and installation.
Let's go inside GLIBC source code and analyse it:
cd $HOME/cross-compilation-tutorial/source/glibc-2.31
ls
You will see somethinhg like:
Let's focus on these three files that I marked on red:
configure
: This is a large script file responsible for the first step in the process of "installing from source code." This file prepares the Makefile for the compilation and installation processes. It accepts command-line arguments, which we can use to customize our software preferences, such as which functionalities we want or do not want, as well as important configuration settings such as the specification of our cross-toolchain.INSTALL
: A document with informations about installation and the descriptions of command line argument that can be passed to configure script. It's possible to run./configure --help
to have a brief description of the arguments too.README
: The description and some informations about the project.
Before configuring, GLBIC requires a diferent folder to hold configuration files, so:
cd $HOME/cross-compilation-tutorial/source/
mkdir config_glibc && cd config_glibc
Another important thing to mention, in the specific case of GLIBC, it's necessary to install the library in the place where it's going to be used, as I'm going to use from the folder /data/usr
on my embedded device I need to make this folder on my personal computer (another approach would be compile inside a container and make this /data/usr
on the container, but I'm going to let this to other day)
# On my personal computer:
cd /
sudo mkdir -p /data/usr
sudo chmod 777 /data/usr
Now, let's run the configuration script! We will use some environment variables to tell the script where the installation folder and the target folder (on the NL668 module) are located. It is also necessary to specify flags regarding our cross-toolchain. Since we are doing cross-compilation, we will specify the build argument as our desktop architecture and the host as the NL668 architecture.
export TARGET_DIR=/data/usr
../glibc-2.31/configure --prefix=$TARGET_DIR --exec-prefix=$TARGET_DIR \
--build=i686-pc-linux-gnu --host=arm-linux-gnueabi \
STRIP="/usr/bin/arm-linux-gnueabi-strip " \
AR="/usr/bin/arm-linux-gnueabi-ar " \
LD="/usr/bin/arm-linux-gnueabi-ld " \
CC="/usr/bin/arm-linux-gnueabi-gcc -march=armv7-a -mfloat-abi=softfp -mfpu=neon" \
CXX="/usr/bin/arm-linux-gnueabi-g++ -march=armv7-a -mfloat-abi=softfp -mfpu=neon" \
CFLAGS="-s -O3 -fexpensive-optimizations -frename-registers -fomit-frame-pointer " \
CXXFLAGS="-s -O3 -fexpensive-optimizations -frename-registers -fomit-frame-pointer " \
LDFLAGS="-Wl,--rpath=$TARGET_DIR/lib -Wl,--dynamic-linker=$TARGET_DIR/lib/ld-linux.so.3 -Wl,-O3 -Wl,--hash-style=gnu -Wl,--as-needed " \
CPP="/usr/bin/arm-linux-gnueabi-gcc -E -march=armv7-a -mfloat-abi=softfp -mfpu=neon"
About the arguments:
prefix
: Architecture independent files.exec-prefix
: Architecture dependent files.build
: It is the CPU architecture that you are building the project on. In this case, i686-pc-linux-gnu represents the x86_64 architecturehost
: The CPU architecture that you are building for. It is, what arch is going to run these binaries.
About the environment variables (VAR=VALUE argments)
STRIP
: Toolchain utility (Binutils) to remove symbols from binary (it reduces the binary final size and also speeds up it's performance).AR
: Toolchain utility (Binutils) to manipulate static library files (.a files).LD
: Stands for "Linker directory".CC
andCXX
: Respectively, the C Compiler and C++ compiler. Also some flags to specify the CPU properties.CFLAGS
andCXXFLAGS
: Respectively, C and C++ compiler flags. It's for specify optimization level (O3)LDFLAGS
: Flags passed to linker. In that case, with rpath and dynamic-linker we are specifying the location where to look for the CLIB interpreterld-linux.so.3
.CPP
: Stands for "C pre processor".
If everything is okay and no error message appears on the screen, you just need to compile the software (this step takes a while, so you might want to grab a cup of coffee):
make -j4
Compilation may take a while but after it finishes with no error message at the end, you can run install to put all the files on the INSTALL_DIR
directory.
make install
If everthing goes okay, you will see the message that is something like:
make[1]:Leaving directory '/home/<user>/cross-compilation-tutorial/source/glibc-2.31'
And no error message. We will be able to see the folders inside the our python directory:
cd $HOME/cross-compilation-tutorial/python
ls
Now it's time to cross compile Python, the process is pretty much similiar with GBLIC. After be downloaded and extracted the source code as described in section 4.2 lets take a look inside Python folder:
cd $HOME/cross-compilation-tutorial/source/Python-3.8.10
ls
Now it's time to run our configure
script.
export TARGET_DIR=/data/usr
./configure --host=arm-linux-gnueabi \
--build=i686-pc-linux-gnu \
--prefix=$TARGET_DIR \
--exec-prefix=$TARGET_DIR \
--enable-optimizations \
--disable-ipv6 \
--without-system-ffi \
--without-pydebug \
--without-doc-strings \
--without-dtrace \
ac_cv_file__dev_ptmx=no \
ac_cv_file__dev_ptc=no \
STRIP="/usr/bin/arm-linux-gnueabi-strip " \
LD="/usr/bin/arm-linux-gnueabi-ld " \
CC="/usr/bin/arm-linux-gnueabi-gcc -march=armv7-a -mfloat-abi=softfp -mfpu=neon " \
CXX="/usr/bin/arm-linux-gnueabi-g++ -march=armv7-a -mfloat-abi=softfp -mfpu=neon " \
CFLAGS="-O3 -fexpensive-optimizations -frename-registers -fomit-frame-pointer " \
CXXFLAGS="-O3 -fexpensive-optimizations -frename-registers -fomit-frame-pointer " \
LDFLAGS="-Wl,--strip-all -Wl,-start-group -ldl -lpthread -lm -lrt -Wl,-end-group -Wl,--rpath=$TARGET_DIR/lib/ -Wl,--dynamic-linker=$TARGET_DIR/lib/ld-linux.so.3 -Wl,-O3 -Wl,--hash-style=gnu -Wl,--as-needed " \
CPP="/usr/bin/arm-linux-gnueabi-gcc -E -march=armv7-a -mfloat-abi=softfp -mfpu=neon "
Let's take a look at the arguments:
host
,build
,prefix
andexec-prefix
: Exactaly the same as in GLIBC.enable-optimizations
: As the name sugest, it enables some optimizations.disable-ipv6
: For now, we are building the Python interpreter with just IPV4 support (becauseipv6
support has some other dependencies), but latter we can come back here and activate.without-xxx
: Disabling some functionalities to guaranties reduced size and compatibility (avoiding the lack of some dependencies)ac_cv_file__dev_ptmx
andac_cv_file__dev_ptc
: These variables are used by the Python build process to determine whether the target platform has/dev/ptmx
and/dev/ptc
device files, respectively. These device files are used for controlling terminal multiplexing and terminal emulator functionality, and are typically found on Unix-like systems. Since we are cross compiling it's necessary to deactivate to avoid Python tries to check in our build system.STRIP
,LD
,CC
,CXX
,CFLAGS
,CXXFLAGS
,CPP
: Same as in GLIBC.
LDFLAGS: The diference now is that I'm adding a command to strip all binary files (-Wl,--strip-all) and linking a group of libraries beeing there: dynamic linker library (-ldl), the pthread (-lpthread) library is a library that provides support for POSIX threads and the math library (-lm).
if everthing works fine, you will see the message at the end:
creating Modules/Setup.local
creating Makefile
Now just compile with the make command:
make -j4
After that, if any error occurs you can just run:
make install
And Python will be installed in our temporary folder.
You can find the Python binary on /data/usr/bin/python3.8
:
cd /data/usr/python/bin
file python3.8
As you can see, we have an ARM, EABI5 binary with the interpreter(ld-linux.so.3) in our custom directory. On systems that use the ELF binary format (like our case), the C standard library is typically provided by the libc.so.6 file. This file (and others like it) is typically loaded and linked to C programs by the dynamic linker/loader (ld-linux.so.3) at runtime. As the name suggests, the "interpreter" ld-linux.so.3 decides when the other shared libraries are loaded.
6. Installing Python on NL668We installed Python in a temporary folder on our build machine, now it's time to transfer it to the NL668 Openlinux filesystem. A quick note: the important folders that contain the files needed to run the Python interpreter on the NL668 board are just the lib
, include
and bin
folders. So let's put these folders in a tarball and compress (using gzip) it to send.
cd ~/cross-compilation-tutorial/python
tar -cvhzf py.tar.gz bin lib include
Now we just have to send the py.tar.gz
using ADB to the correct folder:
adb push py.tar.gz /data/usr
Them decompress.
adb shell
cd /data/usr
tar -xvhf py.tar.gz
So remove the compressed file.
rm py.tar.gz
exit
About the tarballs file, if you explore the folders of Python application you may have noticed a bunch of symlinks, in order to not lose these symlinks and the aplication works, we are using the -h
(the --dereference
argument as documented here) to have our symlinks transformed in actual files.
Them just for facility let's create a symlink to /usr/bin
folder:
adb shell ln -s /data/usr/bin/python3.8 /usr/bin/python3.8
7. TestingNow let's make a test of Python:
In this article, we saw many important concepts of cross-compilation, such as cross-compiling a LIBC to use in our system, how to interpret documentation, and understand the parameters of a configure file. Keep in mind that many of these concepts can be used to cross-compile any other open-source application. I hope this tutorial was helpful for you. Let me know if you have any questions or comments. That's all, happy hacking!
Comments
Please log in or sign up to comment.