将 GHC 移植到 Alpine

来自 Alpine Linux
此材料已过时...

ghc 现在已在 aports 中提供;并且这些说明是针对旧版本的 ghc(截至 2017 年 1 月,版本为 v8.0.2)(讨论

我想将 Glasgow Haskell Compiler 移植到 Alpine,为此,您需要从一些已编译的 GHC 二进制文件开始。这意味着需要从二进制文件已经可用的系统进行交叉编译。

为 Alpine 的每个目标架构完成一次此操作后,其他用户可以使用这些现有的 Alpine 二进制文件直接在 Alpine 上编译更新版本的 GHC,这比下面详述的步骤容易得多。(但这仍然需要很长时间。)但我正在记录首次移植到 Alpine 所需的步骤,以便其他人可以验证或重现我的工作。这也可能有助于其他人将 GHC 移植到其他系统。

我假设任何遵循这些指示的人都已经在一个 chroot 中设置了针对 Alpine 的交叉编译器,该 chroot 持有一个现有的 Linux 系统,其中 GHC 二进制文件可用。以下页面解释了如何做到这一点

这就是我们如何使用 Arch 的 GHC 来交叉编译一个我们可以在 Alpine 上运行的 GHC 版本

  1. 在您的外部 Alpine 系统中,执行此操作。我假设 $ARCH_ROOT 包含 Arch chroot 的 /您的 Alpine 系统上的绝对路径。我还假设 $ARCH_HOME 包含您将用于构建 GHC 的非 root Arch 用户的 home 目录在您的 Arch 系统上的绝对路径。(另一方面,我们下面设置的普通 $ARCH 变量指示您的机器架构---与 ArchLinux 无关。)

    sudo apk add paxctl libedit-dev libiconv-dev gmp-dev mkdir -p $HOME/alien-scripts ln -s $ARCH_ROOT/$ARCH_HOME/ghc-7.6.3 $HOME/ghc-7.6.3 mkdir /tmp/ghc-bootstrap $ARCH_ROOT/tmp/ghc-bootstrap sudo mount --bind /tmp/ghc-bootstrap $ARCH_ROOT/tmp/ghc-bootstrap case `uname -m` in x86_64) ARCH=x86_64; T=$ARCH;; i?86) ARCH=x86 T=i486;; *) echo Unknown architecture;; esac mkdir -p $HOME/buildroot-$ARCH/host/usr/bin/ cat << "__EOF__" > $HOME/buildroot-$ARCH/host/usr/bin/$T-buildroot-linux-uclibc-gcc #!/bin/sh /usr/bin/gcc -nopie "$@" __EOF__ chmod +x $HOME/buildroot-$ARCH/host/usr/bin/$T-buildroot-linux-uclibc-gcc

  2. 在 Arch chroot 内部,以非 root 用户身份,执行此操作

    sudo pacman -S base-devel python2 wget ghc openssh ssh-keygen

    您可以为 ssh 密钥设置密码,但这不是必需的。

  3. 回到外部 Alpine 系统,执行此操作

    cat $ARCH_ROOT/$ARCH_HOME/.ssh/id_rsa.pub >> $HOME/.ssh/authorized_keys sudo paxctl -cm $ARCH_ROOT/usr/lib/ghc-7.6.3/ghc

  4. 再次回到Arch chroot 内部,执行

    eval `ssh-agent` ssh-add

    如果您为 ssh 密钥设置了密码,请解锁。现在从这个 Arch 会话中,我们可以对外部 Alpine 系统进行无密码 ssh 或 scp。

  5. 仍然在 Arch chroot 内部,执行

    mkdir -p sources && cd sources wget -N http://www.haskell.org/ghc/dist/7.6.3/ghc-7.6.3-src.tar.bz2 wget -N http://www.haskell.org/ghc/dist/7.6.3/ghc-7.6.3-testsuite.tar.bz2 mkdir -p patches/ghc/7.6.3 && cd patches/ghc/7.6.3 GIST_BASE=https://gist.github.com/dubiousjim/5607734/raw wget -N $GIST_BASE/configure.patch wget -N $GIST_BASE/libraries-configure.patch wget -N $GIST_BASE/h_wcwidth.patch cd $HOME/sources cat << "__EOF__" > alien #!/bin/sh PORT=22 # change if you use different ssh port on your Alpine system CMD=$2; shift 2 set -x scp -P$PORT "$CMD" $USER@localhost:alien-scripts/ || exit 1 ssh -p$PORT $USER@localhost alien-scripts/"${CMD##*/}" "$@" __EOF__ cat << "__EOF__" > alien-ghc #!/bin/sh PORT=22 # change if you use different ssh port on your Alpine system set -x ssh -p$PORT $USER@localhost "sh -c 'cd $PWD; $0.exe $*'" __EOF__ chmod +x alien alien-ghc

    这两个 alien 脚本假设您在 Alpine 和 Arch 系统上使用相同的用户名;如果这是不正确的,请将 $USER 替换为相关的 Alpine 用户名。 alien-ghc 脚本还假设您不会在任何名称需要 shell 转义的目录下工作。

  6. 仍然在 Arch chroot 内部,执行此操作

    cd case `uname -m` in x86_64) ARCH=x86_64; T=$ARCH;; i?86) ARCH=x86 T=i486;; *) echo Unknown architecture;; esac tar -xjf sources/ghc-7.6.3-src.tar.bz2 # next is optional, if you want to ... # tar -xjf sources/ghc-7.6.3-testsuite.tar.bz2 BUILDROOT=$HOME/buildroot-$ARCH export PATH=$HOME/python2-path:$PATH:$BUILDROOT/host/usr/bin cd ghc-7.6.3 sed -e 's/^#BuildFlavour = unreg/BuildFlavour = unreg/' mk/build.mk.sample > mk/build.mk patch -p1 -i $HOME/sources/patches/ghc/7.6.3/configure.patch patch -p1 -i $HOME/sources/patches/ghc/7.6.3/libraries-configure.patch patch -p1 -i $HOME/sources/patches/ghc/7.6.3/h_wcwidth.patch ./configure --prefix=/tmp/ghc-bootstrap \ --host=$T-buildroot-linux-uclibc \ --target=$T-buildroot-linux-uclibc \ --with-alien=$HOME/sources/alien 2>&1 | tee build.log

    GHC wiki 页面关于交叉编译 似乎表明您应该省略 --host=...,但如果您比较 7.6 和(此时,预发布)7.7 GHC 构建系统源代码,您会看到这(如果有的话)仅在 7.6 之后才成立。

    继续执行

    make 2>&1 | tee -a build.log

  7. 构建最终将失败并显示此错误消息

    ".../buildroot-x86_64/host/usr/bin/x86_64-buildroot-linux-uclibc-gcc"  -fno-stack-protector   libraries/integer-gmp/cbits/mkGmpDerivedConstants.c -o libraries/integer-gmp/cbits/mkGmpDerivedConstants
    libraries/integer-gmp/cbits/mkGmpDerivedConstants > libraries/integer-gmp/cbits/GmpDerivedConstants.h
    /bin/sh: libraries/integer-gmp/cbits/mkGmpDerivedConstants: No such file or directory
    make[1]: *** [libraries/integer-gmp/cbits/GmpDerivedConstants.h] Error 127
    make[1]: *** Deleting file `libraries/integer-gmp/cbits/GmpDerivedConstants.h'
    make: *** [all] Error 2
    

    输入此内容以继续

    $HOME/sources/alien run libraries/integer-gmp/cbits/mkGmpDerivedConstants \ > libraries/integer-gmp/cbits/GmpDerivedConstants.h make 2>&1 | tee -a build.log


  8. 稍后,构建将再次失败并显示此错误消息

      HC [stage 1] libraries/base/dist-install/build/Foreign/C/Types.o
    
    libraries/base/Foreign/C/Types.hs:162:25:
        Not in scope: type constructor or class `HTYPE_FLOAT'
    
    libraries/base/Foreign/C/Types.hs:162:215:
        Not in scope: type constructor or class `HTYPE_FLOAT'
    
    libraries/base/Foreign/C/Types.hs:162:290:
        Not in scope: type constructor or class `HTYPE_FLOAT'
    
    libraries/base/Foreign/C/Types.hs:162:398:
        Not in scope: type constructor or class `HTYPE_FLOAT'
    
    libraries/base/Foreign/C/Types.hs:162:470:
        Not in scope: type constructor or class `HTYPE_FLOAT'
    
    libraries/base/Foreign/C/Types.hs:162:548:
        Not in scope: type constructor or class `HTYPE_FLOAT'
    
    libraries/base/Foreign/C/Types.hs:164:27:
        Not in scope: type constructor or class `HTYPE_DOUBLE'
    
    libraries/base/Foreign/C/Types.hs:164:219:
        Not in scope: type constructor or class `HTYPE_DOUBLE'
    
    libraries/base/Foreign/C/Types.hs:164:295:
        Not in scope: type constructor or class `HTYPE_DOUBLE'
    
    libraries/base/Foreign/C/Types.hs:164:405:
        Not in scope: type constructor or class `HTYPE_DOUBLE'
    
    libraries/base/Foreign/C/Types.hs:164:478:
        Not in scope: type constructor or class `HTYPE_DOUBLE'
    
    libraries/base/Foreign/C/Types.hs:164:557:
        Not in scope: type constructor or class `HTYPE_DOUBLE'
    make[1]: *** [libraries/base/dist-install/build/Foreign/C/Types.o] Error 1
    make: *** [all] Error 2
    

    输入此内容以继续

    cat << __EOF__ >> libraries/base/include/HsBaseConfig.h #define HTYPE_DOUBLE Double #define HTYPE_FLOAT Float __EOF__ make 2>&1 | tee -a build.log

    HsBaseConfig.h 文件仅在构建期间生成,因此您无法提前进行此更改;您必须等待构建失败,进行更改,然后重新发出 make

  9. 最后,构建将第三次失败并显示此错误

      HC [stage 2] utils/ghctags/dist-install/build/Main.o
    inplace/bin/ghc-stage2: line 7: .../ghc-7.6.3/inplace/lib/ghc-stage2: No such file or directory
    make[1]: *** [utils/ghctags/dist-install/build/Main.o] Error 127
    make: *** [all] Error 2
    

    输入此内容以继续

    mv inplace/lib/ghc-stage2 inplace/lib/ghc-stage2.exe cp $HOME/sources/alien-ghc inplace/lib/ghc-stage2 make 2>&1 | tee -a build.log

  10. 在此之后,构建应该完成。但是,我们仍然需要做一些工作才能使其正确安装。执行此操作

    mkdir -p /tmp/ghc-bootstrap/lib/ghc-7.6.3 mv ghc/stage2/build/tmp/ghc-stage2 /tmp/ghc-bootstrap/lib/ghc-7.6.3/ghc.exe mv utils/ghc-pkg/dist-install/build/tmp/ghc-pkg /tmp/ghc-bootstrap/lib/ghc-7.6.3/ghc-pkg.exe cp $HOME/sources/alien-ghc ghc/stage2/build/tmp/ghc-stage2 cp $HOME/sources/alien-ghc utils/ghc-pkg/dist-install/build/tmp/ghc-pkg make install 2>&1 | tee -a build.log

    安装完成后,使用以下命令清理

    cd /tmp/ghc-bootstrap mv lib/ghc-7.6.3/ghc.exe lib/ghc-7.6.3/ghc mv lib/ghc-7.6.3/ghc-pkg.exe lib/ghc-7.6.3/ghc-pkg for f in bin/*-buildroot-linux-uclibc-*; do mv $f bin/${f#*-uclibc-}; done ln -sf ghc-7.6.3 bin/ghc ln -sf ghc-pkg-7.6.3 bin/ghc-pkg cd -

  11. 现在切换回您的外部 Alpine 系统并执行此操作

    paxctl -cm /tmp/ghc-bootstrap/lib/ghc-7.6.3/ghc sudo umount $ARCH_ROOT/tmp/ghc-bootstrap sed -i -e 's|"C compiler command",.*|"C compiler command", "/usr/bin/gcc"),|' \ -e 's|"C compiler flags", "|"C compiler flags", " -nopie |' /tmp/ghc-bootstrap/lib/ghc-7.6.3/settings rm -r $HOME/alien-scripts $HOME/buildroot-$ARCH rm $HOME/ghc-7.6.3

    现在您的 Alpine 系统中的所有内容都已清理干净,并且您在 /tmp/ghc-bootstrap 中获得了一个可用的 GHC 安装,Alpine 的 ghc APKBUILD 将会识别并使用它。您现在可以删除 Arch chroot 内部的 $ARCH_HOME/ghc-7.6.3 文件夹,并关闭该会话。

(我将为 ghc 准备一个 Alpine APKBUILD,并与开发团队安排如何尽快提供初始二进制文件。)