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

K900 ceb3ac5f eafe4723

Changed files
+120 -96
pkgs
development
compilers
gcc
+113 -90
pkgs/development/compilers/gcc/common/builder.nix
···
stdenv,
enableMultilib,
targetConfig,
-
withoutTargetLibc,
}:
let
···
'';
preInstall =
''
-
mkdir -p "$out/''${targetConfig}/lib"
-
mkdir -p "''${!outputLib}/''${targetConfig}/lib"
''
+
-
# 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
''
+
-
# 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"
'';
-
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}"
-
if [ -z "$enableShared" ]; then
-
moveToOutput "''${targetConfig+$targetConfig/}lib/lib*.a" "''${!outputLib}"
-
fi
-
for i in "''${!outputLib}/''${targetConfig}"/lib/*.{la,py}; do
-
substituteInPlace "$i" --replace "$out" "''${!outputLib}"
-
done
-
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}/''${targetConfig}"/lib64/*.{la,py}; do
-
substituteInPlace "$i" --replace "$out" "''${!outputLib}"
-
done
-
fi
-
# 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
-
# More dependencies with the previous gcc or some libs (gccbug stores the build command line)
-
rm -rf $out/bin/gccbug
-
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
-
# Get rid of some "fixed" header files
-
rm -rfv $out/lib/gcc/*/*/include-fixed/{root,linux,sys/mount.h,bits/statx.h,pthread.h}
-
# 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
-
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
-
'';
-
}
-
// lib.optionalAttrs ((stdenv.targetPlatform.config != stdenv.hostPlatform.config) && withoutTargetLibc) {
-
dontCheckForBrokenSymlinks = true;
}
))
···
stdenv,
enableMultilib,
targetConfig,
}:
let
···
'';
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.
''
+
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
+
}
''
+
+
# 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
''
+
+
# 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 =
+
''
+
# Clean up our compatibility symlinks (see above)
+
for link in "''${compatibilitySymlinks[@]}"; do
+
echo "Removing compatibility symlink: $link"
+
rm -f "$link"
+
done
+
# 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}"
+
if [ -z "$enableShared" ]; then
+
moveToOutput "lib/lib*.a" "''${!outputLib}"
+
fi
+
for i in "''${!outputLib}"/lib/*.{la,py}; do
+
substituteInPlace "$i" --replace "$out" "''${!outputLib}"
+
done
+
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}"
+
for i in "''${!outputLib}"/lib64/*.{la,py}; do
+
substituteInPlace "$i" --replace "$out" "''${!outputLib}"
+
done
+
fi
+
+
# 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
+
# More dependencies with the previous gcc or some libs (gccbug stores the build command line)
+
rm -rf $out/bin/gccbug
+
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
+
# Get rid of some "fixed" header files
+
rm -rfv $out/lib/gcc/*/*/include-fixed/{root,linux,sys/mount.h,bits/statx.h,pthread.h}
+
# 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
+
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
+
'';
}
))
+7 -6
pkgs/development/compilers/gcc/common/libgcc.nix
···
+ 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/
''
+ lib.optionalString (targetPlatformSlash != "") ''
-
ln -s ${targetPlatformSlash}lib $libgcc/lib
''
#
# Nixpkgs ordinarily turns dynamic linking into pseudo-static linking:
···
+ lib.optionalString enableLibGccOutput (
''
# move libgcc from lib to its own output (libgcc)
+
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 != "") ''
+
mkdir -p $libgcc/${targetPlatformSlash}
+
ln -s $libgcc/lib $libgcc/${targetPlatformSlash}lib
''
#
# Nixpkgs ordinarily turns dynamic linking into pseudo-static linking: