No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

VisualStudioInstallation.cs 6.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. /*---------------------------------------------------------------------------------------------
  2. * Copyright (c) Microsoft Corporation. All rights reserved.
  3. * Licensed under the MIT License. See License.txt in the project root for license information.
  4. *--------------------------------------------------------------------------------------------*/
  5. using System;
  6. using System.IO;
  7. using Microsoft.Win32;
  8. using Unity.CodeEditor;
  9. using IOPath = System.IO.Path;
  10. namespace Microsoft.Unity.VisualStudio.Editor
  11. {
  12. internal interface IVisualStudioInstallation
  13. {
  14. string Path { get; }
  15. bool SupportsAnalyzers { get; }
  16. Version LatestLanguageVersionSupported { get; }
  17. string[] GetAnalyzers();
  18. CodeEditor.Installation ToCodeEditorInstallation();
  19. }
  20. internal class VisualStudioInstallation : IVisualStudioInstallation
  21. {
  22. public string Name { get; set; }
  23. public string Path { get; set; }
  24. public Version Version { get; set; }
  25. public bool IsPrerelease { get; set; }
  26. public bool SupportsAnalyzers
  27. {
  28. get
  29. {
  30. if (VisualStudioEditor.IsWindows)
  31. return Version >= new Version(16, 3);
  32. if (VisualStudioEditor.IsOSX)
  33. return Version >= new Version(8, 3);
  34. return false;
  35. }
  36. }
  37. // C# language version support for Visual Studio
  38. private static VersionPair[] WindowsVersionTable =
  39. {
  40. // VisualStudio 2022
  41. new VersionPair(17,4, /* => */ 11,0),
  42. new VersionPair(17,0, /* => */ 10,0),
  43. // VisualStudio 2019
  44. new VersionPair(16,8, /* => */ 9,0),
  45. new VersionPair(16,0, /* => */ 8,0),
  46. // VisualStudio 2017
  47. new VersionPair(15,7, /* => */ 7,3),
  48. new VersionPair(15,5, /* => */ 7,2),
  49. new VersionPair(15,3, /* => */ 7,1),
  50. new VersionPair(15,0, /* => */ 7,0),
  51. };
  52. // C# language version support for Visual Studio for Mac
  53. private static VersionPair[] OSXVersionTable =
  54. {
  55. // VisualStudio for Mac 2022
  56. new VersionPair(17,4, /* => */ 11,0),
  57. new VersionPair(17,0, /* => */ 10,0),
  58. // VisualStudio for Mac 8.x
  59. new VersionPair(8,8, /* => */ 9,0),
  60. new VersionPair(8,3, /* => */ 8,0),
  61. new VersionPair(8,0, /* => */ 7,3),
  62. };
  63. public Version LatestLanguageVersionSupported
  64. {
  65. get
  66. {
  67. VersionPair[] versions = null;
  68. if (VisualStudioEditor.IsWindows)
  69. versions = WindowsVersionTable;
  70. if (VisualStudioEditor.IsOSX)
  71. versions = OSXVersionTable;
  72. if (versions != null)
  73. {
  74. foreach (var entry in versions)
  75. {
  76. if (Version >= entry.IdeVersion)
  77. return entry.LanguageVersion;
  78. }
  79. }
  80. // default to 7.0 given we support at least VS 2017
  81. return new Version(7, 0);
  82. }
  83. }
  84. private static string ReadRegistry(RegistryKey hive, string keyName, string valueName)
  85. {
  86. try
  87. {
  88. var unitykey = hive.OpenSubKey(keyName);
  89. var result = (string)unitykey?.GetValue(valueName);
  90. return result;
  91. }
  92. catch (Exception)
  93. {
  94. return null;
  95. }
  96. }
  97. private string GetWindowsBridgeFromRegistry()
  98. {
  99. var keyName = $"Software\\Microsoft\\Microsoft Visual Studio {Version.Major}.0 Tools for Unity";
  100. const string valueName = "UnityExtensionPath";
  101. var bridge = ReadRegistry(Registry.CurrentUser, keyName, valueName);
  102. if (string.IsNullOrEmpty(bridge))
  103. bridge = ReadRegistry(Registry.LocalMachine, keyName, valueName);
  104. return bridge;
  105. }
  106. // We only use this to find analyzers, we do not need to load this assembly anymore
  107. private string GetExtensionPath()
  108. {
  109. if (VisualStudioEditor.IsWindows)
  110. {
  111. const string extensionName = "Visual Studio Tools for Unity";
  112. const string extensionAssembly = "SyntaxTree.VisualStudio.Unity.dll";
  113. var vsDirectory = IOPath.GetDirectoryName(Path);
  114. var vstuDirectory = IOPath.Combine(vsDirectory, "Extensions", "Microsoft", extensionName);
  115. if (File.Exists(IOPath.Combine(vstuDirectory, extensionAssembly)))
  116. return vstuDirectory;
  117. }
  118. if (VisualStudioEditor.IsOSX)
  119. {
  120. const string addinName = "MonoDevelop.Unity";
  121. const string addinAssembly = addinName + ".dll";
  122. // user addins repository
  123. var localAddins = IOPath.Combine(
  124. Environment.GetFolderPath(Environment.SpecialFolder.Personal),
  125. $"Library/Application Support/VisualStudio/${Version.Major}.0" + "/LocalInstall/Addins");
  126. // In the user addins repository, the addins are suffixed by their versions, like `MonoDevelop.Unity.1.0`
  127. // When installing another local user addin, MD will remove files inside the folder
  128. // So we browse all VSTUM addins, and return the one with an addin assembly
  129. if (Directory.Exists(localAddins))
  130. {
  131. foreach (var folder in Directory.GetDirectories(localAddins, addinName + "*", SearchOption.TopDirectoryOnly))
  132. {
  133. if (File.Exists(IOPath.Combine(folder, addinAssembly)))
  134. return folder;
  135. }
  136. }
  137. // Check in Visual Studio.app/
  138. // In that case the name of the addin is used
  139. var addinPath = IOPath.Combine(Path, $"Contents/Resources/lib/monodevelop/AddIns/{addinName}");
  140. if (File.Exists(IOPath.Combine(addinPath, addinAssembly)))
  141. return addinPath;
  142. addinPath = IOPath.Combine(Path, $"Contents/MonoBundle/Addins/{addinName}");
  143. if (File.Exists(IOPath.Combine(addinPath, addinAssembly)))
  144. return addinPath;
  145. }
  146. return null;
  147. }
  148. private static string[] GetAnalyzers(string path)
  149. {
  150. var analyzersDirectory = IOPath.GetFullPath(IOPath.Combine(path, "Analyzers"));
  151. if (Directory.Exists(analyzersDirectory))
  152. return Directory.GetFiles(analyzersDirectory, "*Analyzers.dll", SearchOption.AllDirectories);
  153. return Array.Empty<string>();
  154. }
  155. public string[] GetAnalyzers()
  156. {
  157. var vstuPath = GetExtensionPath();
  158. if (string.IsNullOrEmpty(vstuPath))
  159. return Array.Empty<string>();
  160. if (VisualStudioEditor.IsOSX)
  161. return GetAnalyzers(vstuPath);
  162. if (VisualStudioEditor.IsWindows)
  163. {
  164. var analyzers = GetAnalyzers(vstuPath);
  165. if (analyzers?.Length > 0)
  166. return analyzers;
  167. var bridge = GetWindowsBridgeFromRegistry();
  168. if (File.Exists(bridge))
  169. return GetAnalyzers(IOPath.Combine(IOPath.GetDirectoryName(bridge), ".."));
  170. }
  171. // Local assets
  172. // return FileUtility.FindPackageAssetFullPath("Analyzers a:packages", ".Analyzers.dll");
  173. return Array.Empty<string>();
  174. }
  175. public CodeEditor.Installation ToCodeEditorInstallation()
  176. {
  177. return new CodeEditor.Installation() { Name = Name, Path = Path };
  178. }
  179. }
  180. }