143 lines
6.2 KiB
C#
143 lines
6.2 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using UnityEditor;
|
|
using UnityEditor.Build.Reporting;
|
|
using UnityEngine;
|
|
|
|
namespace ES.EditorTools
|
|
{
|
|
/// <summary>
|
|
/// ES Android 빌드 메뉴 (APK · AAB).
|
|
/// 씬 목록 = EditorBuildSettings(활성 씬) · 버전·패키지명 = PlayerSettings 를 그대로 읽는다.
|
|
/// CLI: unity build "<project>" --target Android --execute-method ES.EditorTools.ESBuildMenu.BuildApkCli
|
|
/// </summary>
|
|
public static class ESBuildMenu
|
|
{
|
|
const string MenuRoot = "ES/Build/";
|
|
const string LogTag = "[ESBuild]";
|
|
const string OutputFolder = "Builds/Android";
|
|
|
|
[MenuItem(MenuRoot + "Android APK (Development)", priority = 1)]
|
|
static void MenuApkDev() => Build(aab: false, development: true);
|
|
|
|
[MenuItem(MenuRoot + "Android APK (Release)", priority = 2)]
|
|
static void MenuApkRelease() => Build(aab: false, development: false);
|
|
|
|
[MenuItem(MenuRoot + "Android AAB (Release)", priority = 3)]
|
|
static void MenuAab() => Build(aab: true, development: false);
|
|
|
|
[MenuItem(MenuRoot + "Open Build Folder", priority = 20)]
|
|
static void MenuOpenFolder()
|
|
{
|
|
string dir = GetOutputDir();
|
|
Directory.CreateDirectory(dir);
|
|
EditorUtility.RevealInFinder(dir);
|
|
}
|
|
|
|
[MenuItem(MenuRoot + "Android APK (Development)", true)]
|
|
[MenuItem(MenuRoot + "Android APK (Release)", true)]
|
|
[MenuItem(MenuRoot + "Android AAB (Release)", true)]
|
|
static bool ValidateBuild() => !EditorApplication.isPlayingOrWillChangePlaymode && !EditorApplication.isCompiling;
|
|
|
|
// CLI 진입점 (batchmode) — 실패 시 종료 코드 1
|
|
public static void BuildApkCli() => ExitWith(Build(aab: false, development: HasArg("-esDevelopment")));
|
|
public static void BuildAabCli() => ExitWith(Build(aab: true, development: false));
|
|
|
|
public static bool Build(bool aab, bool development) => Build(aab, development, !Application.isBatchMode);
|
|
|
|
/// <param name="interactive">false = 완료 대화상자 생략 (CLI eval 로 GUI 에디터에서 호출할 때)</param>
|
|
public static bool Build(bool aab, bool development, bool interactive)
|
|
{
|
|
if (EditorApplication.isPlayingOrWillChangePlaymode)
|
|
{
|
|
Debug.LogError($"{LogTag} Play 중에는 빌드할 수 없습니다.");
|
|
return false;
|
|
}
|
|
|
|
string[] scenes = EditorBuildSettings.scenes.Where(s => s.enabled).Select(s => s.path).ToArray();
|
|
if (scenes.Length == 0)
|
|
{
|
|
Debug.LogError($"{LogTag} Build Settings 에 활성 씬이 없습니다.");
|
|
return false;
|
|
}
|
|
|
|
if (EditorUserBuildSettings.activeBuildTarget != BuildTarget.Android)
|
|
{
|
|
Debug.Log($"{LogTag} 빌드 타깃을 Android 로 전환합니다.");
|
|
if (!EditorUserBuildSettings.SwitchActiveBuildTarget(BuildTargetGroup.Android, BuildTarget.Android))
|
|
{
|
|
Debug.LogError($"{LogTag} Android 타깃 전환 실패 (Android 모듈 설치 확인).");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (!PlayerSettings.Android.useCustomKeystore)
|
|
Debug.LogWarning($"{LogTag} 사용자 키스토어 미설정 — 디버그 키로 서명됩니다 (스토어 업로드 불가).");
|
|
|
|
string dir = GetOutputDir();
|
|
Directory.CreateDirectory(dir);
|
|
string fileName = string.Format("{0}_{1}_{2}{3}_{4}.{5}",
|
|
Sanitize(PlayerSettings.productName),
|
|
PlayerSettings.bundleVersion,
|
|
PlayerSettings.Android.bundleVersionCode,
|
|
development ? "_dev" : "",
|
|
DateTime.Now.ToString("yyyyMMdd_HHmm"),
|
|
aab ? "aab" : "apk");
|
|
string outPath = Path.Combine(dir, fileName);
|
|
|
|
bool prevAab = EditorUserBuildSettings.buildAppBundle;
|
|
EditorUserBuildSettings.buildAppBundle = aab;
|
|
try
|
|
{
|
|
var options = new BuildPlayerOptions
|
|
{
|
|
scenes = scenes,
|
|
locationPathName = outPath,
|
|
target = BuildTarget.Android,
|
|
targetGroup = BuildTargetGroup.Android,
|
|
options = development ? BuildOptions.Development : BuildOptions.None,
|
|
};
|
|
|
|
Debug.Log($"{LogTag} 시작 — {(aab ? "AAB" : "APK")} · 씬 {scenes.Length}개 ({string.Join(", ", scenes.Select(Path.GetFileNameWithoutExtension))}) · {outPath}");
|
|
BuildReport report = BuildPipeline.BuildPlayer(options);
|
|
BuildSummary s = report.summary;
|
|
bool ok = s.result == BuildResult.Succeeded;
|
|
|
|
string msg = ok
|
|
? $"{LogTag} 성공 — {outPath} · {new FileInfo(outPath).Length / (1024f * 1024f):F1} MB · {s.totalTime.TotalSeconds:F0} s"
|
|
: $"{LogTag} 실패 — result={s.result} · errors={s.totalErrors}";
|
|
if (ok) Debug.Log(msg); else Debug.LogError(msg);
|
|
|
|
if (interactive)
|
|
{
|
|
if (ok && EditorUtility.DisplayDialog("ES Build", msg, "폴더 열기", "닫기"))
|
|
EditorUtility.RevealInFinder(outPath);
|
|
else if (!ok)
|
|
EditorUtility.DisplayDialog("ES Build", msg + "\nConsole 을 확인하세요.", "확인");
|
|
}
|
|
return ok;
|
|
}
|
|
finally
|
|
{
|
|
EditorUserBuildSettings.buildAppBundle = prevAab;
|
|
}
|
|
}
|
|
|
|
static string GetOutputDir() => Path.Combine(Directory.GetParent(Application.dataPath).FullName, OutputFolder);
|
|
|
|
static string Sanitize(string name)
|
|
{
|
|
foreach (char c in Path.GetInvalidFileNameChars()) name = name.Replace(c, '_');
|
|
return name.Replace(' ', '_');
|
|
}
|
|
|
|
static bool HasArg(string arg) => Environment.GetCommandLineArgs().Contains(arg);
|
|
|
|
static void ExitWith(bool ok)
|
|
{
|
|
if (Application.isBatchMode) EditorApplication.Exit(ok ? 0 : 1);
|
|
}
|
|
}
|
|
}
|