UnityのiOSビルドをコマンドラインから行う (Mac用)

[前回](https://qiita.com/beckyJPN/items/e61834c4b92112bc872d)はAndroidのビルドを行いましたが、今回はiOSビルドです。

コマンドラインでビルドを行った後、Archive処理を進め、
最終的に`ipaファイル`を生成するのが目的となります。

Debugビルド & プロビジョニングを自分で設定するパターンで進めていきます。

Unityだと、Xcodeのプロジェクトビルドまでしか行われず、
Xcodeのビルドは別途自分で書かないといけないみたいので、
Anrdroidと比べて、結構手数が多くなります。

# 環境
Mac : 10.14.5 (Mojave)
Unity : 2018.3.0f2
Xcode : 10.2.1

# unity側でのビルド手続き

Androidでは、直接Unity側に成果物のパスを指定してましたが
iOSの場合、Xcodeビルド時に、成果物のパスが必要になる為、
コマンドラインからUnityにパスを受け渡せる様にしています。

`GetParameterFrom`関数にargの引数を指定して続くパラメータを取得しています。
（最近`swift`での開発が多かったので、関数名の付け方とかがちょっとswift脳になってます・・・

stringのOptionalほしい・・ 
（C# 8.0からクラスのオプショナルも対応するらしいです

```C#:Builder.cs
using UnityEditor;
using UnityEngine;
using System.Collections.Generic;
using System.Linq;

public static class Builder
{
    public static void iOSBuild()
    {

        var outputDirKey = "-output-dir";

        var paths = GetBuildScenePaths();
        var outputDir = GetParameterFrom(key: outputDirKey);
        var buildTarget = BuildTarget.iOS;
        var buildOptions = BuildOptions.Development;

        Debug.Assert(!string.IsNullOrEmpty(outputDir), $"'{outputDirKey}'の取得に失敗しました");

        var buildReport = BuildPipeline.BuildPlayer(
            paths.ToArray(),
            outputDir,
            buildTarget,
            buildOptions
        );

        var summary = buildReport.summary;

        if (summary.result == UnityEditor.Build.Reporting.BuildResult.Succeeded)
        {
            Debug.Log("Success");
        }
        else
        {
            Debug.LogError("Error");
        }
    }

    private static IEnumerable<string> GetBuildScenePaths()
    {
        var scenes = new List<EditorBuildSettingsScene>(EditorBuildSettings.scenes);
        return scenes
            .Where((arg) => arg.enabled)
            .Select((arg) => arg.path);
    }

    private static string GetParameterFrom(string key)
    {
        var args = System.Environment.GetCommandLineArgs();
        var index = args.ToList().FindIndex((arg) => arg == key);
        var paramIndex = index + 1;

        if (index < 0 || args.Count() <= paramIndex)
        {
            return null;
        }

        return args[paramIndex];
    }
}
```

# シェルスクリプト (Unityビルド)
Xcodeビルドもあるため、シェルスクリプトが長めになるので、
UnityビルドとXcodeビルドでシェルスクリプトファイルを分割しています。

`unityiOSBuild.sh`、`xcodeBuild.sh`両ファイルとも、
Unityのプロジェクトフォルダ直下に作成してください。

`-output-dir`でXcodeプロジェクトの出力先を指定してます。

また unityのビルドが完了したら引き続き`PROJECT_DIR=$PROJECT_DIR sh xcodeBuild.sh`で
Xcodeのビルドに処理を引き継いでいます。

ちなみに、変数は明示的に指定しないと引き継げないみたいです。
（なんかもっとスマートなやり方がありそうな気もしますが・・・


```bash:unityiOSBuild.sh
#!/bin/bash

UNITY_APP_PATH="/Applications/Unity/Unity.app/Contents/MacOS/Unity"
UNITY_PROJECT_PATH="./"
UNITY_BUILDE_NAME="Builder.iOSBuild"
UNITY_LOG_PATH="./build/iOS/build.log"
PROJECT_DIR="./build/iOS"

$UNITY_APP_PATH -batchmode \
    -quit \
    -projectPath $UNITY_PROJECT_PATH \
    -executeMethod $UNITY_BUILDE_NAME \
    -logfile $UNITY_LOG_PATH \
    -output-dir $PROJECT_DIR

if [ $? -eq 1 ]; then
    echo "error!! check logfile: ${UNITY_LOG_PATH}"
    exit 1
fi

echo "unity build success!!"
PROJECT_DIR=$PROJECT_DIR sh xcodeBuild.sh
```

# シェルスクリプト (Xcodeビルド)

xcodebuildのシェルスクリプトです。

最終的にほしいのはipaファイル

一旦Archiveファイル(`xcarchive`)を生成し、それを元に
ipaファイルを生成します。

ただし、ビルドするために`provisioning profile`の`UUID`が必要となり、
ipaファイルの作成に`ExportOptions.plist`が必要となります。

## provisioning profile のUUID
こちらはプロビジョニングプロファイルのファイルをテキストエディタ等で開くことで確認できます。

中身はXml形式で記述されているので keyがUUIDの部分のstringから値を確認して
`PROVISIONING_PROFILE`に指定してください。

```xml:XXX.mobileprovision
	<key>UUID</key>
	<string>XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX</string>
```

## ExportOptions.plist
このplistはipaファイルをビルドする際の設定情報を記述するファイルになります。
正直記述するのが面倒なので、Xcodeのプロジェクトファイルができたら、
一旦XcodeからArchiveビルドを行ってipaファイルを吐き出しましょう。

そうすると、ipaファイルが生成されたフォルダ内にExportOptions.plistが生成されてるので
それをUnityのプロジェクトフォルダ直下にコピペって来るだけでOKです。

## スクリプト

以下、xcodeBuildのビルド手続きです。
`PROJECT_DIR`は`unityiOSBuild.sh`より引き継いでいます。

```bash:xcodeBuild.sh
#!/bin/bash

SCHEME="Unity-iPhone"
PROJECT_PATH="${PROJECT_DIR}/${SCHEME}.xcodeproj"
ARCHIVE_FILE="${SCHEME}.xcarchive"
ARCHIVE_DIR="${PROJECT_DIR}/archive"
ARCHIVE_PATH="${ARCHIVE_DIR}/${ARCHIVE_FILE}"
IPA_DIR="${ARCHIVE_DIR}/output_ipa"
EXPORT_OPTIONS_PLIST="ExportOptions.plist"
PROVISIONING_PROFILE="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"

mkdir -p $ARCHIVE_PATH

# ARCHIVE
xcodebuild -project $PROJECT_PATH \
    -scheme $SCHEME \
    archive -archivePath $ARCHIVE_PATH \
    PROVISIONING_PROFILE=$PROVISIONING_PROFILE

# ipaファイルの作成
xcodebuild -exportArchive -archivePath $ARCHIVE_PATH \
    -exportPath $IPA_DIR \
    -exportOptionsPlist $EXPORT_OPTIONS_PLIST \
    PROVISIONING_PROFILE=$PROVISIONING_PROFILE
```

# ターミナルから実行

以下コマンドをターミナルから実行します。
Unityが起動した状態で行うと、うまくビルドができないので、
Unityは終了させた状態で実行します。

```bash:ターミナルから実行
sh unityiOSBuild.sh
```

実行後正常に完了したら、ipaファイルがプロジェクトフォルダは以下の
`./build/iOS/archive/output_ipa`に生成されているはずです。

ipaファイルをXcodeから実機にインストールして動作を確認しましょう。

# github

https://github.com/becky3/unity_command_build/tree/commandline_iOS

# 補足

ちなみに、`ExportOptions.plist`の中身はこんな感じでした。 
(認証情報が含まれるので一部 !!! で囲んで伏せ字にしてます

```xml:ExportOptions.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>compileBitcode</key>
	<false/>
	<key>method</key>
	<string>ad-hoc</string>
	<key>provisioningProfiles</key>
	<dict>
		<key>!!! Bundle Identifer !!!</key>
		<string>!!! code signing identifier !!!</string>
	</dict>
	<key>signingCertificate</key>
	<string>iPhone Distribution</string>
	<key>signingStyle</key>
	<string>manual</string>
	<key>stripSwiftSymbols</key>
	<true/>
	<key>teamID</key>
	<string>!!! teamID !!!</string>
	<key>thinning</key>
	<string>&lt;none&gt;</string>
</dict>
</plist>