diff --git a/scripts/build.sh b/scripts/build.sh new file mode 100644 index 0000000..605d95c --- /dev/null +++ b/scripts/build.sh @@ -0,0 +1,185 @@ +#!/bin/bash + +# MPVJ Build Script +# Runs on x86_64 (e.g., Docker) to cross-compile artifacts for aarch64 (RPi 3B). +# Outputs: +# frontend/dist/ — pre-built Vite bundle +# bin/ofxPiMapper — aarch64 binary +# +# Usage: bash scripts/build.sh [REPO_DIR] +# REPO_DIR defaults to the directory containing this script's parent. + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_DIR="${1:-$(dirname "$SCRIPT_DIR")}" +OF_VERSION="0.12.1" +OF_DIR="$REPO_DIR/.build/openFrameworks" +OF_FILE="of_v${OF_VERSION}_linuxaarch64_release.tar.gz" +OF_URL="https://github.com/openframeworks/openFrameworks/releases/download/${OF_VERSION}/${OF_FILE}" +BIN_OUT="$REPO_DIR/bin" + +# ── Validate repo ──────────────────────────────────────────────────────────── +if [ ! -d "$REPO_DIR/frontend" ] || [ ! -d "$REPO_DIR/backend" ]; then + echo "ERROR: $REPO_DIR does not look like the mapper repo (missing frontend/ or backend/)." + exit 1 +fi +echo "Building from: $REPO_DIR" + +# ── 1. Install build dependencies ─────────────────────────────────────────── +echo "Installing build dependencies..." +if command -v apt-get > /dev/null 2>&1; then + apt-get update -y + DEBIAN_FRONTEND=noninteractive apt-get install -y \ + git curl wget unzip build-essential \ + gcc-aarch64-linux-gnu g++-aarch64-linux-gnu \ + binutils-aarch64-linux-gnu \ + libmpg123-dev libsndfile1-dev libopenal-dev libassimp-dev \ + libglew-dev libglfw3-dev liburiparser-dev \ + libcurl4-openssl-dev libpugixml-dev libasound2-dev \ + libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev \ + gstreamer1.0-plugins-good gstreamer1.0-plugins-bad \ + gstreamer1.0-plugins-ugly gstreamer1.0-libav \ + libgtk-3-dev libboost-filesystem-dev \ + libfontconfig1-dev libfreetype-dev libx11-dev \ + libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev \ + libpulse-dev libudev-dev libfreeimage-dev librtaudio-dev \ + freeglut3-dev libxxf86vm-dev +fi + +# Install Node.js if missing +if ! command -v node > /dev/null 2>&1; then + echo "Installing Node.js 20..." + curl -fsSL https://deb.nodesource.com/setup_20.x | bash - + apt-get install -y nodejs +fi + +# ── 2. Build frontend ──────────────────────────────────────────────────────── +echo "" +echo "=== Building frontend ===" +cd "$REPO_DIR/frontend" +rm -rf node_modules package-lock.json +npm install --omit=optional --package-lock=false +NODE_OPTIONS="--max-old-space-size=512" npm run build +echo "Frontend built → $REPO_DIR/frontend/dist/" + +# ── 3. Download OpenFrameworks (aarch64 pre-built release) ─────────────────── +echo "" +echo "=== Downloading OpenFrameworks ${OF_VERSION} (aarch64) ===" +mkdir -p "$REPO_DIR/.build" +if [ ! -d "$OF_DIR" ]; then + cd "$REPO_DIR/.build" + if [ ! -f "$OF_FILE" ]; then + wget "$OF_URL" || curl -L -O "$OF_URL" + fi + tar -xzf "$OF_FILE" + mv of_v${OF_VERSION}_linux*_release openFrameworks + rm -f "$OF_FILE" + echo "OpenFrameworks extracted to $OF_DIR" +else + echo "OpenFrameworks already present, skipping download." +fi + +# ── 4. Clone & patch ofxPiMapper ──────────────────────────────────────────── +echo "" +echo "=== Cloning ofxPiMapper ===" +ADDON_DIR="$OF_DIR/addons/ofxPiMapper" +rm -rf "$ADDON_DIR" +git clone --depth 1 https://github.com/kr15h/ofxPiMapper.git "$ADDON_DIR" +cd "$ADDON_DIR" + +echo "Applying compatibility patches..." + +# 4a. OscControl.cpp highlight patch +OSC_FILE=$(find src -name "OscControl.cpp" | head -n 1) +if [ -n "$OSC_FILE" ]; then + echo "Patching $OSC_FILE..." + sed -i '/if (m.getAddress() == "\/ofxPiMapper\/surface\/select"){/r /dev/stdin' "$OSC_FILE" <<'EOF_PATCH' + if (m.getAddress() == "/ofxPiMapper/surface/highlight"){ + int surfaceIndex = m.getArgAsInt32(0); + mapper->getSurfaceManager()->selectSurface(surfaceIndex); + return; + } +EOF_PATCH +fi + +# 4b. C++11 compatibility header +cat <<'EOF_COMPAT' > src/cpp11_compat.h +#ifndef OFXPIMAPPER_CPP11_COMPAT_H +#define OFXPIMAPPER_CPP11_COMPAT_H +#include +#include +#include +#include +#include +#if __cplusplus < 201402L +namespace std { + template + std::unique_ptr make_unique(Args&&... args) { + return std::unique_ptr(new T(std::forward(args)...)); + } +} +#endif +using std::unique_ptr; +using std::make_unique; +using std::vector; +using std::string; +#endif +EOF_COMPAT + +# 4c. Inject compat header into the main addon header +sed -i '1i #include "cpp11_compat.h"' src/ofxPiMapper.h + +# 4d. Qualify unique_ptr / make_unique calls +find src -type f \( -name "*.h" -o -name "*.cpp" \) \ + -exec sed -i 's/\([^:a-zA-Z0-9]\)unique_ptr\b/\1std::unique_ptr/g' {} + \ + -exec sed -i 's/\([^:a-zA-Z0-9]\)make_unique\b/\1std::make_unique/g' {} + + +# 4e. Ensure is included where needed +for f in $(grep -l "unique_ptr" src/*.h 2>/dev/null || true); do + grep -q "" "$f" || sed -i '1i #include ' "$f" +done + +# ── 5. Cross-compile ofxPiMapper for aarch64 ──────────────────────────────── +echo "" +echo "=== Compiling ofxPiMapper (aarch64 target) ===" +cd "$ADDON_DIR/example_basic" + +CROSS_CC="aarch64-linux-gnu-gcc" +CROSS_CXX="aarch64-linux-gnu-g++" +CROSS_AR="aarch64-linux-gnu-ar" +CROSS_LD="aarch64-linux-gnu-ld" + +if ! command -v "$CROSS_CXX" > /dev/null 2>&1; then + echo "ERROR: Cross-compiler $CROSS_CXX not found. Install gcc-aarch64-linux-gnu." + exit 1 +fi + +GLOBAL_FLAGS="-Wno-error -Wno-reorder -Wno-sign-compare -Wno-delete-non-virtual-dtor -std=c++11 -O0 -fno-tree-vrp" + +make clean 2>/dev/null || true +env \ + CC="$CROSS_CC" CXX="$CROSS_CXX" AR="$CROSS_AR" LD="$CROSS_LD" \ + make -j1 \ + PLATFORM_CFLAGS="$GLOBAL_FLAGS -I$ADDON_DIR/src" \ + USER_CFLAGS="$GLOBAL_FLAGS" \ + USER_CPPFLAGS="-std=c++11" \ + OF_PROJECT_OPTIMIZATION_FLAGS="-O0" + +# ── 6. Copy artifact to repo/bin/ ────────────────────────────────────────── +echo "" +echo "=== Collecting artifacts ===" +mkdir -p "$BIN_OUT" +cp bin/example_basic "$BIN_OUT/ofxPiMapper" +chmod +x "$BIN_OUT/ofxPiMapper" + +echo "" +echo "============================================" +echo " BUILD COMPLETE" +echo "============================================" +echo " Frontend: $REPO_DIR/frontend/dist/" +echo " ofxPiMapper: $BIN_OUT/ofxPiMapper (aarch64)" +echo "" +echo " Commit and push these artifacts, then run" +echo " setup.sh on the Pi to deploy." +echo "============================================" diff --git a/scripts/setup.sh b/scripts/setup.sh index c67474b..a7e6a7c 100644 --- a/scripts/setup.sh +++ b/scripts/setup.sh @@ -1,170 +1,114 @@ #!/bin/bash -# MPVJ Headless Setup Script (Final Polish & Robustness) -# Usage: curl -sSL /setup.sh | sudo bash -s -- +# MPVJ Deployment Script +# Runs on the Raspberry Pi. Expects pre-built artifacts in the repo: +# frontend/dist/ — built by scripts/build.sh +# bin/ofxPiMapper — aarch64 binary built by scripts/build.sh +# +# Usage: sudo bash setup.sh set -e REPO_URL=$1 if [ -z "$REPO_URL" ]; then - echo "Usage: $0 " - exit 1 + echo "Usage: sudo bash $0 " + exit 1 fi -# 0. BASH & ROOT CHECK if [ -z "$BASH_VERSION" ]; then - echo "ERROR: This script must be run with BASH, not SH." - echo "Please use: sudo bash setup.sh $REPO_URL" + echo "ERROR: This script must be run with bash." exit 1 fi if [ "$(id -u)" -ne 0 ]; then - echo "ERROR: This script must be run as root (use sudo)." + echo "ERROR: Must be run as root (sudo)." exit 1 fi -# Ensure interactive prompt tooling is available -if ! command -v whiptail > /dev/null 2>&1; then - echo "Installing whiptail for interactive prompts..." - if command -v apt-get > /dev/null 2>&1; then - apt-get update -y - DEBIAN_FRONTEND=noninteractive apt-get install -y whiptail - elif command -v pacman > /dev/null 2>&1; then - pacman -S --noconfirm whiptail - fi -fi - -# Detect user (supports non-login environments like Docker) +# Detect real user REAL_USER=${SUDO_USER:-$(logname 2>/dev/null || true)} -if [ -z "$REAL_USER" ]; then - REAL_USER=root -fi -REAL_HOME=$(eval echo "~$REAL_USER" 2>/dev/null || true) -if [ -z "$REAL_HOME" ]; then - REAL_HOME=/root -fi - +[ -z "$REAL_USER" ] && REAL_USER=root +REAL_HOME=$(eval echo "~$REAL_USER" 2>/dev/null || echo "/root") echo "Target User: $REAL_USER ($REAL_HOME)" -# 0.5 SCRUB & SANITIZE PHASE -echo "Scrubbing system for a clean installation..." +# Ensure whiptail is available +if ! command -v whiptail > /dev/null 2>&1; then + apt-get update -y + DEBIAN_FRONTEND=noninteractive apt-get install -y whiptail +fi + +# ── 0. SCRUB ───────────────────────────────────────────────────────────────── +echo "Stopping any running services..." systemctl stop mpvj-backend.service 2>/dev/null || true pkill -9 ofxPiMapper 2>/dev/null || true pkill -9 node 2>/dev/null || true - -# Remove old setup artifacts -swapoff /swapfile_mpvj 2>/dev/null || true -rm -f /swapfile_mpvj 2>/dev/null || true - -# Clear any stale apt locks rm -f /var/lib/dpkg/lock-frontend /var/lib/apt/lists/lock /var/cache/apt/archives/lock 2>/dev/null || true -# Ask to wipe previous data for a fresh start -if [ -d "$REAL_HOME/mpvj" ] || [ -d "$REAL_HOME/openFrameworks" ]; then - if whiptail --title "Cleanup" --yesno "Found existing MPVJ or OpenFrameworks data. Wipe them for a clean install?" 10 60 < /dev/tty; then - echo "Wiping old directories..." - rm -rf "$REAL_HOME/mpvj" "$REAL_HOME/openFrameworks" +if [ -d "$REAL_HOME/mpvj" ]; then + if whiptail --title "Cleanup" --yesno "Found existing MPVJ install. Wipe it for a clean install?" 10 60 < /dev/tty; then + rm -rf "$REAL_HOME/mpvj" fi fi -# 1. INTERROGATION PHASE -echo "Interrogating user for configuration..." - -# 1.1 Hostname -HOSTNAME=$(whiptail --inputbox "Enter Hostname (e.g. mpvj)" 8 45 "mpvj" --title "Hostname Configuration" 3>&1 1>&2 2>&3 < /dev/tty) || exit 1 +# ── 1. CONFIGURATION ───────────────────────────────────────────────────────── +HOSTNAME=$(whiptail --inputbox "Enter Hostname" 8 45 "mpvj" --title "Hostname" 3>&1 1>&2 2>&3 < /dev/tty) || exit 1 HOSTNAME=${HOSTNAME:-mpvj} -# 1.2 SSID -WIFI_SSID=$(whiptail --inputbox "Enter WiFi SSID (e.g. MPVJ-AP)" 8 45 "MPVJ-AP" --title "WiFi SSID Configuration" 3>&1 1>&2 2>&3 < /dev/tty) || exit 1 +WIFI_SSID=$(whiptail --inputbox "Enter WiFi SSID" 8 45 "MPVJ-AP" --title "WiFi SSID" 3>&1 1>&2 2>&3 < /dev/tty) || exit 1 WIFI_SSID=${WIFI_SSID:-MPVJ-AP} -# 1.3 Country Code & Password while true; do - WIFI_COUNTRY=$(whiptail --inputbox "Enter 2-letter Country Code (e.g. US, GB)" 8 45 "US" --title "WiFi Country Configuration" 3>&1 1>&2 2>&3 < /dev/tty) || exit 1 + WIFI_COUNTRY=$(whiptail --inputbox "Enter 2-letter Country Code (e.g. US, GB)" 8 45 "US" --title "WiFi Country" 3>&1 1>&2 2>&3 < /dev/tty) || exit 1 WIFI_COUNTRY=$(echo "$WIFI_COUNTRY" | tr '[:lower:]' '[:upper:]') - if ! echo "$WIFI_COUNTRY" | grep -qE "^[A-Z]{2}$"; then - whiptail --msgbox "Error: Country Code must be 2 letters." 8 45 < /dev/tty - else - break - fi + echo "$WIFI_COUNTRY" | grep -qE "^[A-Z]{2}$" && break + whiptail --msgbox "Country Code must be exactly 2 letters." 8 45 < /dev/tty done while true; do - WIFI_PASS=$(whiptail --passwordbox "Enter WiFi Password (min 8 chars)" 8 45 --title "WiFi Password Configuration" 3>&1 1>&2 2>&3 < /dev/tty) || exit 1 - if [ ${#WIFI_PASS} -lt 8 ]; then - whiptail --msgbox "Error: Minimum 8 characters required." 8 45 < /dev/tty - else - break - fi + WIFI_PASS=$(whiptail --passwordbox "Enter WiFi Password (min 8 chars)" 8 45 --title "WiFi Password" 3>&1 1>&2 2>&3 < /dev/tty) || exit 1 + [ ${#WIFI_PASS} -ge 8 ] && break + whiptail --msgbox "Minimum 8 characters required." 8 45 < /dev/tty done -# 2. SYSTEM STAGING PHASE -echo "System Staging Phase..." - -# 2.1 Pre-Flight Disk Check +# ── 2. SYSTEM SETUP ────────────────────────────────────────────────────────── FREE_SPACE_KB=$(df / --output=avail | tail -n1) -if [ "$FREE_SPACE_KB" -lt 4194304 ]; then - whiptail --msgbox "Error: 4GB free space required." 8 45 < /dev/tty +if [ "$FREE_SPACE_KB" -lt 2097152 ]; then + whiptail --msgbox "Error: At least 2GB free space required." 8 45 < /dev/tty exit 1 fi -# 2.2 System Updates -if command -v apt-get > /dev/null; then - apt-get update -y && DEBIAN_FRONTEND=noninteractive apt-get upgrade -y -elif command -v pacman > /dev/null; then - pacman -Syu --noconfirm +echo "Installing runtime dependencies..." +apt-get update -y +DEBIAN_FRONTEND=noninteractive apt-get install -y \ + git hostapd dnsmasq avahi-daemon curl ffmpeg \ + libzmq3-dev libavahi-compat-libdnssd-dev + +if ! command -v node > /dev/null 2>&1; then + curl -fsSL https://deb.nodesource.com/setup_20.x | bash - + apt-get install -y nodejs fi -# 2.3 Install Dependencies -echo "Installing Core Dependencies..." -if command -v apt-get > /dev/null; then - DEBIAN_FRONTEND=noninteractive apt-get install -y git hostapd dnsmasq avahi-daemon curl ffmpeg build-essential libzmq3-dev libavahi-compat-libdnssd-dev wget unzip -elif command -v pacman > /dev/null; then - pacman -S --noconfirm git hostapd dnsmasq avahi curl ffmpeg base-devel zeromq wget unzip -fi - -# Install Node.js if missing -if ! command -v node > /dev/null; then - curl -fsSL https://deb.nodesource.com/setup_20.x | bash - || true - command -v apt-get > /dev/null && apt-get install -y nodejs -fi - -# 2.4 Hostname & WiFi Country -# hostnamectl requires systemd/D-Bus — use direct file writes as a universal fallback -if command -v hostnamectl > /dev/null 2>&1 && systemctl is-system-running --quiet 2>/dev/null; then - hostnamectl set-hostname "$HOSTNAME" -else - echo "$HOSTNAME" > /etc/hostname - hostname "$HOSTNAME" 2>/dev/null || true -fi +# Hostname +hostnamectl set-hostname "$HOSTNAME" sed -i "s/127.0.1.1.*/127.0.1.1\t$HOSTNAME/g" /etc/hosts || true -# 2.5 OS-Agnostic Swap Increase (2GB) -echo "Ensuring 2GB Swap for build..." -if [ ! -f /swapfile_mpvj ]; then - fallocate -l 2G /swapfile_mpvj || dd if=/dev/zero of=/swapfile_mpvj bs=1M count=2048 - chmod 600 /swapfile_mpvj - mkswap /swapfile_mpvj - swapon /swapfile_mpvj -fi - -# 3. NETWORKING -echo "Configuring Networking..." -# hostapd +# ── 3. NETWORKING ──────────────────────────────────────────────────────────── +echo "Configuring networking..." cat < /etc/hostapd/hostapd.conf interface=wlan0 driver=nl80211 ssid=$WIFI_SSID hw_mode=g channel=7 +country_code=$WIFI_COUNTRY wpa=2 wpa_passphrase=$WIFI_PASS wpa_key_mgmt=WPA-PSK EOF -[ -f /etc/default/hostapd ] && sed -i 's/#DAEMON_CONF=""/DAEMON_CONF="\/etc\/hostapd\/hostapd.conf"/g' /etc/default/hostapd +[ -f /etc/default/hostapd ] && \ + sed -i 's|#DAEMON_CONF=""|DAEMON_CONF="/etc/hostapd/hostapd.conf"|g' /etc/default/hostapd -# dnsmasq [ -f /etc/dnsmasq.conf ] && mv /etc/dnsmasq.conf /etc/dnsmasq.conf.bak || true cat < /etc/dnsmasq.conf interface=wlan0 @@ -172,41 +116,40 @@ dhcp-range=192.168.4.2,192.168.4.20,255.255.255.0,24h address=/$HOSTNAME.local/192.168.4.1 EOF -# dhcpcd / network manager -if [ -f /etc/dhcpcd.conf ]; then - if ! grep -q "interface wlan0" /etc/dhcpcd.conf; then - echo -e "\ninterface wlan0\n static ip_address=192.168.4.1/24\n nohook wpa_supplicant" >> /etc/dhcpcd.conf - fi +if [ -f /etc/dhcpcd.conf ] && ! grep -q "interface wlan0" /etc/dhcpcd.conf; then + printf '\ninterface wlan0\n static ip_address=192.168.4.1/24\n nohook wpa_supplicant\n' >> /etc/dhcpcd.conf fi -# 4. DEPLOYMENT -echo "Deploying Application..." +# ── 4. DEPLOY APP ──────────────────────────────────────────────────────────── +echo "Deploying application from $REPO_URL ..." [ -d "$REAL_HOME/mpvj" ] && mv "$REAL_HOME/mpvj" "$REAL_HOME/mpvj.old.$(date +%s)" sudo -u "$REAL_USER" git clone "$REPO_URL" "$REAL_HOME/mpvj" + +# Verify pre-built artifacts exist +if [ ! -d "$REAL_HOME/mpvj/frontend/dist" ]; then + echo "ERROR: frontend/dist/ not found in repo." + echo "Run scripts/build.sh on your x86_64 machine first and commit the dist folder." + exit 1 +fi +if [ ! -f "$REAL_HOME/mpvj/bin/ofxPiMapper" ]; then + echo "ERROR: bin/ofxPiMapper not found in repo." + echo "Run scripts/build.sh on your x86_64 machine first and commit the binary." + exit 1 +fi + +# Install npm deps for backend only (no build needed) +cd "$REAL_HOME/mpvj/backend" +sudo -u "$REAL_USER" npm install --omit=optional --package-lock=false + +# Install ofxPiMapper binary +install -m 755 "$REAL_HOME/mpvj/bin/ofxPiMapper" /usr/local/bin/ofxPiMapper + +# Media directory mkdir -p "$REAL_HOME/media" chown -R "$REAL_USER:$REAL_USER" "$REAL_HOME/mpvj" "$REAL_HOME/media" -# 4.2 Build -export NODE_OPTIONS="--max-old-space-size=512" -cd "$REAL_HOME/mpvj/backend" -echo "Installing backend dependencies..." -rm -rf node_modules package-lock.json -sudo -u "$REAL_USER" npm install --omit=optional --package-lock=false - -cd "$REAL_HOME/mpvj/frontend" -if [ ! -d "dist" ]; then - echo "Installing frontend dependencies and building..." - rm -rf node_modules package-lock.json - sudo -u "$REAL_USER" npm install --omit=optional --package-lock=false - sudo -u "$REAL_USER" npm run build -else - echo "Pre-built frontend detected. Skipping frontend build." -fi - -# 4.3 Setup system service (only if systemd is active) -# Note: in non-systemd contexts (containers, chroots), systemctl will fail. -if command -v systemctl > /dev/null 2>&1 && systemctl is-system-running --quiet 2>/dev/null; then - cat < /etc/systemd/system/mpvj-backend.service +# ── 5. SYSTEMD SERVICE ─────────────────────────────────────────────────────── +cat < /etc/systemd/system/mpvj-backend.service [Unit] Description=MPVJ Backend After=network.target @@ -219,14 +162,10 @@ Restart=on-failure [Install] WantedBy=multi-user.target EOF - systemctl daemon-reload - systemctl enable mpvj-backend.service -else - echo "WARNING: systemd is not active/accessible. Skipping systemd service setup." - echo "You can run the backend manually: node $REAL_HOME/mpvj/backend/index.js" -fi +systemctl daemon-reload +systemctl enable mpvj-backend.service -# 4.4 .env +# Write .env cat < "$REAL_HOME/mpvj/backend/.env" PORT=80 MPVJ_HOSTNAME=$HOSTNAME @@ -234,139 +173,6 @@ MPVJ_SSID=$WIFI_SSID MPVJ_MEDIA_DIR=$REAL_HOME/media EOF -# 4.5 THE PRE-BUILT ENGINE SOLUTION -echo "Installing Pre-compiled OpenFrameworks (FAST)..." -cd "$REAL_HOME" -if [ ! -d "openFrameworks" ]; then - echo "Downloading OpenFrameworks v0.12.1..." - ARCH=$(uname -m) - OF_FILE="of_v0.12.1_linuxaarch64_release.tar.gz" - [ "$ARCH" != "aarch64" ] && [ "$ARCH" != "x86_64" ] && OF_FILE="of_v0.12.1_linuxarmv6l_release.tar.gz" - DOWNLOAD_URL="https://github.com/openframeworks/openFrameworks/releases/download/0.12.1/$OF_FILE" - if ! sudo -u "$REAL_USER" wget "$DOWNLOAD_URL"; then - sudo -u "$REAL_USER" curl -L -O "$DOWNLOAD_URL" - fi - sudo -u "$REAL_USER" tar -xzf "$OF_FILE" - sudo -u "$REAL_USER" mv of_v0.12.1_linux*_release openFrameworks - sudo -u "$REAL_USER" rm "$OF_FILE" -fi - -# ALWAYS verify dependencies, even if OF is already present -echo "Ensuring modern dependencies for Debian Trixie..." -if command -v apt-get > /dev/null; then - DEBIAN_FRONTEND=noninteractive apt-get install -y \ - libmpg123-dev libsndfile1-dev libopenal-dev libassimp-dev \ - libglew-dev libglfw3-dev liburiparser-dev \ - libcurl4-openssl-dev libpugixml-dev libasound2-dev \ - libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev \ - gstreamer1.0-plugins-good gstreamer1.0-plugins-bad \ - gstreamer1.0-plugins-ugly gstreamer1.0-libav \ - libgtk-3-dev libboost-filesystem-dev \ - libfontconfig1-dev libfreetype-dev libx11-dev \ - libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev \ - libpulse-dev libudev-dev libfreeimage-dev librtaudio-dev \ - freeglut3-dev libxxf86vm-dev -fi - -echo "Downloading and building ofxPiMapper Addon..." -cd "$REAL_HOME/openFrameworks/addons" -# FORCE CLEAN CLONE to fix corrupted source files from previous failed runs -rm -rf ofxPiMapper -sudo -u "$REAL_USER" git clone --depth 1 https://github.com/kr15h/ofxPiMapper.git - -# Apply patches to the addon before building -cd "$REAL_HOME/openFrameworks/addons/ofxPiMapper" -echo "Applying clean Global Compatibility Patches..." - -# 1. Locate OscControl.cpp and apply highlight patch -# Use 'find' to handle different subfolder structures -OSC_FILE=$(find src -name "OscControl.cpp" | head -n 1) -if [ -n "$OSC_FILE" ]; then - echo "Patching $OSC_FILE..." - cat <<'EOF_PATCH' > /tmp/highlight.patch - if (m.getAddress() == "/ofxPiMapper/surface/highlight"){ - int surfaceIndex = m.getArgAsInt32(0); - mapper->getSurfaceManager()->selectSurface(surfaceIndex); - return; - } -EOF_PATCH - sed -i '/if (m.getAddress() == "\/ofxPiMapper\/surface\/select"){/r /tmp/highlight.patch' "$OSC_FILE" - rm -f /tmp/highlight.patch -fi - -# 2. Create a single compatibility header for C++11 -cat <<'EOF_COMPAT' > src/cpp11_compat.h -#ifndef OFXPIMAPPER_CPP11_COMPAT_H -#define OFXPIMAPPER_CPP11_COMPAT_H -#include -#include -#include -#include -#include - -#if __cplusplus < 201402L -namespace std { - template - std::unique_ptr make_unique(Args&&... args) { - return std::unique_ptr(new T(std::forward(args)...)); - } -} -#endif - -// Explicitly bridge common namespaces to bypass C++20 strictness -using std::unique_ptr; -using std::make_unique; -using std::vector; -using std::string; -#endif -EOF_COMPAT - -# 3. Include the compat header in the main addon header -sed -i '1i #include "cpp11_compat.h"' src/ofxPiMapper.h - -# 4. Fix unique_ptr and make_unique globally (Using relative paths and avoiding std::std::) -find src -type f \( -name "*.h" -o -name "*.cpp" \) -exec sed -i 's/\([^:a-zA-Z0-9]\)unique_ptr\b/\1std::unique_ptr/g' {} + -find src -type f \( -name "*.h" -o -name "*.cpp" \) -exec sed -i 's/\([^:a-zA-Z0-9]\)make_unique\b/\1std::make_unique/g' {} + - -# 5. Add memory header only where needed -for f in $(grep -l "unique_ptr" src/*.h 2>/dev/null || true); do - grep -q "" "$f" || sed -i '1i #include ' "$f" -done - -# Build Example -cd "$REAL_HOME/openFrameworks/addons/ofxPiMapper/example_basic" -echo "Cleaning old objects..." -sudo -u "$REAL_USER" make clean - -echo "Starting compilation (Nuclear Stability Mode: -O0 + C++11 + VRP disabled)..." -GLOBAL_FLAGS="-Wno-error -Wno-reorder -Wno-sign-compare -Wno-delete-non-virtual-dtor -std=c++11 -O0 -fno-tree-vrp" - -# Cross-compile support: if host is not aarch64 and cross toolchain is present, use it -MAKE_ENV="" -if [ "$(uname -m)" != "aarch64" ] && command -v aarch64-linux-gnu-g++ > /dev/null 2>&1; then - echo "Detected x86_64 host with aarch64 toolchain; building aarch64 binaries." - MAKE_ENV="CC=aarch64-linux-gnu-gcc CXX=aarch64-linux-gnu-g++ AR=aarch64-linux-gnu-ar LD=aarch64-linux-gnu-ld" -fi - -sudo -u "$REAL_USER" env $MAKE_ENV make -j1 \ - PLATFORM_CFLAGS="$GLOBAL_FLAGS -I$REAL_HOME/openFrameworks/addons/ofxPiMapper/src" \ - USER_CFLAGS="$GLOBAL_FLAGS" \ - USER_CPPFLAGS="-std=c++11" \ - OF_PROJECT_OPTIMIZATION_FLAGS="-O0" - -cp bin/example_basic /usr/local/bin/ofxPiMapper -chmod +x /usr/local/bin/ofxPiMapper - -# 5. FINALIZATION -echo "Cleaning up..." -swapoff /swapfile_mpvj 2>/dev/null || true -rm /swapfile_mpvj 2>/dev/null || true - -if [ "$(cat /proc/1/comm 2>/dev/null)" = "systemd" ]; then - whiptail --title "Success" --msgbox "INSTALL COMPLETE!\n\nRebooting into AP Mode.\nSSID: $WIFI_SSID\nIP: 192.168.4.1" 12 45 < /dev/tty - reboot -else - echo "INSTALL COMPLETE. Running inside a non-systemd environment (e.g., container)." - echo "Start the backend with: node $REAL_HOME/mpvj/backend/index.js" - echo "(No reboot performed.)" -fi +# ── 6. DONE ─────────────────────────────────────────────────────────────────── +whiptail --title "Success" --msgbox "INSTALL COMPLETE!\n\nRebooting into AP Mode.\nSSID: $WIFI_SSID\nIP: 192.168.4.1" 12 45 < /dev/tty +reboot