Files

270 lines
8.4 KiB
Nix

{
lib,
stdenv,
fetchurl,
perl,
gcc,
ncurses5,
ncurses6,
gmp,
libiconv,
numactl,
libffi,
coreutils,
targetPackages,
}:
# Prebuilt only does native
assert stdenv.targetPlatform == stdenv.hostPlatform;
let
version = "9.6.6";
# GHC upstream doesn't release bindist tarballs for some platforms.
# We're using Debian's binary package, and patching it into a usable-in-Nixpkgs state.
ghcDebs = {
powerpc64-linux = {
src = {
urls = [
"http://ftp.ports.debian.org/debian-ports/pool-ppc64/main/g/ghc/ghc_9.6.6-4_ppc64.deb"
"https://snapshot.debian.org/archive/debian-ports/20250917T193713Z/pool-ppc64/main/g/ghc/ghc_9.6.6-4_ppc64.deb"
];
sha256 = "722cc301b6ba70b342e5e3d9d0671440bcd749cd2f13dcccbd23c3f6a6060171";
};
exePathForLibraryCheck = null;
archSpecificLibraries = [
{
nixPackage = gmp;
fileToCheckFor = null;
}
{
nixPackage = ncurses6;
fileToCheckFor = "libtinfo.so.6";
}
{
nixPackage = numactl;
fileToCheckFor = null;
}
{
nixPackage = libffi;
fileToCheckFor = null;
}
];
};
};
debUsed =
ghcDebs.${stdenv.hostPlatform.system}
or (throw "cannot bootstrap GHC on this platform ('${stdenv.hostPlatform.system}') from Debian debs");
gmpUsed =
(builtins.head (
builtins.filter (drv: lib.hasPrefix "gmp" (drv.nixPackage.name or "")) debUsed.archSpecificLibraries
)).nixPackage;
runtimeDeps = [
targetPackages.stdenv.cc
targetPackages.stdenv.cc.bintools
coreutils # for cat
];
extraLibraryMapping = {
gmp = gmpUsed;
numa = numactl;
ffi = libffi;
};
in
stdenv.mkDerivation (finalAttrs: {
inherit version;
pname = "ghc-debian-binary";
src = fetchurl debUsed.src;
nativeBuildInputs = [ perl ];
sourceRoot = "${finalAttrs.pname}-${finalAttrs.version}";
# Custom unpack phase to handle .deb files
unpackPhase = ''
runHook preUnpack
ar x $src
tar xf data.tar.xz
mkdir -p ${finalAttrs.sourceRoot}
mv -t ${finalAttrs.sourceRoot}/ usr var
runHook postUnpack
'';
postUnpack =
# Verify our assumptions of which `libtinfo.so` (ncurses) version is used,
# so that we know when ghc debs upgrade that and we need to update the
# version used in `archSpecificLibraries`.
lib.optionalString (debUsed.exePathForLibraryCheck != null) (
lib.concatStringsSep "\n" [
''
shopt -u nullglob
echo "Checking that ghc binary exists in deb at ${debUsed.exePathForLibraryCheck}"
if ! test -e ${debUsed.exePathForLibraryCheck}; then
echo >&2 "GHC binary ${debUsed.exePathForLibraryCheck} could not be found in the deb unpack directory for arch ${stdenv.hostPlatform.system}, please check that ghcDebs correctly reflect the deb dependencies!"; exit 1;
fi
''
(lib.concatMapStringsSep "\n" (
{ fileToCheckFor, nixPackage }:
lib.optionalString (fileToCheckFor != null) ''
echo "Checking deb for ${fileToCheckFor} to ensure that is still used"
if ! readelf -d ${debUsed.exePathForLibraryCheck} | grep "${fileToCheckFor}"; then
echo >&2 "File ${fileToCheckFor} could not be found in ${debUsed.exePathForLibraryCheck} for arch ${stdenv.hostPlatform.system}, please check that ghcDebs correctly reflect the deb dependencies!"; exit 1;
fi
echo "Checking that the nix package ${nixPackage} contains ${fileToCheckFor}"
if ! test -e "${lib.getLib nixPackage}/lib/${fileToCheckFor}"; then
echo >&2 "Nix package ${nixPackage} did not contain ${fileToCheckFor} for arch ${stdenv.hostPlatform.system}, please check that ghcDebs correctly reflect the deb dependencies!"; exit 1;
fi
''
) debUsed.archSpecificLibraries)
]
)
# Linking to non-compiler libraries requires GHC to know about our non-FHS paths for those libraries
+ (lib.strings.concatMapAttrsStringSep "\n" (libName: libPackage: ''
for packageDbDir in $(find . -name package.conf.d); do
for packageDb in $(grep -l 'extra-libraries:.*${libName}' "$packageDbDir"/*.conf); do
echo "Patching include & library path for ${libName} into package DB: $packageDb"
sed -i "$packageDb" \
-e '/^[a-z-]*include-dirs/a \ ${lib.getDev libPackage}/include' \
-e '/^[a-z-]*library-dirs/a \ ${lib.getLib libPackage}/lib'
done
done
'') extraLibraryMapping)
+
# Rename needed libraries and binaries, fix interpreter
lib.optionalString stdenv.hostPlatform.isLinux ''
find . -type f -executable -exec patchelf \
--interpreter ${stdenv.cc.bintools.dynamicLinker} {} \;
'';
# Not a bindist, nothing to configure
dontConfigure = true;
# Not a bindist, it's already built
dontBuild = true;
# Install prebuilt GHC files
installPhase = ''
runHook preInstall
mkdir -p $out
cp -t $out/ -a usr/*
rm -f $out/lib/ghc/lib/package.conf.d
find var -name "package.conf.d" -type d -exec cp -a {} $out/lib/ghc/lib/ \;
runHook postInstall
'';
postInstall =
# Patch scripts to include runtime dependencies in $PATH.
''
for i in "$out/bin/"*; do
test ! -h "$i" || continue
isScript "$i" || continue
sed -i -e '2i export PATH="${lib.makeBinPath runtimeDeps}:$PATH"' "$i"
done
''
# Patch /usr paths
+ ''
for i in "$out/bin/"*; do
test ! -h "$i" || continue
isScript "$i" || continue
substituteInPlace "$i" \
--replace-fail '="/usr' '="${placeholder "out"}'
done
find "$out/lib/ghc/lib/package.conf.d" -type f -name '*.conf' \
-exec sed -i "s|/usr/|$out/|g" {} +
''
# Patch ghc settings
+ ''
substituteInPlace $out/lib/ghc/lib/settings \
--replace-fail powerpc64-linux-gnu-gcc gcc \
--replace-fail powerpc64-linux-gnu-g++ g++ \
--replace-fail powerpc64-linux-gnu-ld ld \
--replace-fail powerpc64-linux-gnu-ar ar \
--replace-fail powerpc64-linux-gnu-ranlib ranlib \
--replace-fail llc-18 llc \
--replace-fail opt-18 opt
'';
# On Linux, use patchelf to modify the executables so that they can
# find editline/gmp.
postFixup =
lib.optionalString (stdenv.hostPlatform.isLinux && !(debUsed.isStatic or false))
# Keep rpath as small as possible, running autoPatchelf makes everything segfault (maybe similar to patchelf#244).
# All Elfs are 2 directories deep from $out/lib, so pooling symlinks there makes a short rpath.
''
(cd $out/lib; ln -s ${ncurses6.out}/lib/libtinfo.so.6)
(cd $out/lib; ln -s ${lib.getLib gmpUsed}/lib/libgmp.so.10)
(cd $out/lib; ln -s ${numactl.out}/lib/libnuma.so.1)
(cd $out/lib; ln -s ${libffi.out}/lib/libffi.so.8)
for p in $(find "$out/lib" -type f -name "*\.so*"); do
(cd $out/lib; ln -s $p)
done
for p in $(find "$out/lib" -type f -executable); do
if isELF "$p"; then
echo "Patchelfing $p"
patchelf --set-rpath "\$ORIGIN:\$ORIGIN/../.." $p
fi
done
''
# Recache package db which needs to happen because
# we modify the package db
+ ''
"$out/bin/ghc-pkg" --package-db=$out/lib/ghc/lib/package.conf.d recache
'';
doInstallCheck = true;
installCheckPhase = ''
# Sanity check, can ghc create executables?
cd $TMP
mkdir test-ghc; cd test-ghc
cat > main.hs << EOF
{-# LANGUAGE TemplateHaskell #-}
module Main where
main = putStrLn \$([|"yes"|])
EOF
env -i $out/bin/ghc --make main.hs || exit 1
echo compilation ok
[ $(./main) == "yes" ]
'';
passthru = {
targetPrefix = "";
enableShared = true;
llvmPackages = null;
# Our Cabal compiler name
haskellCompilerName = "ghc-${version}";
# Normal GHC derivations expose the hadrian derivation used to build them
# here. In the case of debs we just make sure that the attribute exists,
# as it is used for checking if a GHC derivation has been built with hadrian.
hadrian = null;
};
meta = {
homepage = "http://haskell.org/ghc";
description = "Glasgow Haskell Compiler";
license = lib.licenses.bsd3;
platforms = builtins.attrNames ghcDebs;
maintainers = [ lib.maintainers.OPNA2608 ];
teams = [ lib.teams.haskell ];
};
})