記事の内容
自作プログラムをWindows用にgccでコンパイルしたいとします。
Cygwin等、何種類かソリューションがありますが、本記事ではMinGW-w64クロスコンパイラを使用します。
MinGW-w64クロスコンパイラは、Ubuntuで「sudo apt install mingw-w64」でインストールできますので、試したところgccのバージョンが7.3でした。
$ x86_64-w64-mingw32-gcc --version
x86_64-w64-mingw32-gcc (GCC) 7.3-win32 20180312
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
C++を使う場合は、言語の進化に追随して新しいコンパイラを使いたいことが多いと思います。
そこで本記事では、パッケージからインストールしたクロスコンパイラを使用せず、新しいバージョンのソースコードからMinGW-w64クロスコンパイラをビルドしました。
クロスコンパイラのビルド手順
ビルド作業は、Windows10のWSL(Ubuntu 18.04)で行いました。
「/opt」の下に各ソフトウェア毎にサブディレクトリを作成して、インストール先とします。
事前準備
以下のように、ビルドに必要なUbuntuパッケージをインストールします。
sudo apt install gcc g++ m4 make texinfo
以下のように、ビルド作業で使用するディレクトリを作成しておきます。
ターボールのダウンロード先、ソースコードの展開先、ビルド作業ディレクトリです。
mkdir -p $HOME/Downloads $HOME/src $HOME/build
ターボールのダウンロードとソースコードの展開
以下のサイトで、インストールするソフトウェアの新しいバージョンを調査します。
- gmp入手先:http://gmplib.org/
- mpfr入手先:http://www.mpfr.org/mpfr-current/
- mpc入手先:http://www.multiprecision.org/mpc/download.html
- binutils入手先:http://ftp.gnu.org/gnu/binutils/
- mingw-w64 toolchain入手先:https://sourceforge.net/projects/mingw-w64/files/mingw-w64/mingw-w64-release/
- gcc入手先:https://gcc.gnu.org/
以下のように、使用するバージョンを環境変数に設定しました。
本記事でインストールするgccのバージョンは9.2.0です。
export VERSION_GMP=6.2.0
export VERSION_MPFR=4.0.2
export VERSION_MPC=1.1.0
export VERSION_BINUTILS=2.34
export VERSION_MINGW_W64=v7.0.0
export VERSION_GCC=9.2.0
以下のように、ターボールをダウンロードします。
cd $HOME/Downloads
wget https://gmplib.org/download/gmp/gmp-${VERSION_GMP}.tar.xz
wget https://www.mpfr.org/mpfr-current/mpfr-${VERSION_MPFR}.tar.xz
wget https://ftp.gnu.org/gnu/mpc/mpc-${VERSION_MPC}.tar.gz
wget http://ftp.gnu.org/gnu/binutils/binutils-${VERSION_BINUTILS}.tar.bz2
wget https://sourceforge.net/projects/mingw-w64/files/mingw-w64/mingw-w64-release/mingw-w64-${VERSION_MINGW_W64}.tar.bz2
wget http://ftp.tsukuba.wide.ad.jp/software/gcc/releases/gcc-${VERSION_GCC}/gcc-${VERSION_GCC}.tar.xz
以下のように、ソースコードを展開します。
cd $HOME/src
tar xf $HOME/Downloads/gmp-${VERSION_GMP}.tar.xz
tar xf $HOME/Downloads/mpfr-${VERSION_MPFR}.tar.xz
tar xf $HOME/Downloads/mpc-${VERSION_MPC}.tar.gz
tar xf $HOME/Downloads/binutils-${VERSION_BINUTILS}.tar.bz2
tar xf $HOME/Downloads/mingw-w64-${VERSION_MINGW_W64}.tar.bz2
tar xf $HOME/Downloads/gcc-${VERSION_GCC}.tar.xz
gmpのビルド&インストール
以下の手順でインストールまで行います。
cd $HOME/build
mkdir gmp-${VERSION_GMP}
cd gmp-${VERSION_GMP}
$HOME/src/gmp-${VERSION_GMP}/configure ABI=64 --prefix=/opt/gmp-${VERSION_GMP} --enable-cxx
make
sudo make install
以下のように、インストールされたことを確認しました。
username:~/build/gmp-6.2.0$ ls /opt
gmp-6.2.0
mpfrのビルド&インストール
以下の手順でインストールまで行います。
cd $HOME/build
mkdir mpfr-${VERSION_MPFR}
cd mpfr-${VERSION_MPFR}
$HOME/src/mpfr-${VERSION_MPFR}/configure ABI=64 --prefix=/opt/mpfr-${VERSION_MPFR} --with-gmp=/opt/gmp-${VERSION_GMP}
make
sudo make install
以下のように、インストールされたことを確認しました。
username:~/build/mpfr-4.0.2$ ls /opt
gmp-6.2.0
mpfr-4.0.2
mpcのビルド&インストール
以下の手順でインストールまで行います。
cd $HOME/build
mkdir mpc-${VERSION_MPC}
cd mpc-${VERSION_MPC}
$HOME/src/mpc-${VERSION_MPC}/configure ABI=64 --prefix=/opt/mpc-${VERSION_MPC} --with-gmp=/opt/gmp-${VERSION_GMP} --with-mpfr=/opt/mpfr-${VERSION_MPFR}
make
sudo make install
以下のように、インストールされたことを確認しました。
username:~/build/mpc-1.1.0$ ls /opt
gmp-6.2.0
mpc-1.1.0
mpfr-4.0.2
binutilsのビルド&インストール
クロスコンパイラのビルドですので、targetとしてx86_64-w64-mingw32を指定します。
以下の手順でインストールまで行います。
cd $HOME/build
mkdir binutils-${VERSION_BINUTILS}
cd binutils-${VERSION_BINUTILS}
$HOME/src/binutils-${VERSION_BINUTILS}/configure --target=x86_64-w64-mingw32 --disable-multilib --with-sysroot=/opt/mingw-w64-${VERSION_MINGW_W64}-gcc-${VERSION_GCC} --prefix=/opt/mingw-w64-${VERSION_MINGW_W64}-gcc-${VERSION_GCC} --with-windres --with-windmc
make
sudo make install
以下のように、インストールされたことを確認しました。
username:~/build/binutils-2.34$ ls /opt
gmp-6.2.0
mingw-w64-v7.0.0-gcc-9.2.0
mpc-1.1.0
mpfr-4.0.2
インストール先にcdして中を見てみます。
username:~/build/binutils-2.34$ cd /opt/mingw-w64-${VERSION_MINGW_W64}-gcc-${VERSION_GCC}/
username:/opt/mingw-w64-v7.0.0-gcc-9.2.0$ ls
bin
share
x86_64-w64-mingw32
username:/opt/mingw-w64-v7.0.0-gcc-9.2.0$ ls bin
x86_64-w64-mingw32-addr2line
x86_64-w64-mingw32-ar
x86_64-w64-mingw32-as
x86_64-w64-mingw32-c++filt
x86_64-w64-mingw32-dlltool
x86_64-w64-mingw32-dllwrap
x86_64-w64-mingw32-elfedit
x86_64-w64-mingw32-gprof
x86_64-w64-mingw32-ld
x86_64-w64-mingw32-ld.bfd
x86_64-w64-mingw32-nm
x86_64-w64-mingw32-objcopy
x86_64-w64-mingw32-objdump
x86_64-w64-mingw32-ranlib
x86_64-w64-mingw32-readelf
x86_64-w64-mingw32-size
x86_64-w64-mingw32-strings
x86_64-w64-mingw32-strip
x86_64-w64-mingw32-windmc
x86_64-w64-mingw32-windres
username:/opt/mingw-w64-v7.0.0-gcc-9.2.0$ ls x86_64-w64-mingw32/
bin
lib
username:/opt/mingw-w64-v7.0.0-gcc-9.2.0$ ls x86_64-w64-mingw32/bin
ar
as
dlltool
ld
ld.bfd
nm
objcopy
objdump
ranlib
readelf
strip
username:/opt/mingw-w64-v7.0.0-gcc-9.2.0$ ls x86_64-w64-mingw32/lib
ldscripts
以下のように、シンボリックリンクを作ります(その1)。
cd /opt/mingw-w64-${VERSION_MINGW_W64}-gcc-${VERSION_GCC}
sudo ln -s x86_64-w64-mingw32 mingw
シンボリックリンクが作られたか確認:
username:/opt/mingw-w64-v7.0.0-gcc-9.2.0$ ll
bin
mingw -> x86_64-w64-mingw32
share
x86_64-w64-mingw32
以下のように、シンボリックリンクを作ります(その2)。
cd mingw
sudo ln -s lib lib64
シンボリックリンクが作られたか確認:
username:/opt/mingw-w64-v7.0.0-gcc-9.2.0/mingw$ ll
bin/
lib/
lib64 -> lib/
以下のように、シンボリックリンクを作ります(その3)。
cd bin
sudo ln -s ../../bin/x86_64-w64-mingw32-windres windres
sudo ln -s ../../bin/x86_64-w64-mingw32-windmc windmc
シンボリックリンクが作られたか確認:
username:/opt/mingw-w64-v7.0.0-gcc-9.2.0/mingw/bin$ ll wind*
windmc -> ../../bin/x86_64-w64-mingw32-windmc*
windres -> ../../bin/x86_64-w64-mingw32-windres*
「mingw-w64 toolchain」の中の、headerをインストール
binutilsではtargetにx86_64-w64-mingw32を指定しましたが、headerでは以下のようにhostにx86_64-w64-mingw32を指定してインストールします。
理屈はわかりませんが、試行錯誤の結果これでうまくいきました。
cd $HOME/build
mkdir -p mingw-w64-${VERSION_MINGW_W64}/mingw-w64-headers
cd mingw-w64-${VERSION_MINGW_W64}/mingw-w64-headers
$HOME/src/mingw-w64-${VERSION_MINGW_W64}/mingw-w64-headers/configure --host=x86_64-w64-mingw32 --prefix=/opt/mingw-w64-${VERSION_MINGW_W64}-gcc-${VERSION_GCC}/x86_64-w64-mingw32
sudo make install
インストール先にcdして中を見てみます。
cd /opt/mingw-w64-${VERSION_MINGW_W64}-gcc-${VERSION_GCC}
username:/opt/mingw-w64-v7.0.0-gcc-9.2.0$ ls
bin
mingw
share
x86_64-w64-mingw32
username:/opt/mingw-w64-v7.0.0-gcc-9.2.0$ ls mingw
bin
include
lib
lib64
username:/opt/mingw-w64-v7.0.0-gcc-9.2.0$ ls mingw/include
GL
KHR
_bsd_types.h
_cygwin.h
_dbdao.h
_mingw.h
_mingw_dxhelper.h
_mingw_mac.h
_mingw_off_t.h
_mingw_secapi.h
_mingw_stat64.h
_mingw_stdarg.h
_mingw_unicode.h
_timeval.h
accctrl.h
aclapi.h
(以下略)
gcc(クロスコンパイラ)のコア部分のコンパイル/インストール
以下の手順でインストールまで行います。
targetにx86_64-w64-mingw32を指定します。
コンパイラの言語はc、c++、fortranを指定しましたが、お好みの言語をどうぞ。
また、std::threadでのコンパイルエラーを避けるために、Thread modelにposixを指定しました。
参考:Ubuntu 16.04 で MinGW を使い C++11 プログラムの Windows バイナリをクロスコンパイルする
cd $HOME/build
mkdir mingw-w64-gcc-${VERSION_GCC}
cd mingw-w64-gcc-${VERSION_GCC}
$HOME/src/gcc-${VERSION_GCC}/configure --target=x86_64-w64-mingw32 --disable-multilib --prefix=/opt/mingw-w64-${VERSION_MINGW_W64}-gcc-${VERSION_GCC} --with-sysroot=/opt/mingw-w64-${VERSION_MINGW_W64}-gcc-${VERSION_GCC} --with-gmp=/opt/gmp-${VERSION_GMP} --with-mpfr=/opt/mpfr-${VERSION_MPFR} --with-mpc=/opt/mpc-${VERSION_MPC} --with-headers=/opt/mingw-w64-${VERSION_MINGW_W64}-gcc-${VERSION_GCC}/mingw/include --with-libs=/opt/mingw-w64-${VERSION_MINGW_W64}-gcc-${VERSION_GCC}/mingw/lib --with-threads=posix --enable-languages=c,c++,fortran --enable-__cxa_atexit --enable-libstdcxx-allocator=new
make all-gcc
sudo make install-gcc
以下のように、インストール先のbinサブディレクトリ配下のファイルを確認しました。
binutilsをインストールした時と比べると、gcc、g++、gfortran等のクロスコンパイラが加わって、ファイルが増えています。
username:~/build/mingw-w64-gcc-9.2.0$ ls /opt/mingw-w64-${VERSION_MINGW_W64}-gcc-${VERSION_GCC}/bin
x86_64-w64-mingw32-addr2line
x86_64-w64-mingw32-ar
x86_64-w64-mingw32-as
x86_64-w64-mingw32-c++
x86_64-w64-mingw32-c++filt
x86_64-w64-mingw32-cpp
x86_64-w64-mingw32-dlltool
x86_64-w64-mingw32-dllwrap
x86_64-w64-mingw32-elfedit
x86_64-w64-mingw32-g++
x86_64-w64-mingw32-gcc
x86_64-w64-mingw32-gcc-9.2.0
x86_64-w64-mingw32-gcc-ar
x86_64-w64-mingw32-gcc-nm
x86_64-w64-mingw32-gcc-ranlib
x86_64-w64-mingw32-gcov
x86_64-w64-mingw32-gcov-dump
x86_64-w64-mingw32-gcov-tool
x86_64-w64-mingw32-gfortran
x86_64-w64-mingw32-gprof
x86_64-w64-mingw32-ld
x86_64-w64-mingw32-ld.bfd
x86_64-w64-mingw32-nm
x86_64-w64-mingw32-objcopy
x86_64-w64-mingw32-objdump
x86_64-w64-mingw32-ranlib
x86_64-w64-mingw32-readelf
x86_64-w64-mingw32-size
x86_64-w64-mingw32-strings
x86_64-w64-mingw32-strip
x86_64-w64-mingw32-windmc
x86_64-w64-mingw32-windres
以下のように、上で確認したbinサブディレクトリにPATHを通します。
.bashrcにも追加しておくと良いでしょう( ${VERSION_MINGW_W64} と ${VERSION_GCC} は実際の値で書き直してください)。
export PATH=/opt/mingw-w64-${VERSION_MINGW_W64}-gcc-${VERSION_GCC}/bin:$PATH
クロスコンパイラにPATHが通ったか、以下のようにwhichコマンドで確認しました。
username:~/build/mingw-w64-gcc-9.2.0$ which x86_64-w64-mingw32-g++
/opt/mingw-w64-v7.0.0-gcc-9.2.0/bin/x86_64-w64-mingw32-g++
以下のように、クロスコンパイラのバージョンを表示させてみました。
9.2.0と表示されました。
username:~/build/mingw-w64-gcc-9.2.0$ x86_64-w64-mingw32-g++ --version
x86_64-w64-mingw32-g++ (GCC) 9.2.0
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
「mingw-w64 toolchain」の中の、crt関連をインストール
以下の手順でインストールまで行います。
hostにx86_64-w64-mingw32を指定します。
cd $HOME/build
mkdir -p mingw-w64-${VERSION_MINGW_W64}/mingw-w64-crt
cd mingw-w64-${VERSION_MINGW_W64}/mingw-w64-crt
$HOME/src/mingw-w64-${VERSION_MINGW_W64}/mingw-w64-crt/configure --host=x86_64-w64-mingw32 --prefix=/opt/mingw-w64-${VERSION_MINGW_W64}-gcc-${VERSION_GCC}/x86_64-w64-mingw32 --with-sysroot=/opt/mingw-w64-${VERSION_MINGW_W64}-gcc-${VERSION_GCC}
make
sudo -s
make install
exit
インストール先にcdして中を見てみます。
username:/opt/mingw-w64-v7.0.0-gcc-9.2.0/mingw/lib$ cd /opt/mingw-w64-${VERSION_MINGW_W64}-gcc-${VERSION_GCC}/mingw
gcc-9.2.0/mingw$ ls lib
CRT_fp10.o
CRT_fp8.o
CRT_glob.o
CRT_noglob.o
binmode.o
crt1.o
crt1u.o
crt2.o
crt2u.o
crtbegin.o
crtend.o
dllcrt1.o
dllcrt2.o
gcrt0.o
gcrt1.o
gcrt2.o
ldscripts
libCINTIME.a
libPS5UI.a
(以下略)
gcc-9.2.0/mingw$ ls lib32
CRT_fp10.o
CRT_fp8.o
CRT_glob.o
CRT_noglob.o
binmode.o
crt1.o
crt1u.o
crt2.o
crt2u.o
crtbegin.o
crtend.o
dllcrt1.o
dllcrt2.o
gcrt0.o
gcrt1.o
gcrt2.o
libaclui.a
libactiveds.a
(以下略)
gcc(クロスコンパイラ)の残りをコンパイル/インストール
以下の手順で、残り全てをインストールします。
cd $HOME/build/mingw-w64-gcc-${VERSION_GCC}
make
sudo make install
以上でクロスコンパイラのインストールは完了です。
サンプルプログラムで確認
適当なディレクトリでサンプルプログラムを作ってコンパイルしてみます。
cd
editor hello.cpp
サンプルプログラムのソースコード:
// x86_64-w64-mingw32-g++ -o hello_d.exe hello.cpp
// x86_64-w64-mingw32-g++ -o hello_s.exe hello.cpp -static
#include <iostream>
int main()
{
std::cout << "Hello world!" << std::endl;
}
以下のようにクロスコンパイルします。
「-static」オプションを与えると、スタティックリンクされます。
ダイナミックリンクとスタティックリンクの両方でコンパイルしてみます。
x86_64-w64-mingw32-g++ -o hello_d.exe hello.cpp
x86_64-w64-mingw32-g++ -o hello_s.exe hello.cpp -static
以下のように、ファイルサイズを確認してみます。
スタティックリンク版はでかいです。
username:~$ ll hello*
193 hello.cpp
331661 hello_d.exe*
14729708 hello_s.exe*
実行ファイルをWindows側に転送して動かしてみます。
スタティックリンク版は、以下のように動きました。
>hello_s.exe
Hello world!
ダイナミックリンク版を動かすと、以下のようにdllが見つからないと叱られました。
>hello_d.exe
以下のように、必要なdllをUbuntu側で探して、Windows側に転送します。
username:~/build/mingw-w64-gcc-9.2.0$ cd /opt/mingw-w64-${VERSION_MINGW_W64}-gcc-${VERSION_GCC}
username:/opt/mingw-w64-v7.0.0-gcc-9.2.0$ find . -name 'libstdc++*.dll'
./x86_64-w64-mingw32/lib/libstdc++-6.dll
username:/opt/mingw-w64-v7.0.0-gcc-9.2.0$ find . -name 'libgcc_s_*.dll'
./x86_64-w64-mingw32/lib/libgcc_s_seh-1.dll
dllがあると、以下のようにダイナミックリンク版も動きました。
>hello_d.exe
Hello world!