BuildDeployWindow.cs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511
  1. // Copyright (c) Microsoft Corporation. All rights reserved.
  2. // Licensed under the MIT License. See LICENSE in the project root for license information.
  3. using System;
  4. using System.Collections.Generic;
  5. using System.IO;
  6. using System.Linq;
  7. using System.Threading;
  8. using UnityEditor;
  9. using UnityEditor.Build.Reporting;
  10. using UnityEngine;
  11. namespace XRTool.Util
  12. {
  13. public enum BuildTargets
  14. {
  15. NoHand = 0,
  16. Hand = 1,
  17. Both = 2,
  18. None = 3
  19. }
  20. /// <summary>
  21. /// 打包窗口
  22. /// </summary>
  23. public class BuildDeployWindow : EditorWindow
  24. {
  25. private Vector2 scrollPos;
  26. private static BuildConfig buildConf;
  27. /// <inheritdoc />
  28. public IEnumerable<string> Scenes { get; set; }
  29. public string outputPath;
  30. public bool isAutoUpdateVersion = true;
  31. private bool isOpenOutPath = true;
  32. private string realPath;
  33. private string serverPath;
  34. public BuildOptions buildOptions = BuildOptions.None;
  35. private string[] suffix = new string[] { ".exe", ".apk", "Project", };
  36. public string appName;
  37. private int buildIndex = 2;
  38. public const string initVersion = "1.0.0";
  39. private string allSymbol;
  40. private bool isAutoInstall = true;
  41. private string androidSDPath = "sdcard/Android/data/";
  42. private BuildTargets buildTargets;
  43. [MenuItem("XRTools/Build Window", false, 0)]
  44. public static void OpenWindow()
  45. {
  46. // Dock it next to the Scene View.
  47. var window = GetWindow<BuildDeployWindow>(typeof(SceneView));
  48. window.titleContent = new GUIContent("Build Window");
  49. window.Show();
  50. }
  51. private void OnEnable()
  52. {
  53. buildTargets = BuildTargets.None;
  54. PlayerSettings.companyName = "shadowcreator";
  55. buildIndex = 2;
  56. if (buildConf == null)
  57. {
  58. if (BuildConfigMgr.Instance.IsInit)
  59. {
  60. buildConf = BuildConfig.Instance;
  61. }
  62. else
  63. {
  64. UnityLog.LogError("配置初始化失败!");
  65. }
  66. //buildConf = AssetDatabase.LoadAssetAtPath<BuildConfig>("Assets/GameData/BuildConf/BuildConfig.asset");
  67. }
  68. titleContent = new GUIContent("Build Window");
  69. minSize = new Vector2(512, 256);
  70. CheckVersion();
  71. if (buildConf == null)
  72. {
  73. UnityLog.LogError("buildConf is null");
  74. }
  75. //UnityLog.Log(buildConf);
  76. }
  77. private void CheckVersion()
  78. {
  79. string version = PlayerSettings.bundleVersion;
  80. string[] curversion = version.Split('.');
  81. if (curversion.Length < 3)
  82. {
  83. PlayerSettings.bundleVersion = initVersion;
  84. Debug.Log("版本号初始化:" + initVersion);
  85. }
  86. }
  87. /// <summary>
  88. /// 更新版本号,在打包时调用此函数
  89. /// </summary>
  90. public string UpdateGameVersion()
  91. {
  92. string version = PlayerSettings.bundleVersion;
  93. string[] curversion = version.Split('.');
  94. if (curversion.Length < 3)
  95. {
  96. Debug.LogError("请规范版本号:" + initVersion);
  97. return PlayerSettings.bundleVersion;
  98. }
  99. ///测试版本,程序自测版本
  100. if (buildConf.buildType == BuildType.Test)
  101. {
  102. return PlayerSettings.bundleVersion;
  103. }
  104. int startIndex = (int)buildConf.buildVersion;
  105. int ver = int.Parse(curversion[startIndex]) + 1;
  106. curversion[startIndex] = ver.ToString();
  107. if (buildConf.buildVersion != BuildVersion.Build)
  108. {
  109. for (int i = startIndex + 1; i < curversion.Length; i++)
  110. {
  111. curversion[i] = "0";
  112. }
  113. }
  114. string newVersion = curversion[0];
  115. for (int i = 1; i < curversion.Length; i++)
  116. {
  117. newVersion += "." + curversion[i];
  118. }
  119. return newVersion;
  120. }
  121. private void ChangeHand()
  122. {
  123. if (buildTargets == BuildTargets.Hand)
  124. {
  125. PlayerSettings.productName = BuildConfig.Instance.appProName + "WithHand";
  126. BuildConfig.Instance.useHand = true;
  127. }
  128. else if (buildTargets == BuildTargets.NoHand)
  129. {
  130. PlayerSettings.productName = BuildConfig.Instance.appProName + "NoHand";
  131. BuildConfig.Instance.useHand = false;
  132. }
  133. else
  134. {
  135. PlayerSettings.productName = BuildConfig.Instance.appProName;
  136. BuildConfig.Instance.useHand = false;
  137. }
  138. if (buildTargets != BuildTargets.None)
  139. {
  140. PlayerSettings.applicationIdentifier = "com." + PlayerSettings.companyName + "." + PlayerSettings.productName;
  141. }
  142. BuildConfig.Instance.appPackName = PlayerSettings.applicationIdentifier;
  143. }
  144. /// <summary>
  145. /// 属性展示和同步
  146. /// </summary>
  147. private void OnGUI()
  148. {
  149. //buildConf = EditorGUI.ObjectField(new Rect(0, 5, 600, 20), "BuildSetting", buildConf, typeof(BuildConfig), true) as BuildConfig;
  150. if (!buildConf)
  151. {
  152. return;
  153. }
  154. scrollPos = GUI.BeginScrollView(new Rect(0, 30, position.width, position.height),
  155. scrollPos, new Rect(0, 0, 1920, 2160));
  156. BuildConfig.Instance.appProName = EditorGUILayout.TextField("应用名称", BuildConfig.Instance.appProName);
  157. BuildConfig.Instance.appPackName = EditorGUILayout.TextField("包名", BuildConfig.Instance.appPackName);
  158. buildConf.buildVersion = (BuildVersion)EditorGUILayout.EnumPopup("版本类型", buildConf.buildVersion);
  159. buildConf.buildType = (BuildType)EditorGUILayout.EnumPopup("发布模式", buildConf.buildType);
  160. buildConf.assetsType = (AssetsType)EditorGUILayout.EnumPopup("资源访问方式", buildConf.assetsType);
  161. buildConf.buildTarget = (BuildTarget)EditorGUILayout.EnumPopup("运行的平台", buildConf.buildTarget);
  162. buildConf.playType = (PlayType)EditorGUILayout.EnumPopup("运行的模式", buildConf.playType);
  163. //buildConf.buildTargetGroup = (BuildTargetGroup)EditorGUILayout.EnumPopup("发布的平台", buildConf.buildTargetGroup);
  164. buildConf.otherSymbol = EditorGUILayout.TextField("宏", buildConf.otherSymbol);
  165. allSymbol = EditorGUILayout.TextField("全部宏", allSymbol);
  166. if (string.IsNullOrEmpty(allSymbol))
  167. {
  168. allSymbol = PlayerSettings.GetScriptingDefineSymbolsForGroup(buildConf.buildTargetGroup);
  169. }
  170. isAutoUpdateVersion = EditorGUILayout.Toggle("更新版本", isAutoUpdateVersion);
  171. isOpenOutPath = EditorGUILayout.Toggle("打开输出路径", isOpenOutPath);
  172. if (buildConf.buildTarget == BuildTarget.Android)
  173. {
  174. isAutoInstall = EditorGUILayout.Toggle("自动安装", isAutoInstall);
  175. }
  176. EditorGUILayout.LabelField("Version", PlayerSettings.bundleVersion);
  177. EditorGUILayout.LabelField("发布时间", buildConf.buildTime);
  178. outputPath = EditorGUILayout.TextField("输出路径", outputPath);
  179. if (string.IsNullOrEmpty(outputPath))
  180. {
  181. string path = Application.dataPath;
  182. path = path.Substring(0, path.LastIndexOf("/"));
  183. outputPath = path;
  184. }
  185. realPath = EditorGUILayout.TextField("完整路径", realPath);
  186. serverPath = EditorGUILayout.TextField("服务器地址", serverPath);
  187. androidSDPath = EditorGUILayout.TextField("安卓sd卡路径", androidSDPath);
  188. if (string.IsNullOrEmpty(realPath))
  189. {
  190. realPath = Path.Combine(outputPath, buildConf.buildTarget.ToString(), buildConf.playType.ToString(), Application.productName);
  191. }
  192. buildOptions = (BuildOptions)EditorGUILayout.EnumPopup("发布设置", buildOptions);
  193. var bd = buildTargets;
  194. buildTargets = (BuildTargets)EditorGUILayout.EnumPopup("发布操作方式", buildTargets);
  195. if (bd != buildTargets)
  196. {
  197. ChangeHand();
  198. }
  199. if (GUILayout.Button("重置版本号", GUILayout.Width(200)))
  200. {
  201. buildConf.appVersion = initVersion;
  202. PlayerSettings.bundleVersion = buildConf.appVersion;
  203. }
  204. if (GUILayout.Button("强制升级版本", GUILayout.Width(200)))
  205. {
  206. PlayerSettings.bundleVersion = UpdateGameVersion();
  207. }
  208. if (GUILayout.Button("同步设置", GUILayout.Width(200)))
  209. {
  210. SetDef();
  211. }
  212. if (GUILayout.Button("清空编辑器日志", GUILayout.Width(200)))
  213. {
  214. UnityLog.ClearAllLog();
  215. }
  216. if (GUILayout.Button("打开编辑器缓存路径", GUILayout.Width(200)))
  217. {
  218. OpenDirectory(Application.persistentDataPath);
  219. }
  220. if (buildConf.buildTarget == BuildTarget.Android && GUILayout.Button("安装", GUILayout.Width(200)))
  221. {
  222. if (!string.IsNullOrEmpty(appName) && !string.IsNullOrEmpty(realPath))
  223. {
  224. InstallApp(Path.Combine(realPath, appName));
  225. }
  226. }
  227. if (buildConf.buildTarget == BuildTarget.Android && GUILayout.Button("卸载", GUILayout.Width(200)))
  228. {
  229. if (!string.IsNullOrEmpty(appName) && !string.IsNullOrEmpty(realPath))
  230. {
  231. UnInstallApp();
  232. }
  233. }
  234. if (GUILayout.Button("清空Androd日志", GUILayout.Width(200)))
  235. {
  236. if (Directory.Exists(realPath + "/LogData"))
  237. {
  238. Directory.Delete(realPath + "/LogData", true);
  239. }
  240. }
  241. if (GUILayout.Button("拷贝缓存数据", GUILayout.Width(150)))
  242. {
  243. string resPath = "sdcard/ShadowCreate/MRStores/";
  244. string logPath = realPath + "/ShadowCreate/MRStores/";
  245. string[] files = new string[] { "DeviceDownInfo.xml" };
  246. if (!Directory.Exists(logPath))
  247. {
  248. Directory.CreateDirectory(logPath);
  249. }
  250. for (int i = 0; i < files.Length; i++)
  251. {
  252. string tmpPath = logPath + "/" + files[i];
  253. if (File.Exists(tmpPath))
  254. {
  255. File.Delete(tmpPath);
  256. }
  257. Thread newThread = new Thread(new ParameterizedThreadStart(RunCmd));
  258. string adb = "adb pull " + resPath + files[i] + " " + logPath;
  259. newThread.Start(adb);
  260. }
  261. OpenDirectory(logPath);
  262. }
  263. if (GUILayout.Button("拷贝日志到本地", GUILayout.Width(150)))
  264. {
  265. string resPath = androidSDPath + Application.identifier + "/files/LogData/";
  266. string logPath = realPath + "/LogData/" + buildConf.buildType.ToString() +
  267. "_" + PlayerSettings.bundleVersion + "/" + Application.version;
  268. string[] files = new string[] { "error.txt", "except.txt", "log.txt" };
  269. if (!Directory.Exists(logPath))
  270. {
  271. Directory.CreateDirectory(logPath);
  272. }
  273. for (int i = 0; i < files.Length; i++)
  274. {
  275. string tmpPath = logPath + "/" + files[i];
  276. if (File.Exists(tmpPath))
  277. {
  278. File.Delete(tmpPath);
  279. }
  280. Thread newThread = new Thread(new ParameterizedThreadStart(RunCmd));
  281. string adb = "adb pull " + resPath + files[i] + " " + logPath;
  282. newThread.Start(adb);
  283. }
  284. OpenDirectory(logPath);
  285. }
  286. if (GUILayout.Button("推送服务器地址", GUILayout.Width(150)))
  287. {
  288. string targetPath = androidSDPath + Application.identifier + "/files/";
  289. Thread newThread = new Thread(new ParameterizedThreadStart(RunCmd));
  290. string adb = "adb push " + serverPath + " " + targetPath;
  291. newThread.Start(adb);
  292. }
  293. if (GUILayout.Button("发布", GUILayout.Width(150)))
  294. {
  295. if (buildTargets != BuildTargets.Both)
  296. {
  297. DisPlayBuild(buildTargets);
  298. }
  299. else
  300. {
  301. ///先安装手势版本,再安装无手势版本
  302. DisPlayBuild(BuildTargets.Hand);
  303. isAutoUpdateVersion = false;
  304. DisPlayBuild(BuildTargets.NoHand);
  305. }
  306. }
  307. GUI.EndScrollView();
  308. //GUI.EndScrollView();
  309. if (GUI.changed)
  310. {
  311. UpdateSetting();
  312. }
  313. }
  314. public void DisPlayBuild(BuildTargets buildTargets)
  315. {
  316. string lastBuildTime = buildConf.buildTime;
  317. buildConf.buildTime = DateTime.Now.ToString();
  318. EditorUtility.SetDirty(buildConf);
  319. this.buildTargets = buildTargets;
  320. ChangeHand();
  321. ///设置宏编译指令
  322. SetDef();
  323. string lastVersion = PlayerSettings.bundleVersion;
  324. UpdateSetting();
  325. string newVersion = isAutoUpdateVersion ? UpdateGameVersion() : lastVersion;
  326. PlayerSettings.bundleVersion = newVersion;
  327. appName = Application.productName + "_" + buildConf.buildType.ToString() +
  328. "_" + PlayerSettings.bundleVersion + "_" + DateTime.Now.ToString("MMddHHmm") + suffix[buildIndex];
  329. bool isSucess = PlayerBuild(Path.Combine(realPath, appName));
  330. if (!isSucess)
  331. {
  332. PlayerSettings.bundleVersion = lastVersion;
  333. buildConf.buildTime = lastBuildTime;
  334. EditorUtility.SetDirty(buildConf);
  335. }
  336. else
  337. {
  338. if (isOpenOutPath)
  339. {
  340. OpenDirectory(realPath);
  341. }
  342. if (buildConf.buildTarget == BuildTarget.Android && isAutoInstall)
  343. {
  344. InstallApp(Path.Combine(realPath, appName));
  345. }
  346. }
  347. Debug.Log("当前版本" + PlayerSettings.bundleVersion);
  348. }
  349. private void InstallApp(string realPath)
  350. {
  351. if (File.Exists(realPath))
  352. {
  353. Thread newThread = new Thread(new ParameterizedThreadStart(RunCmd));
  354. string cmd = "adb install -r -d " + realPath;
  355. newThread.Start(cmd);
  356. }
  357. else
  358. {
  359. UnityLog.LogError(realPath + " 不存在");
  360. }
  361. }
  362. private void UnInstallApp()
  363. {
  364. Thread newThread = new Thread(new ParameterizedThreadStart(RunCmd));
  365. string cmd = "adb uninstall " + Application.identifier;
  366. newThread.Start(cmd);
  367. }
  368. private void UpdateSetting()
  369. {
  370. if (buildConf.buildTarget == BuildTarget.Android)
  371. {
  372. buildConf.buildTargetGroup = BuildTargetGroup.Android;
  373. if (EditorUserBuildSettings.exportAsGoogleAndroidProject)
  374. {
  375. buildIndex = 2;
  376. }
  377. else
  378. {
  379. buildIndex = 1;
  380. }
  381. }
  382. if (buildConf.buildTarget == BuildTarget.iOS)
  383. {
  384. buildConf.buildTargetGroup = BuildTargetGroup.iOS;
  385. buildIndex = 2;
  386. }
  387. if (buildConf.buildTarget == BuildTarget.StandaloneWindows)
  388. {
  389. buildConf.buildTargetGroup = BuildTargetGroup.Standalone;
  390. buildIndex = 0;
  391. }
  392. //buildConf.buildType.ToString(), buildConf.playType.ToString(),
  393. realPath = Path.Combine(outputPath, Application.productName, buildConf.buildTarget.ToString(), buildConf.playType.ToString());
  394. }
  395. private bool PlayerBuild(string path)
  396. {
  397. if (EditorUserBuildSettings.activeBuildTarget != buildConf.buildTarget)
  398. {
  399. EditorUserBuildSettings.SwitchActiveBuildTarget(buildConf.buildTargetGroup, buildConf.buildTarget);
  400. }
  401. BuildReport buildReport = Build(path);
  402. bool success = buildReport != null && buildReport.summary.result == BuildResult.Succeeded;
  403. return success;
  404. }
  405. private BuildReport Build(string path)
  406. {
  407. Scenes = EditorBuildSettings.scenes.Where(scene => scene.enabled).Select(scene => scene.path);
  408. EditorUtility.DisplayProgressBar("Build Pipeline", "Gathering Build Data...", 0.25f);
  409. BuildReport buildReport = default;
  410. try
  411. {
  412. buildReport = BuildPipeline.BuildPlayer(
  413. Scenes.ToArray(),
  414. path,
  415. EditorUserBuildSettings.activeBuildTarget, buildOptions);
  416. }
  417. catch (Exception e)
  418. {
  419. Debug.LogError($"{e.Message}\n{e.StackTrace}");
  420. }
  421. EditorUtility.ClearProgressBar();
  422. return buildReport;
  423. }
  424. private void SetDef()
  425. {
  426. string lastSym = PlayerSettings.GetScriptingDefineSymbolsForGroup(buildConf.buildTargetGroup);
  427. Debug.Log(lastSym);
  428. ///设置指令用;分割
  429. string symbol = string.Format("buildType_{0};assetsType_{1};buildPlatform_{2};playType_{3}",
  430. buildConf.buildType, buildConf.assetsType, buildConf.buildTarget, buildConf.playType).ToUpper() + ";" + buildConf.otherSymbol;
  431. if (symbol != lastSym)
  432. {
  433. Debug.Log(lastSym + "当前宏编译配置:" + symbol);
  434. PlayerSettings.SetScriptingDefineSymbolsForGroup(buildConf.buildTargetGroup, symbol);
  435. }
  436. allSymbol = symbol;
  437. }
  438. /// <summary>
  439. /// 弹出指定的文件窗口,仅windows和mac使用
  440. /// </summary>
  441. /// <param name="path"></param>
  442. public static void OpenDirectory(string path)
  443. {
  444. if (string.IsNullOrEmpty(path)) return;
  445. if (!Directory.Exists(path))
  446. {
  447. UnityEngine.Debug.LogError("No Directory: " + path);
  448. return;
  449. }
  450. //Application.dataPath 只能在主线程中获取
  451. int lastIndex = Application.dataPath.LastIndexOf("/");
  452. string shellPath = Application.dataPath.Substring(0, lastIndex) + "/Shell/";
  453. // 新开线程防止锁死
  454. Thread newThread = new Thread(new ParameterizedThreadStart(CmdOpenDirectory));
  455. newThread.Start(path);
  456. }
  457. private static void CmdOpenDirectory(object obj)
  458. {
  459. System.Diagnostics.Process p = new System.Diagnostics.Process();
  460. #if UNITY_EDITOR_WIN
  461. p.StartInfo.FileName = "cmd.exe";
  462. p.StartInfo.Arguments = "/c start " + obj.ToString();
  463. #elif UNITY_EDITOR_OSX
  464. p.StartInfo.FileName = "bash";
  465. string shPath = shellPath + "openDir.sh";
  466. p.StartInfo.Arguments = shPath + " " + obj.ToString();
  467. #endif
  468. //UnityEngine.Debug.Log(p.StartInfo.Arguments);
  469. p.StartInfo.UseShellExecute = false;
  470. p.StartInfo.RedirectStandardInput = true;
  471. p.StartInfo.RedirectStandardOutput = true;
  472. p.StartInfo.RedirectStandardError = true;
  473. p.StartInfo.CreateNoWindow = true;
  474. p.Start();
  475. p.WaitForExit();
  476. p.Close();
  477. }
  478. private static void RunCmd(object command)
  479. {
  480. //例Process
  481. System.Diagnostics.Process p = new System.Diagnostics.Process();
  482. p.StartInfo.FileName = "cmd.exe"; //确定程序名
  483. p.StartInfo.Arguments = "/c " + command.ToString(); //确定程式命令行
  484. p.StartInfo.UseShellExecute = false; //Shell的使用
  485. p.StartInfo.RedirectStandardInput = true; //重定向输入
  486. p.StartInfo.RedirectStandardOutput = true; //重定向输出
  487. p.StartInfo.RedirectStandardError = true; //重定向输出错误
  488. p.StartInfo.CreateNoWindow = false; //设置置不显示示窗口
  489. p.Start();
  490. //return p.StandardOutput.ReadToEnd(); //输出出流取得命令行结果果
  491. }
  492. }
  493. }