在Linux上纯手工建立Win64交叉编译环境

I have made an dockerfile for steps described in this article:
https://github.com/netwarm007/dockerfile-mingw64

因为想要在linux上面编译gimp给win10(64bit)使用,花了好几天时间研究了一下在Linux上面建立Windows 64bit交叉编译环境的方法。

虽然很多Linux发行版提供mingw64的安装包,但为了最大的自由度,我选择了从源代码手工进行建立的方法。过程很苦逼,但是很能学习到一些东西。

一开始我是选用FreeBSD进行搭建的。但由于FreeBSD系统所带make sed等命令均不是gnu版本,中间遇到一些麻烦,所以又换用基于gnu的Ubuntu。这并不是说FreeBSD不能搭建,只是相对麻烦些。

(主要参考文档:MinGW-w64 – for 32 and 64 bit Windows

首先,需要建立一个交叉编译的目录树。参考了crosstool的相关脚本。为了方便,首先导出几个环境变量:

文件名: setbuilddev.sh

#!/usr/local/bin/bash
export PRJROOT=`realpath ~/work/w64`
export TARGET=x86_64-w64-mingw32
export PREFIX=${PRJROOT}/tools
export BUILD=${PRJROOT}/build-tools
export TARGET_PREFIX=${PREFIX}/${TARGET}

然后是生成交叉编译的目录树:

文件名: createdir.sh

#!/usr/local/bin/bash
if [ -d ${PRJROOT} ]; then
DATE=`date +%y%m%d`
mv ${PRJROOT} ${PRJROOT}_${DATE}
fi
mkdir -p ${PRJROOT}
mkdir -p ${PRJROOT}/build-tools ${PRJROOT}/tools ${PRJROOT}/kernel
mkdir -p ${BUILD}/build-binutils ${BUILD}/build-boot-gcc ${BUILD}/build-gcc
mkdir -p ${BUILD}/build-glibc ${BUILD}/build-glibc-headers
mkdir -p ${TARGET_PREFIX}
之后通过下面的命令建立交叉编译的目录结构。
source ./setbuilddev.sh
chmod +x createdir.sh
./createdir.sh
之后就是准备binutils和gcc的源代码。可以利用crosstool提供的源代码,也可以自己去下。我这里为了以后版本更新的方便,以及追踪我自己的变更,采用了git clone方式。由于国外的git仓库速度太慢,我在Git@OSC上面做了一个中转仓库,加速国内访问的速度。
cd $BUILD
git clone https://git.oschina.net/netwarm007/binutils-gdb.git
git clone https://git.oschina.net/netwarm007/gcc.git
这里我首先遇到的一个问题是,gcc.git太大,即使是从Git@OSC进行clone,中间也会出问题。而git clone一旦出问题,不会在本地保留任何东西,下次必须从头再来。通过查阅互联网上的相关资料,最后找到的解决方法是使用–depth这个选项。(如果是用git://或者git+ssh://类型的REPO似乎可以避免这个问题)
首先
git clone –recursive https://git.oschina.net/netwarm007/gcc.git –depth=100
(–recursive选项是因为这个REPO当中含有gmp/mpc/mpfr 3个子模块。如果是直接使用官方的git repo或者源代码包,则需要另外下载这3个模块。具体参见后文。)
然后
git pull –depth=10000
git pull –depth=100000
git pull –unshallow
根据实际的网络情况,可能需要调整–depth的具体数值。
然后开始交叉编译binutils
cd $BUILD/buid-binutils/
../binutils-gdb/configure –target=$TARGET –prefix=$PREFIX –with-sysroot=$PREFIX
make -j4
make install -j4
注意:如果是FreeBSD等BSD系统,需要使用gmake,而不是make
"-j8"选项需要根据实际编译的环境调整。我用的是4核心环境。如果是8核心,需要改为-j8。这个选项是打开多线程,对于最终编译的结果应该没有影响。也可以添加为环境变量:export MAKEOPTS=-j4,这样就不用每次手动指定了。
注意:由于MAKEOPTS的设置,本文之后的make都不再指定-j4。
之后是gcc的交叉编译。但是编译gcc之前,需要安装TARGET环境的头文件。由于本次是采用mingw-w64,那么就要下载安装mingw-64的源代码。同样,首先是git clone获取源代码。(也可以直接下载源代码包)
cd $BUILD
git clone https://git.oschina.net/netwarm007/mingw-w64.git
然后开始安装头文件。首先创建编译用工作目录:(最好不要在源代码目录进行编译,会打乱源代码目录)
mkdir -p $BUILD/build-mingw-w64-header/
cd $BUILD/build-mingw-w64-header/
配置并安装mingw头文件:
../mingw-w64/configure –prefix=$TARGET_PREFIX –without-crt
make install
这里需要特别注意的就是–prefix的指定比较特别。这是因为接下来交叉编译gcc的需要。

不过在此之前,我们需要建几个软link满足gcc的要求:

ln -s $TARGET_PREFIX $PREFIX/mingw
mkdir -p $TARGET_PREFIX/lib
ln -s $TARGET_PREFIX/lib $TARGET_PREFIX/lib64
如果使用的gcc是官方的版本,还需要如下的dependencies:
configure: error: Building GCC requires GMP 4.2+, MPFR 2.4.0+ and MPC 0.8.0+.
Try the –with-gmp, –with-mpfr and/or –with-mpc options to specify their locations.
注意: --with-gmp, --with-mpfr and/or --with-mpc 这几个选项是用来指定install之后的lib的位置的。不是用来指定源代码的位置的。
因此,我们回到$BUILD目录,获取相关的代码:
cd $BUILD
hg clone  https://gmplib.org/repo/gmp/
git clone https://scm.gforge.inria.fr/anonscm/git/mpc/mpc.git
svn checkout svn://scm.gforge.inria.fr/svn/mpfr/trunk mpfr
然后将这3个目录放在$BUILD/gcc目录的下面。(注意:似乎将一个git clone出来的目录放在另外一个git clone出来的目录里会带来问题。因此,如果gcc目录已经是一个git仓库了,请用
gti archive master | tar -x -C ./gcc/xxx
的方式来将目录放在gcc下面) (注意:这3个组件如果是以源代码的形式和GCC一起编译,必须放在gcc目录下面,并且必须是gmp,mpc,mpfr这样的名字,不能带版本号)
注意gmp目录下面如果没有configure,则需要运行.bootstrap来生成。mpc和mpfr也类似(需要先生成configure)。
接下来我们可以进行gcc的交叉编译了。注意gcc的交叉编译分为两步,第一步只编译编译器本身:

 

cd $BUILD/build-boot-gcc
../gcc/configure –target=${TARGET} –prefix=${PREFIX} –with_sysroot=${PREFIX} –enable-languages=c,c++
make all-gcc
make install-gcc
如果使用master进行编译,有时会遇到错误。
写这篇文章的时候,我这里使用的版本如下:
  • gcc 5.2.0
  • gmp 6.0.0
  • mpc 1.0.3
  • mpfr 3.1.3
注意:如果切换了gcc分支重新编译的时候,需要首先清空build-gcc目录。
现在我们有了交叉gcc了,接下来我们编译C运行时库函数,mingw-crt。
重要: export PATH="$PATH:$PREFIX/bin"
       * 因为从这里起其实已经是交叉编译。要让configure能够找到上面构建的交叉工具 
cd $BUILD/build-mingw-w64-crt
 ../mingw-w64/configure –-host=$TARGET –prefix=$TARGET_PREFIX –without-header –with-sysroot=$TARGET_PREFIX
make
make install
现在我们有了CRT(C语言运行时库)了,可以继续编译交叉GCC相关的库了。
cd $BUILD/build-gcc
../gcc/configure –target=${TARGET} –prefix=${PREFIX} –with_sysroot=${PREFIX} –enable-languages=c,c++
make all
make install
 至此交叉编译器以及相关的头文件、基础库就好了。写一个shell脚本进行交叉编译环境的设定,以供之后交叉编译软件使用。
文件名:setbuilddev_w64.sh
#!/usr/local/bin/bash
export PRJROOT=`realpath ~/work/w64`
export TARGET=x86_64-w64-mingw32
export PREFIX=${PRJROOT}/tools
export BUILD=${PRJROOT}/build-tools
export TARGET_PREFIX=${PREFIX}/${TARGET}
export PATH=${PATH}:${PREFIX}/bin
export PKG_CONFIG_LIBDIR=
export PKG_CONFIG_PATH=$TARGET_PREFIX/lib/pkgconfig
#export LD_LIBRARY_PATH=$TARGET_PREFIX/lib #设置这个会导致某些host工具无法正常运行,所以注释掉。
export C_INCLUDE_PATH=$TARGET_PREFIX/include
export ACLOCAL_FLAGS=”-I $TARGET_PREFIX/share/aclocal”
export CFLAGS=”-I$TARGET_PREFIX/include”
export CPATH=”$TARGET_PREFIX/include”
export LDFLAGS=”-L$TARGET_PREFIX/lib”
好了,大功告成,编译一个hello_world.exe在windows上面跑跑看吧。不仅可以写console程序,还可以写win32程序哦。