D/SDL on MacOSX

んで次は D のゲームのコンパイルしてたわけですな。

http://shinh.skr.jp/osxbin/

D のコンパイラは gdcmac がすばらしいのでありがたく使う。

http://gdcmac.sourceforge.net/

まずは普通に D の非互換によるエラーを潰す。そんなにたいした量なかった。

リンクは大変。まず昔も書いたような話。

http://zinnia.dyndns.org/~hiki/SDLKB/?MacOSX%2BSDL%2BD%B8%C0%B8%EC%A4%C7%A4%CE%A5%A2%A5%D7%A5%EA%A5%B1%A1%BC%A5%B7%A5%E7%A5%F3%A5%B3%A5%F3%A5%D1%A5%A4%A5%EB%CB%A1

要は Ruby/SDL と同じで、 libphobos.a も main を奪いたがるのでここでも競合する。しゃーないなーということで main の無い libgphobos.a を作ることにする。めんどくさいから shell script をはる。

#!/bin/sh -x

rm -fr t
mkdir t
cd t
cp /usr/lib/libgphobos.a .
for i in i386 ppc ; do
    mkdir $i
    cd $i
    lipo ../libgphobos.a -extract $i -output libgphobos.a
    libtool -static libgphobos.a -o gp.a
    ruby ../../arext.rb gp.a
    rm cmain.o || exit
    ar crus libgphobosnm.a *.o
    cd ..
done

lipo -create -arch i386 i386/libgphobosnm.a -arch ppc ppc/libgphobosnm.a -output ../libgphobosnm.a

何してるかっていうと libgphobos.a が Universal Binary なので、 i386ppc でそれぞれ libgphobos.a を取り出して、 .a を展開してから cmain.o を削除、そんでから .a にまとめて、もう一度 Universal Binary にしている。 lipo は Universal Binary を操作するツールで、 i386 だけを取り出したりできる。この段階ではまだ新しいフォーマットなので、古き良き ar アーカイブにするために libtool -static を使い、そんでから展開している。展開は ar でできるのだけど、うっといことに同じ名前オブジェクトファイルが入ってたので ar 展開ツールをサクっと作った。こんなの:

if ARGF.read(8) != "!<arch>\n"
  abort 'it is not ar'
end

m=[]
while x=ARGF.gets
  x = x.split
  abort 'wrong format?' if x[6] != '`'
  x = [x[0], Time.at(x[1].to_i), x[2], x[3], x[4], x[5].to_i, x[6]]

  if /\#(\d+)\/(\d+)/ =~ fn=x[0]
    fn = ARGF.read(s=$2.to_i)[/[^\0]+/]
    x[5] -= s
  end
  c = ARGF.read(x[5])

  on = fn
  while m.include?(fn)
    en = File.extname(fn)
    bn = File.basename(fn, '.*')
    fn = bn + '_' + en
  end
  if on != fn
    puts "filename changed: #{on} => #{fn}"
  end
  m << fn
  File.open(fn, 'w') do |o|
    o.print(c)
  end
end

これで libgphobos からは main を消せた。後は D の初期化をする SDL_main 相当を書いてやればいい。以下 boot.d

import std.c.stdio;
extern (C) int chdir(char* s);
private extern (C) char* rindex(char* s, int c);
private extern (C) int _d_run_main(int argc, char **argv, void * p);
int main();
extern (C) int SDL_main(int argc, char** argv) {
    char buf[256];
    char* p = rindex(argv[0], '/')+1;
    sprintf(buf.ptr, "%s.app/Contents/Resources", p);
    if (0 != chdir(buf.ptr)) {
        printf("cannot change dir\n");
    }
    return _d_run_main(argc, argv, & main);
}

こんなのを一緒にリンクしてやると、

  • libSDLmain.a の main から起動
    • Cocoa 初期化
    • SDL_main を呼び出し
  • boot.d の SDL_main
    • .app 形式に対応するため chdir (この時まだ D の GC は初期化されてないので使えないので注意)
    • _d_run_main で _Dmain を呼ぶ
  • ユーザーコード

という流れになる。

で、 Universal Binary を作る話だけど、基本的には gdc -arch i386 -arch ppc などとすればよろしくやってくれる。ところがリンクは、 libgphobos.a を変えちゃってる都合で自前でやることになった。 gcc のオプションに -v をつけて ld のオプションを調べて、それを適当に編集して -lgphobos を自作のに変えればいい。それぞれリンクできたら lipo でひっつける。うちだとリンクオプションはこんな感じになった。 crt が微妙に違うんだなーと思った。

LIBS=../libgphobosnm.a -lstdc++ ../bulletml/libbulletml_d.a -F$(FPATH) -framework Cocoa $(FOPTS) -framework OpenGL ../SDLMain.o
LD_386=/usr/libexec/gcc/i686-apple-darwin8/4.0.1/collect2 -dynamic -arch i386 -arch_multiple -macosx_version_min 10.3 -multiply_defined suppress -weak_reference_mismatches non-weak -lcrt1.o /usr/lib/gcc/i686-apple-darwin8/4.0.1/crt3.o -L/usr/lib/gcc/i686-apple-darwin8/4.0.1 -L/usr/lib/gcc/i686-apple-darwin8/4.0.1 -L/usr/lib/gcc/i686-apple-darwin8/4.0.1/../../.. -lgcc_s.10.4 -lgcc -lm -lSystem
LD_PPC=/usr/libexec/gcc/powerpc-apple-darwin8/4.0.1/collect2 -dynamic -arch ppc -arch_multiple -macosx_version_min 10.3 -multiply_defined suppress -weak_reference_mismatches non-weak -lcrt1.o /usr/lib/gcc/powerpc-apple-darwin8/4.0.1/crt2.o -L/usr/lib/gcc/powerpc-apple-darwin8/4.0.1 -L/usr/lib/gcc/powerpc-apple-darwin8/4.0.1 -L/usr/lib/gcc/powerpc-apple-darwin8/4.0.1/../../.. -lgcc_s.10.4 -lgcc -lm -lSystemStubs -lSystem

あとはここに昔書いたような方法でパッケージを作りました。要は hdiutils ってコマンドを使うと良い。

http://zinnia.dyndns.org/~hiki/SDLKB/?MacOSX%2BSDL%A4%C7%A4%CE%C7%DB%C9%DB%CA%AA%BA%EE%C0%AE%CB%A1

なにかあれば下記メールアドレスへ。
shinichiro.hamaji _at_ gmail.com
shinichiro.h