name: 'Install macOS Source Dependencies' description: 'Builds (and per-dependency caches) the source-only deps not available from Homebrew (GTSAM, libnabo+libpointmatcher, OrbbecSDK, depthai) and installs them into /usr/local.' inputs: os: description: 'Runner os label (e.g. matrix.os), used to namespace the per-dependency caches. Required because intel/arm produce different binaries with identical Homebrew version strings.' required: true build_type: description: 'CMake build type.' required: false default: 'Release' runs: using: "composite" steps: - name: Resolve dependency versions id: depver shell: bash # The source deps link against these Homebrew libraries and are compiled # with Xcode's toolchain, so both are folded into the cache keys: a # Homebrew bump (e.g. a new Eigen) or an Xcode update forces a rebuild. run: | BREW=$(brew list --versions eigen boost yaml-cpp | sort) XCODE=$(xcodebuild -version 2>/dev/null || clang --version | head -1) HASH=$(printf '%s\n%s\n' "$BREW" "$XCODE" | shasum | cut -d' ' -f1) echo "hash=$HASH" >> "$GITHUB_OUTPUT" # ----------------------------- GTSAM ----------------------------- - name: Cache GTSAM id: cache-gtsam uses: actions/cache@v4 with: path: ${{ runner.temp }}/deps-stage/gtsam key: gtsam-4.2.1-${{ inputs.os }}-${{ hashFiles('.github/actions/install-macos-source-deps/patches/gtsam-4.2.1-eigen5.patch') }}-${{ steps.depver.outputs.hash }} - name: Build GTSAM if: steps.cache-gtsam.outputs.cache-hit != 'true' shell: bash run: | set -e NPROC=$(sysctl -n hw.logicalcpu) PATCHES="${{ github.action_path }}/patches" SRC="${{ runner.temp }}/src-deps/gtsam" STAGE="${{ runner.temp }}/deps-stage/gtsam" rm -rf "$SRC"; mkdir -p "$SRC" "$STAGE" git clone --depth 1 --branch 4.2.1 https://github.com/borglab/gtsam.git "$SRC/gtsam" cd "$SRC/gtsam" git apply "$PATCHES/gtsam-4.2.1-eigen5.patch" cmake -B build \ -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} \ -DGTSAM_BUILD_WITH_MARCH_NATIVE=OFF \ -DGTSAM_WITH_TBB=OFF \ -DGTSAM_BUILD_EXAMPLES_ALWAYS=OFF \ -DGTSAM_BUILD_TESTS=OFF \ -DGTSAM_BUILD_UNSTABLE=OFF \ -DGTSAM_USE_SYSTEM_EIGEN=ON \ -DCMAKE_POLICY_VERSION_MINIMUM=3.5 cmake --build build -j$NPROC DESTDIR="$STAGE" cmake --install build # ----------------- libnabo + libpointmatcher -------------------- # Built together: libpointmatcher depends on libnabo, so keeping them in one # unit keeps the build order internal (lpm finds libnabo in the unit's stage). - name: Cache pointmatcher id: cache-pointmatcher uses: actions/cache@v4 with: path: ${{ runner.temp }}/deps-stage/pointmatcher key: pointmatcher-nabo1.1.2-lpm1.4.4-${{ inputs.os }}-${{ hashFiles('.github/actions/install-macos-source-deps/patches/libnabo-1.1.2.patch', '.github/actions/install-macos-source-deps/patches/libpointmatcher-1.4.4-boost.patch') }}-${{ steps.depver.outputs.hash }} - name: Build pointmatcher if: steps.cache-pointmatcher.outputs.cache-hit != 'true' shell: bash run: | set -e NPROC=$(sysctl -n hw.logicalcpu) PATCHES="${{ github.action_path }}/patches" SRC="${{ runner.temp }}/src-deps/pointmatcher" STAGE="${{ runner.temp }}/deps-stage/pointmatcher" rm -rf "$SRC"; mkdir -p "$SRC" "$STAGE" # libnabo (required by libpointmatcher) git clone --depth 1 --branch 1.1.2 https://github.com/ethz-asl/libnabo.git "$SRC/libnabo" cd "$SRC/libnabo" git apply "$PATCHES/libnabo-1.1.2.patch" cmake -B build -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} cmake --build build -j$NPROC DESTDIR="$STAGE" cmake --install build # libpointmatcher (finds libnabo from the unit's staged prefix) git clone --depth 1 --branch 1.4.4 https://github.com/ethz-asl/libpointmatcher.git "$SRC/libpointmatcher" cd "$SRC/libpointmatcher" git apply "$PATCHES/libpointmatcher-1.4.4-boost.patch" cmake -B build -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DCMAKE_PREFIX_PATH="$STAGE/usr/local" -DBUILD_EVALUATIONS=OFF -DBUILD_EXAMPLES=OFF cmake --build build -j$NPROC DESTDIR="$STAGE" cmake --install build # ----------------------------- OrbbecSDK ------------------------ - name: Cache OrbbecSDK id: cache-orbbec uses: actions/cache@v4 with: path: ${{ runner.temp }}/deps-stage/orbbec key: orbbec-2.8.7-usrlocal-${{ inputs.os }}-${{ hashFiles('.github/actions/install-macos-source-deps/patches/orbbecsdk-2.8.7-cmake-config-install.patch') }}-${{ steps.depver.outputs.hash }} - name: Build OrbbecSDK if: steps.cache-orbbec.outputs.cache-hit != 'true' shell: bash run: | set -e NPROC=$(sysctl -n hw.logicalcpu) PATCHES="${{ github.action_path }}/patches" SRC="${{ runner.temp }}/src-deps/orbbec" STAGE="${{ runner.temp }}/deps-stage/orbbec" rm -rf "$SRC"; mkdir -p "$SRC" "$STAGE" git clone --depth 1 --branch v2.8.7 https://github.com/orbbec/OrbbecSDK_v2.git "$SRC/OrbbecSDK_v2" cd "$SRC/OrbbecSDK_v2" git apply "$PATCHES/orbbecsdk-2.8.7-cmake-config-install.patch" # OrbbecSDK defaults its install prefix to /opt/OrbbecSDK; force /usr/local # so it stages under usr/local like the other deps and is installed/found. cmake -B build -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DCMAKE_INSTALL_PREFIX=/usr/local -DOB_BUILD_DOCS=OFF -DOB_BUILD_EXAMPLES=OFF -DOB_BUILD_TOOLS=OFF -DOB_INSTALL_EXAMPLES_SOURCE=OFF cmake --build build -j$NPROC DESTDIR="$STAGE" cmake --install build # ----------------------------- depthai -------------------------- - name: Cache depthai id: cache-depthai uses: actions/cache@v4 with: path: ${{ runner.temp }}/deps-stage/depthai key: depthai-2.32.0-usrlocal-${{ inputs.os }}-${{ hashFiles('.github/actions/install-macos-source-deps/patches/depthai-2.32.0-hunter-macos.patch') }}-${{ steps.depver.outputs.hash }} - name: Build depthai if: steps.cache-depthai.outputs.cache-hit != 'true' shell: bash run: | set -e NPROC=$(sysctl -n hw.logicalcpu) PATCHES="${{ github.action_path }}/patches" SRC="${{ runner.temp }}/src-deps/depthai" STAGE="${{ runner.temp }}/deps-stage/depthai" rm -rf "$SRC"; mkdir -p "$SRC" "$STAGE" # depthai 2.32.0 (Hunter-based; v2 does NOT use vcpkg/CMake presets) git clone --depth 1 --branch v2.32.0 https://github.com/luxonis/depthai-core.git "$SRC/depthai-core" cd "$SRC/depthai-core" git submodule update --init --recursive # Route zlib to Homebrew: Hunter's pinned zlib fork has a classic # TARGET_OS_MAC fdopen block that won't compile against the modern macOS SDK. git apply "$PATCHES/depthai-2.32.0-hunter-macos.patch" # v2 and its old Hunter deps declare cmake_minimum_required < 3.5, which the # runner's CMake 4.x rejects, and CMAKE_POLICY_VERSION_MINIMUM does not # propagate into Hunter's sub-builds. Use a pinned CMake 3.x just for depthai # (Hunter reuses the same cmake binary for its dependency sub-builds). python3 -m venv "$SRC/cmake3-venv" "$SRC/cmake3-venv/bin/pip" install -q "cmake==3.31.6" CMAKE3="$SRC/cmake3-venv/bin/cmake" # DEPTHAI_ENABLE_CURL=OFF: Hunter's CURL drags in the broken zlib and also # fails to configure on this toolchain; rtabmap doesn't need depthai's CURL. # depthai-core defaults its install prefix to /install; force # /usr/local so it stages under usr/local like the other deps. "$CMAKE3" -B build \ -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} \ -DCMAKE_INSTALL_PREFIX=/usr/local \ -DBUILD_SHARED_LIBS=ON \ -DDEPTHAI_ENABLE_CURL=OFF \ -DDEPTHAI_BUILD_TESTS=OFF \ -DDEPTHAI_BUILD_EXAMPLES=OFF \ -DCMAKE_PREFIX_PATH="$(brew --prefix zlib)" "$CMAKE3" --build build -j$NPROC DESTDIR="$STAGE" "$CMAKE3" --install build # ----------------------------- opengv --------------------------- - name: Cache opengv id: cache-opengv uses: actions/cache@v4 with: path: ${{ runner.temp }}/deps-stage/opengv key: opengv-91f4b19c-usrlocal-${{ inputs.os }}-${{ hashFiles('.github/actions/install-macos-source-deps/patches/opengv-91f4b19c-macos.patch') }}-${{ steps.depver.outputs.hash }} - name: Build opengv if: steps.cache-opengv.outputs.cache-hit != 'true' shell: bash run: | set -e NPROC=$(sysctl -n hw.logicalcpu) PATCHES="${{ github.action_path }}/patches" SRC="${{ runner.temp }}/src-deps/opengv" STAGE="${{ runner.temp }}/deps-stage/opengv" rm -rf "$SRC"; mkdir -p "$SRC" "$STAGE" # opengv pins a commit (not a tag), so clone then checkout. git clone https://github.com/laurentkneip/opengv.git "$SRC/opengv" cd "$SRC/opengv" git checkout 91f4b19c73450833a40e463ad3648aae80b3a7f3 # Disable the -march=native/armv7-a/armv8-a flags so it builds portably on # both Intel and Apple Silicon runners. git apply "$PATCHES/opengv-91f4b19c-macos.patch" # CMAKE_POLICY_VERSION_MINIMUM=3.5: opengv declares an ancient # cmake_minimum_required that modern CMake rejects. EIGEN_INCLUDE_DIR points # opengv's bundled FindEigen at Homebrew's eigen (covers /opt/homebrew on arm64). cmake -B build \ -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} \ -DCMAKE_INSTALL_PREFIX=/usr/local \ -DBUILD_TESTS=OFF \ -DBUILD_SHARED_LIBS=ON \ -DCMAKE_POLICY_VERSION_MINIMUM=3.5 \ -DEIGEN_INCLUDE_DIR="$(brew --prefix eigen)/include/eigen3" cmake --build build -j$NPROC DESTDIR="$STAGE" cmake --install build # ----------------------------- g2o ------------------------------ - name: Cache g2o id: cache-g2o uses: actions/cache@v4 with: path: ${{ runner.temp }}/deps-stage/g2o key: g2o-67cbe15c-${{ inputs.os }}-${{ steps.depver.outputs.hash }} - name: Build g2o if: steps.cache-g2o.outputs.cache-hit != 'true' shell: bash run: | set -e NPROC=$(sysctl -n hw.logicalcpu) SRC="${{ runner.temp }}/src-deps/g2o" STAGE="${{ runner.temp }}/deps-stage/g2o" rm -rf "$SRC"; mkdir -p "$SRC" "$STAGE" # g2o pins a commit (not a tag), so clone then checkout. git clone https://github.com/RainerKuemmerle/g2o.git "$SRC/g2o" cd "$SRC/g2o" git checkout 67cbe15c998737ac6705d3cd18201a72be0d073d # CMAKE_INSTALL_PREFIX=/usr/local so it stages under usr/local like the # other deps. Homebrew's libomp is keg-only and g2o does not propagate # OpenMP's include/lib dirs, so with Apple clang isn't found and # libomp isn't linked; add its include (-I) and lib (-L -lomp) explicitly. LIBOMP=$(brew --prefix libomp) cmake -B build \ -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} \ -DCMAKE_INSTALL_PREFIX=/usr/local \ -DCMAKE_PREFIX_PATH="$LIBOMP" \ -DCMAKE_C_FLAGS="-I$LIBOMP/include" \ -DCMAKE_CXX_FLAGS="-I$LIBOMP/include" \ -DCMAKE_EXE_LINKER_FLAGS="-L$LIBOMP/lib -lomp" \ -DCMAKE_SHARED_LINKER_FLAGS="-L$LIBOMP/lib -lomp" \ -DG2O_BUILD_APPS=OFF \ -DG2O_BUILD_EXAMPLES=OFF \ -DG2O_USE_OPENMP=ON \ -DG2O_USE_OPENGL=OFF cmake --build build -j$NPROC DESTDIR="$STAGE" cmake --install build # ------------------- install all units into /usr/local ---------- - name: Install source dependencies into /usr/local shell: bash # Copy each (cached or freshly built) staged tree into /usr/local so # rtabmap's find_package() and the bundle fixup (which search /usr/local) # pick them up. Runs on both cache hit and miss. run: | set -e STAGE="${{ runner.temp }}/deps-stage" found=0 for unit in gtsam pointmatcher orbbec depthai opengv g2o; do if [ -d "$STAGE/$unit/usr/local" ]; then sudo cp -a "$STAGE/$unit/usr/local/." /usr/local/ found=1 fi done if [ "$found" != "1" ]; then echo "No staged source dependencies found under $STAGE" >&2 exit 1 fi