diff --git a/docker/scripts/build_opencv.sh b/docker/scripts/build_opencv.sh index c0106f3..2665ed6 100755 --- a/docker/scripts/build_opencv.sh +++ b/docker/scripts/build_opencv.sh @@ -1,6 +1,11 @@ #!/usr/bin/env bash set -eo pipefail +OPENCV_VERSION=$1 +# Build directory defaults to /OpenCV (Dockerfile) but can be overridden when +# running locally via setup_local.sh. +OPENCV_BUILD_DIR="${OPENCV_BUILD_DIR:-/OpenCV}" + CMAKE_FLAGS=" \ -DCPACK_BINARY_DEB=ON \ -DBUILD_EXAMPLES=OFF \ @@ -9,7 +14,7 @@ CMAKE_FLAGS=" \ -DBUILD_opencv_java=OFF \ -DCMAKE_BUILD_TYPE=RELEASE \ -DCMAKE_INSTALL_PREFIX=/usr/local \ - -DOPENCV_EXTRA_MODULES_PATH=/OpenCV/opencv_contrib/modules \ + -DOPENCV_EXTRA_MODULES_PATH=${OPENCV_BUILD_DIR}/opencv_contrib/modules \ -DCUDA_FAST_MATH=ON \ -DEIGEN_INCLUDE_PATH=/usr/include/eigen3 \ -DWITH_EIGEN=ON \ @@ -18,13 +23,14 @@ CMAKE_FLAGS=" \ -DBUILD_PERF_TESTS=OFF \ -DBUILD_TESTS=OFF" -OPENCV_VERSION=$1 +# Use nproc on Linux, sysctl on macOS +JOBS=$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 4) -mkdir /OpenCV && cd /OpenCV && +mkdir -p "${OPENCV_BUILD_DIR}" && cd "${OPENCV_BUILD_DIR}" && -git clone --depth 1 --branch ${OPENCV_VERSION} https://github.com/opencv/opencv.git -git clone --depth 1 --branch ${OPENCV_VERSION} https://github.com/opencv/opencv_contrib.git +[ -d opencv ] || git clone --depth 1 --branch ${OPENCV_VERSION} https://github.com/opencv/opencv.git +[ -d opencv_contrib ] || git clone --depth 1 --branch ${OPENCV_VERSION} https://github.com/opencv/opencv_contrib.git cmake -S opencv -B build ${CMAKE_FLAGS} && \ -cmake --build build --config Release -- -j$(nproc) && \ +cmake --build build --config Release -- -j${JOBS} && \ cmake --install build --config Release --prefix ./install \ No newline at end of file diff --git a/docker/scripts/build_px4.sh b/docker/scripts/build_px4.sh index b49351b..cc3eb4c 100755 --- a/docker/scripts/build_px4.sh +++ b/docker/scripts/build_px4.sh @@ -2,12 +2,58 @@ set -eo pipefail PX4_VERSION=$1 +# Where PX4 will be cloned/built. Dockerfile leaves this as /home/${USER}; +# setup_local.sh overrides to $HOME so it works for any local username. +PX4_BUILD_DIR="${PX4_BUILD_DIR:-/home/${USER}}" -git clone --recurse-submodules -b ${PX4_VERSION} https://github.com/PX4/PX4-Autopilot.git /home/${USER}/PX4-Autopilot +[ -d "${PX4_BUILD_DIR}/PX4-Autopilot" ] || \ + git clone --recurse-submodules -b ${PX4_VERSION} https://github.com/PX4/PX4-Autopilot.git "${PX4_BUILD_DIR}/PX4-Autopilot" -cd /home/${USER}/PX4-Autopilot +cd "${PX4_BUILD_DIR}/PX4-Autopilot" -# Patch to allow setting parameters via env variables +# Patch to allow setting parameters via env variables. Applied via `git apply +# --reverse --check` first to make this idempotent on re-runs. +if git apply --reverse --check - <<'EOF' >/dev/null 2>&1 +diff --git a/ROMFS/px4fmu_common/init.d-posix/rcS b/ROMFS/px4fmu_common/init.d-posix/rcS +index fed292b9d1..1d34854077 100644 +--- a/ROMFS/px4fmu_common/init.d-posix/rcS ++++ b/ROMFS/px4fmu_common/init.d-posix/rcS +@@ -126,15 +126,6 @@ then + set AUTOCNF yes + fi + +-# Allow overriding parameters via env variables: export PX4_PARAM_{name}={value} +-env | while IFS='=' read -r line; do +- value=${line#*=} +- name=${line%%=*} +- case $name in +- "PX4_PARAM_"*) param set "${name#PX4_PARAM_}" "$value" ;; +- esac +-done +- + # multi-instance setup + # shellcheck disable=SC2154 + param set MAV_SYS_ID $((px4_instance+1)) +@@ -239,6 +230,15 @@ then + exit 1 + fi + ++# Allow overriding parameters via env variables: export PX4_PARAM_{name}={value} ++env | while IFS='=' read -r line; do ++ value=${line#*=} ++ name=${line%%=*} ++ case $name in ++ "PX4_PARAM_"*) param set "${name#PX4_PARAM_}" "$value" ;; ++ esac ++done ++ + dataman start + + # only start the simulator if not in replay mode, as both control the lockstep time +EOF +then + echo "Patch already applied, skipping." +else git apply - <<'EOF' diff --git a/ROMFS/px4fmu_common/init.d-posix/rcS b/ROMFS/px4fmu_common/init.d-posix/rcS index fed292b9d1..1d34854077 100644 @@ -43,13 +89,28 @@ index fed292b9d1..1d34854077 100644 +done + dataman start - + # only start the simulator if not in replay mode, as both control the lockstep time EOF +fi + +# Platform-specific PX4 setup. Docker / Ubuntu host uses ubuntu.sh; macOS host +# (when invoked from setup_local.sh) uses macos.sh from PX4-Autopilot itself. +case "$(uname -s)" in + Linux*) + ./Tools/setup/ubuntu.sh --no-nuttx --no-sim-tools + ;; + Darwin*) + ./Tools/setup/macos.sh + ;; + *) + echo "Unsupported PX4 host platform: $(uname -s)" >&2 + exit 1 + ;; +esac -./Tools/setup/ubuntu.sh --no-nuttx --no-sim-tools make px4_sitl_default -cd .. +cd "${PX4_BUILD_DIR}" mkdir -p px4_sitl/bin mkdir -p px4_sitl/romfs/etc cp -a PX4-Autopilot/build/px4_sitl_default/bin px4_sitl/ diff --git a/docker/scripts/build_ros_deps.sh b/docker/scripts/build_ros_deps.sh index 9526e09..156596c 100755 --- a/docker/scripts/build_ros_deps.sh +++ b/docker/scripts/build_ros_deps.sh @@ -5,16 +5,19 @@ MICRO_XRCE_DDS_AGENT_VERSION=$1 PX4_MSGS_VERSION=$2 PX4_ROS2_INTERFACE_LIB_VERSION=$3 PX4_ROS_COM_VERSION=$4 +# Workspace path defaults to /root/px4_ros_ws (Dockerfile) but can be overridden +# when running locally via setup_local.sh. +PX4_ROS_WS="${PX4_ROS_WS:-/root/px4_ros_ws}" -mkdir -p /root/px4_ros_ws/src && cd /root/px4_ros_ws/src && \ -git clone --depth 1 -b ${MICRO_XRCE_DDS_AGENT_VERSION} https://github.com/eProsima/Micro-XRCE-DDS-Agent.git && \ -git clone --depth 1 -b ${PX4_MSGS_VERSION} https://github.com/PX4/px4_msgs.git && \ -git clone --depth 1 -b ${PX4_ROS2_INTERFACE_LIB_VERSION} https://github.com/Auterion/px4-ros2-interface-lib.git && \ -git clone --depth 1 -b ${PX4_ROS_COM_VERSION} https://github.com/PX4/px4_ros_com.git && \ -git clone --depth 1 -b humble https://github.com/ros-perception/vision_opencv.git && \ -git clone --depth 1 -b humble https://github.com/ros-perception/image_common.git && \ -git clone --depth 1 -b humble https://github.com/ros-perception/image_transport_plugins.git && \ -git clone --depth 1 -b humble https://github.com/gazebosim/ros_gz.git && \ +mkdir -p "${PX4_ROS_WS}/src" && cd "${PX4_ROS_WS}/src" && \ +[ -d Micro-XRCE-DDS-Agent ] || git clone --depth 1 -b ${MICRO_XRCE_DDS_AGENT_VERSION} https://github.com/eProsima/Micro-XRCE-DDS-Agent.git +[ -d px4_msgs ] || git clone --depth 1 -b ${PX4_MSGS_VERSION} https://github.com/PX4/px4_msgs.git +[ -d px4-ros2-interface-lib ] || git clone --depth 1 -b ${PX4_ROS2_INTERFACE_LIB_VERSION} https://github.com/Auterion/px4-ros2-interface-lib.git +[ -d px4_ros_com ] || git clone --depth 1 -b ${PX4_ROS_COM_VERSION} https://github.com/PX4/px4_ros_com.git +[ -d vision_opencv ] || git clone --depth 1 -b humble https://github.com/ros-perception/vision_opencv.git +[ -d image_common ] || git clone --depth 1 -b humble https://github.com/ros-perception/image_common.git +[ -d image_transport_plugins ] || git clone --depth 1 -b humble https://github.com/ros-perception/image_transport_plugins.git +[ -d ros_gz ] || git clone --depth 1 -b humble https://github.com/gazebosim/ros_gz.git rm -rf ros_gz/ros_ign* \ ros_gz/ros_gz_sim_demos \ image_transport_plugins/compressed_depth_image_transport \ diff --git a/docker/scripts/install_deps.sh b/docker/scripts/install_deps.sh index 66e631d..1daaf37 100755 --- a/docker/scripts/install_deps.sh +++ b/docker/scripts/install_deps.sh @@ -1,16 +1,20 @@ #!/usr/bin/env bash set -euo pipefail -apt-get update && \ -apt-get upgrade -y && \ -apt-get install -y --no-install-recommends \ +# SUDO is empty inside the Dockerfile (running as root); setup_local.sh sets +# it to "sudo" so the same script works for a non-root local install. +SUDO="${SUDO:-}" + +${SUDO} apt-get update && \ +${SUDO} apt-get upgrade -y && \ +${SUDO} apt-get install -y --no-install-recommends \ curl \ lsb-release \ gnupg && \ -curl https://packages.osrfoundation.org/gazebo.gpg --output /usr/share/keyrings/pkgs-osrf-archive-keyring.gpg && \ -echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/pkgs-osrf-archive-keyring.gpg] http://packages.osrfoundation.org/gazebo/ubuntu-stable $(lsb_release -cs) main" | tee /etc/apt/sources.list.d/gazebo-stable.list > /dev/null && \ -apt-get update && \ -apt-get install -y --no-install-recommends \ +${SUDO} sh -c 'curl https://packages.osrfoundation.org/gazebo.gpg --output /usr/share/keyrings/pkgs-osrf-archive-keyring.gpg' && \ +echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/pkgs-osrf-archive-keyring.gpg] http://packages.osrfoundation.org/gazebo/ubuntu-stable $(lsb_release -cs) main" | ${SUDO} tee /etc/apt/sources.list.d/gazebo-stable.list > /dev/null && \ +${SUDO} apt-get update && \ +${SUDO} apt-get install -y --no-install-recommends \ gz-harmonic \ ros-humble-foxglove-bridge \ bc \ @@ -37,5 +41,5 @@ apt-get install -y --no-install-recommends \ libgflags-dev \ python3-rospkg -rm -rf /var/lib/apt/lists/* -apt-get clean \ No newline at end of file +${SUDO} rm -rf /var/lib/apt/lists/* +${SUDO} apt-get clean \ No newline at end of file diff --git a/docker/scripts/install_qgc.sh b/docker/scripts/install_qgc.sh index 8db0d14..03b23e7 100755 --- a/docker/scripts/install_qgc.sh +++ b/docker/scripts/install_qgc.sh @@ -3,16 +3,21 @@ set -eo pipefail QGC_VERSION=$1 TARGETARCH=$2 +# Install location. Dockerfile leaves this as /home/${USER}; setup_local.sh +# overrides to $HOME for any local username. +QGC_INSTALL_DIR="${QGC_INSTALL_DIR:-/home/${USER}}" if [ "${TARGETARCH}" = "amd64" ]; then - cd /home/${USER} - wget https://github.com/mavlink/qgroundcontrol/releases/download/${QGC_VERSION}/QGroundControl-x86_64.AppImage + cd "${QGC_INSTALL_DIR}" + [ -f QGroundControl-x86_64.AppImage ] || \ + wget https://github.com/mavlink/qgroundcontrol/releases/download/${QGC_VERSION}/QGroundControl-x86_64.AppImage chmod +x ./QGroundControl-x86_64.AppImage - ./QGroundControl-x86_64.AppImage --appimage-extract - mv /home/${USER}/squashfs-root /home/${USER}/QGroundControl - chmod +x /home/${USER}/QGroundControl/AppRun - ln -s /home/${USER}/QGroundControl/AppRun /home/${USER}/QGroundControl/qgroundcontrol + [ -d squashfs-root ] || ./QGroundControl-x86_64.AppImage --appimage-extract + [ -d "${QGC_INSTALL_DIR}/QGroundControl" ] || mv "${QGC_INSTALL_DIR}/squashfs-root" "${QGC_INSTALL_DIR}/QGroundControl" + chmod +x "${QGC_INSTALL_DIR}/QGroundControl/AppRun" + [ -L "${QGC_INSTALL_DIR}/QGroundControl/qgroundcontrol" ] || \ + ln -s "${QGC_INSTALL_DIR}/QGroundControl/AppRun" "${QGC_INSTALL_DIR}/QGroundControl/qgroundcontrol" else - mkdir -p /home/${USER}/QGroundControl - echo "QGroundControl is only available for amd64 architecture." >> /home/${USER}/QGroundControl/install.log + mkdir -p "${QGC_INSTALL_DIR}/QGroundControl" + echo "QGroundControl is only available for amd64 architecture." >> "${QGC_INSTALL_DIR}/QGroundControl/install.log" fi \ No newline at end of file diff --git a/setup_local.sh b/setup_local.sh new file mode 100755 index 0000000..7809e0a --- /dev/null +++ b/setup_local.sh @@ -0,0 +1,397 @@ +#!/usr/bin/env bash +# setup_local.sh +# +# Reproduce the workshop docker image stages on the host. Use this if Docker +# is not an option (e.g. macOS without Docker Desktop, GPU driver issues +# inside the container, restricted environments) and you want to run the +# workshop natively. +# +# What it does — mirrors docker/Dockerfile: +# 1. install system packages (Gazebo Harmonic, ROS 2 Humble, build deps) +# 2. build OpenCV 4.10 from source (aruco_tracker depends on it) +# 3. clone + build ROS 2 deps (Micro-XRCE-DDS-Agent, px4_msgs, +# px4-ros2-interface-lib, px4_ros_com, vision_opencv, image_common, +# image_transport_plugins, ros_gz) +# 4. clone + build PX4 v1.16 SITL +# 5. install QGroundControl v5.0.8 +# 6. build the workshop workspace +# +# Supported: +# Ubuntu 22.04 (matches the docker base image, recommended) +# Ubuntu 24.04 (mostly OK; some apt package names may differ) +# macOS: ROS 2 Humble + Gazebo Harmonic are NOT officially supported +# on macOS. The script refuses by default and points you to +# Docker via OrbStack / Colima / Lima. +# +# Time: ~45-60 min on a fast SSD with good network +# Space: ~20 GB + +set -eo pipefail + +# ---------- defaults (matched against the Dockerfile) ---------- +OPENCV_VERSION="4.10.0" +MICRO_XRCE_DDS_AGENT_VERSION="v2.4.2" +PX4_MSGS_VERSION="release/1.16" +PX4_ROS2_INTERFACE_LIB_VERSION="release/1.16" +PX4_ROS_COM_VERSION="release/1.16" +PX4_VERSION="v1.16.0" +QGC_VERSION="v5.0.8" + +# ---------- arg parsing ---------- +SKIP_DEPS=false +SKIP_OPENCV=false +SKIP_ROS_DEPS=false +SKIP_PX4=false +SKIP_QGC=false +SKIP_WS=false +PREFIX="" + +usage() { + cat <&2; usage; exit 1 ;; + esac +done + +# ---------- locate this script + repo root ---------- +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="${SCRIPT_DIR}" +DOCKER_SCRIPTS="${REPO_ROOT}/docker/scripts" + +if [ ! -d "${DOCKER_SCRIPTS}" ]; then + echo "Error: cannot find ${DOCKER_SCRIPTS}. Run this from the repo root." >&2 + exit 1 +fi + +# ---------- OS detection ---------- +case "$(uname -s)" in + Linux*) + if ! command -v lsb_release >/dev/null 2>&1; then + sudo apt-get update -qq && sudo apt-get install -y -qq lsb-release + fi + UBUNTU_CODENAME="$(lsb_release -cs)" + UBUNTU_VERSION="$(lsb_release -rs)" + if [ "$(lsb_release -is)" != "Ubuntu" ]; then + echo "Error: only Ubuntu is supported on Linux. Got: $(lsb_release -is)" >&2 + exit 1 + fi + case "${UBUNTU_VERSION}" in + 22.04|24.04) ;; + *) echo "Warning: only Ubuntu 22.04 (recommended) and 24.04 are tested. You have ${UBUNTU_VERSION}." >&2 ;; + esac + OS=ubuntu + SUDO=sudo + ARCH="$(dpkg --print-architecture)" + ;; + Darwin*) + OS=macos + SUDO="" + MACOS_VERSION="$(sw_vers -productVersion 2>/dev/null || echo unknown)" + ARCH="$(uname -m)" + cat >&2 < macOS detected (${MACOS_VERSION}, ${ARCH}). + +This script can attempt a native install on macOS but be aware of the +upstream support situation (verified against current REP-2000 and Gazebo +docs): + + * ROS 2 Humble on macOS is **Tier 3** in REP-2000 — community-supported, + source-build only, no binaries. Build is non-trivial and slow. + https://docs.ros.org/en/humble/Installation/Alternatives/macOS-Development-Setup.html + * Gazebo Harmonic on macOS is brew-installable via the osrf/simulation + tap, but officially listed for Big Sur and Monterey. + https://gazebosim.org/docs/harmonic/install_osx/ + macOS 15.x (Sequoia) has known OGRE build failures in the tap. + +If you want the path of least resistance, prefer Docker via OrbStack +(or Colima/Lima/Docker Desktop): + + brew install orbstack + ./docker/docker_build.sh && ./docker/docker_run.sh + +Otherwise, this script will install Homebrew prereqs and Gazebo Harmonic, +build OpenCV and PX4 from source, and STUB the ROS 2 + workshop-workspace +steps (because building ROS 2 Humble from source is a 1-2 hour separate +process — follow the official docs after this script finishes). + +Continue with the partial native install? [y/N] +EOF + if [[ "${OSSNA_FORCE_MACOS:-}" != "1" ]]; then + read -r ans + case "${ans,,}" in + y|yes) ;; + *) echo "Aborted. (Set OSSNA_FORCE_MACOS=1 to skip this prompt.)"; exit 1 ;; + esac + fi + ;; + *) + echo "Error: unsupported OS $(uname -s)" >&2 + exit 1 + ;; +esac + +# ---------- prefix paths ---------- +[ -n "${PREFIX}" ] || PREFIX="${HOME}" +mkdir -p "${PREFIX}" + +# Match Dockerfile layout: $HOME/PX4-Autopilot, $HOME/px4_ros_ws, $HOME/px4_sitl, +# $HOME/QGroundControl, $HOME/PX4-gazebo-models, $HOME/ossna-26-workshop_ws. +export OPENCV_BUILD_DIR="${PREFIX}/OpenCV" +export PX4_ROS_WS="${PREFIX}/px4_ros_ws" +export PX4_BUILD_DIR="${PREFIX}" # PX4-Autopilot is cloned into this dir +export QGC_INSTALL_DIR="${PREFIX}" # QGroundControl/ is created under this dir + +WS_DIR="${PREFIX}/ossna-26-workshop_ws" + +echo "==> Install prefix: ${PREFIX}" +echo "==> OS: ${OS} ${UBUNTU_VERSION:-} ${ARCH}" +echo "==> Workshop ws: ${WS_DIR}" +echo "" + +# ---------- 1. system packages ---------- +if ! ${SKIP_DEPS}; then + if [ "${OS}" = "ubuntu" ]; then + echo "==> [1/6] Installing system packages (apt)..." + + # ROS 2 Humble apt repo (the docker image starts FROM ros:humble-ros-base, + # which already has this set up; locally we need to add it ourselves). + if [ ! -f /etc/apt/sources.list.d/ros2.list ] && [ ! -f /etc/apt/sources.list.d/ros2.sources ]; then + ${SUDO} apt-get update -qq + ${SUDO} apt-get install -y -qq curl gnupg lsb-release software-properties-common + ${SUDO} add-apt-repository -y universe + ${SUDO} curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key \ + -o /usr/share/keyrings/ros-archive-keyring.gpg + echo "deb [arch=${ARCH} signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu ${UBUNTU_CODENAME} main" \ + | ${SUDO} tee /etc/apt/sources.list.d/ros2.list > /dev/null + fi + + ${SUDO} apt-get update + ${SUDO} apt-get install -y --no-install-recommends \ + ros-humble-ros-base \ + ros-dev-tools \ + python3-colcon-common-extensions \ + python3-rosdep \ + python3-vcstool \ + git + + if [ ! -f /etc/ros/rosdep/sources.list.d/20-default.list ]; then + ${SUDO} rosdep init || true + fi + rosdep update || true + + # Now run the same apt-install pass the Dockerfile does (Gazebo Harmonic + # repo + workshop deps). install_deps.sh is sudo-aware. + SUDO="${SUDO}" "${DOCKER_SCRIPTS}/install_deps.sh" + elif [ "${OS}" = "macos" ]; then + echo "==> [1/6] Installing system packages (Homebrew)..." + + if ! command -v brew >/dev/null 2>&1; then + echo "Error: Homebrew is required. Install it from https://brew.sh first." >&2 + exit 1 + fi + + # Gazebo Harmonic via the official OSRF tap, plus build deps that + # mirror docker/scripts/install_deps.sh on the Linux side. + brew tap osrf/simulation + brew install \ + gz-harmonic \ + cmake \ + eigen \ + boost \ + gflags \ + pkg-config \ + protobuf \ + wget \ + git \ + python@3.11 + # PX4's own macOS prerequisites (matches Tools/setup/macos.sh deps). + brew install --quiet \ + gcc \ + ninja \ + ccache \ + genromfs \ + kconfig-language \ + xz \ + || true + + # ROS 2 Humble on macOS is Tier 3 / source-only. We don't attempt to + # build it here — that would add ~1-2 hours and frequently fails on + # the latest macOS. Tell the user how to proceed. + cat <<'EOF' + +NOTE: macOS system deps installed. ROS 2 Humble itself is NOT installed by +this script — on macOS it is Tier 3 in REP-2000 and must be built from +source. After this script finishes you have two options: + + (a) Easier: switch to Docker via OrbStack, run ./docker/docker_run.sh. + (b) Native: follow the official ROS 2 macOS source-install guide: + https://docs.ros.org/en/humble/Installation/Alternatives/macOS-Development-Setup.html + then rerun this script with --skip-deps --skip-px4 --skip-opencv + so it just builds the ROS 2 deps + workshop workspace. + +The remaining stages (OpenCV, PX4 SITL) WILL run and produce useful +artifacts even without ROS 2 present. + +EOF + fi +else + echo "==> [1/6] (deps step skipped)" +fi + +# Source ROS 2 for the rest of the script (Ubuntu only — on macOS the user is +# responsible for sourcing whatever ROS 2 install they built themselves). +if [ "${OS}" = "ubuntu" ]; then + if [ -f /opt/ros/humble/setup.bash ]; then + # ROS setup.bash references unbound vars; relax nounset around it + set +u + source /opt/ros/humble/setup.bash + set -u + else + echo "Error: /opt/ros/humble/setup.bash not found after deps install" >&2 + exit 1 + fi +fi + +# ---------- 2. OpenCV 4.10 ---------- +if ! ${SKIP_OPENCV}; then + echo "==> [2/6] Building OpenCV ${OPENCV_VERSION} (~10-15 min)..." + OPENCV_BUILD_DIR="${OPENCV_BUILD_DIR}" \ + "${DOCKER_SCRIPTS}/build_opencv.sh" "${OPENCV_VERSION}" + + # Mirror Dockerfile pre-dev stage: install OpenCV into /usr/local and run ldconfig + if [ "${OS}" = "ubuntu" ] && [ -d "${OPENCV_BUILD_DIR}/install" ]; then + ${SUDO} cp -r "${OPENCV_BUILD_DIR}/install/"* /usr/local/ + echo "/usr/local/lib" | ${SUDO} tee /etc/ld.so.conf.d/opencv.conf >/dev/null + ${SUDO} ldconfig + fi +fi + +# ---------- 3. ROS 2 deps ---------- +if ! ${SKIP_ROS_DEPS}; then + if [ "${OS}" = "ubuntu" ]; then + echo "==> [3/6] Building ROS 2 dependencies into ${PX4_ROS_WS} (~10-15 min)..." + PX4_ROS_WS="${PX4_ROS_WS}" \ + "${DOCKER_SCRIPTS}/build_ros_deps.sh" \ + "${MICRO_XRCE_DDS_AGENT_VERSION}" \ + "${PX4_MSGS_VERSION}" \ + "${PX4_ROS2_INTERFACE_LIB_VERSION}" \ + "${PX4_ROS_COM_VERSION}" + else + echo "==> [3/6] (skipped on macOS — needs a working ROS 2 Humble. See note above.)" + fi +fi + +# ---------- 4. PX4 v1.16 SITL ---------- +if ! ${SKIP_PX4}; then + echo "==> [4/6] Building PX4 ${PX4_VERSION} SITL into ${PX4_BUILD_DIR}/PX4-Autopilot (~10-15 min)..." + # build_px4.sh detects the platform internally and calls + # Tools/setup/ubuntu.sh on Linux, Tools/setup/macos.sh on macOS. + USER="${USER}" PX4_BUILD_DIR="${PX4_BUILD_DIR}" \ + "${DOCKER_SCRIPTS}/build_px4.sh" "${PX4_VERSION}" + + # The Dockerfile also copies the PX4 Gazebo models into ~/PX4-gazebo-models. + # Replicate that so the workshop docs commands work verbatim. + if [ ! -d "${PREFIX}/PX4-gazebo-models" ]; then + cp -r "${PX4_BUILD_DIR}/PX4-Autopilot/Tools/simulation/gz" "${PREFIX}/PX4-gazebo-models" + fi +fi + +# ---------- 5. QGroundControl ---------- +if ! ${SKIP_QGC}; then + if [ "${OS}" = "ubuntu" ]; then + echo "==> [5/6] Installing QGroundControl ${QGC_VERSION}..." + QGC_INSTALL_DIR="${QGC_INSTALL_DIR}" \ + "${DOCKER_SCRIPTS}/install_qgc.sh" "${QGC_VERSION}" "${ARCH}" + else + # macOS: install the official QGC .dmg release. This is best-effort + # because the official build for macOS is itself an x86_64 image + # that runs under Rosetta on Apple Silicon. + if [ ! -d "${QGC_INSTALL_DIR}/QGroundControl.app" ]; then + echo "==> [5/6] Downloading QGroundControl ${QGC_VERSION} .dmg..." + DMG="${QGC_INSTALL_DIR}/QGroundControl-installer.dmg" + curl -L -o "${DMG}" \ + "https://github.com/mavlink/qgroundcontrol/releases/download/${QGC_VERSION}/QGroundControl-installer.dmg" || \ + { echo "QGC download failed; install manually from https://qgroundcontrol.com/downloads/"; DMG=""; } + if [ -n "${DMG}" ] && [ -f "${DMG}" ]; then + MNT="$(hdiutil attach "${DMG}" -nobrowse -quiet | tail -1 | awk '{print $NF}')" + if [ -d "${MNT}/QGroundControl.app" ]; then + cp -R "${MNT}/QGroundControl.app" "${QGC_INSTALL_DIR}/" + hdiutil detach "${MNT}" -quiet || true + fi + rm -f "${DMG}" + fi + else + echo "==> [5/6] QGroundControl already installed at ${QGC_INSTALL_DIR}/QGroundControl.app" + fi + fi +fi + +# ---------- 6. Workshop workspace ---------- +if ! ${SKIP_WS}; then + if [ "${OS}" = "ubuntu" ]; then + echo "==> [6/6] Building the workshop workspace into ${WS_DIR}..." + mkdir -p "${WS_DIR}/src" + # Link this repo into the workspace src/ instead of copying it + if [ ! -e "${WS_DIR}/src/ossna-26-workshop" ]; then + ln -s "${REPO_ROOT}" "${WS_DIR}/src/ossna-26-workshop" + fi + set +u + source "${PX4_ROS_WS}/install/setup.bash" + set -u + ( cd "${WS_DIR}" && colcon build --symlink-install --cmake-args -DCMAKE_BUILD_TYPE=RelWithDebInfo ) + else + echo "==> [6/6] (skipped on macOS — needs a working ROS 2 Humble. See note above.)" + fi +fi + +cat <