關於 web service, unity, blogger 等軟體工程筆記

使用 Batchmode (CLI) 建置 Unity Android 專案,從環境設置到建置成功

Edit icon 沒有留言
Android & Unity

紀錄最近在自家新機器上安裝 Unity 建置 Android 環境,以及採了兩個莫名奇妙的坑,最後能用 CLI (Command-line interface) 呼叫 Unity 將專案發佈成 Android APK,讓整套建置工作能搬到 Jenkins 這套建置服務上。

環境安裝 Android SDK + NDK + JDK

本次採用 Unity5.5.2,作業系統為 Windows7。

  • 安裝 Java SDK (JDK),從 Java 官方下載安裝,此時的 JDK 版本是 1.8.0。

  • 安裝 Android SDKs,直接下載 Android Studio 這套 Google 推出的強大 IDE,也使用它來管理 Android SDKs。

    在這個時間點安裝完 Android Studio,預設 SDK platforms 是 Andioid 7.1.1 (Nougat),SDK tools 為 25.3.1。個人喜好,重新設定 SDK 位置到 C:\android\sdk。

    Android SDK Manager - Platforms

    SDK Platforms

    Android SDK Manager - Tools

    SDK Tools

  • 安裝 Android NDK,由於專案 Script backend 採用 IL2CPP,所以一定得安裝,Unity5.5.2 要求 Android NDK r10e,上官網選擇舊版本下載安裝。

  • 設定 Unity 環境,Menu > Edit > Preferences > External Tools,設定以上三個 SDKs 位置。

    Preferences

    Preferences,設定 SDKs 路徑

測試環境建置,發生建置錯誤 Invalid command android

當環境設定好後,先使用 Unity Editor 進行一次的建置,來確認環境是否安裝正確。但建置失敗並且丟出以下錯誤:

Error building Player: CommandInvokationFailure: Unable to list target platforms. Please make sure the android sdk path is correct. See the Console for more details.
C:/Program Files/Java/jdk1.8.0_121\bin\java.exe -Xmx2048M -Dcom.android.sdkmanager.toolsdir="C:/android/sdk\tools" -Dfile.encoding=UTF8 -jar "C:\Unity\Unity5.5.2f1\Editor\Data\PlaybackEngines\AndroidPlayer/Tools\sdktools.jar" -

stderr[
   Error:Invalid command android
]
stdout[
]
exit code: 64

求 Google 找到解答,原來是找不到適合的 Android SDK Tools,所以發生錯誤 Invalid command android。

因此手動重新安裝 Android SDK Tools r25.2.5,將原先 SDK 中的 Tools 資料夾砍掉 (或是改名),從 Google 這個位置下載 r25.2.5,解壓縮將其 Tools 資料夾放回原本 SDK 的位置。

這相當於重新安裝 SDK Tools,完成此步驟後,便可成功在 Unity Editor 中完成 Android 專案的建置。

Batchmode 建置,建置錯誤 Unable to locate Android SDK

由於我們要丟到 Jenkins 上管理建置,由 Jenkins 更新專案程式碼並自動呼叫 Unity 建置專案,因此我們得為專案寫 Unity Build pipeline 程序,使得 Jenkins 能透過 CLI 來建置專案,一個簡單的透過 C# 呼叫建置範例:

using System.Linq;
using System.IO;
using UnityEngine;
using UnityEditor;
using UnityEditorInternal;

public static class Builder
{
   /* 不使用 Environment.GetEnvironmentVariable,
   主要是因為路徑若包含有空白,其 Parser 會有些問題,
   就算加上雙引號也是一樣(但也有可能是我們搞錯格式),
   因此採用 Regex 自己抓 command args
   */
   public static string GetOutputPath(string defaultPath = null)
   {
      var regex = new Regex("(^| )-output \"?([^\"]+)\"?( -)?");
      var matches = regex.Match(Environment.CommandLine);
      if (matches.Success)
      {
         var path = matches.Groups[2].Value;
         Debug.LogFormat("{0}\r\n where path is {1}", matches.Groups[0], path);
         return path;
      }

      return defaultPath;
   }

   // 一個簡單的 Build pipeline 範例
   public static void BuildAndroid()
   {
      var outputPath = Path.GetFullPath(GetOutputPath() + Path.DirectorySeparatorChar + Application.productName + ".apk");
      var outputDir = Path.GetDirectoryName(outputPath);

      if (!Directory.Exists(outputDir))
      {
         Directory.CreateDirectory(outputDir);
      }

      var buildScenes = EditorBuildSettings.scenes.Where(v => v.enabled).Select(v => v.path);
      var buildPlayerOptions = new BuildPlayerOptions()
      {
         target = BuildTarget.Android,
         scenes = buildScenes.ToArray(),
         options = BuildOptions.None,
         locationPathName = outputPath,
      };

      var result = BuildPipeline.BuildPlayer(buildPlayerOptions);
      if (!string.IsNullOrEmpty(result))
      {
         throw new System.Exception(result);
      }

      if (!InternalEditorUtility.inBatchMode)
      {
         System.Diagnostics.Process.Start(outputDir);
      }
   }
}

便可以透過 CLI 呼叫 Builder.BuildAndroid 執行建置工作:

"C:\Unity\Unity5.5.2f1\Editor\Unity.exe" -projectPath D:\ExampleProject -logFile D:\ExampleProject\unity3d_editor.log -quit -batchmode -nographics -executeMethod Builder.BuildAndroid -output D:\ExampleProject\Builds\

在這裡遇到第二個坑,使用 CLI 會發生建置錯誤說找不到 SDK 路徑:

*** Cancelled 'Build.Player.AndroidPlayer' in 1 seconds (627 ms)

Exception: Error building Player: UnityException: Build failure!
Unable to locate Android SDK
  at Builder.BuildAndroid () [0x00160] in C:\workspace\Assets\App\Editor\Builder.cs:43

(Filename: Assets/App/Editor/Builder.cs Line: 43)

executeMethod method Builder.BuildAndroid threw exception.

(Filename: C:\buildslave\unity\build\Runtime/Utilities/Argv.cpp Line: 172)

從一些網路上文章中得知,就算在 Unity Editor 中設定好 SDK 位置,使用 CLI 呼叫 Unity 建置指令時,並不會去抓 Unity Editor 的 SDK 設定,需要額外加入 SDK 路徑的設定才可以正常編譯,因此使用以下屬性來 SDK 路徑:

public class EditorSetup {
   public static string AndroidSdkRoot {
      get { return EditorPrefs.GetString("AndroidSdkRoot"); }
      set { EditorPrefs.SetString("AndroidSdkRoot", value); }
   }

   public static string JdkRoot {
      get { return EditorPrefs.GetString("JdkPath"); }
      set { EditorPrefs.SetString("JdkPath", value); }
   }

   // This requires Unity 5.3 or later
   public static string AndroidNdkRoot {
      get { return EditorPrefs.GetString("AndroidNdkRoot"); }
      set { EditorPrefs.SetString("AndroidNdkRoot", value); }
   }
}

BuildAndroid 中加入 SDK 位置設定,這些位置設定最好也是能讀取 CLI 介面參數會比較有彈性,但我們這邊直接將路徑寫死:

public static void BuildAndroid()
{
   EditorSetup.AndroidSdkRoot = @"C:\android\sdk";
   EditorSetup.AndroidNdkRoot = @"C:\android-ndk-r10e";
   EditorSetup.JdkRoot = @"C:\Program Files\Java\jdk1.8.0_121";
   ... // 以下省略,請參考前述程式碼
}

經過以上這些步驟,便可以成功在 Jenkins 上設定 CLI 參數,自動建置 Unity Android 專案了。

Reference

沒有留言: