gcc: rework, clean up, and document the cursed symlink hack (#378569)

This commit is contained in:
K900
2025-02-07 20:25:01 +03:00
committed by GitHub
2 changed files with 120 additions and 96 deletions
+113 -90
View File
@@ -3,7 +3,6 @@
stdenv,
enableMultilib,
targetConfig,
withoutTargetLibc,
}:
let
@@ -210,112 +209,136 @@ originalAttrs:
'';
preInstall =
# What follows is a horribly cursed hack.
#
# GCC will install its libraries to $out/lib, $out/lib32, $out/lib64,
# $out/$targetConfig/lib, $out/$targetConfig/lib32 or $out/$targetConfig/lib64,
# depending on whether it's built as native or cross, and the exact target spec.
#
# We can't predict what it's actually going to do, and we also can't just tell it
# to always install to $out/lib, but we want everything to end up in $out/lib
# for consistency (multilib weirdness aside).
#
# So, we create a bunch of symlinks before we run GCC's install phase,
# redirecting every possible directory it may want to write to to the place
# we actually want things to be installed.
# We will then nuke the symlinks in postInstall.
#
# FIXME: there must be a better way to do this.
''
mkdir -p "$out/''${targetConfig}/lib"
mkdir -p "''${!outputLib}/''${targetConfig}/lib"
declare -ga compatibilitySymlinks=()
makeCompatibilitySymlink() {
for output in "$out" "''${!outputLib}"; do
local linkTarget="$1"
local linkName="$output/$2"
echo "Creating compatibility symlink: $linkTarget -> $linkName"
mkdir -p "$(dirname "$linkName")"
ln -s "$linkTarget" "$linkName"
compatibilitySymlinks+=("$linkName")
done
}
''
+
# if cross-compiling, link from $lib/lib to $lib/${targetConfig}.
# since native-compiles have $lib/lib as a directory (not a
# symlink), this ensures that in every case we can assume that
# $lib/lib contains the .so files
lib.optionalString (with stdenv; targetPlatform.config != hostPlatform.config) ''
ln -Ts "''${!outputLib}/''${targetConfig}/lib" $lib/lib
# This will redirect $output/lib{32,64} to $output/lib.
# Multilib is special, because it creates $out/lib (for 32-bit)
# and $out/lib64 (for 64-bit). No other targets can have both.
lib.optionalString (!enableMultilib) ''
makeCompatibilitySymlink lib lib32
makeCompatibilitySymlink lib lib64
''
+
# Make `lib64` symlinks to `lib`.
lib.optionalString
(!enableMultilib && stdenv.hostPlatform.is64bit && !stdenv.hostPlatform.isMips64n32)
''
ln -s lib "$out/''${targetConfig}/lib64"
ln -s lib "''${!outputLib}/''${targetConfig}/lib64"
''
+
# On mips platforms, gcc follows the IRIX naming convention:
#
# $PREFIX/lib = mips32
# $PREFIX/lib32 = mips64n32
# $PREFIX/lib64 = mips64
#
# Make `lib32` symlinks to `lib`.
lib.optionalString (!enableMultilib && stdenv.targetPlatform.isMips64n32) ''
ln -s lib "$out/''${targetConfig}/lib32"
ln -s lib "''${!outputLib}/''${targetConfig}/lib32"
# This will redirect $output/$targetConfig/lib{,32,64} to $output/lib.
lib.optionalString (with stdenv; targetPlatform.config != hostPlatform.config) ''
makeCompatibilitySymlink lib $targetConfig/lib32
makeCompatibilitySymlink lib $targetConfig/lib64
makeCompatibilitySymlink ../lib $targetConfig/lib
'';
postInstall = ''
# Move runtime libraries to lib output.
moveToOutput "''${targetConfig+$targetConfig/}lib/lib*.so*" "''${!outputLib}"
moveToOutput "''${targetConfig+$targetConfig/}lib/lib*.la" "''${!outputLib}"
moveToOutput "''${targetConfig+$targetConfig/}lib/lib*.dylib" "''${!outputLib}"
moveToOutput "''${targetConfig+$targetConfig/}lib/lib*.dll.a" "''${!outputLib}"
moveToOutput "''${targetConfig+$targetConfig/}lib/lib*.dll" "''${!outputLib}"
moveToOutput "share/gcc-*/python" "''${!outputLib}"
postInstall =
''
# Clean up our compatibility symlinks (see above)
for link in "''${compatibilitySymlinks[@]}"; do
echo "Removing compatibility symlink: $link"
rm -f "$link"
done
if [ -z "$enableShared" ]; then
moveToOutput "''${targetConfig+$targetConfig/}lib/lib*.a" "''${!outputLib}"
fi
# Move runtime libraries to lib output.
moveToOutput "lib/lib*.so*" "''${!outputLib}"
moveToOutput "lib/lib*.la" "''${!outputLib}"
moveToOutput "lib/lib*.dylib" "''${!outputLib}"
moveToOutput "lib/lib*.dll.a" "''${!outputLib}"
moveToOutput "lib/lib*.dll" "''${!outputLib}"
moveToOutput "share/gcc-*/python" "''${!outputLib}"
for i in "''${!outputLib}/''${targetConfig}"/lib/*.{la,py}; do
substituteInPlace "$i" --replace "$out" "''${!outputLib}"
done
if [ -z "$enableShared" ]; then
moveToOutput "lib/lib*.a" "''${!outputLib}"
fi
if [ -n "$enableMultilib" ]; then
moveToOutput "''${targetConfig+$targetConfig/}lib64/lib*.so*" "''${!outputLib}"
moveToOutput "''${targetConfig+$targetConfig/}lib64/lib*.la" "''${!outputLib}"
moveToOutput "''${targetConfig+$targetConfig/}lib64/lib*.dylib" "''${!outputLib}"
moveToOutput "''${targetConfig+$targetConfig/}lib64/lib*.dll.a" "''${!outputLib}"
moveToOutput "''${targetConfig+$targetConfig/}lib64/lib*.dll" "''${!outputLib}"
for i in "''${!outputLib}"/lib/*.{la,py}; do
substituteInPlace "$i" --replace "$out" "''${!outputLib}"
done
for i in "''${!outputLib}/''${targetConfig}"/lib64/*.{la,py}; do
substituteInPlace "$i" --replace "$out" "''${!outputLib}"
done
fi
if [ -n "$enableMultilib" ]; then
moveToOutput "lib64/lib*.so*" "''${!outputLib}"
moveToOutput "lib64/lib*.la" "''${!outputLib}"
moveToOutput "lib64/lib*.dylib" "''${!outputLib}"
moveToOutput "lib64/lib*.dll.a" "''${!outputLib}"
moveToOutput "lib64/lib*.dll" "''${!outputLib}"
# Remove `fixincl' to prevent a retained dependency on the
# previous gcc.
rm -rf $out/libexec/gcc/*/*/install-tools
rm -rf $out/lib/gcc/*/*/install-tools
for i in "''${!outputLib}"/lib64/*.{la,py}; do
substituteInPlace "$i" --replace "$out" "''${!outputLib}"
done
fi
# More dependencies with the previous gcc or some libs (gccbug stores the build command line)
rm -rf $out/bin/gccbug
# Remove `fixincl' to prevent a retained dependency on the
# previous gcc.
rm -rf $out/libexec/gcc/*/*/install-tools
rm -rf $out/lib/gcc/*/*/install-tools
if type "install_name_tool"; then
for i in "''${!outputLib}"/lib/*.*.dylib "''${!outputLib}"/lib/*.so.[0-9]; do
install_name_tool -id "$i" "$i" || true
for old_path in $(otool -L "$i" | grep "$out" | awk '{print $1}'); do
new_path=`echo "$old_path" | sed "s,$out,''${!outputLib},"`
install_name_tool -change "$old_path" "$new_path" "$i" || true
done
done
fi
# More dependencies with the previous gcc or some libs (gccbug stores the build command line)
rm -rf $out/bin/gccbug
# Get rid of some "fixed" header files
rm -rfv $out/lib/gcc/*/*/include-fixed/{root,linux,sys/mount.h,bits/statx.h,pthread.h}
if type "install_name_tool"; then
for i in "''${!outputLib}"/lib/*.*.dylib "''${!outputLib}"/lib/*.so.[0-9]; do
install_name_tool -id "$i" "$i" || true
for old_path in $(otool -L "$i" | grep "$out" | awk '{print $1}'); do
new_path=`echo "$old_path" | sed "s,$out,''${!outputLib},"`
install_name_tool -change "$old_path" "$new_path" "$i" || true
done
done
fi
# Replace hard links for i686-pc-linux-gnu-gcc etc. with symlinks.
for i in $out/bin/*-gcc*; do
if cmp -s $out/bin/gcc $i; then
ln -sfn gcc $i
fi
done
# Get rid of some "fixed" header files
rm -rfv $out/lib/gcc/*/*/include-fixed/{root,linux,sys/mount.h,bits/statx.h,pthread.h}
for i in $out/bin/c++ $out/bin/*-c++* $out/bin/*-g++*; do
if cmp -s $out/bin/g++ $i; then
ln -sfn g++ $i
fi
done
# Replace hard links for i686-pc-linux-gnu-gcc etc. with symlinks.
for i in $out/bin/*-gcc*; do
if cmp -s $out/bin/gcc $i; then
ln -sfn gcc $i
fi
done
# Two identical man pages are shipped (moving and compressing is done later)
for i in "$out"/share/man/man1/*g++.1; do
if test -e "$i"; then
man_prefix=`echo "$i" | sed "s,.*/\(.*\)g++.1,\1,"`
ln -sf "$man_prefix"gcc.1 "$i"
fi
done
'';
}
// lib.optionalAttrs ((stdenv.targetPlatform.config != stdenv.hostPlatform.config) && withoutTargetLibc) {
dontCheckForBrokenSymlinks = true;
for i in $out/bin/c++ $out/bin/*-c++* $out/bin/*-g++*; do
if cmp -s $out/bin/g++ $i; then
ln -sfn g++ $i
fi
done
# Two identical man pages are shipped (moving and compressing is done later)
for i in "$out"/share/man/man1/*g++.1; do
if test -e "$i"; then
man_prefix=`echo "$i" | sed "s,.*/\(.*\)g++.1,\1,"`
ln -sf "$man_prefix"gcc.1 "$i"
fi
done
''
+
# Recreate the target symlink so GCC can find libgcc_s on non-split builds.
lib.optionalString (with stdenv; targetPlatform.config != hostPlatform.config) ''
ln -s $lib/lib $lib/$targetConfig/lib
'';
}
))
@@ -105,14 +105,15 @@ lib.pipe drv
+ lib.optionalString enableLibGccOutput (
''
# move libgcc from lib to its own output (libgcc)
mkdir -p $libgcc/${targetPlatformSlash}lib
mv $lib/${targetPlatformSlash}lib/libgcc_s.so $libgcc/${targetPlatformSlash}lib/
mv $lib/${targetPlatformSlash}lib/libgcc_s.so.${libgcc_s-version-major} $libgcc/${targetPlatformSlash}lib/
ln -s $libgcc/${targetPlatformSlash}lib/libgcc_s.so $lib/${targetPlatformSlash}lib/
ln -s $libgcc/${targetPlatformSlash}lib/libgcc_s.so.${libgcc_s-version-major} $lib/${targetPlatformSlash}lib/
mkdir -p $libgcc/lib
mv $lib/lib/libgcc_s.so $libgcc/lib/
mv $lib/lib/libgcc_s.so.${libgcc_s-version-major} $libgcc/lib/
ln -s $libgcc/lib/libgcc_s.so $lib/lib/
ln -s $libgcc/lib/libgcc_s.so.${libgcc_s-version-major} $lib/lib/
''
+ lib.optionalString (targetPlatformSlash != "") ''
ln -s ${targetPlatformSlash}lib $libgcc/lib
mkdir -p $libgcc/${targetPlatformSlash}
ln -s $libgcc/lib $libgcc/${targetPlatformSlash}lib
''
#
# Nixpkgs ordinarily turns dynamic linking into pseudo-static linking: