Unity Xcode アーカイブ、 ipa作成自動化

UnityでXcodeからipa出力までの各設定の自動化をやってみた。

できた。

※ 勘で書いた部分もあるので注意。

参考

XcodeAPIの使い方
CIなどを使用するときに必要な値を確認する方法
UnityプロジェクトをJenkinsを使用してXCode8対応した
Unityでbashを利用する   xcodebuildでipa作成 -exportOptionsPlist対応版

流れ

  • 1 exportOptions.plist作成、DEVELOPMENT_TEAMの10桁のコードを確認。
  • 2 下記コードのvar ipa_plist = "path/to/exportOptions.plist";の部分を作成したplistのパスに変更。 pbx.SetBuildProperty(targetGuid, "DEVELOPMENT_TEAM", "xxxxxxxxxx");のxの部分に10桁のコードを設定。
  • 3 iOSビルド後に、OnPostprocessBuildが呼ばれる
  • 4 ProjectSettingでXcodeのプロジェクトを書き換える
  • 5 PlistSettingでplistを書き換える
  • 6 AutomationCreatorで、automation.commandという自動化用bashスクリプトを作成し、chmod -xで実行権限を付与。
  • 7 生成されたプロジェクト内のautomation.commandをダブルクリックで実行
  • 8 hoge.xcarchiveとhoge.ipaが作成される

XcodeProjectUpdater.cs

#if UNITY_IOS
using UnityEngine;
using UnityEditor;
using UnityEditor.Callbacks;
using UnityEditor.iOS.Xcode;
using System.IO;
using System.Collections;
using System.Diagnostics;

// http://kan-kikuchi.hatenablog.com/entry/XcodeAPI
public class XcodeProjectUpdater
{
    [PostProcessBuild(100)] // layer 100 小さい方が先に実行
    static void OnPostprocessBuild(BuildTarget buildTarget, string path)
    {
        if (buildTarget != BuildTarget.iOS) return;
        UnityEngine.Debug.Log ("xcode project update onpostprocess!");

        ProjectSetting (path);
        PlistSetting (path);

        AutomationCreator (path, "hoge");
    }

    static void AutomationCreator(string path, string output_name)
    {

        var cmd_cd = "cd `dirname $0`";

        var camera_capture_path = Path.Combine (path, "Classes/Unity/CameraCapture.mm");
        var cmd_camera_reset = string.Format ("sed -i -e 's/!UNITY_TVOS/0/g' \"{0}\"", camera_capture_path);

        var xcodeproj_path = Path.Combine (path, "Unity-iPhone.xcodeproj");
        output_name = Path.Combine (path, output_name);
        var cmd_archive = string.Format("xcodebuild -project \"{0}\" -scheme \"Unity-iPhone\" archive -archivePath \"{1}\"",
            xcodeproj_path,
            output_name
        );

        // http://qiita.com/roworks/items/7ef12acabf9679561d84
        var ipa_plist = "path/to/exportOptions.plist";
        var cmd_ipa = string.Format("xcodebuild -exportArchive -archivePath \"{0}.xcarchive\" -exportPath \"{0}.ipa\" -exportOptionsPlist \"{1}\"",
            output_name,
            ipa_plist
        );

        var output_path = Path.Combine (path, "automation.command");
        using(var sw = new StreamWriter(output_path))
        {
            sw.WriteLine (cmd_cd);
            sw.WriteLine (cmd_camera_reset);
            sw.WriteLine (cmd_archive);
            sw.WriteLine (cmd_ipa);
        }

        BashProcess ("chmod +x " + output_path);
    }

    static void ProjectSetting(string path)
    {
        var pbxProjPath = PBXProject.GetPBXProjectPath (path);
        var pbx = new PBXProject ();
        pbx.ReadFromString (File.ReadAllText (pbxProjPath));

        var targetGuid = pbx.TargetGuidByName (PBXProject.GetUnityTargetName ());

        pbx.SetBuildProperty (targetGuid, "ENABLE_BITCODE", "NO");
        pbx.SetBuildProperty (targetGuid, "ARCHS", "arm64");
        pbx.SetBuildProperty (targetGuid, "VALID_ARCHS", "arm64");
        pbx.SetBuildProperty (targetGuid, "IPHONEOS_DEPLOYMENT_TARGET", "9.0");

        // http://qiita.com/Akira_Kido_N/items/25ffd68cade64ecade2e
        // http://qiita.com/altemina/items/c4747f26615792495a97
        pbx.SetBuildProperty(targetGuid, "DEVELOPMENT_TEAM", "xxxxxxxxxx"); // 10桁のコード

        File.WriteAllText (pbxProjPath, pbx.WriteToString ());
    }
    static void PlistSetting(string path)
    {
        var pListPath = Path.Combine (path, "Info.plist");
        var plist = new PlistDocument ();
        plist.ReadFromFile (pListPath);

        #region ローカライズ 日本語
        plist.root.SetString ("CFBundleDevelopmentRegion", "ja_JP");
        // plist.root.CreateDict ("CFBundleLocalizations");
        var locallization = plist.root.CreateArray ("CFBundleLocalizations");
        locallization.AddString ("ja");
        #endregion

        plist.WriteToFile (pListPath);
    }

    static bool isWorking = false;
    // http://smartgames.hatenablog.com/entry/2016/06/21/230714
    static void BashProcess(string cmd)
    {
        if (isWorking) { 
            UnityEngine.Debug.Log ("working.");
            return;
        }
        isWorking = true;

        var p = new Process ();
        p.StartInfo.FileName = "/bin/bash";
        p.StartInfo.Arguments = "-c \" " + cmd + " \"";
        p.StartInfo.UseShellExecute = false;
        p.StartInfo.RedirectStandardOutput = true;
        p.Start ();

        var output = p.StandardOutput.ReadToEnd ();
        p.WaitForExit ();
        p.Close ();

        isWorking = false;
        UnityEngine.Debug.Log (output);
    }
}

#endif // UNITY_IOS