[Rant] Cross Compiling Qt6 and PySide6 for Raspberry Pi 4
2024-06-19
6 min read
This is a rant on my journey of compiling PySide6 for a Raspberry Pi. It's not a tutorial.
A few months ago, I got the idea of creating my own smart home appliances using Raspberry Pis. Existing smart home devices are pretty limited to certain sectors, mostly sensors and lights, so I decided to build something to suit my needs (you should see some projects in a few months if I stop procrastinating de-prioritizing). Why not use ESPhome, you ask? Well, I was (and still am) a complete noob in embedded development, so I just wanted to work on some sort of full-fledged computers for the ease of development.
So I ordered my first Raspberry Pi 4 ever. But I made a stupid mistake: I ordered the 1 GB model. I was not aware of the resource consumption of a Linux desktop environment—even if Raspberry Pi OS uses XFCE, the most lightweight desktop environment out there, it still takes up ~500 MB upon startup. As soon as I opened a browser the whole system froze. Web development was out of the question.
Okay, fine, I said to myself, it's time to learn a native desktop application framework. And the framework I chose was, unsurprisingly, Qt, the most popular desktop development framework out there.
I decided to use the Python bindings instead of writing C++ code. And most importantly, I needed to find a good-looking UI library so that my project wouldn't look like an application from the year 2000. I limited myself to two libraries: QFluentWidgets and PySide6-FluentUI-QML. QFluentWidgets was developed by a Chinese undergrad. It had more components than its competitor but required a $359 license for commercial use and access to the Python binding. Fluent-QML, on the other hand, was under a GPL-3 license and had a (very) immature Python binding that barely worked. After some mental fighting, I chose Fluent-QML because I wasn't sure if I would finish even a single Qt project, and hence didn't want to throw $400 down the drain.
Now that I'd chosen the library, I had one problem: the Python binding, PySide6-Fluent-QML, basically had a dynamically linked library compiled from the C++ base library under the project folder. The author only published the Windows amd64 version of the compiled library, so when I attempted installing it on my Raspberry Pi, not only did the architecture not match, but it was also looking for a non-existent .so
file. Two choices in front of me: either cross-compile the library for my Raspberry Pi or ditch Qt altogether and get a board with larger memory.
Unfortunately, I chose the first option and opened Pandora's box. I consider C++ to be my most proficient language because I don't really excel at any language to the level of knowing the compiler internals or JVM implementation details, plus answering C++ instead of Python impresses interviewers who have never used a strongly-typed language occasionally. I use C++ to solve Leetcode problems, but I didn't have any experience writing industry applications in C++, which means I had no experience with cross-compiling. I spent two weeks struggling with numerous weird issues before finally getting things working.
So, to cross-compile the project to a Raspberry Pi, I first needed to compile Qt6 from source code for my laptop. That was easy. After that, I needed to cross-compile Qt for Arm64. By following the official guide of cross-compiling Qt6 for Raspberry Pi, it wasn't very hard, aside from quite a few missing libraries. I copied the target Qt libs to the Raspberry Pi; it worked. Nice.
Then I cross-compiled the Fluent-QML C++ project for Arm64 using the Qt toolchain. I don't remember the details, but let's say it worked. I was now able to build the .so
file I dreamed of. I built the demo and attempted to launch it on the Pi with my shaky hand. It worked! The nice Fluent design UI showed up, and it only consumed ~220-300 MB of RAM. Looks promising, I said to myself. I found a way not to waste 4GB boards running a resource-hungry web app on Chromium. Victory!
Then came the nightmare. I needed to cross-compile PySide6, then the PySide6-Fluent-QML binding. I spent days and days attempting the build, but CMake just couldn't find a lot of things, for example, Python3. It was the weirdest bug I ever encountered with CMake, even worse than the days I wrestled with Boost, Eigen and OpenGL in my Computer Graphics course. In the end, the issue was simply due to my WSL running Ubuntu 20.04, and CMake was so outdated that it couldn't find Python 3.11 (WTF????). Why on earth does CMake hard code the accepted Python version? WHY WHY WHY?
I installed everything afresh and finally pulled through the build. Now I was running the PySide version of the Fluent UI demo binary on my Pi. It consumed 50MB more than the native C++ version, but it's still quite good for the 1GB model.
The next day while surfing the Internet, I found that someone already did a fantastic job cross-compiling PySide6 for Arm64 and published it as Docker build files. Good. Now I look more like a sucker reinventing the wheel. I adapted it to also cross-compile the Fluent UI library and set up Gitea Actions to the repo. Now I had a CI-integrated infrastructure package that can support many more platforms other than amd64 and Arm64. Perfect.
Except that three days later, the author of the UI library closed my pull request and archived the repo. I found the explanation on BiliBili, the most popular video platform in China. The author apparently decided to follow his competitor and close-source the PySide bindings after refactoring. I was stuck with the old version.
Fuck. I'd had enough1. I quickly archived my first infrastructure repo in my homelab, removed the 98 GB build generated by the 4 projects after 5 hours of compiling (which I had run countless times). Then I ordered a 4GB Raspberry Pi 4 and started looking at the latest features of Next.js and popular frontend UI libraries. The weeks after were much happier than the previous two weeks. I decided to either stick to web development or go all the way to ESPhome from now on. Ever.
That was the unexciting story of my short Qt development experience. The End.
Footnotes
-
I'm not complaining about the author close-sourcing the lib. The library is years of his voluntary work without payback. It's perfectly fine for him to use his brainchild for profit. I'm just tired after this torture, really. ↩