QT 15.2.4 on jetson nano install script

As always, don’t run random scripts you find online without reading them first.
This script works for both Jetson Nano and Jetson XAvier NX to compile and install QT 15.5.2:

#!/usr/bin/env bash
set -euo pipefail

# ---- Config (override via env or flags) --------------------------------------
QT_VERSION="${QT_VERSION:-5.15.2}"
QT_PREFIX="${QT_PREFIX:-/usr/local/Qt-${QT_VERSION}}"
# For Python 3.10+, use PySide2 5.15.2.1 (5.15.2 tag hard-rejects 3.10)
PYSIDE_VERSION="${PYSIDE_VERSION:-5.15.2.1}"
QT_FLAVOR="${QT_FLAVOR:-qtbase}"                  # qtbase | full
JOBS="${JOBS:-$(nproc)}"
WORKDIR="${WORKDIR:-$PWD/build-qt-pyside}"
VENV_ACTIVATE="${VENV_ACTIVATE:-$HOME/.venv/bin/activate}"
QT_CONFIGURE_ARGS="${QT_CONFIGURE_ARGS:-}"        # extra args if you want
# -----------------------------------------------------------------------------

usage() {
  cat <<EOF
Usage: $0 [--qtbase|--full] [--qt-version X] [--pyside-version X] [--prefix PATH] [--workdir PATH] [--jobs N] [--venv-activate PATH]

Examples:
  $0 --qtbase --prefix /mnt/data/Qt-5.15.2
  $0 --full --qt-version 5.15.2 --pyside-version 5.15.2.1 --prefix /usr/local/Qt-5.15.2
EOF
}

while [[ $# -gt 0 ]]; do
  case "$1" in
    --qtbase) QT_FLAVOR="qtbase"; shift ;;
    --full)   QT_FLAVOR="full"; shift ;;
    --qt-version) QT_VERSION="$2"; shift 2 ;;
    --pyside-version) PYSIDE_VERSION="$2"; shift 2 ;;
    --prefix) QT_PREFIX="$2"; shift 2 ;;
    --workdir) WORKDIR="$2"; shift 2 ;;
    --jobs) JOBS="$2"; shift 2 ;;
    --venv-activate) VENV_ACTIVATE="$2"; shift 2 ;;
    -h|--help) usage; exit 0 ;;
    *) echo "Unknown arg: $1"; usage; exit 1 ;;
  esac
done

mkdir -p "$WORKDIR"
cd "$WORKDIR"

echo "==> Activating venv: $VENV_ACTIVATE"
if [[ ! -f "$VENV_ACTIVATE" ]]; then
  echo "ERROR: venv activate script not found at: $VENV_ACTIVATE"
  echo "Create it first, e.g.: pyenv local 3.10.13 && python -m venv ~/.venv"
  exit 1
fi
# shellcheck disable=SC1090
source "$VENV_ACTIVATE"
echo "==> Venv Python: $(python -V)"

python - <<'PY'
import sys
maj, minor = sys.version_info[:2]
if not ((3,5) <= (maj, minor) <= (3,10)):
    raise SystemExit(f"ERROR: This script targets PySide2/Qt5 builds best with Python 3.5–3.10. You have {sys.version.split()[0]}")
PY

# Ensure Python headers are available (pyenv builds usually include them)
PY_INC="$(python -c 'import sysconfig; print(sysconfig.get_config_var("INCLUDEPY") or "")')"
if [[ -z "$PY_INC" || ! -f "$PY_INC/Python.h" ]]; then
  echo "ERROR: Python development headers not found for $(python -V)."
  echo "If using pyenv, ensure your Python was built with headers (it normally is)."
  exit 1
fi

echo "==> Installing build dependencies (Debian/Ubuntu apt)..."
sudo apt-get update -y
sudo apt-get install -y \
  build-essential git wget perl pkg-config cmake ninja-build \
  libgl1-mesa-dev libegl1-mesa-dev \
  libfontconfig1-dev libfreetype6-dev \
  libx11-dev libx11-xcb-dev libxext-dev libxrender-dev libxi-dev libxrandr-dev libxcursor-dev \
  libxcomposite-dev libxdamage-dev libxtst-dev libdbus-1-dev \
  libxau-dev libxdmcp-dev xcb-proto \
  libxcb1-dev libxcb-util0-dev \
  libxcb-glx0-dev libxcb-keysyms1-dev libxcb-image0-dev libxcb-shm0-dev \
  libxcb-icccm4-dev libxcb-sync-dev libxcb-xfixes0-dev libxcb-shape0-dev libxcb-randr0-dev \
  libxcb-render0-dev libxcb-render-util0-dev libxcb-xinerama0-dev libxcb-xkb-dev libxcb-cursor-dev \
  libxkbcommon-dev libxkbcommon-x11-dev

# clang/llvm/libclang: try versioned first, fallback to defaults
if apt-cache show clang-10 >/dev/null 2>&1; then
  sudo apt-get install -y clang-10 llvm-10 llvm-10-dev libclang-10-dev
elif apt-cache show libclang-dev >/dev/null 2>&1; then
  sudo apt-get install -y clang llvm llvm-dev libclang-dev
else
  echo "WARNING: Could not find clang/llvm packages via apt-cache; install libclang+llvm manually if PySide build later complains."
fi

echo "==> Sanity check: pkg-config visibility for XCB bits..."
missing=0
for m in xcb xcb-icccm xcb-image xcb-keysyms xcb-randr xcb-render xcb-renderutil xcb-shape xcb-shm xcb-sync xcb-xfixes xcb-xinerama xcb-xkb xcb-cursor xkbcommon-x11; do
  if pkg-config --exists "$m"; then
    echo "  OK   $m"
  else
    echo "  MISS $m"
    missing=1
  fi
done
if [[ "$missing" -ne 0 ]]; then
  echo "ERROR: Missing one or more required XCB pkg-config modules above. Install the corresponding -dev packages and rerun."
  exit 1
fi

echo "==> Downloading Qt ${QT_VERSION} (${QT_FLAVOR})..."
QT_BASE_URL="https://master.qt.io/archive/qt/5.15/${QT_VERSION}"
if [[ "$QT_FLAVOR" == "full" ]]; then
  QT_TARBALL="qt-everywhere-src-${QT_VERSION}.tar.xz"
  QT_URL="${QT_BASE_URL}/single/${QT_TARBALL}"
  QT_SRC_DIR="qt-everywhere-src-${QT_VERSION}"
else
  QT_TARBALL="qtbase-everywhere-src-${QT_VERSION}.tar.xz"
  QT_URL="${QT_BASE_URL}/submodules/${QT_TARBALL}"
  QT_SRC_DIR="qtbase-everywhere-src-${QT_VERSION}"
fi

wget -c "$QT_URL" -O "$QT_TARBALL"

echo "==> Extracting Qt sources..."
rm -rf "$QT_SRC_DIR"
tar -xpf "$QT_TARBALL"

echo "==> Configuring Qt (enabling xcb) ..."
# Out-of-source build (easy to clean: rm -rf build-qt)
rm -rf build-qt
mkdir -p build-qt
cd build-qt

"../${QT_SRC_DIR}/configure" \
  -prefix "$QT_PREFIX" \
  -opensource -confirm-license \
  -xcb \
  $QT_CONFIGURE_ARGS

echo "==> Building Qt (jobs=${JOBS})..."
make -j"$JOBS"

echo "==> Installing Qt to ${QT_PREFIX}..."
# Use sudo only if needed
if mkdir -p "$QT_PREFIX" 2>/dev/null && touch "$QT_PREFIX/.writable_test" 2>/dev/null; then
  rm -f "$QT_PREFIX/.writable_test"
  make install
else
  sudo make install
fi

echo "==> Qt installed. Checking qmake..."
QMAKE="${QT_PREFIX}/bin/qmake"
if [[ ! -x "$QMAKE" && -x "${QT_PREFIX}/bin/qmake-qt5" ]]; then
  QMAKE="${QT_PREFIX}/bin/qmake-qt5"
fi
if [[ ! -x "$QMAKE" ]]; then
  echo "ERROR: qmake not found under ${QT_PREFIX}/bin/"
  exit 1
fi
echo "==> Using qmake: $QMAKE"
"$QMAKE" -v || true

echo "==> Preparing venv tooling for PySide2 build..."
python -m pip install --upgrade pip setuptools
# Avoid the 'safer_name' wheel override noise / breakage with newer wheel
python -m pip install --upgrade "wheel<0.46"

# Help pyside find LLVM/Clang (best-effort)
if command -v llvm-config-10 >/dev/null 2>&1; then
  export LLVM_INSTALL_DIR="$(llvm-config-10 --prefix)"
elif command -v llvm-config >/dev/null 2>&1; then
  export LLVM_INSTALL_DIR="$(llvm-config --prefix)"
fi

cd "$WORKDIR"
mkdir -p src
if [[ ! -d "src/pyside-setup" ]]; then
  echo "==> Cloning pyside-setup into $WORKDIR/src..."
  git clone https://code.qt.io/pyside/pyside-setup.git "src/pyside-setup"
fi

cd "src/pyside-setup"
echo "==> Checking out PySide tag/version: ${PYSIDE_VERSION}..."
git fetch --tags
git checkout "v${PYSIDE_VERSION}" 2>/dev/null || git checkout "${PYSIDE_VERSION}"

echo "==> Cleaning old PySide build artifacts..."
rm -rf build */build pyside2-build shiboken2-build out .venv3_build .venv3_install

echo "==> Applying shiboken2 patch for Python 3.10 (_Py_Mangle)..."
python - <<'PY'
import pathlib, re, sys
p = pathlib.Path("sources/shiboken2/libshiboken/pep384impl.cpp")
if not p.exists():
    sys.exit("ERROR: expected file not found: sources/shiboken2/libshiboken/pep384impl.cpp")
s = p.read_text()
# If already patched, do nothing.
if "_Py_Mangle" in s and re.search(r'^\s*#\s*ifdef\s+IS_PY2\s*$', s, re.M):
    print("  Patch already applied.")
    sys.exit(0)
# Replace the guard right above the _Py_Mangle return with Python2-only.
s2 = re.sub(
    r'(#\s*)ifndef\s+Py_LIMITED_API(\s*\n\s*return\s+_Py_Mangle\s*\()',
    r'\1ifdef IS_PY2\2',
    s,
    count=1
)
if s2 == s:
    sys.exit("ERROR: Could not apply patch automatically. Search for '_Py_Mangle' and change the '#ifndef Py_LIMITED_API' above it to '#ifdef IS_PY2'.")
p.write_text(s2)
print("  Patched:", p)
PY

echo "==> Building + installing PySide2 into the venv (no sudo)..."
# 'jobs' is deprecated; use --parallel
python setup.py install --qmake="$QMAKE" --parallel "$JOBS"

echo "==> Verifying PySide2 import inside venv..."
python -c "import PySide2; print('PySide2:', PySide2.__version__)"

echo "==> Done."
echo "Qt:      $QT_PREFIX"
echo "qmake:   $QMAKE"
echo "PySide2: installed into venv at: $(python -c 'import sys; print(sys.prefix)')"

1 Like

Hi,
Thanks for the sharing. Not sure on which Jetpack version you run the script. Would be great if you can share this further information.