EscapeShooter/My project/Assets/ES/Editor/ESBuildMenu.cs

143 lines
6.2 KiB
C#
Raw Normal View History

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);
}
}
}