From c1907fbbf03ca1b775e7391073a1c55872c9d33d Mon Sep 17 00:00:00 2001 From: K900 Date: Sat, 1 Feb 2025 15:20:05 +0300 Subject: [PATCH 1/2] gcc: rework, clean up, and document the cursed symlink hack Fixes dangling symlinks. Also flips the direction of symlinks on cross from $lib/$target/lib -> $lib/lib to $lib/lib -> $lib/$target/lib --- .../compilers/gcc/common/builder.nix | 199 ++++++++++-------- .../compilers/gcc/common/libgcc.nix | 13 +- 2 files changed, 120 insertions(+), 92 deletions(-) diff --git a/pkgs/development/compilers/gcc/common/builder.nix b/pkgs/development/compilers/gcc/common/builder.nix index 633251910dbe..1cb2933977de 100644 --- a/pkgs/development/compilers/gcc/common/builder.nix +++ b/pkgs/development/compilers/gcc/common/builder.nix @@ -210,110 +210,137 @@ 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 - ''; + 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 + ''; } // lib.optionalAttrs ((stdenv.targetPlatform.config != stdenv.hostPlatform.config) && withoutTargetLibc) { dontCheckForBrokenSymlinks = true; diff --git a/pkgs/development/compilers/gcc/common/libgcc.nix b/pkgs/development/compilers/gcc/common/libgcc.nix index 3d4bd9067763..d38be2f6b76a 100644 --- a/pkgs/development/compilers/gcc/common/libgcc.nix +++ b/pkgs/development/compilers/gcc/common/libgcc.nix @@ -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: From 97a3367a330c6648d2bdf6036d960aface807651 Mon Sep 17 00:00:00 2001 From: K900 Date: Sun, 2 Feb 2025 09:49:23 +0300 Subject: [PATCH 2/2] Revert "gcc: disable symlink checks on cross + nolibc for now" This reverts commit 3fd43d5d7494068b2c8a9d904406f64d430dd029. The proper fix is now in. --- pkgs/development/compilers/gcc/common/builder.nix | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pkgs/development/compilers/gcc/common/builder.nix b/pkgs/development/compilers/gcc/common/builder.nix index 1cb2933977de..1a99835aa2bd 100644 --- a/pkgs/development/compilers/gcc/common/builder.nix +++ b/pkgs/development/compilers/gcc/common/builder.nix @@ -3,7 +3,6 @@ stdenv, enableMultilib, targetConfig, - withoutTargetLibc, }: let @@ -342,7 +341,4 @@ originalAttrs: ln -s $lib/lib $lib/$targetConfig/lib ''; } - // lib.optionalAttrs ((stdenv.targetPlatform.config != stdenv.hostPlatform.config) && withoutTargetLibc) { - dontCheckForBrokenSymlinks = true; - } ))