installing pyqt5 (qt version 5.15) on python 3.8.5 and jetpack 4.4

Back in September 2019 I wrote how I failed to build Qt 5.13 from its C++ source, which was the latest Qt release at that time(see https://arcanesciencelab.wordpress.com/2019/09/02/an-absolute-failure-unable-to-build-qt5-on-a-raspberry-pi-4/ ). My primary reason to build the latest Qt was to step up the Qt version used in PyQt5. There were problems I’d discovered in the installed repo version, and I’d wanted to create a development environment where I could investigate, and if possible, try to fix any of those problems, with an eye towards pushing any fixes back up stream. That obviously didn’t work out, and I left the whole thing alone. Until now.

For whatever reason I decided to try again. Except this time, I used JetPack 4.4 on the Nvidia Xavier NX Developer Kit. While the Xavier is ostensibly for machine learning prototyping and development, the fact it’s based on Ubuntu 18.04.5, ported onto Nvidia’s six core ARM chip, makes it too tempting for me just to use for ML. And thus I decided to try to run with the latest PyQt5 on the latest Python 3 release.

Installation

I’m starting from my Python 3.8 installation inside an activated Python virtual environment. I’d documented how to build Python from scratch as well as create a working virtual environment here: https://arcanesciencelab.wordpress.com/2020/05/24/building-python-3-8-3-on-jetpack-4-4-dp-and-the-jetson-xavier-nx/ , so I won’t go through that again.

However, before we really get started, you’ll need to install the basic Qt5 development tools, otherwise PyQt5 won’t install. You do that with sudo apt install qt5-default . That gives all the basic tools and support libraries.

With Qt5 default installed, activate the Python 3.8 virtual environment. I’ve since moved up to Python 3.8.5, and I’m getting ready to install Python 3.9 alongside 3.8.5 when it’s officially released. That’s another reason I like virtual environments; it’s incredibly easy to keep multiple Python versions on hand.

Within the virtual environment, do a simple pip install pyqt5 .

You might as well go off and do house work or watch an episode or two of your favorite streaming shows, because this is going to take a while (as in hours). No, I didn’t time it.

Once it’s done, it should announce it’s successful. From there you can work with PyQt5 within that virtual Python environment. I would strongly recommend you always work within a Python virtual environment. It all works with your regular security permissions (no sudo), and if you want to blow it away, it’s very easy to do so. I just don’t like adulterating the system installation of Python anymore.

So far, except for the long build time, everything is working without any drama.

my very first aarch64 assembly program

I have been thinking for some number of years of learning how to write ARM assembly language applications. The driver for this is an old book, “Threaded Interpretive Languages: Their Design and Implementation” by R. G. Loeliger. I purchased a copy back in 1981, the year the book was first published, at a local Book Stop in Atlanta. A threaded interpretive language, or TIL, is the general classification for Forth and other languages like it. I wasn’t yet married, was still something of a night owl, and so I had time on my hands so to speak to investigate how to implement a TIL. Over a period of three years I implemented the same personal TIL on the Z80 (the book was written to use a Z80), an MOS 6502, a Motorola 68000, and an Intel 8086. By the time I put the book aside I was doing a lot more “serious” work in C and Pascal on DEC minis and IBM PCs. The only time I went back to writing any assembly was later in the 1990s when I wrote a fair bit of code on the side for a 65c802 and an Intel 80c196. Both of these were custom designed embedded systems. Unfortunately I didn’t write a TIL for either one, as the requirements directed me to spend my time on other functionality.

So this weekend I decided to dig a bit into the ARM processor driving the Nvidia Xavier NX. I went around the net a bit looking for examples and tutorials, and finally managed to cobble together a “Hello World” program in aarch64/ARMv8 assembly. Here’s the tiny application I wrote.

#include "include.h"

	.global	_start
	.text

_start:
	mov	x8, __NR_write
	mov x2, hello_len
	adr x1, hello_txt
	mov	x0, STDOUT_FILENO
	svc	0

	mov	x8, __NR_exit
	svc	0

	.data

hello_txt:	.ascii "Hello, World!\n"
hello_len = . - hello_txt

I’m following, for the most part, this tutorial: https://modexp.wordpress.com/2018/10/30/arm64-assembly/, “A Guide to ARM64 / AArch64 Assembly on Linux with Shellcodes and Cryptography.” The code and instructions for how to assemble it are towards the middle. My only comment about the program is that line 9 in my listing is different from the original. I found that if I wanted to load the register with the address to the string to print, then I needed to explicitly code the mnemonic. For whatever reason the tools on the Xavier simply interpreted it as a mov instruction, and nothing would print.

Because of the number of steps involved in building the app, I wrote a bit of Python 3 to automate the process a bit. My Python code turned out to be longer than my assembly code.

#!/usr/bin/env python3

import argparse
import os
from pathlib import Path
import subprocess
import sys

if not sys.version_info.minor >= 6:
    print("You are using Python version {}.{}.{}".
        format(sys.version_info.major,sys.version_info.minor,sys.version_info.micro))
    print("Python version 3.6.0 or higher is required.")
    sys.exit(1)

parser = argparse.ArgumentParser()
parser.add_argument("source", help="Assembly source file name is required.")
args = parser.parse_args()

if not os.path.isfile(args.source):
    print("File {} can't be found.".format(args.source))
    sys.exit(1)

filestem = Path(args.source).stem

preprocess = "cpp -E {} -o {}.as".format(args.source, filestem)
print(preprocess)
p = subprocess.run([preprocess], shell=True)
if p.returncode != 0:
    sys.exit(1)

assemble = "as {}.as -o {}.o".format(filestem, filestem)
print(assemble)
p = subprocess.run([assemble], shell=True)
if p.returncode != 0:
    sys.exit(1)

link = "ld {}.o -o {}".format(filestem, filestem)
print(link)
p = subprocess.run([link], shell=True)

There are no comments, and only a little white space to make it readable to me. I really didn’t feel like diving into either make or cmake. Hopefully I haven’t embarresed myself too much with either program.

What I’ve discovered so far is that there are a lot of 32-bit ARM assembly tutorials that won’t work at all with the Xavier. They just won’t assemble. But I am moving along a bit after this. I have Loeliger’s inner and outer interpreter coded, and two words in a dictionary. I’ll begin to post this effort shortly. As for why, well, why not? If nothing else, this hello program is 1,104 bytes long, which beats the size of Go’s basic hello world program by, what, four orders of magnitude?

There’s just something bracingly honest about writing in assembly that no other method of coding can approach.