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/localstructure to prevent overriding system or user-compiled versions of Eigen. - CMake Configuration Location: The necessary
Eigen3Config.cmake,Eigen3ConfigVersion.cmake, andEigen3Targets.cmakefiles 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/cmakeor/usr/local/lib), thefind_packagecommand fails to resolve the location unless theEigen3_DIRcache 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
aptoryum) 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_packageto “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_DIRconfiguration 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.
- Locate the Config File: Verify where the package manager installed the CMake configuration files.
- Command:
ls -d /usr/local/share/eigen3/cmake/
- Command:
- Pass the Variable at Configure Time: Pass the location directly to CMake via the command line using the
-Dflag. This avoids editing CMakeLists.txt or relying on fragile environment variables.- Command:
cmake -DEigen3_DIR=/usr/local/share/eigen3/cmake/ ..
- Command:
- 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")
- Look for:
- Automation (Optional): For team-wide consistency, wrap this logic in a build script or a
CMakePresets.jsonfile 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/includeentry) 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/localhierarchy.