diff --git a/.github/actions/fetch-libcvc-deps/action.yml b/.github/actions/fetch-libcvc-deps/action.yml index 2549f20f..22f9eda1 100644 --- a/.github/actions/fetch-libcvc-deps/action.yml +++ b/.github/actions/fetch-libcvc-deps/action.yml @@ -1,189 +1,54 @@ name: 'Fetch libcvc-deps' description: | - Attempts to download a pinned libcvc-deps release archive matching - the current platform / build config and extract it to a known - prefix path. The extracted root contains a self-contained CMake - prefix (Boost, HDF5, FFTW, GSL, log4cplus, CGAL, ImageMagick, - Qt 6, VTK 9.5) so workflow steps can skip the slow - VTK-from-source build whenever a matching dep bundle is available. + Installs libcvc-deps component bundles into a CMake-ready prefix. - Behaviour: - * Computes the URL from inputs (defaults match libcvc-deps's - release.yml stem naming). - * curl -fL the archive into a temp file; falls back to nothing - on any HTTP error (404, network blip, etc.) instead of - failing the build. This keeps the calling workflow robust to - the libcvc-deps release not yet existing for a given version. - * On success, extracts to /libcvc-deps-<...> and emits - that path as the `path` output. - * Caller is expected to gate the from-source VTK build step on - `steps..outputs.path != ''` and to add `path` to - CMAKE_PREFIX_PATH when set. + This action now uses `cvcpkg-install` from transfix/libcvc-deps to + download only the components listed in `cvc-requirements.yaml`, + rather than a monolithic ~2 GB archive. + + The output interface (`path`) is unchanged — callers that gate steps + on `steps..outputs.path != ''` and add the path to + CMAKE_PREFIX_PATH continue to work without modification. + + Falls back gracefully: if the catalog is unavailable (e.g. no + release has published component bundles yet), `path` is set to + empty so the calling workflow can fall back to building from + source or apt/brew. inputs: - version: - description: 'libcvc-deps release version (without leading v).' - required: false - default: '1.0.2' os: - description: 'linux | macos | windows. Defaults to runner.os.' + description: 'linux | macos | windows. Defaults to runner.os (unused by cvcpkg-install, kept for interface compat).' required: false default: '' arch: - description: 'CPU arch (x86_64, arm64). Defaults to runner host arch.' + description: 'CPU arch (x86_64, arm64). Defaults to runner host arch (unused by cvcpkg-install, kept for interface compat).' required: false default: '' build_type: description: 'Debug or Release. Used to pick the matching archive flavor.' required: true link: - description: 'shared or static. Picks the -static suffix if static.' + description: 'shared or static. Picks the matching archive flavor.' required: false default: 'shared' prefix: - description: 'Directory under which to extract the archive.' + description: 'Directory under which to install the bundles.' required: false default: '' outputs: path: - description: 'Absolute path to the extracted libcvc-deps root, or empty string if no matching archive was available.' - value: ${{ steps.extract.outputs.path }} + description: 'Absolute path to the installed libcvc-deps prefix, or empty string if no matching bundles were available.' + value: ${{ steps.cvcpkg.outputs.path }} runs: using: 'composite' steps: - - name: 'libcvc-deps: try fetch + extract' - id: extract - shell: bash - env: - LCD_VERSION: ${{ inputs.version }} - LCD_OS: ${{ inputs.os }} - LCD_ARCH: ${{ inputs.arch }} - LCD_BUILD_TYPE: ${{ inputs.build_type }} - LCD_LINK: ${{ inputs.link }} - LCD_PREFIX: ${{ inputs.prefix }} - RUNNER_OS: ${{ runner.os }} - run: | - set -euo pipefail - - # Resolve defaults from the runner context when caller did - # not pin os/arch/prefix. - os="${LCD_OS}" - if [ -z "$os" ]; then - case "$RUNNER_OS" in - Linux) os=linux ;; - macOS) os=macos ;; - Windows) os=windows ;; - *) os=$(echo "$RUNNER_OS" | tr '[:upper:]' '[:lower:]') ;; - esac - fi - - arch="${LCD_ARCH}" - if [ -z "$arch" ]; then - if [ "$os" = "windows" ]; then - arch="x86_64" - else - arch=$(uname -m) - fi - fi - - btlc=$(echo "${LCD_BUILD_TYPE}" | tr '[:upper:]' '[:lower:]') - - linksfx="" - if [ "${LCD_LINK}" = "static" ]; then - linksfx="-static" - fi - - # Linux ships .tar.gz; macOS ships .zip; Windows ships .7z - # (v1.1.0+) or .zip (v1.0.x). We probe candidate extensions in - # order of preference and use the first one that exists. - if [ "$os" = "linux" ]; then - exts="tar.gz" - elif [ "$os" = "macos" ]; then - exts="zip" - else - exts="7z zip" - fi - - stem="libcvc-deps-${LCD_VERSION}-${os}-${arch}-${btlc}${linksfx}" - - prefix="${LCD_PREFIX}" - if [ -z "$prefix" ]; then - if [ "$os" = "windows" ]; then - prefix="D:/libcvc-deps" - else - prefix="$HOME/libcvc-deps" - fi - fi - mkdir -p "$prefix" - - tmp="$(mktemp -d)" - archive="" - ext="" - for try_ext in $exts; do - try_url="https://github.com/transfix/libcvc-deps/releases/download/v${LCD_VERSION}/${stem}.${try_ext}" - try_archive="$tmp/${stem}.${try_ext}" - echo "libcvc-deps: trying $try_url" - if curl -fL -o "$try_archive" "$try_url"; then - archive="$try_archive" - ext="$try_ext" - break - fi - rm -f "$try_archive" - done - - if [ -z "$archive" ]; then - echo "libcvc-deps: archive not available (HTTP error); falling back." - echo "path=" >> "$GITHUB_OUTPUT" - exit 0 - fi - - # Sanity-check the archive is non-empty before extracting. - if [ ! -s "$archive" ]; then - echo "libcvc-deps: archive is empty; ignoring." - echo "path=" >> "$GITHUB_OUTPUT" - exit 0 - fi - - case "$ext" in - tar.gz) - tar -xzf "$archive" -C "$prefix" - ;; - zip) - # unzip is preinstalled on all GH runners (incl. Git Bash on Windows). - unzip -q "$archive" -d "$prefix" - ;; - 7z) - # 7z is preinstalled on GH Windows runners (`7z` on PATH). - # On Linux/macOS we'd need apt/brew install; not expected - # to hit this branch outside Windows. - 7z x -y -o"$prefix" "$archive" >/dev/null - ;; - esac - - # Some archives extract as a single root dir matching $stem - # (Linux + macOS via tar/ditto-keepParent); the Windows zip - # is flat from libcvc-deps's `Compress-Archive -Path $dist/*`. - # Normalise both layouts so the output path always points at - # a directory that contains include/, lib/, etc. - if [ -d "$prefix/$stem" ]; then - root="$prefix/$stem" - elif [ -d "$prefix/lib" ] || [ -d "$prefix/include" ]; then - # Flat archive; the prefix itself is the root. - root="$prefix" - else - # Fall back to the largest subdirectory at the prefix. - root="$(find "$prefix" -mindepth 1 -maxdepth 1 -type d | head -n1)" - fi - - if [ -z "$root" ] || [ ! -d "$root" ]; then - echo "libcvc-deps: extraction did not yield a usable root; falling back." - echo "path=" >> "$GITHUB_OUTPUT" - exit 0 - fi - - echo "libcvc-deps: ready at $root" - ls "$root" | head -20 || true - echo "path=$root" >> "$GITHUB_OUTPUT" -# v1.0.2 fetch action default + - name: Install libcvc-deps via cvcpkg + id: cvcpkg + uses: transfix/libcvc-deps/.github/actions/cvcpkg-install@master + with: + requirements: cvc-requirements.yaml + build_type: ${{ inputs.build_type }} + link: ${{ inputs.link }} + prefix: ${{ inputs.prefix }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e9c03193..ae2d3d64 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,7 +12,7 @@ on: # * Cheap jobs (format-check, test) cancel in-progress on new pushes for # fast feedback. # * Heavy package jobs use job-level concurrency that does NOT cancel -# in-progress, so a long VTK / vcpkg build always finishes and seeds +# in-progress, so a long VTK build always finishes and seeds # its cache. Without this, a push during a 22-min VTK build cancels # the run before the post-job cache save can fire, which traps the # workflow in a permanent cold-cache state. @@ -193,28 +193,24 @@ jobs: if: matrix.kind == 'volrover3' run: | sudo apt-get update - # Ubuntu 24.04's libvtk9-qt-dev is built against Qt5, not Qt6. - # We use Qt6 across the board (matching macOS/Windows), and - # build VTK from source against Qt6 in the next step. The - # libgl1/libxt/libglew/etc. packages are needed by VTK's - # rendering and GUISupportQt modules. + # Qt6 + VTK + Boost/HDF5/FFTW/GSL/ImageMagick/CGAL/log4cplus all + # come from cvcpkg via fetch-libcvc-deps below. We only install + # the system runtime deps that Qt/VTK dlopen at runtime + # (OpenGL, X11, GLEW), plus AppImage-tooling deps (libfuse2, + # file, desktop-file-utils). sudo apt-get install -y --no-install-recommends \ build-essential cmake ninja-build \ - libboost-all-dev libhdf5-dev libfftw3-dev libgsl-dev \ - libmagick++-dev \ - qt6-base-dev qt6-base-dev-tools libqt6opengl6-dev \ libgl1-mesa-dev libxt-dev mesa-common-dev libglew-dev \ libxrender-dev libxcursor-dev libxinerama-dev libxi-dev \ - libcgal-dev \ libfuse2 file desktop-file-utils - # ── Pull dep bundle from libcvc-deps release ── - # For volrover3 builds this lets us skip the slow VTK-from-source - # build (~25-40 min). For libcvc-kind builds the bundle provides - # the `libiimod` CMake package (no longer vendored in this tree). - # On miss the action emits an empty `path` output; libcvc-kind - # will then fail at find_package(libiimod), while volrover3 falls - # back to the cache-then-build path below. + # ── Pull dep bundle from libcvc-deps (via cvcpkg) ── + # For volrover3 builds this provides Qt6 + VTK + all third-party + # C/C++ deps in one shot; there is no fallback build path — if + # cvcpkg cannot fetch the components, CMake configure will fail + # with "Could not find Qt6/VTK", which is the right signal. + # For libcvc-kind builds the bundle also provides the `libiimod` + # CMake package (no longer vendored in this tree). - name: Fetch libcvc-deps id: libcvc-deps uses: ./.github/actions/fetch-libcvc-deps @@ -222,72 +218,27 @@ jobs: build_type: Release link: shared - # ── VTK 9.5 built against Qt6, cached ── - # - # Ubuntu's prebuilt libvtk9-qt-dev is Qt5-only. We build VTK - # from source against Qt6 once per cache key (~25-40 min cold) - # and reuse the install tree from cache thereafter (~30s warm). - # The cache is keyed on (VTK version, Qt major, runner OS) and - # is shared between ci.yml and release.yml. - # - # We use the split restore/save pattern so the cache is committed - # to the store IMMEDIATELY after VTK installs successfully, rather - # than as a post-job step that never runs when the workflow is - # cancelled mid-flight. - - name: Restore VTK (Qt6) cache - if: matrix.kind == 'volrover3' && steps.libcvc-deps.outputs.path == '' - id: vtk-cache - uses: actions/cache/restore@v4 - with: - path: ~/vtk-qt6 - key: vtk-9.5.0-qt6-${{ runner.os }}-v2 - - - name: Build VTK 9.5 from source (Qt6) - if: matrix.kind == 'volrover3' && steps.libcvc-deps.outputs.path == '' && steps.vtk-cache.outputs.cache-hit != 'true' - run: | - set -euo pipefail - curl -L -o vtk.tar.gz https://github.com/Kitware/VTK/archive/refs/tags/v9.5.0.tar.gz - tar xzf vtk.tar.gz - cmake -S VTK-9.5.0 -B vtk-build -G Ninja \ - -DCMAKE_BUILD_TYPE=Release \ - -DCMAKE_INSTALL_PREFIX=$HOME/vtk-qt6 \ - -DBUILD_SHARED_LIBS=ON \ - -DVTK_GROUP_ENABLE_Qt=YES \ - -DVTK_QT_VERSION=6 \ - -DVTK_MODULE_ENABLE_VTK_GUISupportQtQuick=NO \ - -DVTK_MODULE_ENABLE_VTK_RenderingQtQuick=NO \ - -DVTK_BUILD_TESTING=OFF \ - -DVTK_BUILD_EXAMPLES=OFF \ - -DVTK_BUILD_DOCUMENTATION=OFF \ - -DVTK_LEGACY_REMOVE=ON - cmake --build vtk-build --parallel --target install - # Free disk before continuing. - rm -rf vtk-build VTK-9.5.0 vtk.tar.gz - - # Persist the install tree as soon as it lands, before the rest of - # the job (which may take another 5-15 min) has any chance of - # being cancelled. - - name: Save VTK (Qt6) cache - if: matrix.kind == 'volrover3' && steps.libcvc-deps.outputs.path == '' && steps.vtk-cache.outputs.cache-hit != 'true' - uses: actions/cache/save@v4 - with: - path: ~/vtk-qt6 - key: vtk-9.5.0-qt6-${{ runner.os }}-v2 - - name: Configure run: | extra_prefix="" if [ "${{ matrix.kind }}" = "volrover3" ]; then - # Prefer libcvc-deps when the speedup hit; else fall back - # to the from-source VTK install under ~/vtk-qt6. + # Qt6 + VTK + all third-party deps come from cvcpkg via + # fetch-libcvc-deps. No source-build fallback. + extra_prefix="${{ steps.libcvc-deps.outputs.path }}" + if [ -z "$extra_prefix" ]; then + extra_prefix="${LIBCVC_DEPS_PREFIX:-$HOME/libcvc-deps}" + fi + elif [ "${{ matrix.kind }}" = "libcvc" ]; then + # libcvc-kind needs libiimod from the libcvc-deps bundle. + # Fall back to the default cvcpkg-install prefix when the + # step gracefully returned empty (e.g. a single-bundle + # download failed but earlier bundles unpacked into + # $HOME/libcvc-deps). if [ -n "${{ steps.libcvc-deps.outputs.path }}" ]; then extra_prefix="${{ steps.libcvc-deps.outputs.path }}" else - extra_prefix="$HOME/vtk-qt6" + extra_prefix="${LIBCVC_DEPS_PREFIX:-$HOME/libcvc-deps}" fi - elif [ "${{ matrix.kind }}" = "libcvc" ]; then - # libcvc-kind needs libiimod from the libcvc-deps bundle. - extra_prefix="${{ steps.libcvc-deps.outputs.path }}" fi # libcvc-kind builds enable tests so the same artifacts we # ship get exercised by ctest. volrover3 builds keep tests @@ -407,16 +358,13 @@ jobs: # linuxdeploy walks the volrover3 ELF NEEDED entries and # resolves them by name; libcvc.so.3 lives under # AppDir/usr/lib/x86_64-linux-gnu/ (Debian's GNUInstallDirs - # default) which is not on the default search path. VTK 9.5 - # was built into $HOME/vtk-qt6/lib (cached, off-system) or, - # when the libcvc-deps speedup hit, lives under the extracted - # libcvc-deps root. - vtk_lib_path="" - if [ -n "${{ steps.libcvc-deps.outputs.path }}" ]; then - vtk_lib_path="${{ steps.libcvc-deps.outputs.path }}/lib" - else - vtk_lib_path="$HOME/vtk-qt6/lib" + # default) which is not on the default search path. Qt6 + VTK + # come from the libcvc-deps prefix populated by cvcpkg. + deps_root="${{ steps.libcvc-deps.outputs.path }}" + if [ -z "$deps_root" ]; then + deps_root="${LIBCVC_DEPS_PREFIX:-$HOME/libcvc-deps}" fi + vtk_lib_path="$deps_root/lib" export LD_LIBRARY_PATH="$PWD/AppDir/usr/lib:$PWD/AppDir/usr/lib/x86_64-linux-gnu:$vtk_lib_path:${LD_LIBRARY_PATH:-}" ./linuxdeploy --appdir AppDir --plugin qt --output appimage \ --desktop-file AppDir/volrover3.desktop \ @@ -554,20 +502,21 @@ jobs: - name: Install dependencies (libcvc) if: matrix.kind == 'libcvc' run: | - brew install cmake ninja boost hdf5 fftw gsl imagemagick cgal log4cplus zstd - echo "BOOST_ROOT=$(brew --prefix boost)" >> $GITHUB_ENV - echo "CMAKE_PREFIX_PATH=$(brew --prefix boost):$(brew --prefix hdf5)" >> $GITHUB_ENV + # All C/C++ third-party libs (Boost/HDF5/FFTW/GSL/ImageMagick/ + # CGAL/log4cplus/libiimod) come from cvcpkg via + # fetch-libcvc-deps below. Only build tooling here. + brew install cmake ninja zstd - name: Install dependencies (volrover3) if: matrix.kind == 'volrover3' run: | - brew install cmake ninja boost hdf5 fftw gsl imagemagick qt@6 vtk cgal zstd - echo "BOOST_ROOT=$(brew --prefix boost)" >> $GITHUB_ENV - echo "CMAKE_PREFIX_PATH=$(brew --prefix boost):$(brew --prefix qt@6):$(brew --prefix hdf5)" >> $GITHUB_ENV - echo "$(brew --prefix qt@6)/bin" >> $GITHUB_PATH + # Qt6 + VTK + all third-party C/C++ deps come from cvcpkg via + # fetch-libcvc-deps below. Only build tooling here. + brew install cmake ninja zstd - # ── Pull dep bundle from libcvc-deps release ── - # Provides the libiimod CMake package (no longer vendored). + # ── Pull dep prefix via cvcpkg ── + # Provides Qt6 + VTK + all third-party C/C++ deps for volrover3 + # and the libiimod CMake package for libcvc. - name: Fetch libcvc-deps id: libcvc-deps-macos uses: ./.github/actions/fetch-libcvc-deps @@ -579,13 +528,12 @@ jobs: run: | tests=OFF [ "${{ matrix.kind }}" = "libcvc" ] && tests=ON - # Append libcvc-deps to CMAKE_PREFIX_PATH so find_package - # (libiimod) resolves. brew prefixes from the prior step - # remain via $CMAKE_PREFIX_PATH (env var). + # cvcpkg is the sole third-party prefix on macOS. deps_path="${{ steps.libcvc-deps-macos.outputs.path }}" - if [ -n "$deps_path" ]; then - export CMAKE_PREFIX_PATH="${CMAKE_PREFIX_PATH:+$CMAKE_PREFIX_PATH:}$deps_path" + if [ -z "$deps_path" ]; then + deps_path="${LIBCVC_DEPS_PREFIX:-$HOME/libcvc-deps}" fi + export CMAKE_PREFIX_PATH="$deps_path" cmake -B build -G Ninja \ -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} \ -DCVC_BUILD_TESTS=$tests \ @@ -695,8 +643,7 @@ jobs: kind: volrover3 build_type: Release name: package-windows / ${{ matrix.name }} - # Heavy job (vcpkg builds VTK + CGAL from source): do NOT cancel - # in-progress runs. See package-linux for rationale. + # Do NOT cancel in-progress runs; see package-linux for rationale. concurrency: group: ci-${{ github.ref }}-package-windows-${{ matrix.name }} cancel-in-progress: false @@ -717,140 +664,20 @@ jobs: echo "arch=x86_64" >> "$GITHUB_OUTPUT" echo "btlc=$BTLC" >> "$GITHUB_OUTPUT" - # Cache key intentionally excludes hashFiles(workflow) so editing - # this file does not invalidate the warm vcpkg cache. - # - # Split restore/save: we commit the cache immediately after the - # vcpkg install step succeeds rather than waiting for a post-job - # save that gets skipped on cancellation. - # - # NOTE: this step must run AFTER "Free disk space and route - # vcpkg to D" so the cache restores into the junctioned dirs. - - name: 'Free disk space and route vcpkg to D: (Windows)' - if: matrix.kind == 'volrover3' - shell: pwsh - run: | - # vcpkg builds VTK + Qt + CGAL from source. Even with - # --clean-after-build, individual port buildtrees (qtdeclarative - # is ~25 GB) exceed the free space on C: of windows-latest. - # Two complementary fixes: - # 1) Remove pre-installed toolchains we don't use to free C:. - # 2) Redirect vcpkg's heavy working dirs to D: via vcpkg's - # own relocation flags (VCPKG_DOWNLOADS env var, - # --x-buildtrees-root, --x-packages-root). We deliberately - # avoid filesystem junctions because Python's - # os.path.relpath (used by meson inside harfbuzz/qt builds) - # raises ValueError when a path crosses mount points via - # a junction. - $paths = @( - 'C:\Android', - 'C:\Program Files\Android', - 'C:\Program Files (x86)\Android', - 'C:\Program Files (x86)\dotnet', - 'C:\Program Files\dotnet\sdk\NuGetFallbackFolder', - 'C:\Program Files (x86)\Google', - 'C:\Program Files\MySQL', - 'C:\Program Files\Mozilla Firefox', - 'C:\Program Files\Microsoft SDKs\Azure', - 'C:\hostedtoolcache\windows\Ruby', - 'C:\hostedtoolcache\windows\PyPy', - 'C:\hostedtoolcache\windows\go', - 'C:\hostedtoolcache\windows\node' - ) - $cBefore = (Get-PSDrive C).Free / 1GB - $dBefore = (Get-PSDrive D).Free / 1GB - foreach ($p in $paths) { - if (Test-Path $p) { - Remove-Item -Recurse -Force -ErrorAction SilentlyContinue $p - } - } - - # Pre-create D: paths and export them as env vars for later steps. - New-Item -ItemType Directory -Force -Path D:\vcpkg-buildtrees | Out-Null - New-Item -ItemType Directory -Force -Path D:\vcpkg-packages | Out-Null - New-Item -ItemType Directory -Force -Path D:\vcpkg-downloads | Out-Null - "VCPKG_DOWNLOADS=D:\vcpkg-downloads" | Out-File -Append -FilePath $env:GITHUB_ENV -Encoding utf8 - "VCPKG_BUILDTREES=D:\vcpkg-buildtrees" | Out-File -Append -FilePath $env:GITHUB_ENV -Encoding utf8 - "VCPKG_PACKAGES=D:\vcpkg-packages" | Out-File -Append -FilePath $env:GITHUB_ENV -Encoding utf8 - - # Restrict vcpkg to building only the matrix.build_type - # configuration of each port (instead of the upstream default - # which always builds both Debug+Release). The overlay triplet - # at vcpkg-overlay/triplets/x64-windows.cmake reads - # VCPKG_FORCE_BUILD_TYPE and applies it as VCPKG_BUILD_TYPE. - $btlc = '${{ matrix.build_type }}'.ToLower() - "VCPKG_FORCE_BUILD_TYPE=$btlc" | Out-File -Append -FilePath $env:GITHUB_ENV -Encoding utf8 - "VCPKG_OVERLAY_TRIPLETS=${{ github.workspace }}\vcpkg-overlay\triplets" | Out-File -Append -FilePath $env:GITHUB_ENV -Encoding utf8 - - $cAfter = (Get-PSDrive C).Free / 1GB - $dAfter = (Get-PSDrive D).Free / 1GB - Write-Host ("Free space on C: {0:N1} GB -> {1:N1} GB" -f $cBefore, $cAfter) - Write-Host ("Free space on D: {0:N1} GB -> {1:N1} GB" -f $dBefore, $dAfter) - - # Configure vcpkg to build only the matrix.build_type config for - # libcvc Windows jobs (the volrover3-gated disk-routing step - # above already exports VCPKG_FORCE_BUILD_TYPE for volrover3). - - name: Configure vcpkg overlay triplet (libcvc Windows) - if: matrix.kind == 'libcvc' - shell: pwsh - run: | - $btlc = '${{ matrix.build_type }}'.ToLower() - "VCPKG_FORCE_BUILD_TYPE=$btlc" | Out-File -Append -FilePath $env:GITHUB_ENV -Encoding utf8 - "VCPKG_OVERLAY_TRIPLETS=${{ github.workspace }}\vcpkg-overlay\triplets" | Out-File -Append -FilePath $env:GITHUB_ENV -Encoding utf8 - - # ── Optional: pull all third-party deps from a libcvc-deps bundle ── - # When a matching libcvc-deps release archive exists for this - # platform + matrix.build_type, downloading it (~minutes) is far - # cheaper than letting vcpkg compile Boost/HDF5/FFTW/GSL/log4cplus/ - # CGAL/ImageMagick from source AND building VTK separately - # (~25-40 min cold). On hit, we skip: - # * vcpkg restore + install + save - # * install-qt-action (Qt6 is in the bundle) - # * VTK cache restore + from-source build (volrover3 only) - # On miss (e.g. libcvc-deps release not yet published, network - # blip), the action emits an empty `path` output and we fall back - # to the previous vcpkg / aqtinstall / VTK-from-source path. - - name: Fetch libcvc-deps (Windows speedup) + # ── Pull all third-party deps (incl. Qt6 + VTK) from cvcpkg ── + # cvcpkg is the sole source of truth for third-party components + # on Windows: Boost, HDF5, FFTW, GSL, ImageMagick, log4cplus, + # CGAL, Qt6, VTK, etc. If cvcpkg cannot fetch the components, + # CMake configure will fail with an explicit "Could not find X" + # error, which is the correct signal — there is no vcpkg or + # source-build fallback. + - name: Fetch libcvc-deps (Windows) id: libcvc-deps-windows uses: ./.github/actions/fetch-libcvc-deps with: build_type: ${{ matrix.build_type }} link: shared - - name: Restore vcpkg cache - if: steps.libcvc-deps-windows.outputs.path == '' - id: vcpkg-cache - uses: actions/cache/restore@v4 - with: - path: | - ${{ env.VCPKG_INSTALLATION_ROOT }}\installed - ${{ env.VCPKG_INSTALLATION_ROOT }}\packages - ~\AppData\Local\vcpkg\archives - key: vcpkg-pkg-${{ matrix.kind }}-${{ matrix.build_type }}-v7 - restore-keys: | - vcpkg-pkg-${{ matrix.kind }}-${{ matrix.build_type }}-v7- - - # Install Qt6 from official prebuilt binaries via aqtinstall. - # vcpkg's qt6-base port builds Qt from source (~30+ min cold) so - # we route Qt around vcpkg entirely; VTK and CGAL still come from - # vcpkg (they are smaller and don't have a comparable prebuilt). - # install-qt-action prepends Qt's bin/ to PATH and exports - # Qt6_DIR + QT_ROOT_DIR for CMake to find. Skipped on libcvc-deps - # hit (Qt6 ships inside the bundle). - - name: Install Qt6 (volrover3) - if: matrix.kind == 'volrover3' && steps.libcvc-deps-windows.outputs.path == '' - uses: jurplel/install-qt-action@v4 - with: - version: '6.7.*' - host: 'windows' - target: 'desktop' - # Qt does not publish msvc2022_64 binaries for 6.7.x; the - # msvc2019_64 build is ABI-compatible with the MSVC 2022 - # toolchain that windows-latest provides. - arch: 'win64_msvc2019_64' - modules: '' - cache: true - # CUDA toolkit so the shipped Windows artifacts can offload to # NVIDIA GPUs at runtime when an NVIDIA driver is present. cudart # is linked statically (CMAKE_CUDA_RUNTIME_LIBRARY=Static below) @@ -862,122 +689,24 @@ jobs: method: 'network' sub-packages: '["nvcc", "cudart", "visual_studio_integration"]' - - name: Install dependencies - if: steps.libcvc-deps-windows.outputs.path == '' - shell: pwsh - env: - # Pull our hand-rolled imagemagick overlay port (Q16-HDRI x64, - # repackaged from the upstream Inno Setup installer; see - # vcpkg-overlay/ports/imagemagick/portfile.cmake). - VCPKG_OVERLAY_PORTS: ${{ github.workspace }}\vcpkg-overlay\ports - run: | - $packages = @( - 'boost-thread','boost-date-time','boost-regex','boost-filesystem', - 'boost-system','boost-chrono','boost-program-options','boost-signals2', - 'boost-format','boost-property-tree','boost-algorithm','boost-asio', - 'boost-any','boost-array','boost-dynamic-bitset','boost-exception', - 'boost-foreach','boost-function','boost-lambda','boost-lexical-cast', - 'boost-multi-array','boost-optional','boost-smart-ptr','boost-tuple', - 'boost-utility','hdf5[cpp]','fftw3','gsl','imagemagick','log4cplus','cgal' - ) - # qt6-base/vtk are NOT pulled via vcpkg — see install-qt-action - # above and the from-source VTK build below. - for ($i = 1; $i -le 5; $i++) { - Write-Host "vcpkg install attempt $i / 5" - # --clean-after-build deletes each port's buildtree/packages - # immediately after installation so the disk does not fill - # while iterating through hundreds of dependencies. - # --x-buildtrees-root / --x-packages-root relocate vcpkg's - # heavy working directories to D: where free space is - # ~80 GB vs ~14 GB on C:. The install root and binary - # cache stay on C: so actions/cache picks them up. - vcpkg install @packages --triplet x64-windows ` - --clean-after-build ` - --x-buildtrees-root=D:\vcpkg-buildtrees ` - --x-packages-root=D:\vcpkg-packages - if ($LASTEXITCODE -eq 0) { break } - if ($i -eq 5) { exit $LASTEXITCODE } - $sleep = [Math]::Min(60 * $i, 180) - Write-Host "Retrying after transient failure (sleeping $sleep s)..." - Start-Sleep -Seconds $sleep - } - - # Persist vcpkg state as soon as install completes successfully so - # later cancellations don't strand a 20+ minute build. - - name: Save vcpkg cache - if: steps.libcvc-deps-windows.outputs.path == '' && steps.vcpkg-cache.outputs.cache-hit != 'true' - uses: actions/cache/save@v4 - with: - path: | - ${{ env.VCPKG_INSTALLATION_ROOT }}\installed - ${{ env.VCPKG_INSTALLATION_ROOT }}\packages - ~\AppData\Local\vcpkg\archives - key: vcpkg-pkg-${{ matrix.kind }}-${{ matrix.build_type }}-v7 - - # ── VTK 9.5 built from source against aqt Qt6, cached ── - # Mirrors the Ubuntu strategy. Cache key includes build_type - # because MSVC Debug and Release VTK DLLs are not interchangeable. - # Uses Visual Studio generator (windows-latest default) so we - # don't need a separate MSVC env activation step. - - name: Restore VTK (Qt6) cache (Windows) - if: matrix.kind == 'volrover3' && steps.libcvc-deps-windows.outputs.path == '' - id: vtk-cache-windows - uses: actions/cache/restore@v4 - with: - path: D:\vtk-qt6 - key: vtk-9.5.0-qt6-${{ runner.os }}-${{ matrix.build_type }}-v1 - - - name: Build VTK 9.5 from source against Qt6 (Windows) - if: matrix.kind == 'volrover3' && steps.libcvc-deps-windows.outputs.path == '' && steps.vtk-cache-windows.outputs.cache-hit != 'true' - shell: pwsh - run: | - Invoke-WebRequest -Uri https://github.com/Kitware/VTK/archive/refs/tags/v9.5.0.tar.gz -OutFile vtk.tar.gz - tar xzf vtk.tar.gz - cmake -S VTK-9.5.0 -B D:\vtk-build ` - -DCMAKE_INSTALL_PREFIX=D:\vtk-qt6 ` - -DCMAKE_PREFIX_PATH="$env:QT_ROOT_DIR" ` - -DBUILD_SHARED_LIBS=ON ` - -DVTK_GROUP_ENABLE_Qt=YES ` - -DVTK_QT_VERSION=6 ` - -DVTK_MODULE_ENABLE_VTK_GUISupportQtQuick=NO ` - -DVTK_MODULE_ENABLE_VTK_RenderingQtQuick=NO ` - -DVTK_BUILD_TESTING=OFF ` - -DVTK_BUILD_EXAMPLES=OFF ` - -DVTK_BUILD_DOCUMENTATION=OFF ` - -DVTK_LEGACY_REMOVE=ON - cmake --build D:\vtk-build --config ${{ matrix.build_type }} --parallel --target install - Remove-Item -Recurse -Force D:\vtk-build - Remove-Item -Recurse -Force VTK-9.5.0 - Remove-Item -Force vtk.tar.gz - - - name: Save VTK (Qt6) cache (Windows) - if: matrix.kind == 'volrover3' && steps.libcvc-deps-windows.outputs.path == '' && steps.vtk-cache-windows.outputs.cache-hit != 'true' - uses: actions/cache/save@v4 - with: - path: D:\vtk-qt6 - key: vtk-9.5.0-qt6-${{ runner.os }}-${{ matrix.build_type }}-v1 - - name: Configure shell: pwsh run: | $vol = if ('${{ matrix.kind }}' -eq 'volrover3') { 'ON' } else { 'OFF' } $tests = if ('${{ matrix.kind }}' -eq 'libcvc') { 'ON' } else { 'OFF' } $depsPath = '${{ steps.libcvc-deps-windows.outputs.path }}' - $prefixes = @() - # libcvc-deps (when present) ships Boost/HDF5/FFTW/GSL/log4cplus/ - # CGAL/ImageMagick + Qt6 + VTK for BOTH kinds, so prefer it. - if ($depsPath) { - $prefixes += $depsPath - } - if ($env:QT_ROOT_DIR) { $prefixes += $env:QT_ROOT_DIR } - if ('${{ matrix.kind }}' -eq 'volrover3' -and -not $depsPath) { - $prefixes += 'D:\vtk-qt6' + if (-not $depsPath) { + # cvcpkg-install fell back; try the default prefix in case + # some bundles unpacked before the failure. + $depsPath = if ($env:LIBCVC_DEPS_PREFIX) { $env:LIBCVC_DEPS_PREFIX } else { 'D:\libcvc-deps' } + if (-not (Test-Path $depsPath)) { $depsPath = '' } } - $qtPrefix = $prefixes -join ';' + # cvcpkg is the sole third-party prefix. No vcpkg toolchain + # file, no aqt Qt6, no VTK source cache. + $qtPrefix = if ($depsPath) { $depsPath } else { '' } cmake -B build ` -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} ` -DCMAKE_CONFIGURATION_TYPES=${{ matrix.build_type }} ` - -DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake" ` -DCMAKE_PREFIX_PATH="$qtPrefix" ` -DCVC_BUILD_TESTS=$tests ` -DCVC_ENABLE_CUDA=ON ` @@ -1003,7 +732,7 @@ jobs: # Error running test executable. Path: '.../state_test.exe' # Result: Exit code 0xc0000135 # - # CRITICAL: vcpkg ships Debug DLLs at debug/bin/ with the + # CRITICAL: cvcpkg ships Debug DLLs at debug/bin/ with the # SAME filename as their Release counterparts at bin/ (e.g. # both directories contain hdf5.dll, boost_thread-vc143-mt*). # Putting bin/ on PATH first under a Debug build causes the @@ -1012,14 +741,14 @@ jobs: # as SEH access violations (0xc0000005) and "unknown C++ # exception" failures inside HDF5/Boost APIs. # - # libcvc-deps, however, ships Qt6 only at top-level bin/ + # cvcpkg, however, ships Qt6 only at top-level bin/ # (Qt6Core.dll AND Qt6Cored.dll co-located there because Qt # uses a 'd' suffix for Debug). So a Debug build still needs # bin/ on PATH to resolve Qt6Cored.dll, otherwise Qt-based # tests (GraphicsNodeTest, VolumeNodeTest, etc.) exit with # STATUS_DLL_NOT_FOUND (0xc0000135). Put debug/bin FIRST so - # vcpkg-style same-named ports load their debug variant, and - # bin/ second so Qt6's debug-suffixed DLLs still resolve. + # same-named-port DLLs load their debug variant, and bin/ + # second so Qt6's debug-suffixed DLLs still resolve. run: | $depsPath = '${{ steps.libcvc-deps-windows.outputs.path }}' $isDebug = '${{ matrix.build_type }}' -eq 'Debug' diff --git a/.github/workflows/publish-cvcpkg.yml b/.github/workflows/publish-cvcpkg.yml new file mode 100644 index 00000000..06c6be96 --- /dev/null +++ b/.github/workflows/publish-cvcpkg.yml @@ -0,0 +1,215 @@ +# Publish libcvc to cvcpkg.org +# +# Triggered by version tags (v*) and manual dispatch. Builds libcvc on +# Linux, macOS, and Windows, then packs each install into a tar.zst +# archive and publishes to the `cvc` organization on cvcpkg.org (created +# if it doesn't exist yet). +# +# Requires two repository secrets: +# CVCPKG_TOKEN — publisher-role token for the cvc org +# CVCPKG_SERVER_URL — optional; defaults to https://cvcpkg.org +# +# NOTE: the archive packing step depends on `cvcpkg pack-from-prefix` +# which is on the cvcpkg roadmap but not yet released. Until it ships +# this workflow will run through build + install and then exit early at +# the pack step with a clear message. Once cvcpkg gains the subcommand, +# swap the stub for the real invocation and remove the `continue-on-error` +# guard on the publish job. + +name: publish-cvcpkg + +on: + push: + tags: + - 'v*' + workflow_dispatch: + inputs: + dry_run: + description: 'Build and pack only; skip publish.' + required: false + default: 'true' + type: choice + options: + - 'true' + - 'false' + +concurrency: + group: publish-cvcpkg-${{ github.ref }} + cancel-in-progress: false + +env: + CVCPKG_ORG: cvc + CVCPKG_SERVER: ${{ secrets.CVCPKG_SERVER_URL || 'https://cvcpkg.org' }} + +jobs: + ensure-org: + runs-on: ubuntu-latest + env: + CVCPKG_TOKEN: ${{ secrets.CVCPKG_TOKEN }} + steps: + - name: Ensure cvc org exists on cvcpkg.org + if: env.CVCPKG_TOKEN != '' + run: | + set -euo pipefail + server="${CVCPKG_SERVER%/}" + slug="$CVCPKG_ORG" + # POST /v1/orgs — 200 on create, 409 if it already exists. + status=$(curl -sS -o /tmp/org.json -w '%{http_code}' \ + -X POST "$server/v1/orgs" \ + -H "Authorization: Bearer $CVCPKG_TOKEN" \ + -H 'Content-Type: application/json' \ + -d '{ + "slug": "cvc", + "display_name": "Center for Computational Visualization", + "description": "Official CVC-lab package publisher.", + "homepage": "https://www.cs.utexas.edu/~bajaj/cvc/", + "is_private": false + }') + if [ "$status" = "200" ] || [ "$status" = "201" ]; then + echo "Created org $slug" + elif [ "$status" = "409" ]; then + echo "Org $slug already exists — OK" + else + echo "Org creation failed with HTTP $status:" + cat /tmp/org.json + exit 1 + fi + + build: + needs: ensure-org + strategy: + fail-fast: false + matrix: + include: + - os: ubuntu-latest + platform: linux + arch: x86_64 + - os: macos-14 + platform: macos + arch: arm64 + - os: windows-latest + platform: windows + arch: x86_64 + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v5 + + - name: Install cvcpkg (from local workspace clone) + shell: bash + run: | + set -euo pipefail + # Pull from libcvc-deps master rather than PyPI so we always + # pick up server/schema fixes without a release cadence. + python -m pip install --upgrade pip + pip install 'git+https://github.com/transfix/libcvc-deps@master#subdirectory=tools/cvcpkg' + cvcpkg --version + + - name: Fetch libcvc-deps + id: libcvc-deps + uses: ./.github/actions/fetch-libcvc-deps + with: + build_type: Release + link: shared + + - name: Configure (Linux/macOS) + if: matrix.platform != 'windows' + shell: bash + run: | + set -euo pipefail + extra_prefix="${{ steps.libcvc-deps.outputs.path }}" + if [ -z "$extra_prefix" ]; then + extra_prefix="${LIBCVC_DEPS_PREFIX:-$HOME/libcvc-deps}" + fi + cmake -B build -G Ninja \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_PREFIX_PATH="$extra_prefix" \ + -DCMAKE_INSTALL_PREFIX="$PWD/stage" \ + -DCVC_BUILD_TESTS=OFF \ + -DCVC_ENABLE_CUDA=OFF \ + -DDISABLE_CGAL=OFF \ + -DCVC_BUILD_VOLROVER3=OFF \ + -DCVC_ENABLE_MESHER=ON \ + -DCVC_ENABLE_SDF=ON + + - name: Configure (Windows) + if: matrix.platform == 'windows' + shell: pwsh + run: | + $depsPath = '${{ steps.libcvc-deps.outputs.path }}' + if (-not $depsPath) { + $depsPath = if ($env:LIBCVC_DEPS_PREFIX) { $env:LIBCVC_DEPS_PREFIX } else { 'D:\libcvc-deps' } + } + cmake -B build ` + -DCMAKE_BUILD_TYPE=Release ` + -DCMAKE_CONFIGURATION_TYPES=Release ` + -DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake" ` + -DCMAKE_PREFIX_PATH="$depsPath" ` + -DCMAKE_INSTALL_PREFIX="$env:GITHUB_WORKSPACE/stage" ` + -DCVC_BUILD_TESTS=OFF ` + -DCVC_ENABLE_CUDA=OFF ` + -DDISABLE_CGAL=OFF ` + -DCVC_BUILD_VOLROVER3=OFF ` + -DCVC_ENABLE_MESHER=ON ` + -DCVC_ENABLE_SDF=ON + + - name: Build & install + shell: bash + run: | + set -euo pipefail + cmake --build build --parallel --config Release + cmake --install build --config Release --component libcvc + + - name: Pack archive (cvcpkg pack-from-prefix) + id: pack + shell: bash + continue-on-error: true + run: | + set -euo pipefail + mkdir -p dist + # `cvcpkg pack-from-prefix` is planned but not yet shipped. + # Once released: + # cvcpkg pack-from-prefix \ + # --recipe cvcpkg/recipes/libcvc/recipe.yaml \ + # --prefix stage \ + # --platform ${{ matrix.platform }} \ + # --arch ${{ matrix.arch }} \ + # --config release --link shared \ + # --version-from cmake \ + # --output-dir dist + if cvcpkg pack-from-prefix --help >/dev/null 2>&1; then + cvcpkg pack-from-prefix \ + --recipe cvcpkg/recipes/libcvc/recipe.yaml \ + --prefix stage \ + --platform "${{ matrix.platform }}" \ + --arch "${{ matrix.arch }}" \ + --config release --link shared \ + --version-from cmake \ + --output-dir dist + else + echo "cvcpkg pack-from-prefix not available yet; skipping pack step." + echo "Install tree contents:" + find stage -maxdepth 3 -type d | head -40 || true + echo "SKIP_PUBLISH=1" >> "$GITHUB_ENV" + exit 0 + fi + + - name: Upload install tree (debug) + if: env.SKIP_PUBLISH == '1' + uses: actions/upload-artifact@v4 + with: + name: libcvc-stage-${{ matrix.platform }}-${{ matrix.arch }} + path: stage/ + retention-days: 7 + + - name: Publish to cvcpkg.org + if: env.SKIP_PUBLISH != '1' && (github.event_name == 'push' || github.event.inputs.dry_run == 'false') + env: + CVCPKG_TOKEN: ${{ secrets.CVCPKG_TOKEN }} + run: | + set -euo pipefail + cvcpkg publish \ + --all \ + --input-dir dist \ + --server "$CVCPKG_SERVER" \ + --org "$CVCPKG_ORG" \ + --token "$CVCPKG_TOKEN" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4bb2f74d..e94a30d9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -35,18 +35,18 @@ permissions: # # Shared and static archives are both fully self-contained: every # project-level dependency (Boost, HDF5, FFTW, GSL on all platforms; -# plus ImageMagick + CGAL/GMP/MPFR on Windows via vcpkg) is bundled +# plus ImageMagick + CGAL/GMP/MPFR on Windows via cvcpkg) is bundled # alongside libcvc, so consumers can `find_package(cvc CONFIG)` from -# the extracted archive without needing apt / Homebrew / vcpkg on the -# host. Shared archives ship .so/.dylib/.dll (with rpath fixups on -# Linux+macOS) plus their import .lib on Windows; static archives ship -# .a/.lib only. Both flavors include cvc::xmlrpc. VTK is intentionally -# NOT bundled — downstream consumers (F2Dock, MolSurf, volrover) bring -# their own VTK with the configuration they need (e.g. Qt-enabled for -# volrover). +# the extracted archive without needing apt / Homebrew / cvcpkg on +# the host. Shared archives ship .so/.dylib/.dll (with rpath fixups +# on Linux+macOS) plus their import .lib on Windows; static archives +# ship .a/.lib only. Both flavors include cvc::xmlrpc. VTK is +# intentionally NOT bundled — downstream consumers (F2Dock, MolSurf, +# volrover) bring their own VTK with the configuration they need +# (e.g. Qt-enabled for volrover). # # Debug archives on Windows also include MSVC .pdb files for libcvc -# itself and every vcpkg-built dependency, so symbolicated stack +# itself and every cvcpkg-provided dependency, so symbolicated stack # traces work out of the box. # # System libraries (glibc, libstdc++, libm, Windows UCRT) are @@ -127,10 +127,11 @@ jobs: if: matrix.kind == 'libcvc' run: | sudo apt-get update + # Only build-tooling + AppImage helpers here; Boost/HDF5/FFTW/ + # GSL/ImageMagick/CGAL/log4cplus come from cvcpkg via + # fetch-libcvc-deps below. sudo apt-get install -y --no-install-recommends \ - build-essential cmake ninja-build \ - libboost-all-dev libhdf5-dev libfftw3-dev libgsl-dev \ - libmagick++-dev libcgal-dev liblog4cplus-dev libzstd-dev + build-essential cmake ninja-build libzstd-dev # CUDA toolkit so the shipped artifacts can offload to NVIDIA GPUs # at runtime when an NVIDIA driver is present. cudart is linked @@ -149,29 +150,25 @@ jobs: if: matrix.kind == 'volrover3' run: | sudo apt-get update - # Use Qt6 across the board; Ubuntu's libvtk9-qt-dev is - # Qt5-only, so VTK is built from source against Qt6 in the - # next step (cached). + # Qt6 + VTK + all third-party C/C++ deps come from cvcpkg via + # fetch-libcvc-deps below. Only install system runtime libs + # that Qt/VTK dlopen at runtime (OpenGL/X11/GLEW) and the + # AppImage tooling. sudo apt-get install -y --no-install-recommends \ build-essential cmake ninja-build \ - libboost-all-dev libhdf5-dev libfftw3-dev libgsl-dev \ - libmagick++-dev \ - qt6-base-dev qt6-base-dev-tools libqt6opengl6-dev \ libgl1-mesa-dev libxt-dev mesa-common-dev libglew-dev \ libxrender-dev libxcursor-dev libxinerama-dev libxi-dev \ - libcgal-dev \ libfuse2 file desktop-file-utils - # ── Pull deps from a libcvc-deps release bundle ── + # ── Pull deps from cvcpkg (via libcvc-deps composite action) ── # libcvc kind: required for libiimod (no longer vendored, see # PR #63), in addition to providing Boost/HDF5/FFTW/GSL/CGAL/ # log4cplus/ImageMagick prebuilt. - # volrover3 kind: also speedup vs. cold-cache VTK source build - # (~15-20 min). On hit, the subsequent VTK source build is - # skipped and CMAKE_PREFIX_PATH picks up VTK from the deps tree. - # On miss the action emits an empty `path` output; libcvc kind - # will then fail Configure (libiimod is required), which is the - # correct signal. + # volrover3 kind: also provides Qt6 + VTK for the app build. + # On miss the action emits an empty `path` output; both kinds + # then fail Configure with an explicit "Could not find X" error, + # which is the correct signal (no in-workflow source-build + # fallback — cvcpkg is the sole source of truth). - name: Fetch libcvc-deps id: libcvc-deps uses: ./.github/actions/fetch-libcvc-deps @@ -182,49 +179,11 @@ jobs: build_type: ${{ matrix.kind == 'volrover3' && 'Release' || matrix.build_type }} link: shared - # ── VTK 9.5 built against Qt6, cached ── - # Cache key matches ci.yml so the build is shared across both - # workflows and only happens once per (vtk_version, qt_major, os). - - name: Cache VTK (Qt6) install tree - if: matrix.kind == 'volrover3' && steps.libcvc-deps.outputs.path == '' - id: vtk-cache - uses: actions/cache@v4 - with: - path: ~/vtk-qt6 - key: vtk-9.5.0-qt6-${{ runner.os }}-v2 - - - name: Build VTK 9.5 from source (Qt6) - if: matrix.kind == 'volrover3' && steps.libcvc-deps.outputs.path == '' && steps.vtk-cache.outputs.cache-hit != 'true' - run: | - set -euo pipefail - curl -L -o vtk.tar.gz https://github.com/Kitware/VTK/archive/refs/tags/v9.5.0.tar.gz - tar xzf vtk.tar.gz - cmake -S VTK-9.5.0 -B vtk-build -G Ninja \ - -DCMAKE_BUILD_TYPE=Release \ - -DCMAKE_INSTALL_PREFIX=$HOME/vtk-qt6 \ - -DBUILD_SHARED_LIBS=ON \ - -DVTK_GROUP_ENABLE_Qt=YES \ - -DVTK_QT_VERSION=6 \ - -DVTK_MODULE_ENABLE_VTK_GUISupportQtQuick=NO \ - -DVTK_MODULE_ENABLE_VTK_RenderingQtQuick=NO \ - -DVTK_BUILD_TESTING=OFF \ - -DVTK_BUILD_EXAMPLES=OFF \ - -DVTK_BUILD_DOCUMENTATION=OFF \ - -DVTK_LEGACY_REMOVE=ON - cmake --build vtk-build --parallel --target install - rm -rf vtk-build VTK-9.5.0 vtk.tar.gz - - name: Configure run: | - extra_prefix="" - # libcvc-deps (when present) carries libiimod for libcvc and - # VTK for volrover3 alongside the rest of the third-party - # tree. Prefer it for both kinds. - if [ -n "${{ steps.libcvc-deps.outputs.path }}" ]; then - extra_prefix="${{ steps.libcvc-deps.outputs.path }}" - elif [ "${{ matrix.kind }}" = "volrover3" ]; then - # Fall back to from-source VTK install under ~/vtk-qt6. - extra_prefix="$HOME/vtk-qt6" + extra_prefix="${{ steps.libcvc-deps.outputs.path }}" + if [ -z "$extra_prefix" ]; then + extra_prefix="${LIBCVC_DEPS_PREFIX:-$HOME/libcvc-deps}" fi # link=shared → BUILD_SHARED_LIBS=ON (legacy default). # link=static → BUILD_SHARED_LIBS=OFF plus static dep flags so @@ -385,16 +344,13 @@ jobs: # linuxdeploy walks the volrover3 ELF NEEDED entries and # resolves them by name; libcvc.so.3 lives under # AppDir/usr/lib/x86_64-linux-gnu/ (Debian's GNUInstallDirs - # default) which is not on the default search path. VTK 9.5 - # was built into $HOME/vtk-qt6/lib (cached, off-system) or, - # when the libcvc-deps speedup hit, lives under the - # extracted libcvc-deps root. - vtk_lib_path="" - if [ -n "${{ steps.libcvc-deps.outputs.path }}" ]; then - vtk_lib_path="${{ steps.libcvc-deps.outputs.path }}/lib" - else - vtk_lib_path="$HOME/vtk-qt6/lib" + # default) which is not on the default search path. Qt6 + VTK + # live under the cvcpkg-populated libcvc-deps prefix. + deps_root="${{ steps.libcvc-deps.outputs.path }}" + if [ -z "$deps_root" ]; then + deps_root="${LIBCVC_DEPS_PREFIX:-$HOME/libcvc-deps}" fi + vtk_lib_path="$deps_root/lib" export LD_LIBRARY_PATH="$PWD/AppDir/usr/lib:$PWD/AppDir/usr/lib/x86_64-linux-gnu:$vtk_lib_path:${LD_LIBRARY_PATH:-}" ./linuxdeploy --appdir AppDir --plugin qt --output appimage \ --desktop-file AppDir/volrover3.desktop \ @@ -486,22 +442,20 @@ jobs: - name: Install dependencies (libcvc) if: matrix.kind == 'libcvc' run: | - brew install cmake ninja boost hdf5 fftw gsl imagemagick cgal log4cplus zstd - echo "BOOST_ROOT=$(brew --prefix boost)" >> $GITHUB_ENV - echo "CMAKE_PREFIX_PATH=$(brew --prefix boost):$(brew --prefix hdf5)" >> $GITHUB_ENV + # All C/C++ third-party libs come from cvcpkg via + # fetch-libcvc-deps below. Only build tooling here; + # dylibbundler is installed later inside the stage step. + brew install cmake ninja zstd - name: Install dependencies (volrover3) if: matrix.kind == 'volrover3' run: | - brew install cmake ninja boost hdf5 fftw gsl imagemagick qt@6 vtk cgal zstd - echo "BOOST_ROOT=$(brew --prefix boost)" >> $GITHUB_ENV - echo "CMAKE_PREFIX_PATH=$(brew --prefix boost):$(brew --prefix qt@6):$(brew --prefix hdf5)" >> $GITHUB_ENV - echo "$(brew --prefix qt@6)/bin" >> $GITHUB_PATH + # Qt6 + VTK + all third-party C/C++ deps come from cvcpkg via + # fetch-libcvc-deps below. Only build tooling here. + brew install cmake ninja zstd - # ── Pull libiimod (and other prebuilt deps) from libcvc-deps ── - # libcvc no longer vendors libiimod (PR #63); the macOS bundle - # ships libiimodConfig.cmake under share/cmake/libiimod and the - # dylibs alongside. + # ── Pull deps (incl. Qt6/VTK/libiimod) from cvcpkg ── + # cvcpkg is the sole third-party provider on macOS. - name: Fetch libcvc-deps id: libcvc-deps uses: ./.github/actions/fetch-libcvc-deps @@ -511,33 +465,22 @@ jobs: - name: Configure run: | - # Append the libcvc-deps tree (libiimod + bundled deps) to - # the CMAKE_PREFIX_PATH env var that brew set earlier. We do - # NOT pass -DCMAKE_PREFIX_PATH on the cmake command line: - # * The env-var form is `:`-separated on Unix (CMake reads - # it natively). - # * The -D form is a CMake list and is `;`-separated. - # Mixing `:`-joined paths into -DCMAKE_PREFIX_PATH would - # collapse everything into a single path and break find_package. - # - # Order matters: we APPEND (not prepend) so that brew's - # working HDF5 / Boost / Qt configs win over any incomplete - # cmake configs in the libcvc-deps bundle (e.g. the macOS - # bundle's hdf5-config.cmake references a libhdf5.a that is - # not shipped). libcvc-deps is only consulted as a fallback - # for packages brew doesn't provide (libiimod). - if [ -n "${{ steps.libcvc-deps.outputs.path }}" ]; then - if [ -n "${CMAKE_PREFIX_PATH:-}" ]; then - export CMAKE_PREFIX_PATH="$CMAKE_PREFIX_PATH:${{ steps.libcvc-deps.outputs.path }}" - else - export CMAKE_PREFIX_PATH="${{ steps.libcvc-deps.outputs.path }}" - fi + # cvcpkg is the sole third-party prefix on macOS. Do NOT set + # -DCMAKE_PREFIX_PATH on the command line (its list is + # semicolon-separated); use the env var so CMake reads it as + # a native colon-separated PATH. + deps_path="${{ steps.libcvc-deps.outputs.path }}" + if [ -z "$deps_path" ]; then + deps_path="${LIBCVC_DEPS_PREFIX:-$HOME/libcvc-deps}" fi + export CMAKE_PREFIX_PATH="$deps_path" if [ "${{ matrix.link }}" = "static" ]; then shared_libs=OFF - # Homebrew's boost / hdf5 / fftw / gsl bottles ship both .a - # and .dylib; CMake's standard flags select the .a flavor. - static_deps="-DBoost_USE_STATIC_LIBS=ON -DHDF5_USE_STATIC_LIBRARIES=ON" + # cvcpkg publishes both shared (.dylib) and static (.a) + # flavors; the static requirement filter is applied via + # link=static in fetch-libcvc-deps, so no extra find_package + # hints are needed here. + static_deps="" else shared_libs=ON static_deps="" @@ -568,14 +511,17 @@ jobs: cmake --install build --prefix "$DIST" --component libcvc mkdir -p "$DIST/lib" "$DIST/include" + # Third-party prefix populated by cvcpkg. + DEPS="${{ steps.libcvc-deps.outputs.path }}" + if [ -z "$DEPS" ]; then + DEPS="${LIBCVC_DEPS_PREFIX:-$HOME/libcvc-deps}" + fi + # Headers are always needed for downstream `find_package` - # consumers; copy them from Homebrew prefixes. - for pkg in boost hdf5 fftw gsl log4cplus; do - P=$(brew --prefix "$pkg" 2>/dev/null || true) - if [ -n "$P" ] && [ -d "$P/include" ]; then - cp -R "$P/include/." "$DIST/include/" 2>/dev/null || true - fi - done + # consumers; copy them from the cvcpkg prefix. + if [ -d "$DEPS/include" ]; then + cp -R "$DEPS/include/." "$DIST/include/" 2>/dev/null || true + fi if [ "${{ matrix.link }}" = "shared" ]; then # dylibbundler walks libcvc.dylib's transitive deps, copies @@ -584,36 +530,29 @@ jobs: # than hand-rolled install_name_tool plumbing across the deep # Boost/HDF5 dependency graph. brew install dylibbundler - # Build an explicit search-path list of every brewed dep - # plus the standard Homebrew lib roots. Without this, - # dylibbundler hits transitive deps it cannot resolve - # (e.g. libgmp.10.dylib pulled in via CGAL → gmpxx → gmp) - # and drops into an interactive "Try again" prompt; with - # no TTY in CI, that prompt loops forever and burns the - # job until it times out. -s adds search paths, and we - # also close stdin so any unresolved dep aborts cleanly - # instead of looping. - BUNDLER_SEARCH=() - for pkg in boost hdf5 fftw gsl log4cplus cgal gmp mpfr imagemagick libomp libaec libheif libtiff webp x265 aom libde265 zstd libvmaf openexr little-cms2 jpeg-xl xz; do - P=$(brew --prefix "$pkg" 2>/dev/null || true) - [ -n "$P" ] && [ -d "$P/lib" ] && BUNDLER_SEARCH+=( -s "$P/lib" ) - done - BUNDLER_SEARCH+=( -s /opt/homebrew/lib -s /usr/local/lib ) + # Search list: cvcpkg lib dir first (authoritative source of + # every third-party dylib), then macOS system dirs as a + # last-resort fallback. Without this, dylibbundler hits + # unresolved transitive deps and drops into an interactive + # "Try again" prompt; with no TTY in CI, that prompt loops + # forever. -s adds search paths, and we close stdin so any + # remaining unresolved dep aborts cleanly. + BUNDLER_SEARCH=( -s "$DEPS/lib" ) + [ -d /opt/homebrew/lib ] && BUNDLER_SEARCH+=( -s /opt/homebrew/lib ) + [ -d /usr/local/lib ] && BUNDLER_SEARCH+=( -s /usr/local/lib ) for dy in "$DIST"/lib/libcvc*.dylib; do [ -e "$dy" ] || continue [ -L "$dy" ] && continue dylibbundler -b -x "$dy" -d "$DIST/lib" -p '@loader_path/' -of -cd "${BUNDLER_SEARCH[@]}" /dev/null || true) - if [ -n "$P" ] && [ -d "$P/lib" ]; then - find "$P/lib" -maxdepth 1 -name '*.a' -print0 2>/dev/null \ - | xargs -0 -I {} cp -RP {} "$DIST/lib/" 2>/dev/null || true - fi - done + # Static flavour: copy .a archives from the cvcpkg prefix. + # No install_name fixup needed — static linkage embeds + # object code directly. + if [ -d "$DEPS/lib" ]; then + find "$DEPS/lib" -maxdepth 1 -name '*.a' -print0 2>/dev/null \ + | xargs -0 -I {} cp -RP {} "$DIST/lib/" 2>/dev/null || true + fi fi # ── Bundle VTK (no Qt) ────────────────────────────────── @@ -696,121 +635,33 @@ jobs: BTLC=$(echo "${{ matrix.build_type }}" | tr '[:upper:]' '[:lower:]') if [ "${{ matrix.link }}" = "static" ]; then LINKSFX="-static" - TRIPLET="x64-windows-static" else LINKSFX="" - TRIPLET="x64-windows" fi echo "version=$VERSION" >> "$GITHUB_OUTPUT" echo "arch=x86_64" >> "$GITHUB_OUTPUT" echo "btlc=$BTLC" >> "$GITHUB_OUTPUT" echo "linksfx=$LINKSFX" >> "$GITHUB_OUTPUT" - echo "triplet=$TRIPLET" >> "$GITHUB_OUTPUT" - # ── Optional: pull all third-party deps from a libcvc-deps bundle ── - # libcvc-deps ships Boost/HDF5/FFTW/GSL/log4cplus/CGAL/ImageMagick - # + Qt 6 + VTK 9.5 as a self-contained CMake prefix. When a - # matching archive is available we skip vcpkg + aqtinstall + VTK - # build entirely (~tens of minutes saved on cold cache). + # ── Pull all third-party deps (incl. Qt6 + VTK) from cvcpkg ── + # cvcpkg is the sole source of truth for third-party components. + # On miss (network blip, catalog outage) CMake configure fails + # with an explicit "Could not find X" error — there is no vcpkg + # or source-build fallback. # - # NOTE: the Windows libcvc-deps archive is shared-only (no - # -static flavor). Static matrix entries also consume the shared - # archive: libcvc itself stays static (BUILD_SHARED_LIBS=OFF) but - # third-party deps are linked as DLLs. The Configure step below - # then switches the MSVC runtime to /MD so libcvc objects can - # safely cross API boundaries with the shared-CRT-built deps. - - name: Fetch libcvc-deps (Windows speedup) + # NOTE: the Windows cvcpkg prefix is shared-only. Static matrix + # entries also consume the shared prefix: libcvc itself stays + # static (BUILD_SHARED_LIBS=OFF) but third-party deps link as + # DLLs. The Configure step below then switches the MSVC runtime + # to /MD so libcvc objects can safely cross API boundaries with + # the shared-CRT-built deps. + - name: Fetch libcvc-deps (Windows) id: libcvc-deps-windows uses: ./.github/actions/fetch-libcvc-deps with: build_type: ${{ matrix.build_type }} link: shared - # Cache key intentionally excludes hashFiles(workflow) so editing - # this file does not invalidate the warm vcpkg cache. The triplet - # is part of the key so static (x64-windows-static) and shared - # (x64-windows) builds do not share buildtrees. - - name: Cache vcpkg installed packages - if: steps.libcvc-deps-windows.outputs.path == '' - uses: actions/cache@v4 - with: - path: | - ${{ env.VCPKG_INSTALLATION_ROOT }}\installed - ${{ env.VCPKG_INSTALLATION_ROOT }}\packages - ~\AppData\Local\vcpkg\archives - key: vcpkg-release-${{ matrix.kind }}-${{ matrix.build_type }}-${{ steps.meta.outputs.triplet }}-v7 - restore-keys: | - vcpkg-release-${{ matrix.kind }}-${{ matrix.build_type }}-${{ steps.meta.outputs.triplet }}-v7- - - # Install Qt6 from official prebuilt binaries via aqtinstall. - # vcpkg's qt6-base port builds Qt from source (~30+ min cold) so - # we route Qt around vcpkg entirely; VTK and CGAL still come from - # vcpkg. install-qt-action prepends Qt's bin/ to PATH and exports - # Qt6_DIR + QT_ROOT_DIR for CMake to find. Skipped on libcvc-deps - # hit (Qt6 ships inside the bundle). - - name: Install Qt6 (volrover3) - if: matrix.kind == 'volrover3' && steps.libcvc-deps-windows.outputs.path == '' - uses: jurplel/install-qt-action@v4 - with: - version: '6.7.*' - host: 'windows' - target: 'desktop' - # Qt does not publish msvc2022_64 binaries for 6.7.x; the - # msvc2019_64 build is ABI-compatible with the MSVC 2022 - # toolchain that windows-latest provides. - arch: 'win64_msvc2019_64' - modules: '' - cache: true - - # windows-latest C: drive has only ~14 GB free; vcpkg + qt + vtk - # buildtrees blow it up. Free up hosted-tool dirs we don't need - # and route vcpkg's heavy working dirs to D: where there's ~80 GB. - - name: 'Free disk space and route vcpkg to D: (Windows)' - shell: pwsh - run: | - foreach ($dir in @( - 'C:\Android', 'C:\Program Files\dotnet', - 'C:\hostedtoolcache\windows\Ruby', - 'C:\hostedtoolcache\windows\PyPy', - 'C:\hostedtoolcache\windows\go', - 'C:\hostedtoolcache\windows\node')) { - if (Test-Path $dir) { - Write-Host "Removing $dir" - Remove-Item -Recurse -Force -ErrorAction SilentlyContinue $dir - } - } - New-Item -ItemType Directory -Force -Path D:\vcpkg-buildtrees | Out-Null - New-Item -ItemType Directory -Force -Path D:\vcpkg-packages | Out-Null - New-Item -ItemType Directory -Force -Path D:\vcpkg-downloads | Out-Null - "VCPKG_DOWNLOADS=D:\vcpkg-downloads" | Out-File -Append $env:GITHUB_ENV - "VCPKG_BUILDTREES=D:\vcpkg-buildtrees" | Out-File -Append $env:GITHUB_ENV - "VCPKG_PACKAGES=D:\vcpkg-packages" | Out-File -Append $env:GITHUB_ENV - - # Restrict vcpkg to building only the matrix.build_type - # configuration of each port (instead of building both - # Debug+Release upstream-default). The overlay triplet at - # vcpkg-overlay/triplets/x64-windows{,-static}.cmake reads - # VCPKG_FORCE_BUILD_TYPE and applies it as VCPKG_BUILD_TYPE. - $btlc = '${{ matrix.build_type }}'.ToLower() - "VCPKG_FORCE_BUILD_TYPE=$btlc" | Out-File -Append $env:GITHUB_ENV - "VCPKG_OVERLAY_TRIPLETS=${{ github.workspace }}\vcpkg-overlay\triplets" | Out-File -Append $env:GITHUB_ENV - - Get-PSDrive -PSProvider FileSystem | Format-Table -AutoSize - - # Persist vcpkg's source-tarball download cache across runs so a - # transient upstream timeout (e.g. gmplib.org) does not poison - # subsequent attempts. Save runs even if a later step fails so - # partial progress is captured. Downloads are triplet-agnostic, - # so all matrix jobs share a single cache. - - name: Restore vcpkg downloads cache (Windows) - id: vcpkg-downloads-cache - uses: actions/cache/restore@v4 - with: - path: D:\vcpkg-downloads - key: vcpkg-downloads-${{ hashFiles('.github/workflows/release.yml') }} - restore-keys: | - vcpkg-downloads- - # CUDA toolkit so the shipped Windows artifacts can offload to # NVIDIA GPUs at runtime when an NVIDIA driver is present. cudart # is linked statically (CMAKE_CUDA_RUNTIME_LIBRARY=Static below) @@ -822,101 +673,6 @@ jobs: method: 'network' sub-packages: '["nvcc", "cudart", "visual_studio_integration"]' - - name: Install dependencies - if: steps.libcvc-deps-windows.outputs.path == '' - shell: pwsh - env: - # Pull our hand-rolled imagemagick overlay port (Q16-HDRI x64, - # repackaged from the upstream Inno Setup installer; see - # vcpkg-overlay/ports/imagemagick/portfile.cmake). - VCPKG_OVERLAY_PORTS: ${{ github.workspace }}\vcpkg-overlay\ports - run: | - $packages = @( - 'boost-thread','boost-date-time','boost-regex','boost-filesystem', - 'boost-system','boost-chrono','boost-program-options','boost-signals2', - 'boost-format','boost-property-tree','boost-algorithm','boost-asio', - 'boost-any','boost-array','boost-dynamic-bitset','boost-exception', - 'boost-foreach','boost-function','boost-lambda','boost-lexical-cast', - 'boost-multi-array','boost-optional','boost-smart-ptr','boost-tuple', - 'boost-utility','hdf5[cpp]','fftw3','gsl','imagemagick','log4cplus' - ) - # cgal is required by libcvc's SDF/Mesher features and by - # volrover3. qt6-base is intentionally omitted on volrover3 — - # handled by install-qt-action above. vtk[qt,opengl] is also - # omitted: the qt feature pulled qt6-base/qt5compat/qttools/ - # qtdeclarative as transitive build deps, rebuilding all of - # Qt6 from source on every cold-cache run. We build VTK 9.5 - # from source against aqtinstall's prebuilt Qt6 in a separate - # cached step below (mirrors Ubuntu). - $packages += @('cgal') - for ($i = 1; $i -le 5; $i++) { - Write-Host "vcpkg install attempt $i / 5" - # --clean-after-build deletes each port's buildtree/packages - # immediately after installation. --x-buildtrees-root / - # --x-packages-root keep the heavy working dirs on D:. - vcpkg install @packages --triplet ${{ steps.meta.outputs.triplet }} ` - --clean-after-build ` - --x-buildtrees-root=D:\vcpkg-buildtrees ` - --x-packages-root=D:\vcpkg-packages - if ($LASTEXITCODE -eq 0) { break } - if ($i -eq 5) { exit $LASTEXITCODE } - # Clear half-finished buildtrees/packages so the retry does - # not trip over stale state from the previous attempt. - Remove-Item -Recurse -Force -ErrorAction SilentlyContinue D:\vcpkg-buildtrees\* - Remove-Item -Recurse -Force -ErrorAction SilentlyContinue D:\vcpkg-packages\* - $sleep = [Math]::Min(60 * $i, 180) - Write-Host "Retrying after transient failure (sleeping $sleep s)..." - Start-Sleep -Seconds $sleep - } - - - name: Save vcpkg downloads cache (Windows) - if: always() && steps.libcvc-deps-windows.outputs.path == '' && steps.vcpkg-downloads-cache.outputs.cache-hit != 'true' - uses: actions/cache/save@v4 - with: - path: D:\vcpkg-downloads - key: vcpkg-downloads-${{ hashFiles('.github/workflows/release.yml') }} - - # ── VTK 9.5 built from source against aqt Qt6, cached ── - # Mirrors the Ubuntu strategy. Cache key includes build_type - # because MSVC Debug and Release VTK DLLs are not interchangeable. - - name: Restore VTK (Qt6) cache (Windows) - if: matrix.kind == 'volrover3' && steps.libcvc-deps-windows.outputs.path == '' - id: vtk-cache-windows - uses: actions/cache/restore@v4 - with: - path: D:\vtk-qt6 - key: vtk-9.5.0-qt6-${{ runner.os }}-${{ matrix.build_type }}-v1 - - - name: Build VTK 9.5 from source against Qt6 (Windows) - if: matrix.kind == 'volrover3' && steps.libcvc-deps-windows.outputs.path == '' && steps.vtk-cache-windows.outputs.cache-hit != 'true' - shell: pwsh - run: | - Invoke-WebRequest -Uri https://github.com/Kitware/VTK/archive/refs/tags/v9.5.0.tar.gz -OutFile vtk.tar.gz - tar xzf vtk.tar.gz - cmake -S VTK-9.5.0 -B D:\vtk-build ` - -DCMAKE_INSTALL_PREFIX=D:\vtk-qt6 ` - -DCMAKE_PREFIX_PATH="$env:QT_ROOT_DIR" ` - -DBUILD_SHARED_LIBS=ON ` - -DVTK_GROUP_ENABLE_Qt=YES ` - -DVTK_QT_VERSION=6 ` - -DVTK_MODULE_ENABLE_VTK_GUISupportQtQuick=NO ` - -DVTK_MODULE_ENABLE_VTK_RenderingQtQuick=NO ` - -DVTK_BUILD_TESTING=OFF ` - -DVTK_BUILD_EXAMPLES=OFF ` - -DVTK_BUILD_DOCUMENTATION=OFF ` - -DVTK_LEGACY_REMOVE=ON - cmake --build D:\vtk-build --config ${{ matrix.build_type }} --parallel --target install - Remove-Item -Recurse -Force D:\vtk-build - Remove-Item -Recurse -Force VTK-9.5.0 - Remove-Item -Force vtk.tar.gz - - - name: Save VTK (Qt6) cache (Windows) - if: matrix.kind == 'volrover3' && steps.libcvc-deps-windows.outputs.path == '' && steps.vtk-cache-windows.outputs.cache-hit != 'true' - uses: actions/cache/save@v4 - with: - path: D:\vtk-qt6 - key: vtk-9.5.0-qt6-${{ runner.os }}-${{ matrix.build_type }}-v1 - - name: Configure shell: pwsh run: | @@ -925,41 +681,28 @@ jobs: # the GUI app and its extra deps. $xmlrpc = if ('${{ matrix.kind }}' -eq 'libcvc') { 'ON' } else { 'OFF' } $depsPath = '${{ steps.libcvc-deps-windows.outputs.path }}' - # link=shared → BUILD_SHARED_LIBS=ON + x64-windows triplet (DLLs) - # link=static → BUILD_SHARED_LIBS=OFF + x64-windows-static triplet - # (.lib for libcvc and every vcpkg dep, MSVC static runtime). - # libcvc-deps Windows is shared-only: when it hits on a static - # matrix entry, force /MD (DLL CRT) so libcvc objects can - # cross API boundaries with the /MD-built libcvc-deps DLLs - # without heap mismatches. libcvc itself stays static. - if ('${{ matrix.link }}' -eq 'static' -and -not $depsPath) { - $shared = 'OFF' - $msvcrt = 'MultiThreaded$<$:Debug>' - } elseif ('${{ matrix.link }}' -eq 'static') { + if (-not $depsPath) { + $depsPath = if ($env:LIBCVC_DEPS_PREFIX) { $env:LIBCVC_DEPS_PREFIX } else { 'D:\libcvc-deps' } + if (-not (Test-Path $depsPath)) { $depsPath = '' } + } + # The Windows cvcpkg prefix is shared-only. Static matrix + # entries still consume the shared prefix: libcvc stays + # static (BUILD_SHARED_LIBS=OFF) but its third-party deps + # link as DLLs. Force /MD (DLL CRT) so libcvc objects can + # cross API boundaries with the /MD-built deps DLLs without + # heap mismatches. + if ('${{ matrix.link }}' -eq 'static') { $shared = 'OFF' - $msvcrt = 'MultiThreaded$<$:Debug>DLL' } else { $shared = 'ON' - $msvcrt = 'MultiThreaded$<$:Debug>DLL' } - # Pin Qt6 to install-qt-action's tree (QT_ROOT_DIR) and add - # the from-source VTK install tree alongside it (or the - # libcvc-deps tree, when the speedup hit). CMAKE_PREFIX_PATH - # is a semicolon-separated list on Windows. - $prefixes = @() - # libcvc-deps (when present) ships Boost/HDF5/FFTW/GSL/log4cplus/ - # CGAL/ImageMagick + Qt6 + VTK for BOTH kinds, so prefer it. - if ($depsPath) { $prefixes += $depsPath } - if ($env:QT_ROOT_DIR) { $prefixes += $env:QT_ROOT_DIR } - if ('${{ matrix.kind }}' -eq 'volrover3' -and -not $depsPath) { - $prefixes += 'D:\vtk-qt6' - } - $qtPrefix = $prefixes -join ';' + $msvcrt = 'MultiThreaded$<$:Debug>DLL' + # cvcpkg is the sole third-party prefix. No vcpkg toolchain + # file, no aqt Qt6, no VTK source cache. + $qtPrefix = if ($depsPath) { $depsPath } else { '' } cmake -B build ` -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} ` -DCMAKE_CONFIGURATION_TYPES=${{ matrix.build_type }} ` - -DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake" ` - -DVCPKG_TARGET_TRIPLET=${{ steps.meta.outputs.triplet }} ` -DCMAKE_PREFIX_PATH="$qtPrefix" ` -DBUILD_SHARED_LIBS=$shared ` -DCMAKE_MSVC_RUNTIME_LIBRARY="$msvcrt" ` @@ -1011,35 +754,22 @@ jobs: } } - # ── third-party deps: from vcpkg installed/ OR libcvc-deps ── - # On libcvc-deps hit (steps.libcvc-deps-windows.outputs.path - # non-empty) the dep tree is laid out flat under $depsPath - # with include/, lib/, bin/ at the root (shared build, no - # debug/ subtree). On miss, deps come from vcpkg's - # installed//{bin,lib,include}{,/debug}. + # ── third-party deps: from cvcpkg (libcvc-deps prefix) ── + # The cvcpkg prefix is laid out flat under $depsPath with + # include/, lib/, bin/ at the root (shared build, no debug/ + # subtree). Debug bundles include .pdb files alongside the + # DLLs and .lib import libs. $depsPath = '${{ steps.libcvc-deps-windows.outputs.path }}' - if ($depsPath) { - $libSrc = Join-Path $depsPath 'lib' - $binSrc = Join-Path $depsPath 'bin' - $incSrc = Join-Path $depsPath 'include' - $havePdbs = $isDebug # libcvc-deps debug bundles ship .pdb - } else { - $triplet = '${{ steps.meta.outputs.triplet }}' - $vcRoot = Join-Path $env:VCPKG_INSTALLATION_ROOT "installed/$triplet" - if (-not (Test-Path $vcRoot)) { - throw "vcpkg installed tree not found at $vcRoot" + if (-not $depsPath) { + $depsPath = if ($env:LIBCVC_DEPS_PREFIX) { $env:LIBCVC_DEPS_PREFIX } else { 'D:\libcvc-deps' } + if (-not (Test-Path $depsPath)) { + throw "cvcpkg prefix not found (checked steps.libcvc-deps-windows.outputs.path and $depsPath)" } - - # Layout per triplet: - # /{bin,lib,include} — release - # /debug/{bin,lib} — debug (headers shared) - # Debug subtrees also carry .pdb files alongside their .dll / - # .lib outputs (vcpkg builds with /Zi /DEBUG by default). - $libSrc = if ($isDebug) { Join-Path $vcRoot 'debug/lib' } else { Join-Path $vcRoot 'lib' } - $binSrc = if ($isDebug) { Join-Path $vcRoot 'debug/bin' } else { Join-Path $vcRoot 'bin' } - $incSrc = Join-Path $vcRoot 'include' - $havePdbs = $isDebug } + $libSrc = Join-Path $depsPath 'lib' + $binSrc = Join-Path $depsPath 'bin' + $incSrc = Join-Path $depsPath 'include' + $havePdbs = $isDebug New-Item -ItemType Directory -Force -Path "$dist/lib","$dist/include" | Out-Null if (Test-Path $incSrc) { @@ -1052,11 +782,11 @@ jobs: | Copy-Item -Destination "$dist/lib/" -Force } } - # On libcvc-deps hit, third-party DLLs need to ship even for - # static matrix entries because libcvc-deps is shared-only on - # Windows (libcvc itself is still .lib; consumers load the - # shipped DLLs at runtime). - $shipBin = ('${{ matrix.link }}' -eq 'shared') -or [bool]$depsPath + # Third-party DLLs always ship because the Windows cvcpkg + # prefix is shared-only, even for static matrix entries + # (libcvc itself is still .lib; consumers load the shipped + # DLLs at runtime). + $shipBin = $true if ($shipBin) { New-Item -ItemType Directory -Force -Path "$dist/bin" | Out-Null if (Test-Path $binSrc) { diff --git a/cvc-requirements.yaml b/cvc-requirements.yaml index 8c976aaf..48a197b8 100644 --- a/cvc-requirements.yaml +++ b/cvc-requirements.yaml @@ -33,4 +33,6 @@ components: - abseil - protobuf - grpc + - qt6 + - vtk - pthreads4w diff --git a/cvcpkg/recipes/libcvc/recipe.yaml b/cvcpkg/recipes/libcvc/recipe.yaml new file mode 100644 index 00000000..86f48a4d --- /dev/null +++ b/cvcpkg/recipes/libcvc/recipe.yaml @@ -0,0 +1,78 @@ +schema_version: 1 +recipe: + name: libcvc + upstream_version: "3.2.4" + cvc_revision: 1 + maintainer: "cvcpkg group" + maintainer_email: "info@cvcpkg.org" + maintainer_url: "https://cvcpkg.org" + description: "Center for Computational Visualization core C++ library — volumetric data structures, distributed state, geometry & image processing, DSL runtime." + homepage: https://github.com/transfix/libcvc + license: GPL-2.0-or-later + tags: [scientific, volumetric, state, dsl, geometry] + +source: + # The recipe is co-located with the libcvc source tree. `pack-from-prefix` + # (see cvcpkg roadmap) does not consume this field, but the schema + # requires it; `..` points at the libcvc repo root for tools that walk + # the source tree. + type: vendored + path: .. + +patches: [] + +depends: + build: [] + runtime: + - name: zlib + - name: boost + - name: fftw3 + - name: gsl + - name: cgal + - name: hdf5 + - name: log4cplus + - name: tiff + - name: imagemagick + - name: yaml + - name: levmar + - name: libiimod + - name: openblas + - name: openssl + - name: c-ares + - name: re2 + - name: abseil + - name: protobuf + - name: grpc + - name: qt6 + - name: vtk + - name: pthreads4w + platforms: [windows] + host_tools: + - cmake + - ninja + +build: + matrix: + - platform: linux + script: build.sh + - platform: macos + script: build.sh + env: + MACOSX_DEPLOYMENT_TARGET: "13.0" + - platform: windows + script: build.ps1 + +package: + files: + - bin/cvc* + - lib/libcvc* + - lib/cvc* + - lib/cmake/cvc/ + - include/cvc/ + cmake_packages: + - name: cvc + targets: + - cvc::cvc + +abi: + cxx_std: "17" diff --git a/vcpkg-overlay/ports/imagemagick/portfile.cmake b/vcpkg-overlay/ports/imagemagick/portfile.cmake deleted file mode 100644 index 2343ba73..00000000 --- a/vcpkg-overlay/ports/imagemagick/portfile.cmake +++ /dev/null @@ -1,170 +0,0 @@ -# ───────────────────────────────────────────────────────────────────── -# imagemagick (overlay port) -# -# vcpkg upstream does not ship an `imagemagick` port. ImageMagick's -# Windows source build is intricate (its own VisualMagick configure -# wizard, ~20 optional delegate libraries, Q-depth and HDRI matrices) -# and not amenable to a quick vcpkg port. For CI usage we instead -# repackage the official upstream Inno Setup installer: -# -# * Download the installer + a pinned innoextract.exe -# * Run innoextract to unpack the installer into a temp tree -# * Copy headers / import libraries / DLLs into vcpkg's layout so -# CMake's stock FindImageMagick.cmake can locate them via the -# vcpkg toolchain's CMAKE_PREFIX_PATH. -# -# This intentionally tracks the Q16-HDRI x64 build, which matches the -# brew-installed flavor on macOS and keeps MAGICKCORE_QUANTUM_DEPTH=16 -# / MAGICKCORE_HDRI_ENABLE=1 consistent across platforms. -# ───────────────────────────────────────────────────────────────────── - -if(NOT VCPKG_TARGET_IS_WINDOWS) - message(FATAL_ERROR "imagemagick overlay port is Windows-only.") -endif() - -if(NOT VCPKG_TARGET_ARCHITECTURE STREQUAL "x64") - message(FATAL_ERROR "imagemagick overlay port supports x64 only.") -endif() - -set(IM_VERSION "7.1.2-22") - -# Mirror order matters: GitHub Releases is the canonical permanent home for -# every published ImageMagick installer (assets are immutable once a tag -# ships), so we try it first. imagemagick.org/archive/binaries/ only keeps -# the *latest* point release live -- once a new -NN drops, the previous URL -# returns 404 -- so it is best-effort fallback in case GitHub is unreachable. -# vcpkg also resolves cached downloads (e.g. populated by a libcvc-deps -# prefetch step) by SHA512 in $VCPKG_ROOT/downloads regardless of URL, so -# this list never has to enumerate every conceivable mirror. -vcpkg_download_distfile(IM_INSTALLER - URLS - "https://github.com/ImageMagick/ImageMagick/releases/download/${IM_VERSION}/ImageMagick-${IM_VERSION}-Q16-HDRI-x64-dll.exe" - "https://imagemagick.org/archive/binaries/ImageMagick-${IM_VERSION}-Q16-HDRI-x64-dll.exe" - FILENAME "ImageMagick-${IM_VERSION}-Q16-HDRI-x64-dll.exe" - SHA512 c3576a97380e172d6377a91831441d8f4f6bdf57819b8d6892a061355d80444bf68ef5cf08405396a5ca7cdff18eae65ab195319319d6d92026d9502b5de2fd7 -) - -# innoextract 1.9 supports Inno Setup 6.x (the format used by IM 7.1.2-NN). -vcpkg_download_distfile(INNOEXTRACT_ZIP - URLS "https://github.com/dscharrer/innoextract/releases/download/1.9/innoextract-1.9-windows.zip" - FILENAME "innoextract-1.9-windows.zip" - SHA512 eea751bc021b8cb9a979875d7df5ef0438a8ec6157f75fa9d34b0471bb359daf15cf9cee51a21f9115cb8410bdb836a57f1cd6b2198c634cf108e6785f902488 -) - -# Stage everything under the buildtrees temp dir. -set(STAGE_DIR "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-stage") -file(REMOVE_RECURSE "${STAGE_DIR}") -file(MAKE_DIRECTORY "${STAGE_DIR}") - -# Unpack innoextract.exe. -vcpkg_execute_required_process( - COMMAND "${CMAKE_COMMAND}" -E tar xf "${INNOEXTRACT_ZIP}" - WORKING_DIRECTORY "${STAGE_DIR}" - LOGNAME "unzip-innoextract-${TARGET_TRIPLET}" -) -set(INNOEXTRACT_EXE "${STAGE_DIR}/innoextract.exe") -if(NOT EXISTS "${INNOEXTRACT_EXE}") - message(FATAL_ERROR "innoextract.exe missing after unzip: ${INNOEXTRACT_EXE}") -endif() - -# Run innoextract on the IM installer. -s strips the leading "{app}" -# prefix from extracted paths so the layout below is just "app/...". -vcpkg_execute_required_process( - COMMAND "${INNOEXTRACT_EXE}" --silent --extract --output-dir "${STAGE_DIR}" "${IM_INSTALLER}" - WORKING_DIRECTORY "${STAGE_DIR}" - LOGNAME "innoextract-${TARGET_TRIPLET}" -) -set(APP_DIR "${STAGE_DIR}/app") -if(NOT EXISTS "${APP_DIR}/include") - message(FATAL_ERROR "innoextract output missing headers: ${APP_DIR}/include") -endif() - -# Headers: Magick++/, MagickCore/, MagickWand/ subtrees go straight into -# the vcpkg include root. CMake's FindImageMagick recognises this layout. -file(COPY "${APP_DIR}/include/" DESTINATION "${CURRENT_PACKAGES_DIR}/include") - -# Determine which configurations vcpkg expects from this port. When the -# triplet sets VCPKG_BUILD_TYPE to "release" or "debug", vcpkg's policy -# checks reject the opposite tree existing, so we must skip populating -# it. Default (unset) means both. -set(_im_want_release TRUE) -set(_im_want_debug TRUE) -if(DEFINED VCPKG_BUILD_TYPE) - if(VCPKG_BUILD_TYPE STREQUAL "release") - set(_im_want_debug FALSE) - elseif(VCPKG_BUILD_TYPE STREQUAL "debug") - set(_im_want_release FALSE) - endif() -endif() - -# Import libraries. -file(GLOB IM_LIBS "${APP_DIR}/lib/CORE_RL_*.lib") -if(NOT IM_LIBS) - message(FATAL_ERROR "No CORE_RL_*.lib files found under ${APP_DIR}/lib") -endif() -set(_im_lib_dirs) -if(_im_want_release) - file(MAKE_DIRECTORY "${CURRENT_PACKAGES_DIR}/lib") - file(COPY ${IM_LIBS} DESTINATION "${CURRENT_PACKAGES_DIR}/lib") - list(APPEND _im_lib_dirs "${CURRENT_PACKAGES_DIR}/lib") -endif() -if(_im_want_debug) - file(MAKE_DIRECTORY "${CURRENT_PACKAGES_DIR}/debug/lib") - # Upstream ships only one (release) build; alias it for Debug so - # vcpkg's debug-config also resolves. - file(COPY ${IM_LIBS} DESTINATION "${CURRENT_PACKAGES_DIR}/debug/lib") - list(APPEND _im_lib_dirs "${CURRENT_PACKAGES_DIR}/debug/lib") -endif() - -# CMake's FindImageMagick.cmake searches for Magick++/MagickCore/ -# MagickWand/MagickWand-7.Q16HDRI/etc. and also CORE_RL_Magick++_, -# but only on recent versions. Older CMakes (< 3.27 or so) and some -# distro-patched FindImageMagick scripts only know the canonical -# unprefixed names. The IM Windows installer ships only -# 'CORE_RL__.lib', so create canonical-named hardlinks/ -# copies (Magick++.lib, MagickCore.lib, MagickWand.lib) to make -# find_library() succeed regardless of FindImageMagick vintage. -foreach(_im_lib_dir IN LISTS _im_lib_dirs) - foreach(_im_component Magick++ MagickCore MagickWand) - set(_src "${_im_lib_dir}/CORE_RL_${_im_component}_.lib") - set(_dst "${_im_lib_dir}/${_im_component}.lib") - if(EXISTS "${_src}" AND NOT EXISTS "${_dst}") - configure_file("${_src}" "${_dst}" COPYONLY) - endif() - endforeach() -endforeach() - -# Runtime DLLs (CORE_RL_*, IM_MOD_RL_*, FILTER_*) plus the C/C++ runtime -# bundled by the installer; ship them all so apps can resolve symbols. -file(GLOB IM_DLLS - "${APP_DIR}/CORE_RL_*.dll" - "${APP_DIR}/IM_MOD_RL_*.dll" - "${APP_DIR}/FILTER_*.dll" -) -if(IM_DLLS) - if(_im_want_release) - file(MAKE_DIRECTORY "${CURRENT_PACKAGES_DIR}/bin") - file(COPY ${IM_DLLS} DESTINATION "${CURRENT_PACKAGES_DIR}/bin") - endif() - if(_im_want_debug) - file(MAKE_DIRECTORY "${CURRENT_PACKAGES_DIR}/debug/bin") - file(COPY ${IM_DLLS} DESTINATION "${CURRENT_PACKAGES_DIR}/debug/bin") - endif() -endif() - -# License. -if(EXISTS "${APP_DIR}/License.txt") - file(INSTALL "${APP_DIR}/License.txt" - DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}" - RENAME copyright) -elseif(EXISTS "${APP_DIR}/LICENSE.txt") - file(INSTALL "${APP_DIR}/LICENSE.txt" - DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}" - RENAME copyright) -else() - file(WRITE "${CURRENT_PACKAGES_DIR}/share/${PORT}/copyright" - "ImageMagick license: see https://imagemagick.org/script/license.php\n") -endif() - -file(INSTALL "${CMAKE_CURRENT_LIST_DIR}/usage" - DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}") diff --git a/vcpkg-overlay/ports/imagemagick/usage b/vcpkg-overlay/ports/imagemagick/usage deleted file mode 100644 index 596e2e2b..00000000 --- a/vcpkg-overlay/ports/imagemagick/usage +++ /dev/null @@ -1,9 +0,0 @@ -imagemagick provides Q16-HDRI x64 development files for Windows. Use -CMake's stock module to consume it: - - find_package(ImageMagick COMPONENTS Magick++ MagickCore) - target_include_directories(your_target PRIVATE ${ImageMagick_INCLUDE_DIRS}) - target_link_libraries(your_target PRIVATE ${ImageMagick_LIBRARIES}) - target_compile_definitions(your_target PRIVATE - MAGICKCORE_QUANTUM_DEPTH=16 - MAGICKCORE_HDRI_ENABLE=1) diff --git a/vcpkg-overlay/ports/imagemagick/vcpkg.json b/vcpkg-overlay/ports/imagemagick/vcpkg.json deleted file mode 100644 index c723fd97..00000000 --- a/vcpkg-overlay/ports/imagemagick/vcpkg.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "name": "imagemagick", - "version": "7.1.2-22", - "port-version": 0, - "description": "ImageMagick Q16-HDRI development files for Windows, repackaged from the official upstream Inno Setup installer.", - "homepage": "https://imagemagick.org/", - "license": "ImageMagick", - "supports": "windows & x64" -} diff --git a/vcpkg-overlay/triplets/x64-windows-static.cmake b/vcpkg-overlay/triplets/x64-windows-static.cmake deleted file mode 100644 index 331ea7e9..00000000 --- a/vcpkg-overlay/triplets/x64-windows-static.cmake +++ /dev/null @@ -1,10 +0,0 @@ -# vcpkg overlay triplet: x64-windows-static -# -# See x64-windows.cmake for rationale. This is the static-CRT/static-lib -# variant used by the release.yml `link: static` matrix entries. -set(VCPKG_TARGET_ARCHITECTURE x64) -set(VCPKG_CRT_LINKAGE static) -set(VCPKG_LIBRARY_LINKAGE static) -if(DEFINED ENV{VCPKG_FORCE_BUILD_TYPE} AND NOT "$ENV{VCPKG_FORCE_BUILD_TYPE}" STREQUAL "") - set(VCPKG_BUILD_TYPE "$ENV{VCPKG_FORCE_BUILD_TYPE}") -endif() diff --git a/vcpkg-overlay/triplets/x64-windows.cmake b/vcpkg-overlay/triplets/x64-windows.cmake deleted file mode 100644 index 45eb0f9b..00000000 --- a/vcpkg-overlay/triplets/x64-windows.cmake +++ /dev/null @@ -1,18 +0,0 @@ -# vcpkg overlay triplet: x64-windows -# -# Overrides the upstream x64-windows triplet so that we can restrict -# vcpkg to building only one of {Debug,Release} per CI job, instead of -# building both configurations (the upstream default). Each Windows CI -# job consumes only its matching matrix.build_type, so the other half -# is wasted ~equal-time work. -# -# Activated by setting the VCPKG_FORCE_BUILD_TYPE environment variable -# to "release" or "debug" before invoking vcpkg / cmake. When the env -# var is unset or empty, behavior matches the upstream default (both -# debug and release built). -set(VCPKG_TARGET_ARCHITECTURE x64) -set(VCPKG_CRT_LINKAGE dynamic) -set(VCPKG_LIBRARY_LINKAGE dynamic) -if(DEFINED ENV{VCPKG_FORCE_BUILD_TYPE} AND NOT "$ENV{VCPKG_FORCE_BUILD_TYPE}" STREQUAL "") - set(VCPKG_BUILD_TYPE "$ENV{VCPKG_FORCE_BUILD_TYPE}") -endif()