(2008/11/09 記, 2008/12/08 追記)
話の途中だが、最終的な目標は Windows(と、できれば Linux)とのクロスプラットフォームアプリの開発である。うちにもめでたく Intel Mac がやってきて、ついでにパッケージ版の Windows XP と VMWare Fusion 2.0 もやってきたので、wxMSW(Windows 版の wxWidgets)でアプリケーションをビルドできるところまでは進めておくことにした。
これはマニュアル通りに進めるだけ。ただ1点、キーボード配列が JIS と認識されてしまったので、「VMWare Fusion で USキーボード」 という記事を参考に US 配列に直した。一言で言うと、「コントロールパネル」→「システム」→「デバイスマネージャ」→「キーボード」で、ドライバを「PC/AT エンハンス PS/2 キーボード(101/102 キー)」に入れ替える、という方法。Windows については当方ほとんど知識がないので、こういう基本的なところから覚えていかねばならない。
いやあ、VMWare Fusion いいですねぇ。Mac のデスクトップを背景に Windows が走り出すと、何かとんでもないことが起きている気がして、とてもワクワクしてしまう。PowerPC Mac で最初に Virtual PC を走らせたときも、このワクワク感があったなあ。発売されてすぐ手に入れて、高速とは言えない PowerMac 6300(だったかな?)にインストールして、Windows 使いの後輩(彼とはよく Mac vs Windows のプチ論争をやったが)と2人で Mac の画面を覗き込んで、Windows が立ち上がったら「うわあホントに Windows 走ってるよ!」と大騒ぎしたものだった。BIOS Setup の画面を出して「手が込んでるなあ」と感心したりして。いや話がそれちゃった。
VMWare Fusion の設定はほとんどデフォルトのままでよいのだが、一点だけ、「共有フォルダ」の設定に手を入れておく。「仮想マシン」→「共有フォルダ」→「共有フォルダの設定を開く...」で設定ダイアログを開き、Mac 上のホームフォルダが共有設定になっていればそれをオフにして、wxWidgets のアプリケーションを作成する作業フォルダ(Xcode のプロジェクトフォルダの置き場所。仮に wxMyProjects
とする)を新たに共有するよう設定しておく。こうすると、共有フォルダが z:\wxMyProjects
として Windows から見えるようになる。もちろん、ホームフォルダを丸ごと共有してもよいのだが、どうしてもパスが長くなるし、それに Windows 側では慣れない操作で間違ってファイルを消したりする事故が起きやすいので、なるべく共有するのは必要なフォルダに限定した方が安全だと思う。
Windows でプログラム開発するには何を使えばいいんだろう。Microsoft Visual C++ と Cygwin があるのは以前から知っていた。でも、Visual C++ はお金がかかるし、Cygwin で作ったプログラムは Cygwin をインストールしているマシンでないと走らないみたいだし、どっちもどっちだなあ、と思っていた。最近は無償の Visual C++ Express というのもあるみたいだけど、たぶんこれは使っていると有償版にアップグレードしたくなるように作ってあるんだろうなあ、と思うと、今ひとつ食指が伸びない。パッケージ版の Windows XP 高かったんだぞ、これ以上金を絞り取られてたまるか、と変に身構えてしまう。
…などと不毛な気合いをみなぎらせつついろいろ調査しているうち、MinGW を見つけた。GCC と最低限のユーティリティで、ランタイムの DLL なしにネイティブの Windows アプリケーションが作れる。MSYS と組み合わせれば、「configure/make」タイプの UNIX アプリのビルドもできる。これだこれだ、こういうのを探してたんだよ。
さっそくインストールを試みてみたが、どうもうまくいかない。MinGW の標準インストーラは、必要なパッケージをダウンロードするようになっているのだが、ダウンロードが始まって間もなく "Your connection appears to have dropped out. Reconnect dial up or check your IE proxy settings. [再試行][キャンセル]" というダイアログが出て止まってしまう。何度再試行をしてもだめ。IE でネットに直接アクセスすることはできているのだが、VMWare を経由しての接続がダウンローダと相性が悪いのかもしれない。個別のパッケージをダウンロードすることもできる(SourceForge のダウンロードページ)が、そこからのインストールがちょっとややこしい。こんな入口でつまづいてちゃしょうがないな、と思ったので、「MinGW猫科研究所パック」を使わせていただくことにした。これは MinGW + MSYS + msysDTK をパッケージ化したもので、gcc と関連ツール、bash をはじめよく使うコマンド類、autotools などがインストールされる。すべてのファイルは c:\msys
以下に置かれており、ここに c:\msys\1.0\msys.bat
へのショートカットが置かれている。このショートカットをダブルクリックすると、コマンドプロンプトで bash が走り始める。c:\msys\1.0
というディレクトリが "/"
としてマウントされているようだ。
Windows でコマンドプロンプトで作業していると、ついうっかり "ls"
と打ったりパス区切りをスラッシュにしてしまってずっこけることが多いのだが、これは bash なので安心(?)。ちなみに、"ls /"
では見えないが、Windows の c:
ドライブの中身は /c/
にマウントされている。従って、例えば「マイドキュメント」は /c/Documents and Settings/Owner/My Documents
でアクセスできる(スペースを含んでいるので、もちろんエスケープか引用符は必要)。また、先ほど VMWare で設定した Mac との共有フォルダは /z/wxMyProjects
でアクセスできる。
MSYS 上での「ホームディレクトリ」は /home/Owner
である。これは Windows 上では c:\msys\1.0\home\Owner
になる。このフォルダのショートカットを作って Windows のデスクトップに置いておくとよい。Mac からファイルをコピーするには、Windows 上でこのフォルダのウィンドウを開いておいて、Mac の Finder からドラッグ&ドロップすればよい。もちろん、z:\wxMyProjects
を経由することもできる。
「猫科研究所パック」には gdb が含まれていなかったので、これだけは手動でインストールした。上記ダウンロードページから GNU Source-Level Debugger を選び、下の方の "Current Release" のバイナリインストーラ (.exe) をダウンロードする。インストール先を c:\msys\1.0\mingw
にする( "\mingw"
はインストーラが自動的に付加するので、ダイアログ上では c:\msys\1.0
を選べばよい)。インストール後、MSYS のコマンドプロンプトで which gdb
と打って /mingw/bin/gdb
と返ってくれば成功。
wxMSW を MinGW でビルドしてみる。wxWidgets の公式ダウンロードページ から、wxMSW-2.8.9.zip
を取ってきて、MSYS のホームディレクトリに展開する(バージョン番号は2008年11月9日現在)。Windows 上での作業はここまでで、これ以降は MSYS 上で作業する。
インストーラを使ってもよいが、まあ zip を展開する方が何をやっているかわかりやすく安心なので。
MSYS のコマンドプロンプトを開いて、次のように進める。
$ cd ~/wxMSW-2.8.9 $ ../configure --with-msw --with-opengl --disable-shared --with-unicode --with-libjpeg=builtin --with-zlib=builtin --disable-threads $ make
--disable-threads
はちょっと要注意。これを入れておかないと、生成したバイナリが MinGW 独自の DLL に依存してしまう。
configure, make
はかなり時間がかかる。特に、configure
が思いのほか遅い。1行ごとに一呼吸待つ感じがする。ディスクアクセスに VMWare を経由しているので、そのへんがオーバーヘッドになっているのかな? make install
は必要ない。アプリケーションをビルドするときに、今ビルドしたディレクトリを指定すれば済む。
共通のソースからビルドする…そんなの当然じゃん、クロスプラットフォームだろ、って? そりゃそうなんですけど、同じディレクトリでやりたいじゃないですか。wxMac で修正したソースを wxMSW でビルドするとき、例えばいちいち Mac 側で cvs commit
して Window 側で cvs update
するなんてめんどくさいじゃないですか。そのために wxMyProjects
ディレクトリを共有設定したんだから。しかも、なるべくプロジェクトのトップレベルディレクトリを散らかさないように運用したい。今のところ、こんな具合にやっている。
wxMyProjects
の下に、それぞれのプロジェクトのディレクトリを作る。wxSample
とか。wxSample/wxSources
など1つ下のディレクトリに置く。wxSample/xcode-build
ディレクトリを Xcode プロジェクトディレクトリにとする。つまり、プロジェクトファイルは wxSample/xcode-build/wxSample.xcodeproj
で、ソースファイルは ../wxSources/*.{h,cpp}
を参照することになる。
wxSample/msw-build
というサブディレクトリを作ってその中でビルドする。基本的には make
を使うのだが、後で Linux などにも拡張することを考えて、wxSample
ディレクトリに全プラットフォーム共通の Makefile
を置いて、それをサブディレクトリの Makefile
からインクルードする方針でやる。骨格としてはだいたいこんな感じ。Mac でも Makefile でビルドできるようにしてある。wxSample/msw-build/Makefile:
TARGET_PLATFORM = MSW include ../Makefile
wxSample/Makefile:
ifeq ($(TARGET_PLATFORM),MAC) WXCONFIG_PREFIX = $(HOME)/Development/wxMac/osx-build/ CPP_EXTRA_FLAGS = -isysroot /Developer/SDKs/MacOSX10.4u.sdk \ -mmacosx-version-min=10.4 -arch ppc -arch i386 EXECUTABLE = wxMolView endif ifeq ($(TARGET_PLATFORM),MSW) WXCONFIG_PREFIX = $(HOME)/wxMSW-2.8.9/msw-build/ EXECUTABLE = wxMolView.exe endif WXLIB_LIST = core,base,gl OBJECTS = MyApp.o MyCommand.o MyDocument.o MyGLCanvas.o ifeq ($(TARGET_PLATFORM),MAC) PRODUCT = wxMolView.app else PRODUCT = $(EXECUTABLE) endif CC = g++ CFLAGS = -g $(CPPFLAGS) $(CPP_EXTRA_FLAGS) \ $(shell $(WXCONFIG_PREFIX)wx-config --cppflags) LDFLAGS = $(shell $(WXCONFIG_PREFIX)wx-config --libs $(WXLIB_LIST)) \ $(LD_EXTRA_FLAGS) DESTDIR = $(PWD) export CFLAGS export LDFLAGS export DESTDIR all: $(PRODUCT) %.o : ../wxSources/%.cpp $(CC) -c $< -o $@ $(CFLAGS) $(EXECUTABLE) : $(OBJECTS) $(CC) -o $@ $(OBJECTS) $(CFLAGS) $(LDFLAGS) ifeq ($(TARGET_PLATFORM),MAC) $(PRODUCT) : $(EXECUTABLE) rm -rf $(PRODUCT) mkdir -p $(PRODUCT)/Contents/MacOS mkdir -p $(PRODUCT)/Contents/Resources cp -f Info.plist $(PRODUCT)/Contents echo -n "APPL????" > $(PRODUCT)/Contents/PkgInfo mv $(EXECUTABLE) $(PRODUCT)/Contents/MacOS endif clean: rm -f $(EXECUTABLE) $(OBJECTS) rm -rf $(PRODUCT)ポイントは
$(shell $(WXCONFIG_PREFIX)wx-config --cppflags)
とか $(shell $(WXCONFIG_PREFIX)wx-config --libs $(WXLIB_LIST))
あたりかな。wxWidgets をビルドしたディレクトリを WXCONFIG_PREFIX
に指定して、そのディレクトリの中の wx-config
スクリプトを呼び出すと、コンパイラやリンカに渡すべきフラグを教えてくれる。
wxMSW と直接は関係ないのだが、今作っているアプリでは Ruby, CLAPACK の組み込みを予定しているので、これらをインストールしておいた。
Ruby は何も問題はない。One-Click Ruby Installer を使ってインストールする。デフォルトでは c:\Ruby
にインストールされる。
CLAPACK は思いのほか苦戦した。わかってしまえば難しくはなかったのだが、手順は次の通り。
clapack.tgz
を取ってきて、MSYS のホームディレクトリに展開する。make.inc:
最適化レベルを -O2
に下げ、-DMINGW
を追加。最適化レベルは -O3
のままだと BLAS のテストが通らない。MINGW
シンボルは、下の sys/times.h
関連の修正で使う。(2009.12.13.追記) リンク時のフラグに -Wl,--stack=33554432
(スタックサイズ = 32MB)を指定。ライブラリには影響ないが、これをつけないとテストで失敗する。F2CLIBS/libf2c/Makefile:
arithchk.c
をコンパイルして実行するところで、出力ファイル名の a.out
を明示的に指定。F2CLIBS/libf2c/dtime_.c, etime_.c:
MinGW に含まれていない sys/times.h
を使わないように修正。INSTALL/dsecnd.c, second.c
: 同上。INCLUDE/clapack.h, f2c.h
: ビルドには直接関係ないが、C++ プログラムから #include
されても問題ないように、#if __cplusplus...#endif
を入れる。
F2CLIBS/libf2c.a, blasMinGW.a, clapackMinGW.a
の3つのライブラリができる。これらをそれぞれ libf2c.a, libblas.a, libclapack.a
という名前で /lib/clapack
に置く。また、INCLUDE/f2c.h, INCLUDE/blaswrap.h, INCLUDE/clapack.h
を同じく /lib/clapack
にコピーする(ヘッダファイルを /lib
以下に置くのは筋が悪いとお思いなら他の場所でも可)。
(2008.12.08 追記) wxMSW とリンクすると、libf2c.a
が不具合を起こす。シンボル解決の順序の問題なのか、よくわからないのだが、libf2c.a
中の main()
関数がリンクされてしまい、その結果 _MAIN__
が見つからない、というリンクエラーになる。_MAIN__
は f2c で FORTRAN プログラムを C に変換した時のメインルーチンだが、当然そんなものはあるわけがない。main()
がリンクされてしまうことが問題なので、次のようにして libf2c.a
から main.o
を外した。
$ cp libf2c.a libf2c_nomain.a $ ar d libf2c_nomain.a main.o
Ruby 組み込み用のためのコンパイラフラグはこんな感じになる。
MAC: RUBY_CFLAGS = -I/usr/lib/ruby/1.8/universal-darwin8.0 \ -I/usr/lib/ruby/1.8/universal-darwin9.0 RUBY_LDFLAGS = -lruby MSW: RUBY_CFLAGS = -I/c/Ruby/lib/ruby/1.8/i386-mswin32 RUBY_LDFLAGS = -L/c/Ruby/lib /c/Ruby/bin/msvcrt-ruby18.dll
Mac で -I
オプションを2つ書いてあるのは、MacOSX10.4u.sdk
に ruby.h
が含まれていないため。本当は ...darwin8.0
で ruby.h
が見つからないといけないのだが、見つからないので ...darwin9.0
(10.5 組み込み)を併記している。MacOSX10.4u.sdk
と 10.5 では Ruby のバージョンが違うのでこれはちょっと危なく、10.4 から ...darwin8.0
のヘッダファイルをコピーしてきて MacOSX10.4u.sdk
に入れておいた方がいい。まあ、このままでもとりあえずは動いてはいるようだが。
MSW の方は、上で見て明らかな通り msvcrt-ruby18.dll
にバイナリが依存するようになる。これはやむを得ない。スタティックリンクする手もあるのだが、require
で「標準添付ライブラリ」を読み込もうとしたとき困ってしまう。スタティックリンクして、「Ruby の別途インストールは不要です、ただし組み込み以外の添付ライブラリは使えません」というのとどちらが親切か、それは作るアプリケーションの目的次第だろう。
CLAPACK をリンクするためのフラグはこうなる。
MAC: CLAPACK_CFLAGS = -framework Accelerate MSW: CLAPACK_CFLAGS = -I/lib/clapack CLAPACK_LDFLAGS = -L/lib/clapack -llapack -lblas -lf2c_nomain # -lf2c_nomain は 2008/12/08 追記
Mac は標準で CLAPACK が入っている。インクルードパスは指定していないが、そのかわり #include <vecLib/clapack.h>
と書く必要がある。MSW の方は特に問題はない。
スクリーンショットだけ載せてもしょうがないのだが、まあこんな雰囲気だってことで。