ラベル Xcode 4.2 の投稿を表示しています。 すべての投稿を表示
ラベル Xcode 4.2 の投稿を表示しています。 すべての投稿を表示

2012年1月24日火曜日

Jenkins を iOS アプリ開発に導入してみた (gcov編)


前回 はSenTestKitに続いてGHUnitを導入して、Jenkins上でGHUnitによるテストの自動実行を行いました。今回はさらにステップアップして、gcovを使用してコードカバレッジを取るようにしてみようかと思います。


■gcovを使ったコードカバレッジの取得(Xcode 4.3版)

Xcode 4.3の場合は以下の参考ページで紹介されている方法がオススメです。実機ででもシミュレータでも動作してお得です。
http://www.cocoabuilder.com/archive/xcode/314794-xcode-4-3-moved-libprofile-rt-how-to-reference-it-now.html
http://www.infinite-loop.dk/blog/2012/02/code-coverage-and-fopen-unix2003-problems/
https://github.com/InfiniteLoopDK/ILTesting
  1. Generate Test Coverrage FilesとInstrument Program FlowにYESを指定する
  2. http://www.infinite-loop.dk/blog/2012/02/code-coverage-and-fopen-unix2003-problems/で紹介されているILProfilerCompat.cを組み込む(これによってfopen$UNIX2003によるエラーを防ぐ)
これだけで大丈夫です。


■gcovを使ったコードカバレッジの取得(Xcode 4.2版)

【2012/03/15追記】こちらの方法はXcode 4.2が前提となっており、Xcode 4.3以降は動作しません!

gcovとはgccに付属されているC言語用のカバレッジ測定ツールです。これを使うことで、C言語と、その拡張であるObjective-Cのコードカバレッジをバッチリ取得することができます。ということで早速プロジェクトに組み込んでみましょう。幸いにしてすでに先駆者の方がいらっしゃいました。
http://tech.naver.jp/blog/?p=706
基本的に上記に記載されている通りの手順で導入すれば簡単にgcovによるコードカバレッジの取得ができますが、実際にやってみたところ幾つか補足があるので追記します。

先ほど紹介した記事では/Developer/usr/lib/libprofile_rt.dylibの追加と-lgcovの指定両方を行なっているのですが、私が試したところどちらか片方だけで成功するということがわかりました。具体的には以下のとおりになります。

1. /Developer/usr/lib/libprofile_rt.dylibを使うパターン
2. -lgcovを使うパターン

それぞれ見ていきます。

1. /Developer/usr/lib/libprofile_rt.dylibを使うパターン

/Developer/usr/lib/libprofile_rt.dylibをプロジェクトに追加し、Other C Flagsの-fprofile-arcs -ftest-coverageを設定すればOKです。それ以外の手順は必要ありません。
このパターンですとスムーズにgcovの結果を出力することができましたが、残念ながらこの方法はSimulator上でしか使用できません。というのも、/Developer/usr/lib/libprofile_rt.dylibがそもそもarmv6, armv7用のバイナリを含んでいないという問題があるのと、ライブラリ探索パス"/Developer"以下がDeviceビルド時に勝手にiphoneos.sdk以下の/Developerに置換されてしまうため、ビルド時にlibprofile_rt.dylibをリンカが発見できずエラーになる問題があるためです。基本的にSenTestingKitのようなSimulatorでしか実行しないビルドターゲットでやるのが良いと思われます。

2. -lgcovを使うパターン

参考: http://stackoverflow.com/questions/5101014/code-coverage-not-showing-results-using-xcode-gcov/5140459#5140459
-lgcovをOther Linker Flagsに追加し、Generate Test Coverrage FilesとInstrument Program FlowにYESを指定すればOKです。それ以外の手順は必要ありません。
このパターンですとlibgcovはarmv6, armv7でも動作するので実機でのカバレッジ取得ができるのですが、問題はそのままの設定でビルドして実行すると実行時にクラッシュします。これはlibgcovが実行時にカバレッジデータを書きだす際に、デフォルトの設定ではiOSのサンドボックスの外にアクセスしようとするため書き出しに失敗してしまうのが原因です。したがって上記参考URLに従ってGCOV_PREFIX環境変数を使いサンドボックス内部にカバレッジデータを書きだすように指定してやる必要があります。
この方法は環境変数を実行時に設定する必要があって面倒なのと、カバレッジデータが書き出される箇所がJenkinsのビルドディレクトリの中ではなくiOSシミュレータまたは実機のサンドボックスの中になってしまうため、Jenkinsからカバレッジの取得を行うのが困難になってしまう問題があります。

今回は1. のパターンを採用し、SenTestingKit上でSimulatorビルド時のカバレッジ取得だけを行うことにしました。GHUnitでも実機上での動作を諦めれば問題ないのですが・・・まだ課題が多い感じですね。

ビルド設定ができたらビルドして、きちんとgcovコマンド経由でカバレッジが取得できていることを確認しましょう。


■gcovrを使ってCoberturaのXML形式に変換する

gcovのカバレッジデータの取得ができるようになったら、今度はJenkinsと連携させるために、gcovの出力をCoberturaというJava環境のカバレッジ計測ツールの出力するXML形式に変換する必要があります。これはJenkinsがgcovのカバレッジデータ出力の集計に対応していない為です。っていうかgcovの出力は単なるタブで区切られた文字列で人間以外にはとても優しくないので集計ができないのです。
ここで、Python製のgcovrというツールをJenkinsサーバマシンに導入します。 gcovrをgcovの代わりに使うことで、CoberturaのXML形式で結果を出力できるようになるほか、カバレッジ計測対象となるファイルを簡単にフィルタリングできる(たとえばテストケースクラスのカバレッジなんか集計してもしょうがないので除外するなどできる)のが大きな魅力です。
http://wiki.hudson-ci.org/pages/viewpage.action?pageId=45482230
https://software.sandia.gov/trac/fast/wiki/gcovr

インストールはMacマシンであれば非常に簡単で、以下のコマンドを打つだけです。
sudo easy_install gcovr
インストールが完了したら、あとは
gcovr --xml --output=cobertura-report.xml build/
とかやれば一発でbuildディレクトリ配下のすべてのカバレッジを計測結果を集計してXML形式で書きだしてくれます。
ここでもし、テストケースのカバレッジは除外したいなどという要件がある場合は、
gcovr --xml --exclude=".*/.*Test(s)?\.[(c)(cpp)(m)(mm)]" --output="cobertura-report.xml" build/
などとすればOKです。


■JenkinsのCoberturaプラグインを使って結果を集約する

CoberturaのXML形式で結果が取れるようになってしまえばあとは楽勝です。JenkinsにCoberturaプラグインをサクっとインストールして、
https://wiki.jenkins-ci.org/display/JENKINS/Cobertura+Plugin
あとは以下のようにJenkinsのジョブ設定を書いてしまうだけです。先述の通り、SenTestingKit上でSimulatorビルド時のカバレッジ取得だけを行いたいので、既存のSenTestingKit用単体テストジョブのXcodeビルドの後でgcovrを実行するシェルスクリプトを実行してやれば一発です。


カバレッジが取れるようになると断然見栄えがしますね。

2012年1月23日月曜日

Jenkins を iOS アプリ開発に導入してみた (GHUnit編)


前回 はSenTestKitを用いてJenkins上で単体テストの自動実行を行いました。今回はGHUnitを使った単体テストの自動実行にチャレンジしてみたいと思います。またついでといっては何ですが、単体テスト時に必要になってくるモックを作成するためのライブラリOCMockも同時に導入してみようと思います。


■なぜGHUnitを使うのか

GHUnitを使うことで、SenTestingKitと比べて以下のようなメリットが得られます。
  • 非同期処理のテストを行うための仕組みが用意されている(GHAsyncTestCase)これをSenTestingKitないし他のテスティングフレームワークでやろうとすると大変骨が折れます。
  • .app形式(要するに実際のiOSアプリケーション)でテストを実行するため、UIApplicationやUIWindowといったUIコンポーネントを使うクラスのテストが可能になる。UIテストの支援をするための仕組みも最近のGHUnitには追加されている。
    • SenTestingKitの"Logic Test"では想定通りに動作しないUIKitやFoundationのクラスが幾つかあり、中には純粋なロジックで使いそうなクラスも含まれている。そのためSenTestingKitではテストできないロジッククラスが発生する場合があるので、そういう時はGHUnitを使うと良い。
    • 参考: https://gist.github.com/1662887
  • 実機上でテストを実行できる。そのため実機でのみ発生するバグをつかまえることが可能。
  • SenTestingKitのテストケースをそのまま実行できるため上位互換として使用できる。
逆にGHUnitにはSenTestingKitと比べて以下のようなデメリットがあります。
  • Xcode 4で統合されたUnitTestingの恩恵に預かれない。すなわちCmd+U一発でテストケースを実行することができない。Xcode経由だとCmd+Rでアプリケーションとして起動して、その後起動したアプリのテスト実行ボタンを押す手間が必要になるため、開発のテンポが乱されてしまう。特にTDDを採用している場合には深刻。
  • 上記問題を回避するためCUI経由でテストを実行することもできるのだが、Xcodeのエディタと連携しないため、どこがエラーになっているのかが一目でわかりづらい。
  • カバレッジの取得が楽。SenTestingKitをシミュレータ上で実行している時が一番やりやすい。これについては別の記事で解説します。
これを踏まえると、以下のように使い分けができるようになります。
  • SenTestingKitは純粋なロジックの単体テストを実行したいときに良い。Xcodeからすぐに実行できて一瞬で結果がコード上に出るので、開発のテンポを乱さない。TDDに向いている。
  • GHUnitは非同期なAPIを持ったクラスの単体テストを実行したいときに良い。またUIApplication, UIWindowといったUIコンポーネントと結合したり、通信やファイルアクセスなどの外部リソースとの結合をおこなった状態でのテストを作る際にも向いている(純粋な単体テストともユーザー受け入れテストとも異なるため、仮に結合テストと呼ぶことにする)ので、そういったものが必要な場合には最高の選択肢となる。
プロジェクトの開発方針や開発対象によってどのようなテストが必要になるのかが異なってくると思いますが、どれか1つだけ必要であればGHUnitを選択し、きちんとしたXPでやりたいプロジェクトはSenTestingKitに単体テストをやらせ、ユーザー受け入れテストに KIF などを採用し、その中間を埋める必要があればGHUnitも導入する、というのが良いのではないかと考えています。ちょっと面倒ですけど。


■GHUnitを導入する

以下のリポジトリからコードを取得して、XcodeからビルドすればOKです。
https://github.com/gabriel/gh-unit
注意点として、GHUnitは.framework版がそのままgithubからダウンロードできるのですが、
https://github.com/gabriel/gh-unit/issues/69
このような問題が報告されており使えないので、githubからソースコードをクローンしてきて、
cd Project-iOS
make
でビルドしてできたGHUnitIOS.frameworkを使うようにしてください。

frameworkをビルドしたら自分のプロジェクトに追加して、GHUnit用のビルドターゲットを作ります。
http://gabriel.github.com/gh-unit/docs/appledoc_include/guide_install_ios_4.html

ビルドターゲットができたら、次は試しにテストケースを追加してみて、問題なくテストが走るか確認。
http://gabriel.github.com/gh-unit/docs/appledoc_include/guide_testing.html

最後にCUI(要するにxcodebuildコマンド経由)でビルドができるようにします。これをやらないとJenkinsと連携できません。
http://gabriel.github.com/gh-unit/docs/appledoc_include/guide_command_line.html


■OCMockを導入する

以下のリポジトリからコードを取得して、XcodeからビルドすればOKです。
https://github.com/erikdoe/ocmock
iOS向けビルドはスタティックライブラリ用のビルドしか用意されていませんので、どうしてもフレームワークが良いとこだわる人は頑張ってください>< そうでなければ非常に簡単です。ビルドしたら成果物をそのままプロジェクトに突っ込めばすぐ使えるようになります。
OCMock自体の使い方にはここでは触れませんが、例えば以下のようなテストケースが作れたりします。

- (void)testOCMock
{
    // Creating a new stub object from class
    {
        id mockedObject = [OCMockObject mockForClass:[NSString class]];
        STAssertThrows([mockedObject length], @"Mocked object raises exception because the fake method is not ready yet.");
        STAssertThrows([mockedObject isKindOfClass:[NSString class]] , @"Mocked object can't even call isKindOfClass: because it's not ready.");
        STAssertFalse([mockedObject class] == [NSString class], @"Mocked object is not a kind of the target class.");

        [[[mockedObject stub] andCall:@selector(mockedLength) onObject:self] length];
        STAssertEquals([mockedObject length], (NSUInteger)100, @"Mocked object returns the fake value.");

        [[[mockedObject stub] andReturn:@"AllYourBaseAreBelongToUs"] lowercaseString];
        STAssertEqualObjects([mockedObject lowercaseString], @"AllYourBaseAreBelongToUs", @"Returns mocked value.");
        STAssertEquals([mockedObject length], (NSUInteger)100, @"Previously mocked methods are still valid.");
    }

    // Method Stubbing for existing object
    {
        NSString *stubTargetObject = @"I am a stub target.";
        STAssertEquals([stubTargetObject length], (NSUInteger)19, @"The original implementation of the length method.");

        id mockedObject = [OCMockObject partialMockForObject:stubTargetObject];
        STAssertTrue([mockedObject isKindOfClass:[NSString class]] , @"Mocked object is a kind of the target class.");
        STAssertEquals([mockedObject length], (NSUInteger)19, @"Mocked object returns the original value.");

        [[[mockedObject stub] andCall:@selector(mockedLength) onObject:self] length];
        STAssertEquals([mockedObject length], (NSUInteger)100, @"Mocked object returns the fake value.");
        STAssertEquals([stubTargetObject length], (NSUInteger)100, @"Stubbed target object also returns the fake value.");
    }
}
- (NSUInteger)mockedLength
{
    return 100;
}


■Jenkinsと連携させる

最後にJenkins側でビルドターゲットを作りましょう。
http://gabriel.github.com/gh-unit/docs/appledoc_include/guide_ci.html

こんな感じの設定になると思います。

あとはいつもどおりジョブを回してみて問題がないか確認すれば完了です。前回ご紹介したようなSenTestKitの単体テストジョブがうまく回っていれば、それをコピーしてきてちょっと設定を変えれば簡単にできるとおもいます。

2012年1月20日金曜日

Jenkins を iOS アプリ開発に導入してみた (SenTestKit編)


最近、iOSアプリの開発でも継続的インテグレーション(CI)を取り入れていくプロジェクトが増加傾向にあるようで、各種ツールやライブラリ、ノウハウが出回ってきているように感じられます。そこで私も早速iOSアプリ開発でのCI導入を試してみることにしました。今回の導入試験では、以下のような環境を想定して行いました。
  • iOSアプリの開発を、Xcode 4.X系のプロジェクトとして行う。
  • VCSにはgitを採用し、githubの公開リポジトリをリポジトリサーバーとして使用する。
  • CIサーバにはMacを採用し、プロジェクトをビルドするためにXcode 4.Xをインストールしておく。


■必要なツールを準備する

CIといったら、まずは何はなくともJenkinsです。
http://jenkins-ci.org/
ここでは導入について詳しくは挙げませんが、私は以下の本を参考にしました。
https://gihyo.jp/dp/ebook/2011/978-4-7741-4952-3

続いてJenkinsのプラグインを導入します。
これでJenkinsの準備はだいたい完了です。
あとはVCSのgitですが、こちらは準備が出来ているという想定で進めます。


■余談:xcodebuildの使い方

JenkinsのXcodeプラグインを使えば、自分で面倒なantのbuild.xmlを書いたり、ビルドスクリプトを用意しなくても、JenkinsのGUI上でビルド設定を行うことが出来ますが、念のため基本を理解しておくべく、xcodebuildというツールを使ってみます。xcodebuildはXcodeプロジェクトをCUIからビルドするためのツールで、Xcodeに付属しています。JenkinsのXcodeプラグインも内部的にはこいつを使用しています。

基本的な使い方の例は以下の通り。
xcodebuild -project MyApp.xcodeproj -configuration Release -target MyApp clean build
xcodebuild -project MyApp.xcodeproj -configuration Debug -target MyAppTests -sdk iphonesimulator5.0 clean build
xcodebuild -project MyApp.xcodeproj -configuration Debug -target MyAppTests -sdk iphonesimulator clean build
上の例ではデフォルト設定のiOS SDKを用いてビルドターゲットMyAppがReleaseビルドされます。次の例ではiPhone Simulator 5.0 SDKを用いてビルドターゲットMyAppTestsがDebugビルドされます。一番下の例では、手元にある最新のiPhone Simulator SDKが使用されるようです。また -target の代わりに -scheme を使うことも出来ます。ビルドに使うことができるSDKの一覧を得るためには、
xcodebuild -showsdks
を使えばよいです。

ビルドアクションにはclean, build以外にもarchiveとかあるのですが試していないので詳細は不明です。しかし残念ながら、少なくともtestアクションがないことは確認しました。そのため、Xcode上で Cmd+U を押して実行できる単体テストが、xcodebuild経由では実行できません。これについては後ほどまた触れます。


■Xcodeプロジェクト側の準備

Jenkinsによる自動化を行うためには、まずはXcodeプロジェクト側を修正して、xcodebuildツールにより単体テストが実行できるような状態にして置かなければなりません。そのため先にXcodeプロジェクト側の設定を修正します。Xcode 4でテストケース付きの新規プロジェクトを作るとテストケース実行用のターゲットが自動的に用意されると思いますが、先述の通りこのターゲットはxcodebuild経由では実行できないため、以下の画像の様にUnit Testing -> Test Hostを削除して対応します。


このように設定を変更することで、UIWindowやUIApplicationを使わなければならないテストが実行できなくなりますが、それ以外のテストはXcodeからもxcodebuildからも実行できるようになります。試しにxcodebuildを使ってテストターゲットを実行し、テストが走っていることを確認してください。


■Jenkins側の準備(ジョブの設計)

続いていよいよJenkinsの設定に移ります。プラグインは導入してあるので、あとはジョブを作るだけです。Xcode連携のプラグインが入っていれば楽勝です。今回はシミュレータビルドで単体テストを行うので、ビルド後の処理でテスト結果の集約を行い、test-reports/*.xmlを指定しましょう。


デバイスビルドでリリース用のビルドを作る場合には、ビルド後の処理でビルド成果物の保存を行い、build/Debug-iphoneos/*.ipa(必要ならdSYMも)などを指定しておけば良いかと思います。デバイスビルドをするにはビルドをするマシンのKeychainにcodesign用の証明書と鍵が格納されている必要があるので注意してください。Technical VersionやMarketing Versionの値を指定すれば、自動的にInfo.plistに指定されているCFBundleVersionやCFBundleVersionShortStringを置換してくれるので非常に便利です。


ジョブを作ったら早速実行してみましょう。おそらくビルドだけならあまり苦労せずに通ると思います。私は10回目のビルドで完全に問題なくビルドが回りだすようになりました。

2011年11月23日水曜日

静的ライブラリ中のシグネチャが衝突してビルドできないときに再ビルドしないでシグネチャを書き換える

皆さんも以下のようなビルドエラーを見たことが一度はあると思います。

これはビルド時に同一プロジェクト内に同じ名前のシグネチャの関数やクラスが存在するためリンクができなくて失敗しているというエラーです。特に以下のようなケースでよく発生します。
  • 自分が作ったクラスや関数の名前と、外部から持ってきたライブラリが使っているクラスや関数の名前が衝突している
  • 外部から持ってきたライブラリ同士でクラスや関数の名前が衝突している
  • 外部ライブラリをインストールする際に、-all_loadしたり-ObjCしたりている
そういうわけで、外部からライブラリをたくさん導入すると、base64やMD5など、プログラム上でよく使われるのに標準で用意されていないライブラリがよく衝突してしまうわけです。大抵の場合はぶつかっているシグネチャの名前をソースコード上でちょっと書き換えて再度ビルドすることで回避ができるのですが、極稀にソースコードを書き換えることができないケースが存在します。以下にそんな場合の対処方法をまとめます。


■具体例
AdMobのSDK(libGoogleAdMob.a)とopenssl(libcrypto.a)を同時に一つのプロジェクトにインストールした時、冒頭の画像のようにMD5というシグネチャがビルド時に衝突してしまうのです。AdMobのはプロプロエタリなので当然書き換えられませんし、opensslのように巨大で複雑なコードに手を入れて再ビルドするのも非常に危険です。そもそもopensslはビルド自体が難しいのです。

このような場合は、ソースコード自体を書き換えるのではなくビルド済みの静的ライブラリ側のオブジェクトファイルを書き換えることで対処を行うことが可能です。
コンパイル時のリンカの設定を変更すれば対処できそうな気もするんですが、GNU ldにはそのようなオプションが見当たりませんでした。なんかSun Solarisのldだと対処できるみたいです。
参考: http://stackoverflow.com/questions/6940384/how-to-deal-with-symbol-collisions-between-statically-linked-libraries
参考: http://stackoverflow.com/questions/393980/restricting-symbols-in-a-linux-static-library

注意: 以下の手順は失敗すると静的ライブラリ自体が完全に破損したり、実行時に深刻な問題が発生する可能性があります!! 以下の解説を見てもちんぷんかんぷんな人は適用しないことを強くおすすめいたします。この手順を適用したことによって発生したいかなる損害についても私は責任をとりかねます。


■静的ライブラリ内部のシグネチャを書き換える方法
静的ライブラリ内部のシグネチャを書き換えるには、以下のようなツールを使用します。

lipo
以前にもご紹介した、Apple純正のユニバーサルバイナリ/ユニバーサルライブラリ(fat binaryともいいます)を作ったりバラしたりするツールです。iOSのライブラリはほぼすべてがi386, armv6, armv7の三種類に対応するfat binaryになっており、基本的にApple純正でないツールはそういったfat binaryに対して歯が立たないので、まずこのツールを使って普通のライブラリに戻した上で、以下のツールを使うわけです。

nm
こいつもApple純正です。バイナリの中に入っているシグネチャの名前を一覧表示することができます。Apple純正なのでfat binaryに対しても使えて超便利です。

objdump
GNU objdumpというツールがありまして、こいつを使うとバイナリの詳細な中身を覗き見ることができます。nmよりも表示される情報が詳細です。Apple純正ではないので、以下で紹介されているようにしてMacPorts経由でインストールするのをお勧めします。
http://d.hatena.ne.jp/amachang/20080401/1207027290

objcopy
GNU objcopyです。ライブラリ内部のシグネチャを書き換える事ができるツールで、objdumpとセットでついてくるのですが、残念ながらiOS向けのバイナリに対しては全く使えません。話すと長くなるのですがバージョンを変えようがarm向けのセットをインストールしようがarでライブラリからオブジェクトファイルを取り出して試みてみようが何やっても一切無駄です。使えませんので諦めてください。
参考: http://www.mail-archive.com/bug-binutils@gnu.org/msg02829.html
参考: http://stackoverflow.com/questions/2231698/how-can-i-easily-install-arm-elf-gcc-on-os-x

objconv
で、使えないobjcopyに代わって、今回の英雄です。こいつを使って実際にライブラリ内部のシグネチャを自由自在に書き換えることができます。メンテもしっかり行われているようで動作も安定しています。以下のサイトからダウンロードできます。
サイト: http://www.agner.org/optimize/#objconv
マニュアル: http://www.agner.org/optimize/objconv-instructions.pdf
残念ながらsource配布のみしか無いため、自分でビルドしてやる必要があります。と言ってもそこそこ簡単で、以下のようにするだけです。
  • ソースコードをダウンロードする
  • zipを解凍する
  • source.zipを解凍する
  • build.shを実行する。ただしこのシェルスクリプトは1行目だけがひどくバグってるので、自分で中を見てビルドコマンドを叩いてやるようにしてください。ね?簡単でしょう?
さて、これで実際にシグネチャの書き換えを行う準備が整いました。


■実践:全く同一のライブラリのシグネチャだった場合
base64なんかでよく発生します。この場合は片方をweakシンボルにします。
weakシンボルとは: http://d.hatena.ne.jp/syohex/20100610/1276180481 がわかりやすいです。

早速やってみましょう。以下の例ではlibTest.a中のbase64_encodeシグネチャを書き換えます。
まずは以下のコマンドで対象のライブラリのfat binaryを通常のバイナリに戻して:
lipo -thin armv6 libTest.a -output libTest_armv6.a
lipo -thin armv7 libTest.a -output libTest_armv7.a
lipo -thin i386 libTest.a -output libTest_i386.a
objconvを実行:
objconv -fmacho -nw:base64_encode libTest_armv6.a
objconv -fmacho -nw:base64_encode libTest_armv7.a
objconv -fmacho -nw:base64_encode libTest_i386.a
lipoで元通りに戻します:
lipo -create libTest_i386.a libTest_armv6.a libTest_armv7.a -output libTest.a
これでビルドが通るはずです。


■実践:同じ名前の違うライブラリのシグネチャだった場合
冒頭のMD5のケースがこれです。名前が同じなのに実装がまるで違うので、weakシンボルにすると深刻なバグが発生します。こういう時は慎重に見定めた上で、使われていないと思われる方のシグネチャをhiddenシンボル(ローカルシンボル)にして、外部ファイルからリンクできないようにしてしまいます。これなら実装は存在しますがリンクされないようになるだけなので、対象のシンボルが外部から使われていないのであればこれだけでいけます。

今度はlibcrypto.a中の_MD5シグネチャを書き換えてみましょう。
こちらもまずはlipoを使って通常のバイナリに戻して:
lipo -thin armv6 libcrypto.a -output libcrypto_armv6.a
lipo -thin armv7 libcrypto.a -output libcrypto_armv7.a
lipo -thin i386 libcrypto.a -output libcrypto_i386.a
objconvを実行:
objconv -fmacho -nl:_MD5 libcrypto_armv6.a
objconv -fmacho -nl:_MD5 libcrypto_armv7.a
objconv -fmacho -nl:_MD5 libcrypto_i386.a
lipoで元通りにして完成:
lipo -create libcrypto_i386.a libcrypto_armv6.a libcrypto_armv7.a -output libcrypto.a
これで無事ビルドが通りました。


■実践:同じ名前の違うライブラリのシグネチャで、かつ外からバリバリ呼ばれていた場合