lndir
(xorg.lndir
) is a convenient utility to create a “shadow directory” of symlinks, and is used to create “wrapped packages” in Nixpkgs.
However, when encountering symbolic link targeting the absolute path to a file/directory in the source directory (FROMDIR), it creates the same symbolic link targeting that file/directory in FROMDIR. For example:
$ tree /dev/shm/fromdir
/dev/shm/fromdir
└── test_dir
├── test_file
├── test_link1 -> /dev/shm/fromdir/test_dir/test_file
└── test_link2 -> ./test_file
1 directory, 3 files
$ lndir /dev/shm/fromdir /dev/shm/todir_old
$ tree /dev/shm/todir_old/
/dev/shm/todir_old/
└── test_dir
├── test_file -> /dev/shm/fromdir/test_dir/test_file
├── test_link1 -> /dev/shm/fromdir/test_dir/test_file
└── test_link2 -> ./test_file
1 directory, 3 files
When the shadow file (/dev/shm/todir_old/test_dir/test_file
) is wrapped or replaced by a normal file, the change will only be picked up by /dev/shm/todir_old/test_dir/test_link2
but not /dev/shm/todir_old/test_dir/test_link1
, which references an absolute-path-targeted symbolic link.
A solution is to replace the target path that begins with FROMDIR path with TODIR path.
$ nix-run-local tree /dev/shm/todir
/dev/shm/todir
└── test_dir
├── test_file -> /dev/shm/fromdir/test_dir/test_file
├── test_link1 -> /dev/shm/todir/test_dir/test_file
└── test_link2 -> ./test_file
1 directory, 3 files
Here’s a simple BASH implementation:
FROMDIR="$1"
TODIR="$2"
LENGTH_FROMDIR="${#FROMDIR}"
while IFS= read -d $'\0' -r ORIGINAL_DIR; do
mkdir "$TODIR${ORIGINAL_DIR:$LENGTH_FROMDIR}"
done < <(find "$FROMDIR" -mindepth 1 -type d -print0)
while IFS= read -d $'\0' -r ORIGINAL_FILE; do
ln -s "$ORIGINAL_FILE" "$TODIR${ORIGINAL_FILE:$LENGTH_FROMDIR}"
done < <(find "$FROMDIR" -mindepth 1 -type f -print0)
while IFS= read -d $'\0' -r ORIGINAL_LINK; do
NEW_SYMLINK="$TODIR${ORIGINAL_LINK:$LENGTH_FROMDIR}"
ORIGINAL_TARGET="$($READLINK_COMMAND "$ORIGINAL_LINK")"
LENGTH_ORIGINAL_TARGET="${#ORIGINAL_TARGET}"
NEW_TARGET="$ORIGINAL_TARGET"
if [[ "$LENGTH_ORIGINAL_TARGET" -ge "$LENGTH_FROMDIR" ]] \
&& [[ "${ORIGINAL_TARGET:0:$LENGTH_FROMDIR}" == "$FROMDIR" ]]; then
NEW_TARGET="$TODIR${ORIGINAL_TARGET:$LENGTH_FROMDIR}"
fi
$SYMLINK_COMMAND "$NEW_TARGET" "$NEW_SYMLINK"
done < <(find "$FROMDIR" -mindepth 1 -type l -print0)
A generalized version can be found here:
Would it be useful for packaging? Are there reasons that prevent xorg.lndir
from doing so?