TapSDKCoreCompile.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493
  1. using System.Collections.Generic;
  2. using System.IO;
  3. using System.Linq;
  4. using UnityEditor;
  5. using UnityEditor.PackageManager;
  6. using UnityEngine;
  7. using System.Diagnostics;
  8. using System.Text.RegularExpressions;
  9. #if UNITY_IOS
  10. using System;
  11. using Google;
  12. using UnityEditor.iOS.Xcode;
  13. #endif
  14. namespace TapSDK.Core.Editor
  15. {
  16. public static class TapSDKCoreCompile
  17. {
  18. #if UNITY_IOS
  19. public static string GetProjPath(string path)
  20. {
  21. UnityEngine.Debug.Log($"SDX , GetProjPath path:{path}");
  22. return PBXProject.GetPBXProjectPath(path);
  23. }
  24. public static PBXProject ParseProjPath(string path)
  25. {
  26. UnityEngine.Debug.Log($"SDX , ParseProjPath path:{path}");
  27. var proj = new PBXProject();
  28. proj.ReadFromString(File.ReadAllText(path));
  29. return proj;
  30. }
  31. public static string GetUnityFrameworkTarget(PBXProject proj)
  32. {
  33. #if UNITY_2019_3_OR_NEWER
  34. UnityEngine.Debug.Log("SDX , GetUnityFrameworkTarget UNITY_2019_3_OR_NEWER");
  35. string target = proj.GetUnityFrameworkTargetGuid();
  36. return target;
  37. #endif
  38. UnityEngine.Debug.Log("SDX , GetUnityFrameworkTarget");
  39. var unityPhoneTarget = proj.TargetGuidByName("Unity-iPhone");
  40. return unityPhoneTarget;
  41. }
  42. public static string GetUnityTarget(PBXProject proj)
  43. {
  44. #if UNITY_2019_3_OR_NEWER
  45. UnityEngine.Debug.Log("SDX , GetUnityTarget UNITY_2019_3_OR_NEWER");
  46. string target = proj.GetUnityMainTargetGuid();
  47. return target;
  48. #endif
  49. UnityEngine.Debug.Log("SDX , GetUnityTarget");
  50. var unityPhoneTarget = proj.TargetGuidByName("Unity-iPhone");
  51. return unityPhoneTarget;
  52. }
  53. public static bool CheckTarget(string target)
  54. {
  55. return string.IsNullOrEmpty(target);
  56. }
  57. public static string GetUnityPackagePath(string parentFolder, string unityPackageName)
  58. {
  59. var request = Client.List(true);
  60. while (request.IsCompleted == false)
  61. {
  62. System.Threading.Thread.Sleep(100);
  63. }
  64. var pkgs = request.Result;
  65. if (pkgs == null)
  66. return "";
  67. foreach (var pkg in pkgs)
  68. {
  69. if (pkg.name == unityPackageName)
  70. {
  71. if (pkg.source == PackageSource.Local)
  72. return pkg.resolvedPath;
  73. else if (pkg.source == PackageSource.Embedded)
  74. return pkg.resolvedPath;
  75. else
  76. {
  77. return pkg.resolvedPath;
  78. }
  79. }
  80. }
  81. return "";
  82. }
  83. public static bool HandlerIOSSetting(string path, string appDataPath, string resourceName,
  84. string modulePackageName,
  85. string moduleName, string[] bundleNames, string target, string projPath, PBXProject proj, string podSpecName = "")
  86. {
  87. var resourcePath = Path.Combine(path, resourceName);
  88. var parentFolder = Directory.GetParent(appDataPath).FullName;
  89. UnityEngine.Debug.Log($"ProjectFolder path:{parentFolder}" + " resourcePath: " + resourcePath + " parentFolder: " + parentFolder);
  90. if (Directory.Exists(resourcePath))
  91. {
  92. Directory.Delete(resourcePath, true);
  93. }
  94. var podSpecPath = Path.Combine(path + "/Pods", podSpecName);
  95. //使用 cocospod 远程依赖
  96. if (podSpecName != null && podSpecName.Length > 0 && Directory.Exists(podSpecPath))
  97. {
  98. resourcePath = Path.Combine(path + "/Pods", podSpecName + "/Frameworks");
  99. UnityEngine.Debug.Log($"Find {moduleName} use pods resourcePath:{resourcePath}");
  100. }
  101. else
  102. {
  103. Directory.CreateDirectory(resourcePath);
  104. var remotePackagePath = GetUnityPackagePath(parentFolder, modulePackageName);
  105. var assetLocalPackagePath = TapFileHelper.FilterFileByPrefix(parentFolder + "/Assets/TapSDK/", moduleName);
  106. var localPackagePath = TapFileHelper.FilterFileByPrefix(parentFolder, moduleName);
  107. UnityEngine.Debug.Log($"Find {moduleName} path: remote = {remotePackagePath} asset = {assetLocalPackagePath} local = {localPackagePath}");
  108. var tdsResourcePath = "";
  109. if (!string.IsNullOrEmpty(remotePackagePath))
  110. {
  111. tdsResourcePath = remotePackagePath;
  112. }
  113. else if (!string.IsNullOrEmpty(assetLocalPackagePath))
  114. {
  115. tdsResourcePath = assetLocalPackagePath;
  116. }
  117. else if (!string.IsNullOrEmpty(localPackagePath))
  118. {
  119. tdsResourcePath = localPackagePath;
  120. }
  121. if (string.IsNullOrEmpty(tdsResourcePath))
  122. {
  123. throw new Exception(string.Format("Can't find tdsResourcePath with module of : {0}", modulePackageName));
  124. }
  125. tdsResourcePath = $"{tdsResourcePath}/Plugins/iOS/Resource";
  126. UnityEngine.Debug.Log($"Find {moduleName} path:{tdsResourcePath}");
  127. if (!Directory.Exists(tdsResourcePath))
  128. {
  129. throw new Exception(string.Format("Can't Find {0}", tdsResourcePath));
  130. }
  131. TapFileHelper.CopyAndReplaceDirectory(tdsResourcePath, resourcePath);
  132. }
  133. foreach (var name in bundleNames)
  134. {
  135. var relativePath = GetRelativePath(Path.Combine(resourcePath, name), path);
  136. if (!proj.ContainsFileByRealPath(relativePath))
  137. {
  138. var fileGuid = proj.AddFile(relativePath, relativePath, PBXSourceTree.Source);
  139. proj.AddFileToBuild(target, fileGuid);
  140. }
  141. }
  142. File.WriteAllText(projPath, proj.WriteToString());
  143. return true;
  144. }
  145. private static string GetRelativePath(string absolutePath, string rootPath)
  146. {
  147. if (Directory.Exists(rootPath) && !rootPath.EndsWith(Path.AltDirectorySeparatorChar.ToString()))
  148. {
  149. rootPath += Path.AltDirectorySeparatorChar;
  150. }
  151. Uri aboslutePathUri = new Uri(absolutePath);
  152. Uri rootPathUri = new Uri(rootPath);
  153. var relateivePath = rootPathUri.MakeRelativeUri(aboslutePathUri).ToString();
  154. UnityEngine.Debug.LogFormat($"[TapSDKCoreCompile] GetRelativePath absolutePath:{absolutePath} rootPath:{rootPath} relateivePath:{relateivePath} ");
  155. return relateivePath;
  156. }
  157. public static bool HandlerPlist(string pathToBuildProject, string infoPlistPath, bool macos = false)
  158. {
  159. // #if UNITY_2020_1_OR_NEWER
  160. // var macosXCodePlistPath =
  161. // $"{pathToBuildProject}/{PlayerSettings.productName}/Info.plist";
  162. // #elif UNITY_2019_1_OR_NEWER
  163. // var macosXCodePlistPath =
  164. // $"{Path.GetDirectoryName(pathToBuildProject)}/{PlayerSettings.productName}/Info.plist";
  165. // #endif
  166. string plistPath;
  167. if (pathToBuildProject.EndsWith(".app"))
  168. {
  169. plistPath = $"{pathToBuildProject}/Contents/Info.plist";
  170. }
  171. else
  172. {
  173. var macosXCodePlistPath =
  174. $"{Path.GetDirectoryName(pathToBuildProject)}/{PlayerSettings.productName}/Info.plist";
  175. if (!File.Exists(macosXCodePlistPath))
  176. {
  177. macosXCodePlistPath = $"{pathToBuildProject}/{PlayerSettings.productName}/Info.plist";
  178. }
  179. plistPath = !macos
  180. ? pathToBuildProject + "/Info.plist"
  181. : macosXCodePlistPath;
  182. }
  183. UnityEngine.Debug.Log($"plist path:{plistPath}");
  184. var plist = new PlistDocument();
  185. plist.ReadFromString(File.ReadAllText(plistPath));
  186. var rootDic = plist.root;
  187. var items = new List<string>
  188. {
  189. "tapsdk",
  190. "tapiosdk",
  191. "taptap"
  192. };
  193. if (!(rootDic["LSApplicationQueriesSchemes"] is PlistElementArray plistElementList))
  194. {
  195. plistElementList = rootDic.CreateArray("LSApplicationQueriesSchemes");
  196. }
  197. string listData = "";
  198. foreach (var item in plistElementList.values)
  199. {
  200. if (item is PlistElementString)
  201. {
  202. listData += item.AsString() + ";";
  203. }
  204. }
  205. foreach (var t in items)
  206. {
  207. if (!listData.Contains(t + ";"))
  208. {
  209. plistElementList.AddString(t);
  210. }
  211. }
  212. if (string.IsNullOrEmpty(infoPlistPath)) return false;
  213. var dic = (Dictionary<string, object>)Plist.readPlist(infoPlistPath);
  214. var taptapId = "";
  215. foreach (var item in dic)
  216. {
  217. if (item.Key.Equals("taptap"))
  218. {
  219. var taptapDic = (Dictionary<string, object>)item.Value;
  220. foreach (var taptapItem in taptapDic.Where(taptapItem => taptapItem.Key.Equals("client_id")))
  221. {
  222. taptapId = (string)taptapItem.Value;
  223. }
  224. }
  225. else
  226. {
  227. rootDic.SetString(item.Key, item.Value.ToString());
  228. }
  229. }
  230. //添加url
  231. var dict = plist.root.AsDict();
  232. if (!(dict["CFBundleURLTypes"] is PlistElementArray array))
  233. {
  234. array = dict.CreateArray("CFBundleURLTypes");
  235. }
  236. if (!macos)
  237. {
  238. var dict2 = array.AddDict();
  239. dict2.SetString("CFBundleURLName", "TapTap");
  240. var array2 = dict2.CreateArray("CFBundleURLSchemes");
  241. array2.AddString($"tt{taptapId}");
  242. }
  243. else
  244. {
  245. var dict2 = array.AddDict();
  246. dict2.SetString("CFBundleURLName", "TapWeb");
  247. var array2 = dict2.CreateArray("CFBundleURLSchemes");
  248. array2.AddString($"open-taptap-{taptapId}");
  249. }
  250. UnityEngine.Debug.Log("TapSDK change plist Success");
  251. File.WriteAllText(plistPath, plist.WriteToString());
  252. return true;
  253. }
  254. public static string GetValueFromPlist(string infoPlistPath, string key)
  255. {
  256. if (infoPlistPath == null)
  257. {
  258. return null;
  259. }
  260. var dic = (Dictionary<string, object>)Plist.readPlist(infoPlistPath);
  261. return (from item in dic where item.Key.Equals(key) select (string)item.Value).FirstOrDefault();
  262. }
  263. public static void ExecutePodCommand(string command, string workingDirectory)
  264. {
  265. string podPath = FindPodPath();
  266. if (string.IsNullOrEmpty(podPath))
  267. {
  268. UnityEngine.Debug.LogError("[CocoaPods] search pod install path failed");
  269. return;
  270. }
  271. UnityEngine.Debug.Log("[CocoaPods] search pod install path :" + podPath);
  272. command = command.Replace("pod", podPath);
  273. command = "export LANG=en_US.UTF-8 && " + command;
  274. var process = new Process
  275. {
  276. StartInfo = new ProcessStartInfo
  277. {
  278. FileName = "/bin/bash",
  279. Arguments = $"-c \"{command}\"",
  280. WorkingDirectory = workingDirectory,
  281. RedirectStandardOutput = true,
  282. RedirectStandardError = true,
  283. UseShellExecute = false,
  284. CreateNoWindow = true
  285. }
  286. };
  287. process.Start();
  288. process.WaitForExit();
  289. string output = process.StandardOutput.ReadToEnd();
  290. string error = process.StandardError.ReadToEnd();
  291. if (!string.IsNullOrEmpty(output))
  292. UnityEngine.Debug.Log($"[CocoaPods] Output: {output}");
  293. if (!string.IsNullOrEmpty(error))
  294. UnityEngine.Debug.LogError($"[CocoaPods] Error: {error}");
  295. if (process.ExitCode == 0)
  296. UnityEngine.Debug.Log($"[CocoaPods] Success: {command}");
  297. else
  298. UnityEngine.Debug.LogError($"[CocoaPods] Failed: {command} (Exit code: {process.ExitCode})");
  299. }
  300. private static string FindPodPath()
  301. {
  302. string whichResult = RunBashCommand("-l -c \"which pod\"");
  303. whichResult = whichResult.Replace("\n", "");
  304. if (!string.IsNullOrEmpty(whichResult) && File.Exists(whichResult))
  305. {
  306. UnityEngine.Debug.Log($"[PodFinder] Found pod at which result: {whichResult}");
  307. return whichResult;
  308. }
  309. string[] CommonPaths = new string[]
  310. {
  311. "/usr/local/bin",
  312. "/usr/bin",
  313. "/opt/homebrew/bin"
  314. };
  315. // 1. 先在常见路径查找 pod
  316. foreach (var path in CommonPaths)
  317. {
  318. string podPath = Path.Combine(path, "pod");
  319. if (File.Exists(podPath))
  320. {
  321. UnityEngine.Debug.Log($"[PodFinder] Found pod at common path: {podPath}");
  322. return podPath;
  323. }
  324. }
  325. // 2. 如果没找到,执行 gem environment 查找
  326. string gemEnvOutput = RunBashCommand("-l -c \"gem environment\"");
  327. if (string.IsNullOrEmpty(gemEnvOutput))
  328. {
  329. UnityEngine.Debug.LogWarning("[PodFinder] gem environment output is empty.");
  330. return null;
  331. }
  332. // 3. 解析 EXECUTABLE DIRECTORY
  333. string execDir = ParseGemEnvironment(gemEnvOutput, @"EXECUTABLE DIRECTORY:\s*(.+)");
  334. if (!string.IsNullOrEmpty(execDir))
  335. {
  336. string podPath = Path.Combine(execDir.Trim(), "pod");
  337. if (File.Exists(podPath))
  338. {
  339. UnityEngine.Debug.Log($"[PodFinder] Found pod via EXECUTABLE DIRECTORY: {podPath}");
  340. return podPath;
  341. }
  342. }
  343. // 4. 解析 GEM PATHS,尝试从每个路径下的 bin 文件夹查找 pod
  344. var gemPaths = ParseGemEnvironmentMultiple(gemEnvOutput, @"GEM PATHS:\s*((?:- .+\n)+)");
  345. if (gemPaths != null)
  346. {
  347. foreach (var gemPath in gemPaths)
  348. {
  349. // 一般 pod 会在 bin 文件夹或同级目录中
  350. string podPath1 = Path.Combine(gemPath.Trim(), "bin", "pod");
  351. string podPath2 = Path.Combine(gemPath.Trim(), "pod"); // 备选路径
  352. if (File.Exists(podPath1))
  353. {
  354. UnityEngine.Debug.Log($"[PodFinder] Found pod via GEM PATHS (bin): {podPath1}");
  355. return podPath1;
  356. }
  357. if (File.Exists(podPath2))
  358. {
  359. UnityEngine.Debug.Log($"[PodFinder] Found pod via GEM PATHS: {podPath2}");
  360. return podPath2;
  361. }
  362. }
  363. }
  364. UnityEngine.Debug.LogWarning("[PodFinder] pod executable not found.");
  365. return null;
  366. }
  367. private static string RunBashCommand(string arguments)
  368. {
  369. try
  370. {
  371. using (var process = new Process())
  372. {
  373. process.StartInfo.FileName = "/bin/bash";
  374. process.StartInfo.Arguments = arguments;
  375. process.StartInfo.RedirectStandardOutput = true;
  376. process.StartInfo.RedirectStandardError = true;
  377. process.StartInfo.UseShellExecute = false;
  378. process.StartInfo.CreateNoWindow = true;
  379. process.Start();
  380. string output = process.StandardOutput.ReadToEnd();
  381. string err = process.StandardError.ReadToEnd();
  382. process.WaitForExit();
  383. if (!string.IsNullOrEmpty(err))
  384. {
  385. UnityEngine.Debug.LogWarning($"[PodFinder] bash error: {err}");
  386. }
  387. return output;
  388. }
  389. }
  390. catch (Exception e)
  391. {
  392. UnityEngine.Debug.LogError($"[PodFinder] Exception running bash command: {e}");
  393. return null;
  394. }
  395. }
  396. private static string ParseGemEnvironment(string input, string pattern)
  397. {
  398. var match = Regex.Match(input, pattern);
  399. if (match.Success && match.Groups.Count > 1)
  400. {
  401. return match.Groups[1].Value.Trim();
  402. }
  403. return null;
  404. }
  405. private static string[] ParseGemEnvironmentMultiple(string input, string pattern)
  406. {
  407. var match = Regex.Match(input, pattern, RegexOptions.Multiline);
  408. if (!match.Success || match.Groups.Count < 2) return null;
  409. string block = match.Groups[1].Value;
  410. // 每行格式是类似 "- /path/to/gem"
  411. var lines = block.Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);
  412. var paths = new System.Collections.Generic.List<string>();
  413. foreach (var line in lines)
  414. {
  415. string trimmed = line.Trim();
  416. if (trimmed.StartsWith("- "))
  417. {
  418. paths.Add(trimmed.Substring(2).Trim());
  419. }
  420. }
  421. return paths.ToArray();
  422. }
  423. #endif
  424. }
  425. }