将 GHC 移植到 Alpine
![]() 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 版本
在您的外部 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
在 Arch chroot 内部,以非 root 用户身份,执行此操作
sudo pacman -S base-devel python2 wget ghc openssh ssh-keygen
您可以为 ssh 密钥设置密码,但这不是必需的。
回到外部 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
再次回到Arch chroot 内部,执行
eval `ssh-agent` ssh-add
如果您为 ssh 密钥设置了密码,请解锁。现在从这个 Arch 会话中,我们可以对外部 Alpine 系统进行无密码 ssh 或 scp。
仍然在 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 转义的目录下工作。仍然在 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
构建最终将失败并显示此错误消息
".../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
稍后,构建将再次失败并显示此错误消息
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
。最后,构建将第三次失败并显示此错误
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
在此之后,构建应该完成。但是,我们仍然需要做一些工作才能使其正确安装。执行此操作
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 -
现在切换回您的外部 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,并与开发团队安排如何尽快提供初始二进制文件。)