きりかノート 3冊め

おあそびプログラミング

ruby-2.1.2のlibffi-3.1対応

チケットが来てたので対応しました。

内容についてはrubyのissie#9897にあるとおりです。

いちお簡単に説明すると、ビルド時にマクロとしてRUBY_LIBFFI_MODVERSIONという値を定義しているのですが、従来の"3.0.13"のような数字3つのバージョンから"3.1"という数字2の形式に変わったため、その値に入れるバージョンの加工に失敗してfiddleがコンパイルされないという状況になっていました。

trunkやruby_2_1ブランチでは修正済みだったので、その変更を適用しました。ruby-2.0ではRUBY_LIBFFI_MODVERSIONを使っていないためこの問題はありません。

また、別件で新しいXcodeでdlモジュールがインストールされないという問題があったので合わせて対応しています。

port:ruby21でfiddleが使えないなどの状況が起きた場合は、port upgrade ruby21で解消すると思います。

RubyCocoa 1.2.0 リリース!

しました。ついにRuby 2.0以降に対応しました!(1.9は対応しません)

ちょっとパッケージング失敗したので、日本時間で今日の14:30以前にダウンロードした人は取り直したほうがよいかもです。

変更点をNEWS.jaから抜き出し。ruby-2.0対応したのが中心で、他は特にありません。

   == 1.1.0から1.2.0の変更点: 2014-07-27

   === 改良

     * ruby-2.0に対応しました。おおむねruby-1.8と同様に動作します。
       * RubyCocoaのインストーラはruby-1.8用と2.0用で別々のファイルとして
         リリースされます。RubyCocoaアプリケーションをOS X 10.9添付のruby-2.0で
         動作させてみたいときは、RubyCocoa.frameworkを入れ替えてください。
     * 存在しないメソッドが呼び出されたときの例外をNoMethodErrorに変更しました。
       (以前はOSX::OCMessageSendExceptionでした。)

   === 廃止された機能

     * CocoaClass#objc_export()は廃止されました。objc_method()を使ってください。
     * OSX::NSObject#ocm_responds?()は廃止される予定です。ocm_respond_to?()を使ってください。
     * OSX::NSData#dataWithRubyString()およびOSX::NSMutableData#dataWithRubyString()は
       廃止される予定です。[NSString dataUsingEncoding:]を使ってください。

   === 修正

     * Yosemite DP上でのビルドエラーを修正しました。

   === RubyCocoaをRuby-2.0上で使う際の注意

     * ruby-2.xの文字列はエンコーディングを保持するようになります。詳しくは
       https://bugs.ruby-lang.org/projects/ruby-trunk/wiki/MigrationIssuesFrom18 や
       http://magazine.rubyist.net/?0025-Ruby19_m17n をごらんください。
     * (frameworkでない)bundleやdylibのObjective-CのクラスをRubyのコードで
       拡張したいときは、OSX.ns_importを書くようにしてください。

           require 'path/to/your_bundle.bundle'
           OSX.ns_import :YourClass
           module OSX
             class YourClass
               : # extend class in Ruby code
             end
           end

RubyCocoa.frameworkが、ruby-1.8用とruby-2.0用が存在することになるので、Finderに表示するようにしてみた。

今後の予定

このくらいですかねえ。

最後のはまだアタリもつけてないけど、簡単にできるとよいなあ。

RubyCocoa 今週のコミット ..2014-07-26 10.6/10.7での初期化エラーを修正

細かいとこではドキュメント更新したり、コンパイル時の警告つぶしたりしてた。

大きい作業としては、Snow LeopardやLion環境では"trunkでrequire 'osx/cocoa'しただけで落ちる"という致命的な問題があってその対応をしていた。

エラーメッセージはこんなの。

   "/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby" -I../e
   xt/rubycocoa -I../lib testall.rb
   2014-07-25 05:48:43.038 ruby[4778:60f] *** NSInvocation: warning: object 0x7
   fff708db850 of class 'Object' does not implement methodSignatureForSelector:
    -- trouble ahead
   2014-07-25 05:48:43.041 ruby[4778:60f] *** NSInvocation: warning: object 0x7
   fff708db850 of class 'Object' does not implement doesNotRecognizeSelector: -
   - abort
   test failed

ようするに(Objective-Cの)Objectクラスを処理しようとしてエラーになってるわけだ。

Objective-Cのクラスの話

前提として説明すると、Objective-CのランタイムにあるすべてのクラスがCocoaのクラスというわけではない。Cocoaでは、NSObjectとNSProxyの2つのルートクラスがあり、ほとんどはNSObjectの派生クラスとしてFoundation等のフレームワークは構成されている。

で、それらとは別系統のクラスもあって、たぶんObjective-C組み込みと思われる"Object"クラスなんてのもある。

   // OS XのObjective-Cのクラス階層
   + NSObject
     + NSArray
     | + NSMutableArray
     + NSResponder
       + NSView
         + NSText
         | + NSTextView
         + NSTableView
   + NSProxy
   + Object # <= NOT a Cocoa class

ちなみに、今までほとんど表にでてこなかったObjectクラスなんだけど、SwiftのクラスはこのObjectクラスの派生クラスになってるという話もあるみたいね。

話を戻そう。(最近の)RubyCocoaでは、この「Cocoaのクラスかどうか」を

  • 自クラスまたはその親のクラスがNSObjectプロトコルに適合しているかどうか

で判定している。実装はこんな感じ。

   // framework/src/objc/mdl_osxobjc.m
   /*
    * Detects Cocoa Objective-C class or not with protocol "NSObject".
    * - NSObject => YES
    * - NSProxy  => YES
    * - Object   => NO
    */
   static BOOL
   class_is_cocoa_class_p(Class klass) {
     Protocol *proto = objc_getProtocol("NSObject");
     Class klass_sup = klass;
     while (klass_sup) {
       if (class_conformsToProtocol(klass_sup, proto)) {
         return YES;
       }
       klass_sup = class_getSuperclass(klass_sup);
     }
     return NO;
   }

で、これを利用してCocoaのクラスではないクラスはRubyCocoa的には存在しないものとして処理している。

今回のエラーの話

RubyCocoaでは、できるだけObjective-Cのクラス階層をRuby側でも再現するために、あるクラスがRuby側に現れたとき、そのスーパークラスRuby側に呼び出すようにしている。今回はその機能でひっかかるとこができてた。

RubyCocoaの初期化時にOSX.ns_import_allでぜんぶのクラスをRuby側に持ってくるようにしているんだけど、そのとき処理対象となるProtocolクラスのスーパークラスSnow Leopard環境などではObjectになってる。でObjectクラスをRuby側で扱おうとしてもCocoaなクラスじゃないのでRubyCocoaを通した操作ができない。でエラーになるというわけ。

RuntimeBrowserで実装を見てみると、ProtocolクラスのスーパークラスMavericksではNSObjectに、Snow LeopardではObjectになっていることがわかる。

   // OSX 10.9 Mavericks
   @interface Protocol : NSObject  {

   // OSX 10.6 Snow Leopard
   @interface Protocol : Object <NSObject>

これを解消するためには、OSX::Protocol.oc_superclassがnilを返す(スーパークラスなし)ようにすればよい。つまり、Objective-Cのメソッド呼び出しの結果がClassで、そのクラスがCocoaのクラスじゃないときにはnilを返すように変更することになる。

関連するコミット。

  • Cocoaのクラスかどうかの判定基準をisKindOfClass:でなく、NSObjectプロトコルに適合しているかどうかに変更。10.8以前では+[NSProxy isKindOfClass:]を呼び出すとエラーになるため。(r2618)
  • Cocoaのクラスかどうかの判定を関数class_is_cocoa_class_p()に切り出し。(r2620, r2622)
  • メソッドの返り値がクラスで非Cocoaのクラスのときnilを返すようにする。(r2626)

これでOS X 10.6 - 10.9の環境でtrunkのテストがぜんぶ通るようになった。

RubyCocoa 今日のコミット 2014-07-21 NSData.dataWithRubyString()をdeprecatedに

いちおRC出したのでそんなにいじるつもりはないので、yardのドキュメントをメンテしたり。

便利メソッドとしてOSX::NSData.dataWithRubyString(str)というメソッドが昔からあるんだけど、使い道があんまりないなーと思った上に、そもそもエンコーディングなしにバイト取るのもどうなんだろ?と思ったのでdeprecatedにした。NSMutableDataのメソッドも同様。

  • yardで@deprecatedを指定。NEWSにも書いた。実行時にはwarnする。(r2611)
  • テストで使ってるとこを必要な箇所を除きNSString#dataUsingEncoding()に直した。(r2612)
  • NSData#lengthのテストに非asciiなデータも足しておいた。(r2613)

たぶん誰も使ってないと思うので、1.2の次の次くらいのリリースで削除します。

RubyCocoa 1.2.0 RC (Ruby-2.0対応版)を公開

しました。いきなりリリースしてもよかったんだけど、自分だけの確認では不安だったので。

動作確認用なのでOS X 10.9用のみです。なにかあればお知らせください。ヤバそうな問題が報告されなければ次の週末あたりにリリースします。

これまで試してきた印象では、RubyCocoaによる問題よりもrubyが1.8から2.0になった部分のほうが影響が大きいんじゃないかと思います。String#each → #each_lineとかそういうの。

RubyCocoa 今週のコミット ..2014-07-20 サンプルアプリの動作確認

sample/以下にあるアプリの一覧をつくって、ひたすら確認して直してた。

  • もともとおかしかったぽい
    • make系のプロジェクトで-framework AppKitが足りない。
    • import 足しておいた。
  • ruby-2.0によるもの
    • ruby-2.0でSyntax Errorになるものを直した。

他のアプリのプラグインとかは確認するのが大変なのでコンパイルできるとこまでしか確認していないです。

WebVTTファイルを利用して動画に字幕を付ける

小ネタ。自分用のメモです。

WWDCのtranscriptをテキスト化しているasciiwwdcリポジトリwwdc-session-transcriptsをながめてて、拡張子".vtt"てなんじゃろと思って調べてみたところ、WebVTTという規格のテキストであることがわかった。

テキストはこんな感じで、字幕のテキストの他に表示する時間や表示する形式が書かれている。シンプルな構造だね。

   WEBVTT
   X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000

   00:00:13.236 --> 00:00:14.526 A:middle
   &gt;&gt; My name is Olivier Bonnet.

   00:00:14.526 --> 00:00:18.266 A:middle
   I'm the engineering manager for
   the CloudKit on the client side.

動画と組み合わせて字幕を表示させるには、html中のtrack要素で書いてやればよい。

   <video controls autobuffer autoplay loop id="wwdcvideo" width="100%">
       <source src="./2014/408_sd_swift_playgrounds.mov" type="video/quicktime" />
       <track kind="captions" src="./2014/408.vtt" type="text/vtt" srclang="en"
              label="English Subtitles" default />
   </video>

ちなみに2014年のWWDCの動画には最初から字幕データが入っているので、こんなことしなくても字幕付く。てゆか二重に表示される。

いやさ、Safariでそのまま見てると最大化しないと字幕のオプションでてこないから、気付かなかったんですよ!

RubyCocoa 今日のコミット 2014-07-14 インストーラ作成スクリプトの調整

10.9 Mervericks用はruby-1.8とruby-2.0の両方がOSに付いてるので、それぞれ用のRubyCocoa.frameworkのインストーラをリリースする予定。なのでそのあたりの調整あれこれ。

  • リリースファイルの名前を自由に指定できるように。 (r2587)
  • パッケージ作成時の設定ファイルをコマンドラインから渡せるように。 (r2588)
  • 10.9+ruby2.0用の設定ファイルを追加。(r2589)

今は自動的に"RubyCocoa-1.1.0-OSX10.9.dmg"などの名前を生成しているけれど、任意の名前も指定できるように。単にRubyのバージョンを足して、

になる予定。

パッケージ作成時の設定ファイルをpackage/config/以下におくようにして、`ruby install.rb package`で該当OSバージョンのファイルを使うんだけど、OSのバージョンが同じなので実行時のコマンドラインオプションで選べるようにした。

   % ls -1 package/config/
   10.4
   10.5
   10.6
   10.7
   10.8
   10.9
   10.9-ruby2.0 # <= NEW!

エンコーディングの件はまだちょっと悩み中。現在の案だとどうせ直すことになりそうだし、UTF-8使えれば現在の1.8レベルの利用は問題ないはずなので、次の次のバージョン(1.3?)に送っちゃおうかなあ、、とひよっている。今週末までの進行具合で判断しますです。

RubyCocoa 今日のコミット 2014-07-10 ruby-2.xでThreadがおかしい?件に対応

だいたいruby-2.x対応できたので、samples/以下にあるアプリを動かしたりして動作確認してる。で、どうもrubyのThreadを使ったコードがうまく動かなくって、スレッドが切り替わらなかったりするのを調べてたんだけれどようやくわかった。

  • Thread.passがひつよう。(r2584)

RubyCocoa本体側を疑ってコード追っかけてたからぜんぜん気付かなかったぜい。

1.2.0リリースまでの残作業

今回は一度RC出す予定です。

port:pecoを0.2.0に更新

しました。

先日MacPorts公式にport:pecoを登録してから気付いたんだけど、peco-0.1.12とMacPortsのportコマンドとの組み合わせで、どうも動作がおかしい。portの出力をpecoに渡すとそのまま固まってしまう(killしないと終了できない)。0.1.11以前は問題なかった。

pecoの変更を追っかけてみると、0.1.11と0.1.12で入力バッファの処理が変更されていて、どうもそのへんじゃないかと思ってレポートしたりして、報告した問題は解消されたんだけど、portだけまだおかしい。これはたぶんportが悪いんだと思う。

仕方ないので、パイプでつながないようにすることにした>"pbcopy; pbpaste"のあたり。

   % sudo port selfupdate
   % sudo port upgrade $(port outdated | pbcopy; pbpaste | peco | cut -f1 -d\ )

これで更新したいのを選ぶとupgradeされる。地味に便利。