InstallerController.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using System.Text;
  6. using System.Threading.Tasks;
  7. using UnityEditor;
  8. using UnityEngine;
  9. using System.Reflection;
  10. using System.Runtime.InteropServices;
  11. using System.Diagnostics;
  12. using Debug = UnityEngine.Debug;
  13. using System.Text.RegularExpressions;
  14. namespace HybridCLR.Editor.Installer
  15. {
  16. public enum InstallErrorCode
  17. {
  18. Ok,
  19. }
  20. public partial class InstallerController
  21. {
  22. private const string hybridclr_repo_path = "hybridclr_repo";
  23. private const string il2cpp_plus_repo_path = "il2cpp_plus_repo";
  24. public int MajorVersion => _curVersion.major;
  25. private UnityVersion _curVersion;
  26. public InstallerController()
  27. {
  28. _curVersion = ParseUnityVersion(Application.unityVersion);
  29. }
  30. public class UnityVersion
  31. {
  32. public int major;
  33. public int minor1;
  34. public int minor2;
  35. public override string ToString()
  36. {
  37. return $"{major}.{minor1}.{minor2}";
  38. }
  39. }
  40. private static readonly Regex s_unityVersionPat = new Regex(@"(\d+)\.(\d+)\.(\d+)");
  41. public const int min2019_4_CompatibleMinorVersion = 40;
  42. public const int min2020_3_CompatibleMinorVersion = 21;
  43. public const int min2021_3_CompatibleMinorVersion = 0;
  44. public const int min2022_3_CompatibleMinorVersion = 0;
  45. private UnityVersion ParseUnityVersion(string versionStr)
  46. {
  47. var matches = s_unityVersionPat.Matches(versionStr);
  48. if (matches.Count == 0)
  49. {
  50. return null;
  51. }
  52. // 找最后一个匹配的
  53. Match match = matches[matches.Count - 1];
  54. // Debug.Log($"capture count:{match.Groups.Count} {match.Groups[1].Value} {match.Groups[2].Value}");
  55. int major = int.Parse(match.Groups[1].Value);
  56. int minor1 = int.Parse(match.Groups[2].Value);
  57. int minor2 = int.Parse(match.Groups[3].Value);
  58. return new UnityVersion { major = major, minor1 = minor1, minor2 = minor2 };
  59. }
  60. public string GetCurrentUnityVersionMinCompatibleVersionStr()
  61. {
  62. return GetMinCompatibleVersion(MajorVersion);
  63. }
  64. public string GetMinCompatibleVersion(int majorVersion)
  65. {
  66. switch(majorVersion)
  67. {
  68. case 2019: return $"2019.4.{min2019_4_CompatibleMinorVersion}";
  69. case 2020: return $"2020.3.{min2020_3_CompatibleMinorVersion}";
  70. case 2021: return $"2021.3.{min2021_3_CompatibleMinorVersion}";
  71. default: throw new Exception($"not support version:{majorVersion}");
  72. }
  73. }
  74. public bool IsComaptibleVersion()
  75. {
  76. UnityVersion version = _curVersion;
  77. switch (version.major)
  78. {
  79. case 2019:
  80. {
  81. if (version.major != 2019 || version.minor1 != 4)
  82. {
  83. return false;
  84. }
  85. return version.minor2 >= min2019_4_CompatibleMinorVersion;
  86. }
  87. case 2020:
  88. {
  89. if (version.major != 2020 || version.minor1 != 3)
  90. {
  91. return false;
  92. }
  93. return version.minor2 >= min2020_3_CompatibleMinorVersion;
  94. }
  95. case 2021:
  96. {
  97. if (version.major != 2021 || version.minor1 != 3)
  98. {
  99. return false;
  100. }
  101. return version.minor2 >= min2021_3_CompatibleMinorVersion;
  102. }
  103. case 2022:
  104. {
  105. if (version.major != 2022 || version.minor1 != 3)
  106. {
  107. return false;
  108. }
  109. return version.minor2 >= min2022_3_CompatibleMinorVersion;
  110. }
  111. default: throw new Exception($"not support il2cpp_plus branch:{version.major}");
  112. }
  113. }
  114. private string _hybridclrLocalVersion;
  115. public string HybridclrLocalVersion => _hybridclrLocalVersion != null ? _hybridclrLocalVersion : _hybridclrLocalVersion = GetHybridCLRLocalVersion();
  116. public string HybridCLRRepoInstalledVersion
  117. {
  118. get { return EditorPrefs.GetString("hybridclr_repo"); }
  119. set { EditorPrefs.SetString("hybridclr_repo", value); }
  120. }
  121. public string Il2CppRepoInstalledVersion
  122. {
  123. get { return EditorPrefs.GetString("il2cpp_plus_repo"); }
  124. set { EditorPrefs.SetString("il2cpp_plus_repo", value); }
  125. }
  126. private string GetHybridCLRLocalVersion()
  127. {
  128. string workDir = SettingsUtil.HybridCLRDataDir;
  129. string hybridclrRepoDir = $"{workDir}/{hybridclr_repo_path}";
  130. if (Directory.Exists(hybridclrRepoDir))
  131. {
  132. var ret = BashUtil.RunCommand2(hybridclrRepoDir, "git",
  133. new string[] { "log", "HEAD", "-n", "1", "--pretty=format:\"%H\"", },
  134. false);
  135. if (ret.ExitCode == 0)
  136. {
  137. return ret.StdOut.Trim();
  138. }
  139. else
  140. {
  141. return "ERROR";
  142. }
  143. }
  144. return "";
  145. }
  146. private string _il2cppPlusLocalVersion;
  147. public string Il2cppPlusLocalVersion => _il2cppPlusLocalVersion != null ? _il2cppPlusLocalVersion : _il2cppPlusLocalVersion = GetIl2cppPlusLocalVersion();
  148. private string GetIl2cppPlusLocalVersion()
  149. {
  150. string workDir = SettingsUtil.HybridCLRDataDir;
  151. string il2cppPlusRepoDir = $"{workDir}/{il2cpp_plus_repo_path}";
  152. if (Directory.Exists(il2cppPlusRepoDir))
  153. {
  154. var ret = BashUtil.RunCommand2(il2cppPlusRepoDir, "git",
  155. new string[] { "log", "HEAD", "-n", "1", "--pretty=format:\"%H\"", },
  156. false);
  157. if (ret.ExitCode == 0)
  158. {
  159. return ret.StdOut.Trim();
  160. }
  161. else
  162. {
  163. return "ERROR";
  164. }
  165. }
  166. return "";
  167. }
  168. private string GetIl2CppPathByContentPath(string contentPath)
  169. {
  170. return $"{contentPath}/il2cpp";
  171. }
  172. public void InstallLocalHybridCLR(string hybridclrVer, string il2cppPlusVer)
  173. {
  174. RunInitLocalIl2CppData(GetIl2CppPathByContentPath(EditorApplication.applicationContentsPath), _curVersion, hybridclrVer, il2cppPlusVer);
  175. }
  176. public bool HasInstalledHybridCLR()
  177. {
  178. return Directory.Exists($"{SettingsUtil.LocalIl2CppDir}/libil2cpp/hybridclr");
  179. }
  180. private string GetUnityIl2CppDllInstallLocation()
  181. {
  182. #if UNITY_EDITOR_WIN
  183. return $"{SettingsUtil.LocalIl2CppDir}/build/deploy/net471/Unity.IL2CPP.dll";
  184. #else
  185. return $"{SettingsUtil.LocalIl2CppDir}/build/deploy/il2cppcore/Unity.IL2CPP.dll";
  186. #endif
  187. }
  188. private string GetUnityIl2CppDllModifiedPath(string curVersionStr)
  189. {
  190. #if UNITY_EDITOR_WIN
  191. return $"{SettingsUtil.ProjectDir}/{SettingsUtil.HybridCLRDataPathInPackage}/ModifiedUnityAssemblies/{curVersionStr}/Unity.IL2CPP-Win.dll.bytes";
  192. #else
  193. return $"{SettingsUtil.ProjectDir}/{SettingsUtil.HybridCLRDataPathInPackage}/ModifiedUnityAssemblies/{curVersionStr}/Unity.IL2CPP-Mac.dll.bytes";
  194. #endif
  195. }
  196. private void RunInitLocalIl2CppData(string editorIl2cppPath, UnityVersion version, string hybridclrVer, string il2cppPlusVer)
  197. {
  198. #if UNITY_EDITOR_WIN
  199. if (!BashUtil.ExistProgram("git"))
  200. {
  201. throw new Exception($"安装本地il2cpp需要使用git从远程拉取仓库,请先安装git");
  202. }
  203. #endif
  204. string workDir = SettingsUtil.HybridCLRDataDir;
  205. Directory.CreateDirectory(workDir);
  206. //BashUtil.RecreateDir(workDir);
  207. string buildiOSDir = $"{workDir}/iOSBuild";
  208. BashUtil.RemoveDir(buildiOSDir);
  209. BashUtil.CopyDir($"{SettingsUtil.HybridCLRDataPathInPackage}/iOSBuild", buildiOSDir, true);
  210. // clone hybridclr
  211. string hybridclrRepoURL = HybridCLRSettings.Instance.hybridclrRepoURL;
  212. string hybridclrRepoDir = $"{workDir}/{hybridclr_repo_path}";
  213. {
  214. BashUtil.RemoveDir(hybridclrRepoDir);
  215. string[] args = new string[]
  216. {
  217. "clone",
  218. "--depth=1",
  219. "-b",
  220. hybridclrVer,
  221. hybridclrRepoURL,
  222. hybridclrRepoDir,
  223. };
  224. var ret = BashUtil.RunCommand(workDir, "git", args);
  225. //if (ret != 0)
  226. //{
  227. // throw new Exception($"git clone 失败");
  228. //}
  229. }
  230. // clone il2cpp_plus
  231. string il2cppPlusRepoURL = HybridCLRSettings.Instance.il2cppPlusRepoURL;
  232. string il2cppPlusRepoDir = $"{workDir}/{il2cpp_plus_repo_path}";
  233. {
  234. BashUtil.RemoveDir(il2cppPlusRepoDir);
  235. string[] args = new string[]
  236. {
  237. "clone",
  238. "--depth=1",
  239. "-b",
  240. il2cppPlusVer,
  241. il2cppPlusRepoURL,
  242. il2cppPlusRepoDir,
  243. };
  244. var ret = BashUtil.RunCommand(workDir, "git", args);
  245. //if (ret != 0)
  246. //{
  247. // throw new Exception($"git clone 失败");
  248. //}
  249. }
  250. // create LocalIl2Cpp
  251. string localUnityDataDir = SettingsUtil.LocalUnityDataDir;
  252. BashUtil.RecreateDir(localUnityDataDir);
  253. // copy MonoBleedingEdge
  254. BashUtil.CopyDir($"{Directory.GetParent(editorIl2cppPath)}/MonoBleedingEdge", $"{localUnityDataDir}/MonoBleedingEdge", true);
  255. // copy il2cpp
  256. BashUtil.CopyDir(editorIl2cppPath, SettingsUtil.LocalIl2CppDir, true);
  257. // replace libil2cpp
  258. string dstLibil2cppDir = $"{SettingsUtil.LocalIl2CppDir}/libil2cpp";
  259. BashUtil.CopyDir($"{il2cppPlusRepoDir}/libil2cpp", dstLibil2cppDir, true);
  260. BashUtil.CopyDir($"{hybridclrRepoDir}/hybridclr", $"{dstLibil2cppDir}/hybridclr", true);
  261. // clean Il2cppBuildCache
  262. BashUtil.RemoveDir($"{SettingsUtil.ProjectDir}/Library/Il2cppBuildCache", true);
  263. if (version.major == 2019)
  264. {
  265. string curVersionStr = version.ToString();
  266. string srcIl2CppDll = GetUnityIl2CppDllModifiedPath(curVersionStr);
  267. if (File.Exists(srcIl2CppDll))
  268. {
  269. string dstIl2CppDll = GetUnityIl2CppDllInstallLocation();
  270. File.Copy(srcIl2CppDll, dstIl2CppDll, true);
  271. Debug.Log($"copy {srcIl2CppDll} => {dstIl2CppDll}");
  272. }
  273. else
  274. {
  275. Debug.LogError($"未找到当前版本:{curVersionStr} 对应的改造过的 Unity.IL2CPP.dll,打包出的程序将会崩溃");
  276. }
  277. }
  278. if (HasInstalledHybridCLR())
  279. {
  280. Debug.Log("安装成功!");
  281. _hybridclrLocalVersion = null;
  282. _il2cppPlusLocalVersion = null;
  283. HybridCLRRepoInstalledVersion = hybridclrVer;
  284. Il2CppRepoInstalledVersion = il2cppPlusVer;
  285. }
  286. else
  287. {
  288. Debug.LogError("安装失败!");
  289. }
  290. }
  291. }
  292. }