UpmBaseOperation.cs 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. using System;
  2. using System.Globalization;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using Semver;
  6. using UnityEngine;
  7. using UnityEditor.PackageManager.Requests;
  8. namespace UnityEditor.PackageManager.UI
  9. {
  10. internal abstract class UpmBaseOperation : IBaseOperation
  11. {
  12. private static readonly string[] s_WhitelistedUnityPackages =
  13. {
  14. "com.havok.physics",
  15. "com.ptc.vuforia.engine"
  16. };
  17. public static string GroupName(PackageSource origin)
  18. {
  19. var group = PackageGroupOrigins.Packages.ToString();
  20. if (origin == PackageSource.BuiltIn)
  21. group = PackageGroupOrigins.BuiltInPackages.ToString();
  22. return group;
  23. }
  24. protected static IEnumerable<PackageInfo> FromUpmPackageInfo(PackageManager.PackageInfo info, bool isCurrent = true)
  25. {
  26. var packages = new List<PackageInfo>();
  27. var displayName = info.displayName;
  28. if (string.IsNullOrEmpty(displayName))
  29. {
  30. displayName = info.name.Replace("com.unity.modules.", "");
  31. if (displayName.StartsWith("com.")) displayName = displayName.Replace("com.", "");
  32. if (displayName.StartsWith("unity.")) displayName = displayName.Replace("unity.", "");
  33. displayName = new CultureInfo("en-US").TextInfo.ToTitleCase(displayName);
  34. }
  35. string author = info.author.name;
  36. if (info.name.StartsWith("com.unity.") || s_WhitelistedUnityPackages.Any(name => info.name == name))
  37. author = "Unity Technologies Inc.";
  38. if (string.IsNullOrEmpty(author))
  39. author = "Other";
  40. var lastCompatible = info.versions.latestCompatible;
  41. var versions = new List<string>();
  42. versions.AddRange(info.versions.compatible);
  43. if (versions.FindIndex(version => version == info.version) == -1)
  44. {
  45. versions.Add(info.version);
  46. versions.Sort((left, right) =>
  47. {
  48. if (left == null || right == null) return 0;
  49. SemVersion leftVersion = left;
  50. SemVersion righVersion = right;
  51. return leftVersion.CompareByPrecedence(righVersion);
  52. });
  53. SemVersion packageVersion = info.version;
  54. if (!string.IsNullOrEmpty(lastCompatible))
  55. {
  56. SemVersion lastCompatibleVersion =
  57. string.IsNullOrEmpty(lastCompatible) ? (SemVersion)null : lastCompatible;
  58. if (packageVersion != null && string.IsNullOrEmpty(packageVersion.Prerelease) &&
  59. packageVersion.CompareByPrecedence(lastCompatibleVersion) > 0)
  60. lastCompatible = info.version;
  61. }
  62. else
  63. {
  64. if (packageVersion != null && string.IsNullOrEmpty(packageVersion.Prerelease))
  65. lastCompatible = info.version;
  66. }
  67. }
  68. foreach (var version in versions)
  69. {
  70. var isVersionCurrent = version == info.version && isCurrent;
  71. var isBuiltIn = info.source == PackageSource.BuiltIn;
  72. var isVerified = string.IsNullOrEmpty(SemVersion.Parse(version).Prerelease) && version == info.versions.recommended;
  73. var isUnityPackage = info.name.StartsWith("com.unity.") || s_WhitelistedUnityPackages.Any(name => info.name == name);
  74. var state = (isBuiltIn || info.version == lastCompatible ||   !isCurrent) ? PackageState.UpToDate : PackageState.Outdated;
  75. // Happens mostly when using a package that hasn't been in production yet.
  76. if (info.versions.all.Length <= 0)
  77. state = PackageState.UpToDate;
  78. if (info.errors.Length > 0)
  79. state = PackageState.Error;
  80. var packageInfo = new PackageInfo
  81. {
  82. Name = info.name,
  83. DisplayName = displayName,
  84. PackageId = version == info.version ? info.packageId : null,
  85. Version = version,
  86. Description = info.description,
  87. Category = info.category,
  88. IsCurrent = isVersionCurrent,
  89. IsLatest = version == lastCompatible,
  90. IsVerified = isVerified,
  91. Errors = info.errors.ToList(),
  92. Group = GroupName(info.source),
  93. State = state,
  94. Origin = isBuiltIn || isVersionCurrent ? info.source : PackageSource.Registry,
  95. Author = author,
  96. Info = info,
  97. IsUnityPackage = isUnityPackage
  98. };
  99. packages.Add(packageInfo);
  100. }
  101. return packages;
  102. }
  103. public static event Action<UpmBaseOperation> OnOperationStart = delegate {};
  104. public event Action<Error> OnOperationError = delegate {};
  105. public event Action OnOperationFinalized = delegate {};
  106. public Error ForceError { get; set; } // Allow external component to force an error on the requests (eg: testing)
  107. public Error Error { get; protected set; } // Keep last error
  108. public bool IsCompleted { get; private set; }
  109. protected abstract Request CreateRequest();
  110. [SerializeField]
  111. protected Request CurrentRequest;
  112. public readonly ThreadedDelay Delay = new ThreadedDelay();
  113. protected abstract void ProcessData();
  114. protected void Start()
  115. {
  116. Error = null;
  117. OnOperationStart(this);
  118. Delay.Start();
  119. if (TryForcedError())
  120. return;
  121. EditorApplication.update += Progress;
  122. }
  123. // Common progress code for all classes
  124. private void Progress()
  125. {
  126. if (!Delay.IsDone)
  127. return;
  128. // Create the request after the delay
  129. if (CurrentRequest == null)
  130. {
  131. CurrentRequest = CreateRequest();
  132. }
  133. // Since CurrentRequest's error property is private, we need to simulate
  134. // an error instead of just setting it.
  135. if (TryForcedError())
  136. return;
  137. if (CurrentRequest.IsCompleted)
  138. {
  139. if (CurrentRequest.Status == StatusCode.Success)
  140. OnDone();
  141. else if (CurrentRequest.Status >= StatusCode.Failure)
  142. OnError(CurrentRequest.Error);
  143. else
  144. Debug.LogError("Unsupported progress state " + CurrentRequest.Status);
  145. }
  146. }
  147. private void OnError(Error error)
  148. {
  149. try
  150. {
  151. Error = error;
  152. var message = "Cannot perform upm operation.";
  153. if (error != null)
  154. message = "Cannot perform upm operation: " + Error.message + " [" + Error.errorCode + "]";
  155. Debug.LogError(message);
  156. OnOperationError(Error);
  157. }
  158. catch (Exception exception)
  159. {
  160. Debug.LogError("Package Manager Window had an error while reporting an error in an operation: " + exception);
  161. }
  162. FinalizeOperation();
  163. }
  164. private void OnDone()
  165. {
  166. try
  167. {
  168. ProcessData();
  169. }
  170. catch (Exception error)
  171. {
  172. Debug.LogError("Package Manager Window had an error while completing an operation: " + error);
  173. }
  174. FinalizeOperation();
  175. }
  176. private void FinalizeOperation()
  177. {
  178. EditorApplication.update -= Progress;
  179. OnOperationFinalized();
  180. IsCompleted = true;
  181. }
  182. public void Cancel()
  183. {
  184. EditorApplication.update -= Progress;
  185. OnOperationError = delegate {};
  186. OnOperationFinalized = delegate {};
  187. IsCompleted = true;
  188. }
  189. private bool TryForcedError()
  190. {
  191. if (ForceError != null)
  192. {
  193. OnError(ForceError);
  194. return true;
  195. }
  196. return false;
  197. }
  198. }
  199. }