using System; using System.Collections.Generic; using System.IO; using System.IO.Compression; using System.Linq; using System.Text; using Superlazy; using Superlazy.Loader; using UnityEditor; using UnityEditor.AddressableAssets; using UnityEditor.AddressableAssets.Build; using UnityEditor.AddressableAssets.Settings; using UnityEditor.Callbacks; using UnityEditor.SceneManagement; using UnityEngine; using UnityEngine.SceneManagement; #if UNITY_IOS using UnityEditor.iOS.Xcode; #endif public class SLAppBuild { public static string dataAssetPath = "Addressables/Data"; private static readonly SLEntity options = SLEntity.Empty; private enum SLAppBuildTarget { NONE = -1, iOS = 0, Android = 4, Android_x86 = 5, iOS_Cheat = 8, Android_Cheat = 9 } private static void ParseCommandLine() { var args = Environment.GetCommandLineArgs(); for (var i = 0; i < args.Length; ++i) { if (args[i].IsLeft("-")) { if (args[i + 1].Contains(",")) { var splits = args[i + 1].Split(','); for (var j = 0; j < splits.Count(); ++j) { options[args[i].Replace("-", "")][j.ToString()] = splits[j]; } ++i; } else { options[args[i].Replace("-", "")] = args[i + 1]; ++i; } } } } public static void BuildAndroidAPK() { PreBuild(); PlayerSettings.Android.bundleVersionCode = options["BuildSettings"]["BuildVersion"]; PlayerSettings.Android.keystorePass = options["BuildSettings"]["Android"]["KeystorePass"]; PlayerSettings.Android.keyaliasName = options["BuildSettings"]["Android"]["KeyaliasName"]; PlayerSettings.Android.keyaliasPass = options["BuildSettings"]["Android"]["KeyaliasPass"]; EditorUserBuildSettings.buildAppBundle = false; var outputName = Application.productName + ".apk"; GenericBuild(outputName, BuildTargetGroup.Android, BuildTarget.Android, BuildOptions.None); } public static void BuildiOSIPA() { PreBuild(); var targetDir = "./build"; PlayerSettings.iOS.buildNumber = options["BuildSettings"]["BuildVersion"]; var opt = BuildOptions.None; PlayerSettings.statusBarHidden = true; if (Directory.Exists(targetDir)) { Directory.Delete(targetDir, true); } if (Directory.Exists(targetDir) == false) { Directory.CreateDirectory(targetDir); } GenericBuild(targetDir, BuildTargetGroup.iOS, BuildTarget.iOS, opt); } public static void PreBuild() { LoadBuildSettings(); ParseCommandLine(); UpdateDataScript(); UpdateAddressable(); UpdateLoaderID(); } private static void GenericBuild(string target_path, BuildTargetGroup buildTargetGroup, BuildTarget build_target, BuildOptions build_options) { var levels = GetLevelsFromBuildSettings(); EditorUserBuildSettings.SwitchActiveBuildTarget(buildTargetGroup, build_target); // TODO: NamedBuildTarget으로 개선 if (options["Def"]) { // 기존 심볼들 var existingDefs = PlayerSettings.GetScriptingDefineSymbolsForGroup(buildTargetGroup) .Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries) .Select(d => d.Trim()) .ToHashSet(); // 새로 추가할 심볼들 var newDefs = options["Def"].ToString() .Split(";", StringSplitOptions.RemoveEmptyEntries) .Select(d => d.Trim()); // 병합 후 중복 제거 foreach (var def in newDefs) existingDefs.Add(def); // 최종 적용 var mergedDefs = string.Join(";", existingDefs); PlayerSettings.SetScriptingDefineSymbolsForGroup(buildTargetGroup, mergedDefs); } var res = BuildPipeline.BuildPlayer(levels, target_path, build_target, build_options); if (res.summary.result != UnityEditor.Build.Reporting.BuildResult.Succeeded) { throw new Exception("BuildPlayer failure: " + res.summary.ToString()); } } private static string[] GetLevelsFromBuildSettings() { var levels = new List(); foreach (var scene in EditorBuildSettings.scenes) { levels.Add(scene.path); } return levels.ToArray(); } public static void LoadBuildSettings() { var path = Path.Combine(Application.dataPath, "SLBuild", "BuildSettings.json"); options["BuildSettings"] = JsonLoader.LoadJson(File.ReadAllText(path))["BuildSettings"]; } public static void UpdateDataScript() { FileUtil.DeleteFileOrDirectory("Assets/Addressables/Data/"); var destDir = Path.Combine(Application.dataPath, dataAssetPath); Directory.CreateDirectory(destDir); var loader = SLSystemUtility.CreateInstance("SLWindowsLoader"); var system = SLSystemUtility.CreateInstance("SLSystem"); system.Init(loader, DateTime.UtcNow); loader.Load(); foreach (var session in SLSystem.Data) { var fileName = session.ID + ".json"; var paths = Path.Combine(destDir, fileName).Split(Path.DirectorySeparatorChar); var path = ""; for (var i = 0; i < paths.Length - 1; ++i) { path += paths[i] + Path.DirectorySeparatorChar; if (Directory.Exists(path) == false) { Directory.CreateDirectory(path); } } var saveData = SLEntity.Empty; saveData[session.ID] = session; var text = JsonLoader.SaveToJson(saveData); SLFileUtility.MakeFolderFromFilePath(Path.Combine(destDir, fileName)); File.WriteAllText(Path.Combine(destDir, fileName), text, Encoding.UTF8); } AssetDatabase.Refresh(); } private static void UpdateAddressable() { AssetDatabase.ImportAsset("Assets/Addressables", ImportAssetOptions.ImportRecursive); AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport); var settings = AddressableAssetSettingsDefaultObject.Settings; settings.activeProfileId = settings.profileSettings.GetProfileId("Default"); var builder = AssetDatabase.LoadAssetAtPath("Assets/AddressableAssetsData/DataBuilders/BuildScriptPackedMode.asset") as IDataBuilder; settings.ActivePlayerDataBuilderIndex = settings.DataBuilders.IndexOf((ScriptableObject)builder); AddressableAssetSettings.BuildPlayerContent(out var result); if (!string.IsNullOrEmpty(result.Error)) throw new Exception(result.Error); AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport); } public static void UpdateLoaderID() { EditorSceneManager.OpenScene("Assets/Scenes/Game.unity"); var slunity = UnityEngine.Object.FindFirstObjectByType(); if (slunity != null) { slunity.loaderID = "SLUnityDataLoader"; } EditorSceneManager.SaveScene(SceneManager.GetActiveScene()); } [PostProcessBuild(999)] public static void OnPostProcessBuild(BuildTarget buildTarget, string path) { if (buildTarget == BuildTarget.StandaloneWindows64) { var folderPath = SLFileUtility.FolderPath(path); var backupFolder = folderPath + "/" + Application.productName + "_BackUpThisFolder_ButDontShipItWithYourGame"; if (Directory.Exists(backupFolder)) { Directory.Delete(backupFolder, true); } var burstDebugFolder = folderPath + "/" + Application.productName + "_BurstDebugInformation_DoNotShip"; if (Directory.Exists(burstDebugFolder)) { Directory.Delete(burstDebugFolder, true); } var fileName = Application.productName; if (options["FileName"]) { fileName = options["FileName"]; } if (File.Exists(folderPath + $"/../{fileName}.zip")) { File.Delete(folderPath + $"/../{fileName}.zip"); } ; ZipFile.CreateFromDirectory(folderPath, $"./{fileName}.zip"); } if (buildTarget == BuildTarget.iOS) { #if UNITY_IOS string projectPath = path + "/Unity-iPhone.xcodeproj/project.pbxproj"; UnityEditor.iOS.Xcode.PBXProject pbxProject = new UnityEditor.iOS.Xcode.PBXProject(); pbxProject.ReadFromFile(projectPath); string target = pbxProject.GetUnityMainTargetGuid(); pbxProject.SetBuildProperty(target, "ENABLE_BITCODE", "NO"); pbxProject.SetBuildProperty(target, "DEBUG_INFORMATION_FORMAT", "dwarf"); if (options["iOSProvisionID"]) { pbxProject.SetBuildProperty(target, "PROVISIONING_PROFILE", options["iOSProvisionID"]); } if (options["iOSProfileSpecifier"]) { pbxProject.SetBuildProperty(target, "PROVISIONING_PROFILE_SPECIFIER", options["iOSProfileSpecifier"]); } if (options["iOSTeamID"]) { pbxProject.SetBuildProperty(target, "DEVELOPMENT_TEAM", options["iOSTeamID"]); } if (options["iOSDistribution"]) { pbxProject.SetBuildProperty(target, "CODE_SIGN_IDENTITY[sdk=iphoneos*]", "iPhone Distribution"); } pbxProject.WriteToFile(projectPath); { string plistPath = path + "/Info.plist"; PlistDocument plist = new PlistDocument(); plist.ReadFromString(File.ReadAllText(plistPath)); PlistElementDict rootDict = plist.root; rootDict.SetBoolean("ITSAppUsesNonExemptEncryption", false); File.WriteAllText(plistPath, plist.WriteToString()); } #endif } } public static void BuildWindows() { PreBuild(); var targetDir = "./build"; if (Directory.Exists(targetDir)) { Directory.Delete(targetDir, true); } if (Directory.Exists(targetDir) == false) { Directory.CreateDirectory(targetDir); } GenericBuild(targetDir + "/" + Application.productName + ".exe", BuildTargetGroup.Standalone, BuildTarget.StandaloneWindows64, BuildOptions.None); } }