rustPlatform.fetchCargoVendor: dereference symlinks from entire git tree

We accidentally broke the symlink dereferencing logic in https://github.com/NixOS/nixpkgs/pull/379049
After that PR, only symlinks inside the crate subtree got dereferened,
breaking packages which symlink files from a parent directory to the crate subtree

This commit adds a custom ignore function for copytree instead of relying on
ignore_dangling_symlinks=True

TomaSajt e14998cb ec505766

Changed files
+32 -1
pkgs
build-support
+32 -1
pkgs/build-support/rust/fetch-cargo-vendor-util.py
···
import subprocess
import sys
import tomllib
+
from os.path import islink, realpath
from pathlib import Path
from typing import Any, TypedDict, cast
from urllib.parse import unquote
···
def copy_and_patch_git_crate_subtree(git_tree: Path, crate_name: str, crate_out_dir: Path) -> None:
+
+
# This function will get called by copytree to decide which entries of a directory should be copied
+
# We'll copy everything except symlinks that are invalid
+
def ignore_func(dir_str: str, path_strs: list[str]) -> list[str]:
+
ignorelist: list[str] = []
+
+
dir = Path(realpath(dir_str, strict=True))
+
+
for path_str in path_strs:
+
path = dir / path_str
+
if not islink(path):
+
continue
+
+
# Filter out cyclic symlinks and symlinks pointing at nonexistant files
+
try:
+
target_path = Path(realpath(path, strict=True))
+
except OSError:
+
ignorelist.append(path_str)
+
eprint(f"Failed to resolve symlink, ignoring: {path}")
+
continue
+
+
# Filter out symlinks that point outside of the current crate's base git tree
+
# This can be useful if the nix build sandbox is turned off and there is a symlink to a common absolute path
+
if not target_path.is_relative_to(git_tree):
+
ignorelist.append(path_str)
+
eprint(f"Symlink points outside of the crate's base git tree, ignoring: {path} -> {target_path}")
+
continue
+
+
return ignorelist
+
crate_manifest_path = find_crate_manifest_in_tree(git_tree, crate_name)
crate_tree = crate_manifest_path.parent
eprint(f"Copying to {crate_out_dir}")
-
shutil.copytree(crate_tree, crate_out_dir, ignore_dangling_symlinks=True)
+
shutil.copytree(crate_tree, crate_out_dir, ignore=ignore_func)
crate_out_dir.chmod(0o755)
with open(crate_manifest_path, "r") as f: