📖

【Unity/iOS】Unityからのビルド時に、XCode上の設定を自動で行う

2023/12/30に公開

概要

  • Unity(UaaLとして利用)をiOSプラットフォームで書き出し、XCodeでビルドする。その際、XCode上で行ういくつかの設定を、Unityからの出力段階でC#コードから自動で行うようにしたい。

設定内容

UnityFrameworkのターゲットに対し、以下4つの変更を行いたい。

  1. Build SettingsのENABLE_BITCODEをNOに変更する
  2. Build SettingsのOther Linker Flagsに -ld64を追加する
  3. Unity-iPhone/DataフォルダのTarget MembershipをUnity-iPhoneからUnityFrameworkに変更する
  4. Unity-iPhone/Libraries/Plugins/iOS/NativeCallProxy.hのTarge MembershipのUnityFrameworkにおける設定をProjectからPublicに変更する
これらの変更を行いたい背景
  • 1: ボイスチャットライブラリでVivoxを導入したところビルド時にこの設定を行わないとエラーが出力されるようになった。
  • 2: XCode15でビルドする際に必要となった。
  • 3と4: UaaLを利用する際の必要な設定。UnityのサンプルプロジェクトのREADME参照


3の変更

4の変更

  • これらの設定はUnity-iPhone.xcodeproj/project.pbxprojファイルが管理している。よって変更による差分もこのファイルに現れる。

具体的な変更用C#コード

1と2について(Build Settingsの変更)

project.pbxprojに現れる差分について
  • ENABLE_BITCODEの差分
    • ENABLE_BITCODE = NO;が追加

  • Other Linker Flugsの差分
    • "-ld64"が追加

  • 上記の変更が4箇所で行われる。
    • Release, ReleaseForProfiling, ReleaseForRunning, Debug
  • ビルドが完了した際に呼び出されるEditorスクリプトを作成
#if UNITY_IOS

using UnityEditor;
using UnityEditor.Callbacks;
using UnityEditor.iOS.Xcode;
using System.IO;
using System.Text.RegularExpressions;

namespace MyProject.Editor
{
    public static class PostBuildProcessorForiOS
    {
        [PostProcessBuild]
        public static void OnPostProcessBuild(BuildTarget buildTarget, string path)
        {
            if (buildTarget != BuildTarget.iOS) return;
            
            // Xcodeプロジェクトファイルを読み込み
            var projectPath = PBXProject.GetPBXProjectPath(path);
            var pbxProject = new PBXProject();
            pbxProject.ReadFromFile(projectPath);
            var unityFrameworkTargetGuid = pbxProject.GetUnityFrameworkTargetGuid();

            // 各種設定の適用
            pbxProject.SetBuildProperty(unityFrameworkTargetGuid, "ENABLE_BITCODE", "NO");
            pbxProject.AddBuildProperty(unityFrameworkTargetGuid, "OTHER_LDFLAGS", "-ld64");
            
            // 保存
            File.WriteAllText(projectPath, pbxProject.WriteToString());
        }
    }
}

#endif

3について(DataフォルダのTarget Membership)

project.pbxprojに現れる差分について
  • GUIDが変更される。この差分はファイルの上の方に出力される
- 9D9DE4EA221D84E60049D9A1 /* Data in Resources */ = {isa = PBXBuildFile; fileRef = AA31BF961B55660D0013FB1B /* Data */; };
+ 871CEA6D2B3FAD7600FD76CB /* Data in Resources */ = {isa = PBXBuildFile; fileRef = AA31BF961B55660D0013FB1B /* Data */; };
  • 加えて、以下の画像の箇所も変更される。
    • 画像で言う、上側のResource(1D60588D0D05DD3D006BFB54)がUnity-iPhone、下側のResource(9D25AB9B213FB47800354C27)がUnityFrameworkのGUIDと思われる。
  • 上記のスクリプトの中の // Xcodeプロジェクトファイルを読み込み// 保存の間に以下のコードを挟むと実現可能。
var mainTargetGuid = pbxProject.GetUnityMainTargetGuid();
var dataDirGuid = pbxProject.FindFileGuidByProjectPath("Data");
pbxProject.RemoveFileFromBuild(mainTargetGuid, dataDirGuid);
pbxProject.AddFileToBuild(unityFrameworkTargetGuid, dataDirGuid);
  • ただし、ビルドした後の挙動としては問題がないのだが、XCode上で一度Dataの設定を変更し(例えばチェックボックスを外すなど)、再度戻した時に、GUIDが変更されるなどの差分が発生する。
  • 現状、そのことによる影響が判明していないため、自分自身はDataの自動変更はいったんコメントアウトしている。

4について(NativeCallProxy.hをPublicに変更)

project.pbxprojに現れる差分について

- F00448D59F32A5359C57B609 /* NativeCallProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = B813F2A4838599F922071197 /* NativeCallProxy.h */; };
+ F00448D59F32A5359C57B609 /* NativeCallProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = B813F2A4838599F922071197 /* NativeCallProxy.h */; settings = {ATTRIBUTES = (Public, ); }; };
  • 以下のコードを、「1と2について」のコードの // 保存の後に貼り付ける
  • XCodeのプラグインに、headerにpublicを追加するようなAPIを持っていないため、正規表現で置換する原始的な方法をとっている
            // NativeCallProxy.hをpublicに変更する
            var projectContent = File.ReadAllText(projectPath);
            string pattern = @"(\/\* NativeCallProxy\.h in Headers \*\/ = \{isa = PBXBuildFile; fileRef = .*?\/\* NativeCallProxy\.h \*\/; )};";
            string replacement = @"$1settings = {ATTRIBUTES = (Public, ); }; };";
            projectContent = Regex.Replace(projectContent, pattern, replacement, RegexOptions.Singleline);
            
            // 保存
            File.WriteAllText(projectPath, projectContent);
一応最後に全体のコード
#if UNITY_IOS

using UnityEditor;
using UnityEditor.Callbacks;
using UnityEditor.iOS.Xcode;
using System.IO;
using System.Text.RegularExpressions;

namespace MyProject.Editor
{
    public static class PostBuildProcessorForiOS
    {
        [PostProcessBuild]
        public static void OnPostProcessBuild(BuildTarget buildTarget, string path)
        {
            if (buildTarget != BuildTarget.iOS) return;
            
            // Xcodeプロジェクトファイルを読み込み
            var projectPath = PBXProject.GetPBXProjectPath(path);
            var pbxProject = new PBXProject();
            pbxProject.ReadFromFile(projectPath);
            var unityFrameworkTargetGuid = pbxProject.GetUnityFrameworkTargetGuid();

            // 各種設定の適用
            pbxProject.SetBuildProperty(unityFrameworkTargetGuid, "ENABLE_BITCODE", "NO");
            pbxProject.AddBuildProperty(unityFrameworkTargetGuid, "OTHER_LDFLAGS", "-ld64");
            
            
            // NOTE: 問題なく起動するが、XCode上でDataのTarget Membershipを一度他に変えて戻すと、GUID等に差分が生じる。
            // 影響が不明なので、いったん自動でのTarget Membership変更は行わないこととする
            // DataディレクトリをUnityFrameworkに変更
            // var mainTargetGuid = pbxProject.GetUnityMainTargetGuid();
            // var dataDirGuid = pbxProject.FindFileGuidByProjectPath("Data");
            // pbxProject.RemoveFileFromBuild(mainTargetGuid, dataDirGuid);
            // pbxProject.AddFileToBuild(unityFrameworkTargetGuid, dataDirGuid);
            
            // 保存
            File.WriteAllText(projectPath, pbxProject.WriteToString());
            
            // NativeCallProxy.hをpublicに変更する
            var projectContent = File.ReadAllText(projectPath);
            string pattern = @"(\/\* NativeCallProxy\.h in Headers \*\/ = \{isa = PBXBuildFile; fileRef = .*?\/\* NativeCallProxy\.h \*\/; )};";
            string replacement = @"$1settings = {ATTRIBUTES = (Public, ); }; };";
            projectContent = Regex.Replace(projectContent, pattern, replacement, RegexOptions.Singleline);
            
            // 保存
            File.WriteAllText(projectPath, projectContent);
        }
    }
}

#endif

参考

https://gist.github.com/ina-amagami/bc0556ae6e2ce0e0cb9923977e962d76
https://note.com/08_14/n/n2467e9069cf6
https://zenn.dev/mtfum/scraps/244bae5eda1b0ffa11b3

Discussion