seekpoint

devlog

File Extension for Theos Logo.

theosのLogo用拡張子は種類がいくつかあるのでその話。

theosって?

theosはiOS/Mac向けのMakefileフレームワークだ。Makefileからincludeしてあげれば苦行のようなMakefileの記述からある程度解放してくれる。

Logo

どちらかと言えばtheosはビルド過程にこの処理を含ませるためのMakefileという側面が強いが、MobileSubstrate向けのswizzleコードの記述を楽に書けるよう変換も行わせる事ができる。 その際の拡張子が以下だ。

  • .x
  • .xm
  • .xi
  • .xmi

この拡張子の違いについて説明しよう。まず一番一般的な.xmだが、これはLogo変換処理を行い.mmファイルを生成し、Objective-C++としてプリプロセス、コンパイル、リンクを行う。 次に.xはそれのObjective-C版だ。全てに言える事だがLogoの変換処理に違いはない。オリジナルのマクロによってObjc/Objc++で普通にswizzleを書くのと同じ状態へ持っていくのだから至極当然の処理順だ。

最後にそれらにiがついたものだ、これは真っ先にobjc/objc++プリプロセスが行われる。その後にLogo処理、コンパイル、リンクとなる。そこそこコードを書いていると以下のように書きたいと思うこともあるだろう。

#define SHARED_INSTANCE [%c(SBProcess) sharedInstance]

%cマクロはLogo処理向けであるのでプリプロセスに放り込むとエラーになる。このレベルであればobjc_getClassで代用がきくが以下のようなケースはそうもいかない。

%hook UIKBKey 
- (NSString *)displayString {
    NSString *result = %orig();
    return [self.name rangeOfString:@"-Small-"].location != NSNotFound ? [result lowercaseString] : result;
}
%end

%hook UIKBTree
- (NSString *)displayString {
    NSString *result = %orig();
    return [self.name rangeOfString:@"-Small-"].location != NSNotFound ? [result lowercaseString] : result;
%end

処理内容が同じなので変更は一度ですむようにすべきだが、関数にまとめても%orig%hookの外では使用できない。 プリプロセスが先に走る.xi, .xmiではdefineにLogoを書いておく事が可能になるのでdefineでまとめられる。

#define HONOR_CASE \
    NSString *result = %orig(); \
    return [self.name rangeOfString:@"-Small-"].location != NSNotFound ? [result lowercaseString] : result;

%hook UIKBKey
- (NSString *)displayString { HONOR_CASE }
%end

%hook UIKBTree
- (NSString *)displayString { HONOR_CASE }
%end

めでたく修正時には一度の変更ですむようにできた。
コード出典: ShowCase – github/ashikase

まとめ

extension process order
.x Logo –> objc preprocess+compile –> link
.xm Logo –> objc++ preprocess+compile –> link
.xi objc preprocess –> Logo –> objc compile –> link
.xmi objc++ preprocess –> Logo –> objc++ compile –> link

なおこの表はiphonedevwikiにも記述しておいた。