Google

2014年9月13日土曜日

XCodeで静的ライブラリのリンクエラー:後編

前回の続き。

パスが追加されてて削除されないというのは解ったので、じゃあ具体的にどうするか。
元々やりたいことは以下でした。
  • 実機でもシミュレータでもリンクエラーを出さない
  • 可能な限り切り替えの手間を掛けたくない
  • 静的ライブラリを添付し直すとかは絶対無理
なんかXCodeの操作基礎勉強みたいになっちゃってますが、勉強不足だったので仕方なし。
昔のコンパイラと違ってXCodeのようなIDE構造が戸惑わせる部分は、ライブラリへの参照について以下の2つがあるという点じゃないでしょうかね。
  • 設定にサーチパスを指定する(伝統的)
  • プロジェクトにライブラリを追加する(D&D、近代的?)
XCodeプロジェクトはコマンドでビルドでき、その際にはビルドパスを指定したり直指定したりする点を考えると、後者はあくまでGUI上の便宜で存在しているはず。
それが前回のサーチパス自動追加という設計になっていると思われ。

と、ここまで来てふと気づいたことが。

パスが指定できるならビルドターゲットをもう一個作ってしまえいいのでは?
XCodeはスキームやターゲットを複数作って管理できるし、あ、それで即終わりかも。

という事で実験開始。

 やること:
  • プロジェクトのターゲットを複製
  • それぞれ、実機用とシミュレータ用とする
  • 前回設定したサーチパスは目的に合わせたパスを設定する
  • 切り替えてビルドテスト
まず最初にターゲットを複製します。
プロジェクト設定に行き、ターゲットの名前を右クリック。

Duplicateを選択します
一覧からDuplicateを選択すると"(元のターゲット名) copy"っていうターゲットが作成されます。
例えばTestProjectという名前の場合、TestProject copyがコピー時の名称です。
これは設定から後で修正も可能ですがテストなんでそのままにしてます。

コピーされました。
コピーすると、XCodeの最上部にあるスキーム設定から選択が可能になります。
これを切り替えるだけで簡単に使い分けができるという、、、要はXCodeの基礎の基礎でした。

ここの選択にcopyが追加され、選択可能になります。
今回は元々のものをiOS実機用、コピーした方をシミュレータデバッグ用としました。
で、それぞれにライブラリのサーチパスを設定します。

これらは前回の画像です。
元々の方は実機用ライブラリのパスを(例:Debug-iphoneosとあるパス)
シミュレータはそちらのパスになります。(例:Ddebug-iphonesimuratorとあるパス)
やり方は前回のようにグリっと*.aファイルをGUIに持って行っても構いません。
しかし静的ライブラリがそのままプロジェクトのGUIに存在したり混在していれば、XCodeはそれを参照してしまうため、どちらかのビルドは失敗します。
なので、もしD&Dでパスを自動設定するなら、設定し終わった後はプロジェクト階層にある*.aファイルは消去(または参照を解除)しましょう。

9/16追記:
大事なことを書き忘れていたので追記。
要はぐりっと持って行くことで、TARGETSのライブラリがこうなる訳です。



各ターゲット別に正しい静的ライブラリがRequiredになっているので、正常にリンクが通る訳ですね。
XCodeのバージョン相違による仕様ややり方によってはここに追加されない可能性があるかも知れませんので注意を。(未確認ですが)
追記終わり:

終わったら2つのターゲットをビルド実験します。
私の環境では、元々のターゲットはiOS実機用としました。

画像がでかくてすみません。実機ターゲットビルド結果です。
成功しました。
続いてコピーしたシミュレータのターゲット。

コピー側。ターゲットがシミュレータになっているのが左上で解るかと。
こちらも成功。
他にも方法はあるかと思いますが、とりあえず目的達成。
やっとこさ、デバッグ中の手間を緩和できそうです。

 まとめ:

  • ターゲットを増やしてそれぞれのライブラリパスを設定
  • プロジェクトのGUI上には*.aファイルは存在させない
  • あとは切り替えてビルドするだけ

よくよく考えてみればXCodeの元々の機能だし、サーチパス設定は当たり前だしで、やっぱり今更な結果。
ちゃんと機能を理解しないと以外なとこで足を引っ張られるものだと反省しました。

今回はこんなとこで。

2014年9月12日金曜日

XCodeで静的ライブラリのリンクエラー:前編

今更感丸出しですが、しばらく別の仕事してるとすぐ忘れるので備忘録メモ。
似たような記事を探してはみたものの、微妙過ぎて誰も書いてないようなんで。

今回のお題:
  • XCode5で自前の静的ライブラリを作成
    • デバッグ中で、実機用とシミュレータ用に2種ビルド
  • ライブラリを使う別のプロジェクトを作成しインポート
  • 実機ビルド:OK
  • iOSシミュレータビルド:失敗←ココ
静的ライブラリ作成方法とかはあちこちで記事が上がってるので割愛。
この話は静的ライブラリプロジェクトのテストコードで良いとかという話でもなく、以下の様な目的がありました。

 目的:
  1. ある独自のミドルレイヤ機能を持ったiOSアプリが元々あった
  2. ミドルレイヤを使いまわしたくなったので静的ライブラリ化を決定
  3. 分割してビルドし、結合テスト
なので、通常とは微妙に違うケースではあるかと。
ちなみに最初のうち、静的ライブラリの適用方法はこうしてました。


静的ライブラリを使う方にとってはお馴染み、*.aファイルとヘッダの*.h群をぐりっとD&Dで持ってくる方法です。
ヘッダ諸共これで適用されるので簡単といえば簡単。
サードパーティ製の実機用ライブラリの場合は特に迷うことも無いかと。

しかしミドルとGUIの両方を一緒にデバッグする今回のケースではちょっと事情が。
ここに持ってくるライブラリファイルは、「実機ビルド版」と「シミュレータビルド版」のどっちを持ってくるか?という事。

環境に合わせたものを持ってくるのは当たり前ですが、D&Dではどっちか片方しかできません。
かといって何かにつけファイルを入れ直しとか面倒すぎてやりたくありません。
デバイス依存や特殊操作でも無い限りシミュレータでデバッグした方が楽ですし。
なんかやり方あったはずなんですが、ど忘れ。

ちなみに失敗してるシミュレータビルドで出てくる画面はこう。

よくあるケースで、i386用の関数とかクラスがないよ、というメッセージ
XCodeの操作ミスかと思い、*.aファイルを確認して差し替え直しても一緒。
うーん、XCodeの細かい挙動を忘れてしまっている、、、、、

仕方なく、まずはファイル自体の確認として実機用とシミュレータ用両方のライブラリのターゲットに問題がないかを見てみることに。
ターゲット等の情報を確認するにはxcrunを使います。

静的ライブラリの出力先は通常のアプリ同様デフォルトで/Users/(ユーザ名)/Library/Developper/XCode/DerivedData/(プロジェクト名)/...の先にあって、iphoneosとiphonesimuratorでディレクトリが分かれています。
Macのターミナルを起動して、上記のパスに移動して、その中にある*.aファイルに対して、以下のコマンドでターゲットを確認します。

 xcrun -sdk iphoneos lipo -detailed_info (ファイル名)

シミュレータ版の情報

実機版の情報

オプション多めで無駄に情報が多いですが、ちゃんと実機用はarmシリーズ、シミュレータ用はx86系なのを確認。
特に間違ってませんね、、、、、

しかし私はエラーメッセージを適当に読み流す悪い癖があるので、再度ちゃんとエラーメッセージを見てふと気づいた事が。
ライブラリをリンクする-LDの処理に、実機用とシミュレータ用の両方のライブラリパスから読み込んだ形跡が、、、、。
あれれ、そんなはずはと思ってプロジェクト設定を開き、TARGETS→Build Settings→Search Paths→Library Search Pathsを選択。

見ると、2個のパスが追加されていました。

実機ビルドしたライブラリのパス

こっちはシミュレータのパス

静的ライブラリはどちらか片方しか入れていないのに、ビルド設定では両方のパスを見てしまっているようです。
ファイル名も同じなので読み込んでしまっていたのかと。
しかし自分で設定した記憶はない、、、、

そこで、素のプロジェクトを準備して、静的ライブラリをD&Dでインポートし、ここの設定がどうなるのかを確かめて見ることに。

 実験:
  1. 素のXCodeプロジェクト作成
  2. 先程の検索パスになにも記載が無いことを確認
  3. 実機用ライブラリをプロジェクトにインポート(D&D)
  4. 実機のパスがあることを確認
  5. さらにシミュレータ用ライブラリをプロジェクトにインポート(D&D)
  6. 追加でこちらも同様にパスが追加されたことを確認
  7. 2つのパスが同時に存在、現象も一緒なのを確認

まあ、上記まではいいとして、問題はこの後。
プロジェクトから静的ライブラリを削除しても、ライブラリパス設定には何も変化がありませんでした。
同じパスの場合は更なる追加はしないようですが、削除もしないようです。

 今回の結論:
  • XCodeはライブラリインポート時にパスを追加していた
  • 自分で両方のライブラリをインポートしていたorz

うーん、隅から隅までちゃんとXCodeの使い方を読めば書いてあるのでしょうが、全然気づいていなかったのが情けない、、、。

しかしまだ根本問題解決には至ってないので次回に続きます。