Unity Cloud Buildでエラーになった際の解決法 vol3

はじめに

今回の記事で三回目になりますが、Unity Cloud Buildで盛大に詰まった(1週間程このせいで開発が止まった)際の解決方法を記します。

本記事は、UnityにFirebaseとTwitter Kit for Unity, Admobを導入した際に出くわした内容ですので、記事で出て来るエラー内容はこれに付随しています。

が、対応方法を見ていただくと分かる通り、この例以外でも役に立つ方法ですので、同様のエラーに苛まれた際のお役に立てればと思います。

エラーとその解決方法

ファイル名に「error」が付いているとエラーに見える

事象

具体的には、以下のようなログです。

3: [Unity] *** {GUID} replaces {GUID} at path assets/plugins/ios/twitterkit.framework/twitterkitresources.bundle/twttr-icn-tweet-place-holder-photo-error@2x.png 
4: [Unity] *** {GUID} replaces {GUID} at path assets/plugins/ios/twitterkit.framework/twitterkitresources.bundle/twttr-icn-tweet-place-holder-photo-error@3x.png 
5: [Unity] *** {GUID} replaces {GUID} at path assets/plugins/ios/twitterkitresources.bundle/twttr-icn-tweet-place-holder-photo-error@2x.png 
6: [Unity] *** {GUID} replaces {GUID} at path assets/plugins/ios/twitterkitresources.bundle/twttr-icn-tweet-place-holder-photo-error@3x.png 

実際に画面上だと、本当にエラーのように見えます。

f:id:project-unknown:20170907012001p:plain

原因と解決策

これはUnity Cloud Buildが失敗しているのと何ら関係ありません
ログのサマリが「error」という文字に反応して色味を付けているだけです。
私自身、これを疑い該当ファイルを消してbuildしてみたりしたのですが、上述通りbuildが失敗するのと関係ないので勿論コケます。

これについては、forumでも中の人が関係ないと断言しています

なので、解決策は何もする必要が無いです。

Google産SDKで必要な設定ファイルが無い

事象
 [Unity] Player export failed. Reason: No GoogleService-Info.plist files found in your project.
原因と解決策

これはCloud Buildじゃなくて実機テストでも発生するものです。
タイトル通り、必要な設定ファイルがありません。
上記エラーが出た際は、Firebaseで発行するGoogleService-Info.plistファイルをUnityに突っ込み忘れたのが原因です。

なので、解決策は、FirebaseやAdMob等Google産SDKの場合の設定ファイルをちゃんとUnityに入れる事です。

shader snippetでエラー

事象
[Unity] Shader compiler: internal error compiling shader snippet type=0 platform=5: Protocol error - failed to read correct magic number
原因と解決策

OnGUI等、古いAPIを使っていると発生し易いみたいです。
これについては、再度Buildを掛けると直ります。
(何度かお目にかかっているのですが、直接的な発生原因は不明、OnGUIを使った実装を避けるくらいですかね

Twitter KitでLogErrorが出ている。

事象
[Unity] TwitterKit.Internal.Utils:LogError(String) (at Assets/Twitter/Scripts/Utils.cs:33)
原因と解決策

ポカミスが原因

私が出くわしたTeitter Kit for Unity限定かもしれませんが、サマリログだとこの部分しかログが落ちていません。
この場合は、実際にSDKの中を読み込んでこのログが吐かれるきっかけを見たほうが早いです。

以下VSCodeの該当箇所を見た結果、(参照されている所が直ぐ追えるのが本当に便利…)

f:id:project-unknown:20170907013414p:plain

ここで分かるのは、Twitter Kit for Unityの設定にAccessTokenを仕込んでいない事が伺えます。
実際にProjectを作り直した直後で、たしかに仕込んで居なかったので、AccessToken等を埋め込むことで、このエラーは出なくなりました。

Bundle IDでエラー

事象
[Unity] Player export failed. Reason: Project Bundle ID {Bundle ID} does not match any bundle IDs in your GoogleServices-Info.plist files
原因と解決策

ポカミスが原因

これは、Firebaseで設定した際のBundile ID(アプリのID)と、Unity Cloud Buildに設定したBundle IDに差異がある時に発生するエラーです。
IDを揃えることで発生しなくなります。 (ポカミス多い…)

iOSのバージョンがサポートしていない

事象
73:        [xcode] /BUILD_PATH/{BUILD_PATH}/temp.44tpIu/Frameworks/Plugins/iOS/TwitterCore.framework/Headers/TwitterCore.h:24:2: error: "TwitterCore doesn't support iOS 7.x and lower. Please, change your minimum deployment target to iOS 8.0"
74:        [xcode] #error "TwitterCore doesn't support iOS 7.x and lower. Please, change your minimum deployment target to iOS 8.0"
75:        [xcode] /BUILD_PATH/{BUILD_PATH}/temp.44tpIu/Frameworks/Plugins/iOS/TwitterKit.framework/Headers/TwitterKit.h:15:2: error: "TwitterKit doesn't support iOS 8.x and lower. Please, change your minimum deployment target to iOS 9.0"
76:        [xcode] #error "TwitterKit doesn't support iOS 8.x and lower. Please, change your minimum deployment target to iOS 9.0"
原因と解決策

エラーの内容通り、TwitterKitがiOS9以上のAPIを使っているため、UnityのProject設定をiOS9以上にしないといけません。
このエラーを引き起こした際に、UnityのProject設定のiOS SDKの最低Versionを7.0にしていたのが原因でした。
こちら、最低Versionを9.0にすることでかいけつ出来ます。

linker command faild

事象
[xcode] clang: error: linker command failed with exit code 1 (use -v to see invocation)
原因と解決策

iOSでアプリ開発している人ならお馴染みで、たまにドツボに入るエラーです。
この手のエラーは利用しているライブラリのPathが足りていなかったりする時に発生し易いのですが、確実にこれじゃないのが辛い所です。
ましてや、Unity Cliud Buildは手元のXCodeじゃないので尚更調査し辛い所があります。

この手のエラーは汎用的なエラーであるために、このエラーを見ただけだと何が原因か推測し辛いので、Buildログを見て、何処らへんでエラーが発生したのかを見てアタリを付けていきます。

サマリログの一番したに詳細のログを見るボタンがあるので、これをクリックして中身を見てみます。

f:id:project-unknown:20170907013201p:plain

遷移先の詳細ログで、該当する箇所を検索します。

22370:        [xcode] ld: warning: directory not found for option '-L /BUILD_PATH/Library/Developer/Xcode/DerivedData/Unity-iPhone-gnshgjhnidtrmtbkltwpchzfnjbj/Build/Intermediates/ArchiveIntermediates/Unity-iPhone/BuildProductsPath/Release-iphoneos/GTMSessionFetcher /BUILD_PATH/Library/Developer/Xcode/DerivedData/Unity-iPhone-gnshgjhnidtrmtbkltwpchzfnjbj/Build/Intermediates/ArchiveIntermediates/Unity-iPhone/BuildProductsPath/Release-iphoneos/GoogleToolboxForMac /BUILD_PATH/Library/Developer/Xcode/DerivedData/Unity-iPhone-gnshgjhnidtrmtbkltwpchzfnjbj/Build/Intermediates/ArchiveIntermediates/Unity-iPhone/BuildProductsPath/Release-iphoneos/nanopb'
22371:        [xcode] ld: warning: directory not found for option '-F /BUILD_PATH/{BUILD_PATH}/temp.pDVpf7/Pods/FirebaseAnalytics/Frameworks /BUILD_PATH/{BUILD_PATH}/temp.pDVpf7/Pods/FirebaseAuth/Frameworks /BUILD_PATH/{BUILD_PATH}/temp.pDVpf7/Pods/FirebaseCore/Frameworks /BUILD_PATH/{BUILD_PATH}/temp.pDVpf7/Pods/FirebaseInstanceID/Frameworks /BUILD_PATH/{BUILD_PATH}/temp.pDVpf7/Pods/Google-Mobile-Ads-SDK/Frameworks/frameworks'
22372:        [xcode] ld: library not found for -lz}
22373:        [xcode] clang: error: linker command failed with exit code 1 (use -v to see invocation)

どうやら、Firebase SDKがCocoaPodsを経由して「Firebase/Core」等を入れようとしたり、AdMobが「Google-Mobile-Ads-SDK」をCocoaPodsを用いて導入しようとしているのですが、それがうまく言っていないのか、FirebaseやAdMobが必要としているライブラリのリンクが貼れなくてエラーになっているみたいです。

ここでCocoaPodsについて軽く説明すると、
CocoaPodsはiOSエンジニアだと、結構当たり前に使うツールで、簡単に言うとOSSのパッケージ管理ツールです。
通常だと、OSSなどはGitや配布されているファイルを自分でDLしてXCodeに突っ込む等の作業が必要なのですが、CocoaPodsはそこら辺のOSSのinstallをサポートしてくれます。
また、OSSのVersion管理もしてくれて、OSSのVersionが上がったらそのinstall支援もしてくれる優れものですし、そのOSSが動くために必要な為の他のOSSも、ツールの作成者が登録してくれていれば自動でinstallしてくれます。私とかは無いと生きていけません。
FirebaseやAdMobもその恩恵にあやかってCocoaPodsを採用しているのでしょうね。

さて、本題に戻します。
ココらへんを見る限り、現在Unity Cloud BuildはCocoaPods経由でライブラリは入れられないっぽいです。
AdMobやFirebaseのSDKをCocoaPods経由でも入れられるようにupdateするみたいですね。
なので、現状SDKがCocoaPodsからライブラリを入れようとする所を止めて、手動でライブラリを入れる必要があります。

まず、CocoaPodsを使おうとしているコードを特定します。
私の場合は、FirebaseとAdMobが該当するので、以下、ファイルと修正内容を記します。基本は該当する所全てをコメントアウトします。(コードは編集後のコードを載せてます)

AdMobDependencies.cs

#elif UNITY_IOS
        // Type iosResolver = Google.VersionHandler.FindClass(
        //     "Google.IOSResolver", "Google.IOSResolver");
        // if (iosResolver == null) {
        //     return;
        // }
        // Google.VersionHandler.InvokeStaticMethod(
        //     iosResolver, "AddPod",
        //     new object[] { "Google-Mobile-Ads-SDK" },
        //     namedArgs: new Dictionary<string, object>() {
        //         { "version", "7.13+" }
        //     });
#endif  // UNITY_IOS

AppDeps.cs

#elif UNITY_IOS
        // Type iosResolver = Google.VersionHandler.FindClass(
        //     "Google.IOSResolver", "Google.IOSResolver");
        // if (iosResolver == null) {
        //     return;
        // }
        // Google.VersionHandler.InvokeStaticMethod(
        //     iosResolver, "AddPod",
        //     new object[] { "Firebase/Core" }, 
        //     new Dictionary<string, object>() { 
        //         { "version", "4.1.0" },
        //         { "minTargetSdk", null },
        //         { "sources", null }
        //     });
#endif

AuthDeps.cs

#elif UNITY_IOS
        // Type iosResolver = Google.VersionHandler.FindClass(
        //     "Google.IOSResolver", "Google.IOSResolver");
        // if (iosResolver == null) {
        //     return;
        // }
        // Google.VersionHandler.InvokeStaticMethod(
        //     iosResolver, "AddPod",
        //     new object[] { "Firebase/Auth" }, 
        //     new Dictionary<string, object>() { 
        //         { "version", "4.1.0" },
        //         { "minTargetSdk", null },
        //         { "sources", null }
        //     });
#endif

次に必要となるライブラリを入れます。
何が必要になってくるのかは、コードから判断します。
例えば、AppDepsを例に取ると、

new object[] { "Firebase/Core" }, 

この部分がCocoaPodsから入れたいライブラリ名となっています。
上記などを参考に必要なファイルを入れます。
この例の場合は、以下からライブラリを入手します。

Firebaseのセットアップのサイトの、CocoaPodsを使用せずに結合する項目から、「フレームワーク SDK の zip ファイルをダウンロードします」のリンクをクリックして、zipファイルをダウンロード。

f:id:project-unknown:20170907020639p:plain

ダウンロードしたzipファイルを解凍し、必要なFrameworkを探し出します。
まず上記のFirebase/Coreに当たるものを探すと、「Analytics / FirebaseCore.framework」が見つかります

f:id:project-unknown:20170907020841p:plain

このFirebaseCore.frameworkを、Unityの「Plugin/iOS」以下に突っ込みます。

また、今回私の場合、Firebaseの以下の機能を使おうとしています。

  • Auth(Authentication)
  • Database(RealtimeDatabase)

なので、このライブラリも探します。

f:id:project-unknown:20170907021029p:plain

f:id:project-unknown:20170907021043p:plain

上記が見つかりました。
キャプチャに出ている、以下のライブラリは全てUnityの「Plugin/iOS」以下に突っ込んでください。

  • FirebaseAuth.framework
  • GTMSessionFetcher.framework
  • FirebaseDatabase.framework
  • leveldb-library.framework

コメントアウトしたコードには、Authとかしか無いじゃん!とお思いかもしれませんが、上述の通り、CocoaPodsはinstallしようとするライブラリが必要なライブラリを勝手にinstallしてくれます。なので、FirebaseAuth.frameworkをCocoaPods経由でinstallしようとしたら、GTMSessionFetche.frameworkはCocoaPods側で勝手にinstallしてくれます。
今回は手動でライブラリを入れようとしているので、上記4種類のライブラリを入れる必要があります。
(Firebaseの場合は、Core以外、どういつディレクトリ内のライブラリが依存関係にあるように配置されているので非常にわかりやすくなっています。)

後は、AdMobのライブラリを突っ込む作業です。

FirebaseのライブラリからAdMobを入れた場合はzipファイルの中のAdMobフォルダ内に該当するライブラリがあるのでそれを突っ込めば良いのですが、私の場合は別途AdMobのサイトからUnity用SDKを導入しているので、念のためそちらのファイルからライブラリを取得します。
落としてきたzipに「GoogleMobileAdsSDK.framework」があるので、それをこれまでと同様に「Plugin/iOS/」以下に突っ込みます。

最終的に私のPlugin/iOS以下は下のキャプチャの様になりました。

f:id:project-unknown:20170907022125p:plain

まだここで終わりではありません

GoogleなどのSDKは、Objective-Cの「@import」を利用しています。
これについて詳細に記載すると、別途記事を起こすだけの分量になるのでかいつまんで説明すると、C#等でコードを書く時に

using UnityEngine;

とか書くと思うのですが、毎回これを書くのが冗長な為、「@import」で設定されたフレームワークは自動読み込みされるようにでき、このお陰で毎回必要なフレームワークをプロジェクトの設定でimportしなくても済む機能です。

この「@import」機能ですが、古いXCodeだとデフォルト設定で利用できたのですが、、最新のXCodeだとデフォルトではOFFになっています。

これまで投入してきたライブラリは「@import」の機能を利用しているため、これを使えるようにしてあげる必要があります。

やりかたは、Assets/Editor以下のどこでも構いませんので、「XcodePostProcessBuild.cs」のファイルを使って以下のコードを記載してください。

using UnityEditor;
using UnityEditor.Callbacks;
using UnityEditor.iOS.Xcode;

public static class XcodePostProcessBuild {
    [PostProcessBuild]
    public static void OnPostProcessBuild(BuildTarget target, string path) {
        if (target != BuildTarget.iOS) {
            return;
        }

        var xcodeProject = new PBXProject();
        xcodeProject.ReadFromFile(PBXProject.GetPBXProjectPath(path));

        var projectGuid = xcodeProject.TargetGuidByName(PBXProject.GetUnityTargetName());
        xcodeProject.SetBuildProperty(projectGuid, "CLANG_ENABLE_MODULES", "YES");

        xcodeProject.WriteToFile(PBXProject.GetPBXProjectPath(path));
    }
}

これで、先程記載した@import機能が使えるようになります。

長くなったので纏めると

  • この手のエラーはログの詳細を読み込んで、前後のエラーでアタリを付ける
  • 今回の場合の対応は
    • CocoaPodsがやってくれる作業を手動でやる
      • CocoaPods経由でinstallしようとしているライブラリのコードを消す
      • 必要なライブラリを自分で入れる
      • XCodeの設定を自前で設定する

さいごに

Unity Cloud Buildは非常に便利な反面、一度詰まってしまうと凄い時間がかかります。
ただ、それを鑑みても恩恵がでかすぎるので是非とも利用していきたい所。
私と同様のエラーで躓いてしまうのは非常にもったいないので、同様の所で躓いてしまった場合はその助けになれば幸いです。

ちなみに、私は最終的にUnity Cloud Buildのbuildを通すまでに冒頭で申し上げた通り1週間位かかっています。

以下実行履歴 (途中Projectを作り直しているので、20回以上はbuild挑戦しています。無料プランなので1buildが終わったら1時間のクールタイムが発生するので、単純計算でも20時間はかかってます(;´Д`))

f:id:project-unknown:20170907023645p:plain


番外編

実機でTwitterのSession周りでエラーが起きている

現象
(Filename: /Users/builduser/buildslave/unity/build/artifacts/generated/common/runtime/DebugBindings.gen.cpp Line: 51)

NullReferenceException: A null value was found where an object instance was required.
  at TwitterKit.Unity.Twitter.get_Session () [0x00000] in <filename unknown>:0 
 
(Filename: currently not available on il2cpp Line: -1)
原因と解決策

Twitter InitのScriptをアタッチしていない。
これ本当に忘れやすいので注意です。

過去記事

www.project-unknown.jp

www.project-unknown.jp

www.project-unknown.jp