Eigen3, setup for Macos and cmake, to compile the CGAL Samples

Summary

The issue arises because the Eigen3 package installed via Homebrew is configured as keg-only, meaning it is not automatically linked into the standard system paths (like /usr/local/lib or /usr/local/include) to avoid conflicts with other software. Consequently, CMake cannot locate the Eigen3 configuration files unless explicitly guided. The fix involves setting the Eigen3_DIR variable to point directly to the directory containing Eigen3Config.cmake, which Homebrew has placed in /usr/local/share/eigen3/cmake/.

Root Cause

The root cause is a mismatch between the CMake search procedure and the Homebrew installation layout. CMake’s find_package(Eigen3) command searches specific paths and environment variables, but the keg-only installation isolates Eigen3.

  • Keg-only status: Homebrew installs Eigen3 in a versioned cell (/usr/local/Cellar/eigen@3/3.4.1) but does not symlink the files into the global /usr/local structure to prevent overriding system or user-compiled versions of Eigen.
  • CMake Configuration Location: The necessary Eigen3Config.cmake, Eigen3ConfigVersion.cmake, and Eigen3Targets.cmake files are located in /usr/local/share/eigen3/cmake/.
  • Missing Path Hint: Since the files are not in standard CMake search paths (e.g., /usr/local/lib/cmake or /usr/local/lib), the find_package command fails to resolve the location unless the Eigen3_DIR cache variable is manually defined.

Why This Happens in Real Systems

This is a standard dependency management challenge in modern C++ development, particularly on macOS with package managers.

  • Isolation for Safety: Package managers like Homebrew (or Linux equivalents like apt or yum) often keep libraries “keg-only” to ensure system stability and allow multiple versions of a library to coexist.
  • Lack of Universal Standard: Unlike Java or Python, C++ lacks a universally enforced standard library path hierarchy. Different build systems and tools expect files in different locations, requiring explicit configuration.
  • Project Configuration Assumptions: Projects like CGAL often provide CMake configurations that expect find_package to “just work.” When dependencies are installed via non-standard methods (or keg-only), the assumptions break down.

Real-World Impact

Ignoring this configuration leads to immediate build failures and wasted debugging time.

  • Build Failures: The project compiles but misses critical features. In the CGAL example, the build system explicitly detects the missing dependency: NOTICE: These examples require Eigen 3.1 (or greater), and will not be compiled.
  • Silent Degradation: If the build proceeds (e.g., if Eigen headers are accidentally found via another path but the library linking is broken), it can result in runtime crashes or subtle math errors.
  • Developer Friction: Developers spend hours inspecting CMake caches (ccmake) and environment variables rather than coding.
  • CI/CD Pipeline Breakage: Automated build pipelines will fail if they install dependencies via Homebrew but do not handle the Eigen3_DIR configuration in the build script.

Example or Code

The user did not provide the specific CMake command used, but the fix requires invoking CMake with the Eigen3_DIR variable defined. Assuming a standard build directory setup, the command looks like this:

# Assuming a build directory is created inside the CGAL source folder
mkdir build
cd build

# Configure CMake pointing Eigen3_DIR to the Homebrew share directory
cmake -DEigen3_DIR=/usr/local/share/eigen3/cmake/ ..

# For GUI configuration (ccmake), you would set the variable manually:
# Variable: Eigen3_DIR
# Value:    /usr/local/share/eigen3/cmake/

How Senior Engineers Fix It

Senior engineers approach this by explicitly defining dependency paths to ensure reproducibility.

  1. Locate the Config File: Verify where the package manager installed the CMake configuration files.
    • Command: ls -d /usr/local/share/eigen3/cmake/
  2. Pass the Variable at Configure Time: Pass the location directly to CMake via the command line using the -D flag. This avoids editing CMakeLists.txt or relying on fragile environment variables.
    • Command: cmake -DEigen3_DIR=/usr/local/share/eigen3/cmake/ ..
  3. Verify the Result: Check the CMake output logs to confirm that Eigen3 was found.
    • Look for: Found Eigen3: /usr/local/share/eigen3/cmake/ (Required is at least version "3.1")
  4. Automation (Optional): For team-wide consistency, wrap this logic in a build script or a CMakePresets.json file so developers don’t have to memorize the flag.

Why Juniors Miss It

Juniors often miss this because they treat the build system as a “black box” and rely on default behaviors.

  • Reliance on Defaults: They assume find_package(Eigen3) will automatically find anything installed on the system, not realizing that CMake requires specific hints for libraries not in standard paths.
  • Confusion with Header-Only Libraries: Eigen3 is largely header-only. Juniors might see the headers found (perhaps via a stray /usr/include entry) and assume the library is fully configured, missing the specific CMake configuration step required for proper integration.
  • Misinterpreting CMake Errors: The error “Eigen3_DIR-NOTFOUND” is a specific CMake cache variable status. Juniors often read it as a generic “file not found” error and start reinstalling Eigen3 rather than setting the path variable.
  • Lack of Knowledge on Keg-Only: They may not be familiar with the implications of Homebrew’s “keg-only” status and how it isolates files from the standard /usr/local hierarchy.