Distcc/Cross-Compiling
本文向你展示如何配置distcc在不同的处理器架构之间进行交叉编译。
使用distcc进行交叉编译
介绍
distcc
是一个工具,能让你在网络中的多台机器共同编译软件。只要在网络中的机器使用相同的工具链为相同的处理器架构编译,就不需要特殊的distcc
配置。
本指南将向你展示如何配置 distcc
为不同的架构进行编译。
安装必要的工具
首先,你需要在所有的参与编译的机器上安装 crossdev
。crossdev
是一个能够让简化跨架构工具链编译的工具。最初由Joshua Kinard编写,后来Mike Frysinger进行了完全的重写。它的使用非常的直观:crossdev -t sparc
将为 Sparc 架构编译完整的交叉工具链。包括 binutils,gcc,glibc 和 linux-headers。
你需要在所有帮助编译的机器上安装正确的 cross-toolchain。如果你需要更多帮助,试试运行 crossdev --help
。
如果你想对交叉工具链进行微调,这里有个脚本可以在命令行中输出要在帮助编译的机器上安装的交叉开发包的的确切版本(这个脚本需要在目标机器上运行)。
#! /bin/bash
A="sys-devel/binutils" ; B=`equery l $A` ; BINUTILS_VER=`echo $B | cut -d- -f3-`
A=`/usr/bin/gcc-config -c` ; B=`echo $A | cut -d- -f5` ; GCC_VER=`equery l sys-devel/gcc | grep $B | cut -d- -f3-`
KERNEL_VER=`uname -r | sed s/-gentoo//`
A="sys-libs/glibc" ; B=`equery l $A` ; LIBC_VER=`echo $B | cut -d- -f3-`
echo "crossdev --b =$BINUTILS_VER --g =$GCC_VER --k =$KERNEL_VER --l =$LIBC_VER -t `uname -m`"
然后,你需要在所有参与交叉编译的机器上安装distcc
。这些机器包括将运行emerge的机器和进行交叉编译的机器。更多的关于配置和使用distcc
的信息请参考Gentoo Distcc Documentation。
当前版本的 crossdev 有一个
-S (--stable)
标志用于安装稳定版本的编译工具。(例如 crossdev -t i686-pc-linux-gnu --stable --ex-gcc --ex-gdb --portage --pretend)。不使用该选项时, crossdev 安装的是最新的实验版本的编译工具包!当使用该选项的时候,上面的脚本是不需要的,除非特定版本的包工具或者头文件是被 unmasked 的。架构相关说明
通过查看编译目标机器的 /etc/make.conf 文件中的
CHOST
变量来获取架构名称。如果在 crossdev -t 选项中制定了错误的架构名称,crossdev会试图猜测并把工具安装到 /usr 下以错误的架构名称作为名字的目录下(例如 /usr/i686-pc-linux-gnu/, /usr/i686-linux-gnu/……)。要解决这一问题,使用 crossdev --clean 配合错误的架构/目录名称进行删除,或者手工删除这些目录。Intel x86 subarchitectures
如果你要在不同的intel x86子架构(例如i586和i686)上进行交叉编译,你仍然必须为目标CHOST编译完整的交叉工具链,否则编译会失败。这是因为i586和i686实际上是不同的CHOST,尽管它们都被认为是“x86”。当你建立交叉工具链的时候请谨记这一点。例如,如果目标机器是i586,这意味着你必须在i686机器上建立i586的交叉编译链。
SPARC
使用crossdev -t sparc
也许会失败,伴随如下错误:
linker with -z relro support required
support for the tls_model attribute is required
this configuration requires -mlong-double-128 support
如果发生上述错误,尝试使用下面的命令:
user $
crossdev --lenv "CC=sparc-unknown-linux-gnu-gcc" -t sparc-unknown-linux-gnu
配置distcc以便正确的交叉编译
The workaround as described in this section is not needed any more since distcc-3.2, as the functionality for replacing the calls to the compiler with the full compiler name has since then been implemented in distcc itself.
使用默认的distcc配置,交叉编译“不”会正常工作。这是因为很多编译仅仅调用gcc
而不是编译器的全名(例如sparc-unknown-linux-gnu-gcc
)。当这一编译被分发到帮助交叉编译的机器上时,原生的编译器被调用,而非你新安装的交叉编译器。
幸运的是,有一个绕过这一小问题的方案。所需的只是在将要运行emerge
的机器上添加一个封装脚本和几个符号链接。我们将使用一个Sparc机器作为例子。在下面每处你看到sparc-unknown-linux-gnu
的地方,你需要替换成你自己的CHOST(例如对于AMD64的机器来说是x86_64-pc-linux-gnu
)。当你第一次安装完distcc的时候,/usr/lib/distcc/bin看起来像这样:
下面的操作只需要在运行emerge的机器上执行,不要在帮助交叉编译的机器上运行。
root #
cd /usr/lib/distcc/bin
root #
ls -l
total 0 lrwxrwxrwx 1 root root 15 Dec 23 20:13 c++ -> /usr/bin/distcc lrwxrwxrwx 1 root root 15 Dec 23 20:13 cc -> /usr/bin/distcc lrwxrwxrwx 1 root root 15 Dec 23 20:13 g++ -> /usr/bin/distcc lrwxrwxrwx 1 root root 15 Dec 23 20:13 gcc -> /usr/bin/distcc lrwxrwxrwx 1 root root 15 Dec 23 20:13 sparc-unknown-linux-gnu-c++ -> /usr/bin/distcc lrwxrwxrwx 1 root root 15 Dec 23 20:13 sparc-unknown-linux-gnu-g++ -> /usr/bin/distcc lrwxrwxrwx 1 root root 15 Dec 23 20:13 sparc-unknown-linux-gnu-gcc -> /usr/bin/distcc
这里你需要做:
root #
rm c++ g++ gcc cc
然后,你要在这台机器上创建一个新的脚本。打开你喜欢的文本编辑器,创建一个文件,内容如下。然后保存到sparc-unknown-linux-gnu-wrapper。记住替换CHOST(这里是sparc-unknown-linux-gnu
)成你要运行emerge的机器的实际的CHOST。
#!/bin/bash
exec /usr/lib/distcc/bin/sparc-unknown-linux-gnu-g${0:$[-2]} "$@"
接下来,将此脚本设置为可执行,并创建适当的符号链接:
root #
chmod a+x sparc-unknown-linux-gnu-wrapper
root #
ln -s sparc-unknown-linux-gnu-wrapper cc
root #
ln -s sparc-unknown-linux-gnu-wrapper gcc
root #
ln -s sparc-unknown-linux-gnu-wrapper g++
root #
ln -s sparc-unknown-linux-gnu-wrapper c++
当你完成以后,/usr/lib/distcc/bin看起来像这样:
root #
ls -l
total 4 lrwxrwxrwx 1 root root 25 Jan 18 14:20 c++ -> sparc-unknown-linux-gnu-wrapper lrwxrwxrwx 1 root root 25 Jan 18 14:20 cc -> sparc-unknown-linux-gnu-wrapper lrwxrwxrwx 1 root root 25 Jan 18 14:20 g++ -> sparc-unknown-linux-gnu-wrapper lrwxrwxrwx 1 root root 25 Jan 18 14:20 gcc -> sparc-unknown-linux-gnu-wrapper lrwxrwxrwx 1 root root 15 Nov 21 10:42 sparc-unknown-linux-gnu-c++ -> /usr/bin/distcc lrwxrwxrwx 1 root root 15 Nov 21 10:42 sparc-unknown-linux-gnu-g++ -> /usr/bin/distcc lrwxrwxrwx 1 root root 15 Jul 27 10:52 sparc-unknown-linux-gnu-gcc -> /usr/bin/distcc -rwxr-xr-x 1 root root 70 Jan 18 14:20 sparc-unknown-linux-gnu-wrapper
With new distcc versions, the following steps are unnecessary—you can emerge distcc on the client with the
crossdev
USE flag set instead to achieve the same result.然后我们要确保这些封装脚本在升级distcc包之后不会被替换成之前的符号链接。我们可以提通过在/etc/portage/bashrc中添加下面的 代码来实现:
case ${CATEGORY}/${PN} in
sys-devel/distcc
# Hey man, how come that CONFIG PROTECT don't work?
if [ "${EBUILD_PHASE}" == "postinst" ] || [ "${EBUILD_PHASE}" == "postrm" ];
then
cd /usr/lib/distcc/bin
rm cc c++ gcc g++
ln -s sparc-unknown-linux-gnu-wrapper cc
ln -s sparc-unknown-linux-gnu-wrapper c++
ln -s sparc-unknown-linux-gnu-wrapper gcc
ln -s sparc-unknown-linux-gnu-wrapper g++
fi
;;
esac
Then create one of the following files as applicable. If you are not using clang:
#!/bin/bash
sleep 20
# We extract $TUPLE from make.conf to avoid editing the script for each architecture.
TUPLE=$(portageq envvar CHOST)
GCC_VER=$(gcc-config -c|cut -d "-" -f5)
cd /usr/lib/distcc/bin
rm cc c++ gcc g++ gcc-${GCC_VER} g++-${GCC_VER} ${TUPLE}-wrapper
echo '#!/bin/bash' > ${TUPLE}-wrapper
echo "exec ${TUPLE}-g\${0:\$[-2]}" "\"\$@\"" >> ${TUPLE}-wrapper
chmod 755 ${TUPLE}-wrapper
ln -s ${TUPLE}-wrapper cc
ln -s ${TUPLE}-wrapper c++
ln -s ${TUPLE}-wrapper gcc
ln -s ${TUPLE}-wrapper g++
ln -s ${TUPLE}-wrapper gcc-${GCC_VER}
ln -s ${TUPLE}-wrapper g++-${GCC_VER}
If you ARE using clang:
#!/bin/bash
#clang aware, now your >chromium-65 ebuilds will use distcc just like before ;)
sleep 20
# We extract $TUPLE from make.conf to avoid editing the script for each architecture.
TUPLE=$(portageq envvar CHOST)
GCC_VER=$(gcc-config -c|cut -d "-" -f5)
CLANG_VER=$(clang --version|grep version|cut -d " " -f3|cut -d'.' -f1,2)
cd /usr/lib/distcc/bin
rm cc c++ gcc g++ gcc-${GCC_VER} g++-${GCC_VER} clang clang++ clang-${CLANG_VER} clang++-${CLANG_VER} ${TUPLE}-wrapper ${TUPLE}-clang-wrapper
echo '#!/bin/bash' > ${TUPLE}-wrapper
echo "exec ${TUPLE}-g\${0:\$[-2]}" "\"\$@\"" >> ${TUPLE}-wrapper
echo '#!/bin/bash' > ${TUPLE}-clang-wrapper
echo "exec ${TUPLE}-\$(basename \${0}) \"\$@\"" >> ${TUPLE}-clang-wrapper
chmod 755 ${TUPLE}-wrapper
chmod 755 ${TUPLE}-clang-wrapper
ln -s ${TUPLE}-wrapper cc
ln -s ${TUPLE}-wrapper c++
ln -s ${TUPLE}-wrapper gcc
ln -s ${TUPLE}-wrapper g++
ln -s ${TUPLE}-wrapper gcc-${GCC_VER}
ln -s ${TUPLE}-wrapper g++-${GCC_VER}
ln -s ${TUPLE}-clang-wrapper clang
ln -s ${TUPLE}-clang-wrapper clang++
ln -s ${TUPLE}-clang-wrapper clang-${CLANG_VER}
ln -s ${TUPLE}-clang-wrapper clang++-${CLANG_VER}
Give it the proper permissions:
root #
chmod 755 /usr/local/sbin/distcc-fix
恭喜,你已经有一个可以工作的交叉编译的配置了(希望如此)。
这是如何工作的
当distcc
被调用的时候,它会检查自己是以何种方式被调用的(例如i686-pc-linux-gnu-gcc
,sparc-unknown-linux-gnu-g++
等等)。当distcc把编译工作分发到帮助交叉编译的机器上的时候,它会把调用方式一起分发。在帮助交叉编译的机器上,distcc的守护进程会去寻找有相同名字的程序。如果它看到的仅仅是gcc
,它就会去找gcc
,如果帮助交叉编译的机器架构跟运行emerge
的机器不一致,distcc的守护进程找到的gcc很可能是这台机器上的原生的的编译器。如果发送的是编译器的“全名”(例如sparc-unknown-linux-gnu-gcc
),就不会出现混淆。
故障排除
这一节涵盖了一些使用 distcc 做交叉编译时的常见问题。
远程主机 distccd 编译错误
当在远程主机的 /var/log/distccd.log 文件中发现 COMPILE ERRORS
的信息,请查看上面的关于指定正确的架构名称(如 crossdev -t $TARGET)的附注。
其他的解决方案有卸载 crossdev 再重新安装,使用 crossdev --clean选项,或者确保 /usr/$TARGET 不存在后重新安装。
一个明智的做法是编辑远程主机上的 /usr/$TARGET/etc/portage/make.conf,确保所有的要进行便已操作的电脑或主机的 CFLAGS
变量是相似的。
Failed to exec $TARGET-uknown-linux-gnu-gcc: No such file or directory
封装脚本也许无法执行,即使有正确的权限:
distcc[6195] (dcc_execvp) ERROR: failed to exec i686-unknown-linux-gnu-gcc: No such file or directory
要解决这一问题,请确保创建的封装脚本包含目标架构的全名:
user $
ls -alh /usr/lib/distcc/bin/c++
/usr/lib/distcc/bin/c++ ->./i686-pc-linux-gnu-wrapper
See also
- Crossdev — a set of bash scripts that utilize emerge to provide a system integrated cross-compilation capability.
This page is based on a document formerly found on our main website gentoo.org.
The following people contributed to the original document: Andrew Gaffney, Joshua Saddler
They are listed here because wiki history does not allow for any external attribution. If you edit the wiki article, please do not add yourself here; your contributions are recorded on each article's associated history page.