Skip to content

feat(linker): use relative targets for exec wrapper scripts#1932

Open
henry-hsieh wants to merge 1 commit intomason-org:mainfrom
henry-hsieh:feat-linker-exec-relative
Open

feat(linker): use relative targets for exec wrapper scripts#1932
henry-hsieh wants to merge 1 commit intomason-org:mainfrom
henry-hsieh:feat-linker-exec-relative

Conversation

@henry-hsieh
Copy link

Hello William,

It has been a year since I opened the PR #1643. The exec wrapper scripts are generated as absolute paths in the latest version 2.0. I have updated that the scripts can be executed in any paths. I believed that it's the reason why you reject my previous PR. Hope you can merge this one or upload your version. 😄

Copy link
Member

@williamboman williamboman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm actually not sure what happened to these changes, I remember implementing this already (as mentioned in #1643). IIRC I had some concerns about platform support on Unix systems regarding dirname and realpath (GNU vs BSD implementation differences etc).

What systems have you been able to test this with?

@henry-hsieh
Copy link
Author

For example, I installed the lua-language-sever, which will generate the wrapper script to points to the real binary. The organization is as following:

.local/share/nvim/mason
├── bin
│   └── lua-language-server -> ../packages/lua-language-server/lua-language-server (symbolic link)
└── packages
    └── lua-language-server (directory)
        ├── libexec
        │   └── bin
        │       └── lua-language-server (real binary)
        └── lua-language-server (wrapper script)

The symbolic link is pointing to the wrapper script. The content of the wrapper script is as following:

#!/usr/bin/env bash

exec "/home/xxx/.local/share/nvim/mason/packages/lua-language-server/libexec/bin/lua-language-server" "$@"

The change of #1525 is to modify the symbolic link from absolute path to relative path. However, the content of the wrapper script is still absolute path. When I migrate the Neovim plugins to another account or machine, the wrapper script will no longer work.

I have tested this in several Linux Distributions, e.g. CentOS 7, CentOS 8, Ubuntu 14.04, Ubuntu 18.04, and Ubuntu 20.04. However, I don't have MacOS to test the script.

After I did a little search, the dirname is supported by the MacOS by default, but the realpath is not. Sadly, the realpath is required to follow the symbolic link used by the mason.nvim. Maybe we could implement the realpath as bash function to be portable between GNU and BSD? Here is the portable realpath bash function generated by ChatGPT:

realpath() {
  local path="$1"

  if [ -z "$path" ]; then
    echo "Usage: realpath <path>" >&2
    return 1
  fi

  # Convert to absolute path if necessary
  if [[ "$path" != /* ]]; then
    path="$PWD/$path"
  fi

  # Resolve symlinks by walking path components
  local dir component target
  while [ -L "$path" ]; do
    target=$(readlink "$path")
    if [[ "$target" == /* ]]; then
      # Absolute symlink target
      path="$target"
    else
      # Relative symlink target
      dir=$(dirname "$path")
      path="$dir/$target"
    fi
  done

  # Canonicalize the directory portion
  dir=$(cd "$(dirname "$path")" 2>/dev/null && pwd)
  if [ $? -ne 0 ]; then
    echo "realpath: failed to resolve path" >&2
    return 1
  fi

  # Append the final component (basename)
  echo "$dir/$(basename "$path")"
}

@henry-hsieh
Copy link
Author

Another solution is not to generate wrapper script in first place. For example, the clangd does not use wrapper script. The organization is very simple:

.local/share/nvim/mason
├── bin
│   └── clangd -> ../packages/clangd/clangd_20.1.0/bin/clangd (symbolic link)
└── packages
    └── clangd (directory)
        └── clangd_20.1.0
            └── bin
                └── clangd (real binary)

The symbolic link points to the real binary directly.

@henry-hsieh henry-hsieh force-pushed the feat-linker-exec-relative branch from f22811a to d8b81e4 Compare July 28, 2025 11:10
@henry-hsieh
Copy link
Author

I'm assuming that changing mason-registries is too late to the party. Therefore, I'm looking for the tweaks using shell-internal functions to deal with symbolic links. Thanks to this post, I was able to create a version to follow the symbolic link without using readlink -f or realpath. It's not elegant but effective. The wrapper script can be executed anywhere in the filesystem. Sure can be portable to any other paths.

@henry-hsieh henry-hsieh force-pushed the feat-linker-exec-relative branch from d8b81e4 to a10f0d0 Compare January 3, 2026 01:11
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.

2 participants