diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 1b63f0e..ef2468b 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -134,3 +134,96 @@ jobs:
flags: windows
fail_ci_if_error: true
token: ${{ secrets.CODECOV_TOKEN }}
+
+ zlib-only-host-build:
+ name: zlib-only host (${{ matrix.os }})
+ runs-on: ${{ matrix.os }}
+ strategy:
+ fail-fast: false
+ matrix:
+ os: [ubuntu-latest, macos-latest, windows-latest]
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Cache vcpkg (Windows)
+ if: runner.os == 'Windows'
+ uses: actions/cache@v4
+ with:
+ path: ${{ github.workspace }}\vcpkg_cache
+ key: zlib-only-vcpkg-${{ runner.os }}-${{ hashFiles('.github/workflows/ci.yml') }}
+
+ - name: Install dependencies (Ubuntu)
+ if: runner.os == 'Linux'
+ run: |
+ sudo apt-get update
+ sudo apt-get install -y \
+ cmake \
+ g++ \
+ ninja-build \
+ zlib1g-dev \
+ libgtest-dev
+
+ - name: Install dependencies (macOS)
+ if: runner.os == 'macOS'
+ run: |
+ brew update
+ brew install ninja zlib googletest
+
+ - name: Install dependencies (Windows)
+ if: runner.os == 'Windows'
+ shell: pwsh
+ run: |
+ choco install ninja -y
+ New-Item -ItemType Directory -Force "$env:GITHUB_WORKSPACE\vcpkg_cache" | Out-Null
+ git clone https://github.com/microsoft/vcpkg "$env:GITHUB_WORKSPACE\vcpkg"
+ & "$env:GITHUB_WORKSPACE\vcpkg\bootstrap-vcpkg.bat"
+ $env:VCPKG_BINARY_SOURCES="clear;files,$env:GITHUB_WORKSPACE\vcpkg_cache,readwrite"
+ & "$env:GITHUB_WORKSPACE\vcpkg\vcpkg.exe" install `
+ zlib `
+ gtest `
+ --triplet x64-windows
+
+ - name: Setup MSVC environment (Windows)
+ if: runner.os == 'Windows'
+ uses: ilammy/msvc-dev-cmd@v1
+
+ - name: Configure (Ubuntu)
+ if: runner.os == 'Linux'
+ run: |
+ cmake -S . -B build-zlib-only \
+ -G Ninja \
+ -DTRX_USE_CONAN=OFF \
+ -DTRX_BUILD_TESTS=ON \
+ -DTRX_BUILD_EXAMPLES=ON \
+ -DTRX_ENABLE_NIFTI=ON \
+ -DCMAKE_BUILD_TYPE=Release
+
+ - name: Configure (macOS)
+ if: runner.os == 'macOS'
+ run: |
+ BREW_PREFIX="$(brew --prefix)"
+ cmake -S . -B build-zlib-only \
+ -G Ninja \
+ -DTRX_USE_CONAN=OFF \
+ -DTRX_BUILD_TESTS=ON \
+ -DTRX_BUILD_EXAMPLES=ON \
+ -DTRX_ENABLE_NIFTI=ON \
+ -DCMAKE_PREFIX_PATH="${BREW_PREFIX}" \
+ -DCMAKE_BUILD_TYPE=Release
+
+ - name: Configure (Windows)
+ if: runner.os == 'Windows'
+ run: >
+ cmake -S . -B build-zlib-only
+ -G Ninja
+ -DTRX_USE_CONAN=OFF
+ -DTRX_BUILD_TESTS=ON
+ -DTRX_ENABLE_NIFTI=ON
+ -DCMAKE_BUILD_TYPE=Release
+ -DCMAKE_TOOLCHAIN_FILE=${{ github.workspace }}/vcpkg/scripts/buildsystems/vcpkg.cmake
+
+ - name: Build
+ run: cmake --build build-zlib-only --config Release
+
+ - name: Test
+ run: ctest --test-dir build-zlib-only --output-on-failure -C Release
diff --git a/CMakeLists.txt b/CMakeLists.txt
index f89b1dc..5fdf61e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -29,6 +29,7 @@ option(TRX_ENABLE_CLANG_TIDY "Run clang-tidy during builds" OFF)
option(TRX_ENABLE_INSTALL "Install trx-cpp targets" ${TRX_IS_TOP_LEVEL})
option(TRX_BUILD_DOCS "Build API documentation with Doxygen/Sphinx" OFF)
option(TRX_ENABLE_NIFTI "Enable optional NIfTI header utilities" ${TRX_BUILD_EXAMPLES})
+option(TRX_FETCH_EIGEN "Fetch Eigen3 via FetchContent if not found" ON)
if(TRX_ENABLE_CLANG_TIDY)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
@@ -127,8 +128,27 @@ else()
list(PREPEND CMAKE_PREFIX_PATH "${Eigen3_ROOT}")
endif()
find_package(Eigen3 3.4 CONFIG QUIET)
- if(NOT Eigen3_FOUND)
- find_package(Eigen3 REQUIRED) # try module mode
+ if(NOT TARGET Eigen3::Eigen)
+ # Try module mode for distributions that ship FindEigen3.cmake.
+ find_package(Eigen3 3.4 QUIET MODULE)
+ endif()
+ if(NOT TARGET Eigen3::Eigen AND TRX_FETCH_EIGEN)
+ message(STATUS "Eigen3 not found; fetching v3.4.0")
+ # Eigen 3.4.0's CMakeLists.txt unconditionally enters blas/ and lapack/
+ # which call enable_language(Fortran). Use SOURCE_SUBDIR to skip running
+ # Eigen's CMake entirely — Eigen is header-only so we just need the
+ # include path.
+ FetchContent_Declare(
+ eigen
+ URL https://gitlab.com/libeigen/eigen/-/archive/3.4.0/eigen-3.4.0.tar.gz
+ DOWNLOAD_EXTRACT_TIMESTAMP TRUE
+ SOURCE_SUBDIR _nonexistent
+ )
+ FetchContent_MakeAvailable(eigen)
+ add_library(Eigen3::Eigen INTERFACE IMPORTED)
+ set_target_properties(Eigen3::Eigen PROPERTIES
+ INTERFACE_INCLUDE_DIRECTORIES "${eigen_SOURCE_DIR}"
+ )
endif()
# Create an imported target if the package did not provide one (module mode)
if(NOT TARGET Eigen3::Eigen AND EXISTS "${EIGEN3_INCLUDE_DIR}")
@@ -137,6 +157,9 @@ else()
INTERFACE_INCLUDE_DIRECTORIES "${EIGEN3_INCLUDE_DIR}"
)
endif()
+ if(NOT TARGET Eigen3::Eigen)
+ message(FATAL_ERROR "Eigen3 target not found. Install Eigen3 or set TRX_FETCH_EIGEN=ON.")
+ endif()
set(TRX_EIGEN3_TARGET Eigen3::Eigen)
endif()
@@ -277,6 +300,22 @@ if(TRX_BUILD_DOCS)
endif()
# ── Installation and package config ─────────────────────────────────────────
+# Installation requires that link dependencies are IMPORTED targets (so they
+# don't need to be in the export set). This is the case when libzip and Eigen
+# were found via find_package (libzip::zip, Eigen3::Eigen). When they were
+# built via FetchContent (bare "zip" target), installation is not supported —
+# the FetchContent path is a developer/CI convenience, not an installable
+# configuration.
+if(TRX_ENABLE_INSTALL AND TARGET zip)
+ get_target_property(_zip_imported zip IMPORTED)
+ if(NOT _zip_imported)
+ message(STATUS "trx-cpp: skipping install — libzip was built via FetchContent. "
+ "Install libzip separately and reconfigure to enable installation.")
+ set(TRX_ENABLE_INSTALL OFF)
+ endif()
+ unset(_zip_imported)
+endif()
+
if(TRX_ENABLE_INSTALL)
set(TRX_INSTALL_CONFIGDIR "${CMAKE_INSTALL_LIBDIR}/cmake/trx-cpp")
diff --git a/README.md b/README.md
index 9766891..be71291 100644
--- a/README.md
+++ b/README.md
@@ -16,7 +16,7 @@ A C++17 library for reading, writing, and memory-mapping the [TRX tractography f
## Quick start
-**Dependencies:** a C++17 compiler, [libzip](https://libzip.org/), [Eigen 3.4+](https://eigen.tuxfamily.org/)
+**Dependencies:** a C++17 compiler and zlib (`zlib1g-dev` / `zlib-devel` / Homebrew `zlib`). `libzip` and Eigen 3.4+ are found locally when available, otherwise fetched automatically by CMake.
```cmake
# CMakeLists.txt
diff --git a/codecov.yml b/codecov.yml
index 02ccda9..a4c1c12 100644
--- a/codecov.yml
+++ b/codecov.yml
@@ -6,6 +6,7 @@ coverage:
status:
project:
default:
+ informational: true
paths:
- "include/trx"
- "src"
diff --git a/docs/building.rst b/docs/building.rst
index 03ec2d0..820ea05 100644
--- a/docs/building.rst
+++ b/docs/building.rst
@@ -7,16 +7,15 @@ Dependencies
Required:
- C++17 compiler
-- libzip
-- Eigen3
+- zlib (required by libzip)
Installing dependencies
------------------------
The examples below include GoogleTest, which is only required when building
-the tests. Ninja is optional but recommended. zlib is only required for the
-NIfTI I/O features.
+the tests. Ninja is optional but recommended. libzip and Eigen are resolved by
+CMake: it uses local installs when present and otherwise fetches them.
On Debian-based systems the zip tools have been split into separate packages
on recent Ubuntu versions.
@@ -25,8 +24,6 @@ on recent Ubuntu versions.
sudo apt-get install \
zlib1g-dev \
- libeigen3-dev \
- libzip-dev \
zipcmp \
zipmerge \
ziptool \
@@ -37,7 +34,7 @@ On Mac OS, you can install the dependencies with brew:
.. code-block:: bash
- brew install libzip eigen googletest ninja zlib
+ brew install googletest ninja zlib
On Windows, you can install the dependencies through vcpkg and chocolatey:
@@ -45,7 +42,7 @@ On Windows, you can install the dependencies through vcpkg and chocolatey:
.. code-block:: powershell
choco install ninja -y
- vcpkg install libzip eigen3 gtest zlib
+ vcpkg install gtest zlib
Building to use in other projects
@@ -70,6 +67,7 @@ Key CMake options:
- ``TRX_BUILD_DOCS``: Build docs with Doxygen/Sphinx (default OFF)
- ``TRX_ENABLE_CLANG_TIDY``: Run clang-tidy during builds (default OFF)
- ``TRX_USE_CONAN``: Use Conan setup in ``cmake/ConanSetup.cmake`` (default OFF)
+- ``TRX_FETCH_EIGEN``: Fetch Eigen3 with FetchContent when not found locally (default ON)
To use trx-cpp from another CMake project after installation:
@@ -103,7 +101,7 @@ Building for testing
Tests require GTest to be discoverable by CMake (e.g., via a system package or
``GTest_DIR``). If GTest is not found, tests will be skipped.
-NIfTI I/O tests additionally require zlib to be discoverable (``ZLIB::ZLIB``).
+zlib must be discoverable by CMake (``ZLIB::ZLIB``), including for NIfTI I/O.
Building documentation:
diff --git a/docs/quick_start.rst b/docs/quick_start.rst
index 6dd3eee..5afedf8 100644
--- a/docs/quick_start.rst
+++ b/docs/quick_start.rst
@@ -9,8 +9,10 @@ Prerequisites
- A C++17 compiler (GCC ≥ 7, Clang ≥ 5, MSVC 2019+)
- CMake ≥ 3.14
-- `libzip `_
-- `Eigen 3.4+ `_
+- zlib development headers/libraries
+
+``libzip`` and ``Eigen 3.4+`` are discovered from the local system when
+available and fetched automatically by CMake otherwise.
See :doc:`building` for platform-specific installation commands.