Skip to content

Fix CMake package config generation with CMAKE_INSTALL_MODE=SYMLINK#283

Open
PeterCDMcLean wants to merge 4 commits intodevelopfrom
users/pmclean/fix-cmake-symlink-install
Open

Fix CMake package config generation with CMAKE_INSTALL_MODE=SYMLINK#283
PeterCDMcLean wants to merge 4 commits intodevelopfrom
users/pmclean/fix-cmake-symlink-install

Conversation

@PeterCDMcLean
Copy link
Copy Markdown

When CMAKE_INSTALL_MODE=SYMLINK is used, CMake creates symlinks instead of copying files during install. The package config file generation was using REALPATH which follows symlinks to the actual file, breaking the relative path computation.

Changed from REALPATH to ABSOLUTE so that symlink paths are preserved, allowing the relative path calculation to work correctly with both copy and symlink install modes.

This fixes build failures for packages like hipBLASLt and rocRoller when using CMAKE_INSTALL_MODE=SYMLINK.

Motivation

Technical Details

Test Plan

Test Result

Submission Checklist

When CMAKE_INSTALL_MODE=SYMLINK is used, CMake creates symlinks instead of
copying files during install. The package config file generation was using
REALPATH which follows symlinks to the actual file, breaking the relative
path computation.

Changed from REALPATH to ABSOLUTE so that symlink paths are preserved,
allowing the relative path calculation to work correctly with both copy
and symlink install modes.

This fixes build failures for packages like hipBLASLt and rocRoller when
using CMAKE_INSTALL_MODE=SYMLINK.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@PeterCDMcLean PeterCDMcLean force-pushed the users/pmclean/fix-cmake-symlink-install branch from b4593c9 to 517b8b8 Compare March 24, 2026 20:59
@PeterCDMcLean
Copy link
Copy Markdown
Author

PeterCDMcLean commented Mar 24, 2026

REALPATH vs ABSOLUTE Explanation

The issue is in how CMake resolves file paths when dealing with symlinks:

REALPATH

  • Follows all symlinks to their ultimate target
  • Resolves to the actual physical file on disk
  • Example:
    Symlink: /workspace/build/math-libs/BLAS/hipBLAS-common/dist/lib/cmake/hipblas-common/hipblas-common-config.cmake
    ↓ (points to)
    Real file: /workspace/build/math-libs/BLAS/hipBLAS-common/build/hipblas-common-config.cmake

REALPATH returns: /workspace/build/math-libs/BLAS/hipBLAS-common/build/hipblas-common-config.cmake

ABSOLUTE

  • Canonicalizes the path (removes . and ..)
  • Does NOT follow symlinks - uses the symlink path itself
  • Example:
    Symlink: /workspace/build/math-libs/BLAS/hipBLAS-common/dist/lib/cmake/hipblas-common/hipblas-common-config.cmake

ABSOLUTE returns: /workspace/build/math-libs/BLAS/hipBLAS-common/dist/lib/cmake/hipblas-common/hipblas-common-config.cmake

The Problem

When CMAKE_INSTALL_MODE=SYMLINK, CMake creates symlinks instead of copying files during install:

  1. Config location (symlink): /workspace/build/math-libs/BLAS/hipBLAS-common/dist/lib/cmake/hipblas-common/hipblas-common-config.cmake
  2. Symlink target (real file): ../../../../build/hipblas-common-config.cmake

The config file code tries to compute PACKAGE_PREFIX_DIR by going up 3 directories:

With REALPATH (BROKEN):
get_filename_component(_ROCM_CMAKE_CURRENT_LIST_FILE_REAL "${CMAKE_CURRENT_LIST_FILE}" REALPATH)

Returns: /workspace/build/math-libs/BLAS/hipBLAS-common/build/hipblas-common-config.cmake

get_filename_component(_ROCM_CMAKE_CURRENT_LIST_DIR_REAL "${_ROCM_CMAKE_CURRENT_LIST_FILE_REAL}" DIRECTORY)

Returns: /workspace/build/math-libs/BLAS/hipBLAS-common/build

get_filename_component(PACKAGE_PREFIX_DIR "${_ROCM_CMAKE_CURRENT_LIST_DIR_REAL}/../../../" ABSOLUTE)

Goes up 3 from build/: ../../../ = /workspace/build/math-libs ❌ WRONG!

With ABSOLUTE (CORRECT):
get_filename_component(_ROCM_CMAKE_CURRENT_LIST_FILE_ABS "${CMAKE_CURRENT_LIST_FILE}" ABSOLUTE)

Returns: /workspace/build/math-libs/BLAS/hipBLAS-common/dist/lib/cmake/hipblas-common/hipblas-common-config.cmake

get_filename_component(_ROCM_CMAKE_CURRENT_LIST_DIR_ABS "${_ROCM_CMAKE_CURRENT_LIST_FILE_ABS}" DIRECTORY)

Returns: /workspace/build/math-libs/BLAS/hipBLAS-common/dist/lib/cmake/hipblas-common

get_filename_component(PACKAGE_PREFIX_DIR "${_ROCM_CMAKE_CURRENT_LIST_DIR_ABS}/../../../" ABSOLUTE)

Goes up 3 from dist/lib/cmake/hipblas-common: ../../../ = /workspace/build/math-libs/BLAS/hipBLAS-common/dist ✅ CORRECT!

The fix changes line 73 in ROCMPackageConfigHelpers.cmake from REALPATH to ABSOLUTE so it respects symlink install mode. This makes package config files work correctly whether files are copied or symlinked.

@PeterCDMcLean
Copy link
Copy Markdown
Author

PeterCDMcLean commented Mar 25, 2026

I'm very happy that there are tests that caught this. Trying to understand the context of the failure. Looks like my change doesn't work with PREFIX:

Root Cause Analysis

The ABSOLUTE fix (changing from REALPATH to ABSOLUTE on line 73) works correctly for standard installations and CMAKE_INSTALL_MODE=SYMLINK, but breaks when using ROCM_PREFIX with rocm_install_symlink_subdir(). Here's why:

Path Depth Mismatch

When PREFIX=simple is set, the installation structure is:

Real files:

  • Config: /usr/simple/lib/cmake/simple/simple-config.cmake (4 levels deep from /usr)
  • Include: /usr/simple/include/simple.h

Symlinks created by rocm_install_symlink_subdir():

  • Config: /usr/lib/cmake/simple/simple-config.cmake → ../../../simple/lib/cmake/simple/simple-config.cmake (3 levels deep from /usr)
  • Include: /usr/include/simple.h → ../simple/include/simple.h

The PACKAGE_RELATIVE_PATH Calculation

In rocm_export_targets() with PREFIX:
CONFIG_PACKAGE_INSTALL_DIR = simple/lib/cmake/simple # 4 components

In rocm_configure_package_config_file():
ABSOLUTE_INSTALL_DIR = /usr/simple/lib/cmake/simple
INSTALL_PREFIX = /usr
PACKAGE_RELATIVE_PATH = ../../../../ # 4 levels up

This is correct for the real file location.

What Happens with REALPATH (original code)

  # Config found at: /usr/lib/cmake/simple/simple-config.cmake (symlink)
  get_filename_component(...REALPATH)
  # Follows symlink to: /usr/simple/lib/cmake/simple/simple-config.cmake
  # Directory: /usr/simple/lib/cmake/simple
  # Up 4 levels: /usr ✓ CORRECT

What Happens with ABSOLUTE (the fix)

  # Config found at: /usr/lib/cmake/simple/simple-config.cmake (symlink)
  get_filename_component(...ABSOLUTE)
  # Stays at: /usr/lib/cmake/simple/simple-config.cmake (doesn't follow symlink)
  # Directory: /usr/lib/cmake/simple
  # Up 4 levels: /../ ✗ ONE LEVEL TOO HIGH!

The error message confirms this:
Expected: .../pass-simple-prefix-EkB98/usr/simple/include
Actual: .../pass-simple-prefix-EkB98/simple/include # missing /usr/

Why This Happens

The PACKAGE_RELATIVE_PATH is always computed based on the real installation depth (4 levels when PREFIX adds an extra component), but with ABSOLUTE:

  • Real path depth: /usr/simple/lib/cmake/simple = 4 levels from prefix
  • Symlink path depth: /usr/lib/cmake/simple = 3 levels from prefix (PREFIX component removed)

Going up 4 levels from a 3-level-deep symlink overshoots by one directory level.

Solution Approaches

The fix would need to handle this mismatch. Possible approaches:

  1. Detect symlink usage: Check if we're at a symlink and adjust the relative path count
  2. Use PREFIX-aware relative paths: When PREFIX is used with symlinks, compute a separate PACKAGE_RELATIVE_PATH for the symlink location
  3. Fix CHECK_PREFIX logic: The existing CHECK_PREFIX code (lines 39-50 in ROCMPackageConfigHelpers.cmake) tries to handle PREFIX but only for Windows - extend this to handle Unix symlinks
  4. Change symlink strategy: Instead of flattening the PREFIX in symlinks, preserve the directory structure (but this would change the installation layout)

The ABSOLUTE fix is correct for the use case it was designed for (CMAKE_INSTALL_MODE=SYMLINK without PREFIX), but inadvertently breaks the PREFIX+symlink combination because of this path depth assumption mismatch.

When CMAKE_INSTALL_MODE=SYMLINK is used, config files are symlinks from the
install tree (e.g., lib/cmake/foo/) to the build directory (e.g., build/).
The build directory does not preserve the install tree structure, so using
REALPATH breaks PACKAGE_PREFIX_DIR calculation.

This commit adds a structure check: if navigating from the real path via
relative paths returns to the original location, the structure is preserved
(PREFIX case) and REALPATH works. Otherwise, fall back to ABSOLUTE to handle
CMAKE_INSTALL_MODE=SYMLINK correctly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@PeterCDMcLean PeterCDMcLean force-pushed the users/pmclean/fix-cmake-symlink-install branch from 420ccf6 to b60597e Compare March 25, 2026 02:38
PeterCDMcLean and others added 2 commits March 25, 2026 09:33
…symlink combination

CMAKE_INSTALL_MODE with symlink modes (SYMLINK, SYMLINK_OR_COPY,
ABS_SYMLINK, ABS_SYMLINK_OR_COPY) cannot be used with the PREFIX
argument because the symlink structure created by
rocm_install_symlink_subdir() is incompatible with how PACKAGE_PREFIX_DIR
is calculated in the generated config files.

Changes:
- Add error check in ROCMPackageConfigHelpers.cmake to prevent this
  unsupported combination with a clear error message
- Add fail test that verifies the error is properly triggered

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant