Seeker's Memo

個人的で技術的かもしれないメモがメインのブログです。http://seekers.hatenablog.jp/about

【x86_64,multilib】MinGW64-w64-v3.3.0とGCC-4.9.2のクロスビルド手順【from Linux to Win】

まえがき

・ちょっと試してみたくなったので、(”とりあえず”ビルドできる&”とりあえず”動くものができたので)メモ代わりに自分の手順を記しておく。
※注意:本稿のやり方だと、できあがったGCCで「-m32」オプションを使うと、コンパイルできない事態の発生を確認。要するに不完全で、なにか間違っているかやるべきことを見落としている可能性があります。
・以前にもにたような記事を書きましたが、今回はそれを”サッパリと忘れた状態”で1からチャレンジしています。
・本当にWindows上のGCCでなにか作るのならCygwinMinGW、TDMなどのを落としてきて使ったほうが当たり前のように楽です。感謝の気持を忘れずに、ありがたく配布されてるものを使いましょう。
 ※それかVC++とかを利用するとか。
・正直MinGW-GCCをヴァニラから”個人でカスタムしながら”multilib版をクロスビルドとか”伊達酔狂”でやるなら正気の沙汰じゃないたぶんもう趣味の領域です。立派なチャレンジャーです。でもそういうの大好きです(車輪の再発明的な何か)
・この記事も失敗してる可能性大なので、もしやるならmultilib類を無効(disable-sharedもつけて)にしてビルドして別々に作ったほうが幸せかも。
GCCのクロスビルド/ネイティヴビルド。Winネイティヴでビルドの場合は比較的安全と思われる(というか他のサイトの方が成功するやり方を載せていらっしゃるので)が超絶時間がかかるので覚悟。
Linux上でのLinux向けネイティヴコンパイラのビルドなら超絶簡単で、すぐ終わると思うんだけどな。
・どうにもクロスビルドを自分でやる必要がある方は、しょうがないのでなんとかこなしてみますか。
・ところどころ手順やビルドオプション、必要なライブラリなどを勘違いしてるかもしないが、ご容赦を。正式な手順かどうかわからない部分が多数。
・ビルドオプションなどは気に入らなければ適宜置き換えたり、そもそもビルドできないとか要らない場合は修正してみてください。
i686向けやビルドホストがi686の場合は、必要な箇所を置き換えて読んでください。
・本稿は、同じマシン(CPU)を対象としています(デュアルブートしている場合など)
※mulitilib/multiarchじゃないものなら、対象のビルドオプションを修正すれば、GCCのクロスビルド自体は達成できてるみたいです。

【準備】

x86_64なLinux(GNU/Linuxと呼ぶべきか)がインストールされたGCCなどをビルドできそうなPC(何でもいい)
・開発ツール一式(ArchやGentooならインストール時にほとんどor全部入ってしまっている。patch,make,gcc,g++,autotool,automakeとか色々)
x86_64-w64-mingw32なクロス処理系
 ※たぶんGCC推奨。必要ならi686もビルドできるやつが望ましい
・必要ならばwgetcurlなど追加のツールを。
・wine,wine64(テストするのに使う。いきなりWindows上に持ってくならいらない)
・Git版やSVN版を追って最新版などをビルドしたいならばgit,subversion(svn)なども入れて準備/初期化しておく。
・普段お使いの好きなエディタとWebブラウザなど(ドキュメント類を見たりするのに)
・各種ソースコード一式
・気合と根性とたっぷりの時間。
ルーチンワークに耐えられる精神(src展開/configure/make/make installのコンボ繰り返し。エラーが出ればもう一度設定しなおしながらコンボするので)
・くじけぬこころ
・以下のルーチンワークシェルスクリプトに書いておいて適宜修正しながらやると楽です。
・基本的に作業は、一般ユーザで行います。

【用意する各種ソースコード

アーカイブ形式などはなんでもいいが、この記事に従うなら以下の通りのファイルを公式サイトorミラーサーバーから拾ってくる
※一応、ライブラリなどは最新Verのものに準じて(一部枯れ気味のを)使ってみたが、要求Verさえ満たせば何でもいいみたい?
# GCC
gcc-4.9.2.tar.bz2 # gccの本体。zlibも必要みたいだが、今回は同梱さているの使用する。最新版のzlib-1.2.8などを使いたい人は、展開したzlibソースを該当ディレクトリに上書きする。
binutils-2.25.tar.bz2
gmp-6.0.0a.tar.bz2  # 要求ライブラリ
・mpfr-3.1.2.tar.bz2  # 要求ライブラリ
mpc-1.0.3.tar.gz   # 要求ライブラリ
・libiconv-1.14.tar.gz # 必要みたい? ついでに追加でlibiconv-1.14-ja-1.patch.gzが必要なら探してくる。パッチの取得方法やパッチの当て方は他所様のサイトに説明を譲る
・cloog-0.18.1.tar.gz # 追加要求ライブラリ(バックエンド)
・isl-0.12.2.tar.bz2  # 追加要求ライブラリ(cloogのバックエンド)最新版などがあるが、よくわからない&他のサイト様の説明で多かったのでこれを選択

# MinGW-w64
mingw-w64-v3.3.0.tar.bz2 # winpthreadも同梱されている模様

【下準備】

$ cd $HOME && mkdir mingw-gcc-sources && cd mingw-gcc-sources
[~/mingw-gcc-sources]$ cat << EOF >> wget-list
http://ftp.gnu.org/pub/gnu/libiconv/libiconv-1.14.tar.gz
https://gmplib.org/download/gmp/gmp-6.0.0a.tar.bz2
http://www.mpfr.org/mpfr-current/mpfr-3.1.2.tar.bz2
ftp://ftp.gnu.org/gnu/mpc/mpc-1.0.3.tar.gz
ftp://gcc.gnu.org/pub/gcc/infrastructure/cloog-0.18.1.tar.gz
http://isl.gforge.inria.fr/isl-0.14.tar.bz2
http://ftp.tsukuba.wide.ad.jp/software/gcc/releases/gcc-4.9.2/gcc-4.9.2.tar.bz2
http://ftp.gnu.org/gnu/binutils/binutils-2.25.tar.bz2
ftp://gcc.gnu.org/pub/gcc/infrastructure/isl-0.12.2.tar.bz2
EOF
$ wget -N --input-file=wget-list --continue -nv

mingw-w64-v3.3.0をダウンロードして~/mingw-gcc-sourcesに置いておく。

次に環境変数を設定しておく。内容は適宜沿うように変更する。
export PACKAGE_DIR=$HOME/mingw-gcc-sources
export WORK_DIR=$PACKAGE_DIR/build-working
export MINGW_SYSROOT=$HOME/MinGW64
export MINGW_COREROOT=$MINGW_SYSROOT/mingw
export TGT=x86_64-w64-mingw32
内容は適宜沿うように変更する (f.g. marchはx86_64やcorei7など mtuneはgenericなど。他にも設定できるCPUに合わせて種類はある。詳細はWebなどのドキュメントを参照。gcc-safe-flagとかgooglingするといいかも。
export CFLAGS="-O2 -pipe -march=native -mtune=native"
export CXXFLAGS=$CFLAGS

必要なら以下を設定しておく
export MAKEFLAGS="-j9" ※並列コンパイルするなら(並列コンパイルを推奨します)。左記の場合は並列コンパイル数9。並列数3にしたいなら"-j3"。設定しないなら要らない。もしくは直接「make -j6」のようにする。

MinGW-w64のビルド(以下はヘッダ、ランタイム、winpthreadの順)】

$ cd ~/mingw-gcc-sources
$ mkdir -v $WORK_DIR
$ cd $WORK_DIR
$ tar xjf $PACKAGE_DIR/mingw-w64-v3.3.0.tar.bz2
$ cd mingw-w64-v3.3.0
$ ./configure --prefix=$MINGW_COREROOT --enable-sdk=all --enable-secure-api --without-crt --enable-wchar_t --enable-tchar --enable-unicode --host=$TGT --target=$TGT --enable-shared
$ make && make install

$ ./configure --prefix=$MINGW_COREROOT --without-headers --with-tools=all --host=$TGT --target=$TGT --enable-shared
$ make && make install

$ cd mingw-w64-libraries/winpthreads
$ ./configure --prefix=$MINGW_COREROOT --enable-static --host=$TGT --target=$TGT --enable-shared
$ make && make install

$HOME/MinGW64とその配下にいろいろ配置されていることを確認しておく。

libiconvのビルド

$ cd $WORK
$ tar xzf $PACKAGE_DIR/libiconv-1.14.tar.gz
$ cd libiconv-1.14
$ ./configure --prefix=$MINGW_SYSROOT --host=$TGT --target=$TGT
$ make && make install

GCCとライブラリのソースコード配置

$ cd $WORK_DIR
$ tar xjf $PACKAGE_DIR/gcc-4.9.2.tar.bz2
$ cd gcc-4.9.2
$ tar xjf $PACKAGE_DIR/gmp-6.0.0a.tar.bz2
$ mv gmp-6.0.0 gmp
$ tar jf $PACKAGE_DIR/mpfr-3.1.2.tar.bz2
$ mv mpfr-3.1.2 mpfr
$ tar xzf $PACKAGE_DIR/mpc-1.0.3.tar.gz
$ mv mpc-1.0.3 mpc
$ tar xzf $PACKAGE_DIR/cloog-0.18.1.tar.gz
$ tar xjf $PACKAGE_DIR/isl-0.12.2.tar.bz2
$ mv cloog-0.18.1 cloog
$ mv isl-0.12.2 isl && mv isl cloog/isl
$ tar xjf $PACKAGE_DIR/binutils-2.25.tar.bz2
$ mv binutils-2.25 binutils

GCCのインストール】

$ cd $WORK_DIR
$ mkdir -pv build-gcc
$ cd build-gcc
$ ../gcc-4.9.2/configure --prefix=$MINGW_SYSROOT --host=$TGT --target=$TGT \
    --with-pkgversion="GCC 4.9.2-selfcustom  $TGT" \
    --enable-targets=all \
    --enable-threads=posix \
    --enable-clocale=gnu \
    --disable-win32-registry \
    --disable-libstdcxx-debug \
    --enable-libstdcxx-time=yes \
    --enable-languages=c,c++,objc,obj-c++, \
    --disable-nls \
    --disable-tls \
    --disable-werror \
    --with-arch=native --with-mtune=native \
    --with-abi=m64 \
    --enable-checking=release \
    --enable-lto --enable-optimize \
    --enable-libstdc++-v3 \
    --with-fpmath=sse \
    --enable-version-specific-runtime-libs \
    --enable-fully-dynamic-string \
    --enable-cloog-backend=isl \
    --enable-multilib --with-multilib-list=m32,m64 --enable-multiarch --enable-lib32 \
    --with-sysroot=$MINGW_SYSROOT \
    --disable-libsanitizer \
    --disable-libatomic \
    --disable-libgomp \
    --disable-libcilkrts \
    --disable-libvtv \
    --disable-libitm \
    --enable-libstdcxx-threads \
    --enable-unicode \
    --enable-wchar_t \
    --with-libiconv=$MINGW_SYSROOT

$ make && make install

【テストしてみる】

$ export PATH="$MINGW_SYSROOT/bin:$PATH"
$ cd ~/
$ emacs test.cpp

・サンプルコード

#include <iostream>
#include <windows.h>
#include <tchar.h>
#include <locale>
int main(int argc, char** argv){
	//_tsetlocale(LC_ALL, _T("japanese")); /* Cのみの場合。ちなみに_tsetlocale()ねえよってエラーが出たら、普通のsetlocale()を使う*/
        // "japanese"にするべきか""か"ja_JP.UTF-8"がいいのか
        std::locale::global(std::locale("japanese"));
    //どれが正しいやら…
    //std::locale::global(std::locale("ja_JP.UTF-8");
        //std::locale::global(std::locale("");
	
    typedef std::basic_string<TCHAR> tstring;
	tstring str = _T("日本語表示で、こんにちはMinGW64!");
	wprintf(L"%ls\n", L"Hello, MinGW-w64!!");
	_tprintf(_T("%ls\n"), str.c_str());

	MessageBoxW(NULL, str.c_str(), _T("Test"), MB_OK | MB_ICONINFORMATION);
	{
		int sum = 0;
		for(auto i = 1; i <= 10; i++){
			sum += i;
		}
		wchar_t fstr[] = L"1から10の総和=%d\n";
		wprintf(fstr, sum);
	}
	return 0;
}
$ file $MINGW_SYSROOT/bin/gcc.exe
$ wine64 $MINGW_SYSROOT/bin/g++.exe -v
$ wine64 $MINGW_SYSROOT/bin/g++.exe test.cpp -std=c++11 -O2 -D_UNICODE -DUNICODE
$ file a.exe
$ wine64 a.exe

「$ file a.exe」や「file $MINGW_SYSROOT/bin/g++.exe」、他のツールで言うと「objdump -h」とかで「pei-x86_64」とか「Windows Console」とかそれらしき文字列が見えれてればおk。
※32bit版をビルドできるかテストするにはコンパイルオプションに「-m32」をつければいいいみたい。64bitを明示なら「-m64」か。

【できあがったGCCコンパイル時にlibwinpthread-1.dll, lgcc_sなどがないと言われたら】

・$MINGW_SYSROOT配下を検索して、対象のファイルを$MINGW_SYSROOT/binや$MINGW_SYSROOT/libにコピーしてみる。lgcc_sの類はlibgcc_s(拡張子は「.a」かもしくは「.la」かも?)とかlibgcc_sehとかになってるのでそのままコピー。libにあったならlibに、binにあったならbinに。
(f.g.
ファイルの検索:「$ find -name 'libgcc_s.*'」 など
ファイルのコピー:
「$ cd ~/MinGW64/libexec/gcc/x86_64-w64-mingw32/4.9.2 && cp ../../../../bin/libwinpthread-1.dll ./」
「$ cd ~/MinGW64 && cp `find -name 'libgcc_s_seh-1.dll'` ~/MinGW64/bin/
「$ cd ~/MinGW64 && cp `find -name 'libgcc_s.*'` ./lib/」
(「$ cp -rp $MINGW_SYSROOT/mingw/* $MINGW_SYSROOT/ && cp -rp $MINGW_SYSROOT/x86_64-w64-mingw/* $MINGW_SYSROOT/」
その他足りないファイル(dllの類)も同様に配置する。
もしくは、PATHを設定してからやってみる(f.g. 「$ export PATH="$MINGW_SYSROOT/bin:PATH"」

【できあがったGCCコンパイルした自作プログラム実行時にdllがないと言われる】

・対象ファイルを自作実行ファイルと同じ場所に置いてみる
・もしくは、「$ wine64 $MINGW_SYSROOT/bin/g++.exe test.cpp -std=c++11 -O2 -D_UNICODE -DUNICODE -static」のように「-static」をつけてコンパイルして実行してみる。

【文字化けするんだけど】

・端末のエンコードやシェルの環境変数などがUTF-8系になってるか確認する。
GCCのビルドオプションで--disable-nlsになってる確認する。 (f.g. $ wine64 $MINGW_SYSROOT/bin/gcc.exe -v
ソースコードUTF-8で書かれている確認する (f.g. $ cat test.cpp | nkf -g
GCCだったかmingwの方だったか忘れたがビルド時に--enable-wchar_tと--enable-unicodeが指定されたものを使用していることを確認する
・libiconvにjaパッチを当ててGCCをビルドしてみる

UNICODEコンソールプログラムで日本語が表示できない】

・対応する関数(printfなどで、文字列表示の書式指定子は%ls)で適切に書いてみる
・libiconvにjaパッチを当ててGCCをビルドしてみる

【せっかくmultilibなコンパイラ作ったのに、32bit版自作プログラムをコンパイルできないんだが?(#include に関してエラーが出るなど)】

・調べ中。もし対策を知っているのであれば、良ろしければ教えてください。

【最後に】

せっかくなので、Windows上で動かしてみる。MinGW64ディレクトリごと、Windowsの環境に移す。
パスを通したりして, g++ test.cpp などとして実行してみる。

多分うまく行ったと思うけど、なにかおかしいかもわからん。

※補足

ja_JP.UTF-8環境下において。
UNICODE(_UNICODE)を有効にしたプログラムでwprintf関係の日本語が表示されない場合、
$ wine64 ./a.exe | nkf -g としてみるとShift_JISになってた。
試しに、$ wine64 ./a.exe | nkf (もしくはwine64 ./a.exe | iconv -f Shift_JIS -t UTF-8)で正常に表示されました。
#普通のWindows日本版ユーザーは、たいがいShift_JISなんじゃないかと思うからこれでいいけども
#minnttyとかであえてUTF-8にしてるユーザーはどうしたらいいんだろ
#ついでにutf8で標準入出力するにはどうするんだっけ
#何かコンパイルオプションあるかな?