123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457 |
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Linq;
- using JetBrains.Annotations;
- using Microsoft.Win32;
- using Unity.CodeEditor;
- using UnityEngine;
- namespace Packages.Rider.Editor
- {
- public interface IDiscovery
- {
- CodeEditor.Installation[] PathCallback();
- }
- public class Discovery : IDiscovery
- {
- public CodeEditor.Installation[] PathCallback()
- {
- return RiderPathLocator.GetAllRiderPaths()
- .Select(riderInfo => new CodeEditor.Installation
- {
- Path = riderInfo.Path,
- Name = riderInfo.Presentation
- })
- .OrderBy(a=>a.Name)
- .ToArray();
- }
- }
- /// <summary>
- /// This code is a modified version of the JetBrains resharper-unity plugin listed here:
- /// https://github.com/JetBrains/resharper-unity/blob/master/unity/JetBrains.Rider.Unity.Editor/EditorPlugin/RiderPathLocator.cs
- /// </summary>
- public static class RiderPathLocator
- {
- #if !(UNITY_4_7 || UNITY_5_5)
- [UsedImplicitly] // Used in com.unity.ide.rider
- public static RiderInfo[] GetAllRiderPaths()
- {
- try
- {
- switch (SystemInfo.operatingSystemFamily)
- {
- case OperatingSystemFamily.Windows:
- {
- return CollectRiderInfosWindows();
- }
- case OperatingSystemFamily.MacOSX:
- {
- return CollectRiderInfosMac();
- }
- case OperatingSystemFamily.Linux:
- {
- return CollectAllRiderPathsLinux();
- }
- }
- }
- catch (Exception e)
- {
- Debug.LogException(e);
- }
- return new RiderInfo[0];
- }
- #endif
- #if RIDER_EDITOR_PLUGIN // can't be used in com.unity.ide.rider
- internal static RiderInfo[] GetAllFoundInfos(OperatingSystemFamilyRider operatingSystemFamily)
- {
- try
- {
- switch (operatingSystemFamily)
- {
- case OperatingSystemFamilyRider.Windows:
- {
- return CollectRiderInfosWindows();
- }
- case OperatingSystemFamilyRider.MacOSX:
- {
- return CollectRiderInfosMac();
- }
- case OperatingSystemFamilyRider.Linux:
- {
- return CollectAllRiderPathsLinux();
- }
- }
- }
- catch (Exception e)
- {
- Debug.LogException(e);
- }
- return new RiderInfo[0];
- }
- internal static string[] GetAllFoundPaths(OperatingSystemFamilyRider operatingSystemFamily)
- {
- return GetAllFoundInfos(operatingSystemFamily).Select(a=>a.Path).ToArray();
- }
- #endif
- private static RiderInfo[] CollectAllRiderPathsLinux()
- {
- var installInfos = new List<RiderInfo>();
- var home = Environment.GetEnvironmentVariable("HOME");
- if (!string.IsNullOrEmpty(home))
- {
- var toolboxRiderRootPath = GetToolboxBaseDir();
- installInfos.AddRange(CollectPathsFromToolbox(toolboxRiderRootPath, "bin", "rider.sh", false)
- .Select(a => new RiderInfo(a, true)).ToList());
- //$Home/.local/share/applications/jetbrains-rider.desktop
- var shortcut = new FileInfo(Path.Combine(home, @".local/share/applications/jetbrains-rider.desktop"));
- if (shortcut.Exists)
- {
- var lines = File.ReadAllLines(shortcut.FullName);
- foreach (var line in lines)
- {
- if (!line.StartsWith("Exec=\""))
- continue;
- var path = line.Split('"').Where((item, index) => index == 1).SingleOrDefault();
- if (string.IsNullOrEmpty(path))
- continue;
- if (installInfos.Any(a => a.Path == path)) // avoid adding similar build as from toolbox
- continue;
- installInfos.Add(new RiderInfo(path, false));
- }
- }
- }
- // snap install
- var snapInstallPath = "/snap/rider/current/bin/rider.sh";
- if (new FileInfo(snapInstallPath).Exists)
- installInfos.Add(new RiderInfo(snapInstallPath, false));
-
- return installInfos.ToArray();
- }
- private static RiderInfo[] CollectRiderInfosMac()
- {
- var installInfos = new List<RiderInfo>();
- // "/Applications/*Rider*.app"
- var folder = new DirectoryInfo("/Applications");
- if (folder.Exists)
- {
- installInfos.AddRange(folder.GetDirectories("*Rider*.app")
- .Select(a => new RiderInfo(a.FullName, false))
- .ToList());
- }
- // /Users/user/Library/Application Support/JetBrains/Toolbox/apps/Rider/ch-1/181.3870.267/Rider EAP.app
- var toolboxRiderRootPath = GetToolboxBaseDir();
- var paths = CollectPathsFromToolbox(toolboxRiderRootPath, "", "Rider*.app", true)
- .Select(a => new RiderInfo(a, true));
- installInfos.AddRange(paths);
- return installInfos.ToArray();
- }
- private static RiderInfo[] CollectRiderInfosWindows()
- {
- var installInfos = new List<RiderInfo>();
- var toolboxRiderRootPath = GetToolboxBaseDir();
- var installPathsToolbox = CollectPathsFromToolbox(toolboxRiderRootPath, "bin", "rider64.exe", false).ToList();
- installInfos.AddRange(installPathsToolbox.Select(a => new RiderInfo(a, true)).ToList());
-
- var installPaths = new List<string>();
- const string registryKey = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
- CollectPathsFromRegistry(registryKey, installPaths);
- const string wowRegistryKey = @"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall";
- CollectPathsFromRegistry(wowRegistryKey, installPaths);
-
- installInfos.AddRange(installPaths.Select(a => new RiderInfo(a, false)).ToList());
- return installInfos.ToArray();
- }
- private static string GetToolboxBaseDir()
- {
- switch (SystemInfo.operatingSystemFamily)
- {
- case OperatingSystemFamily.Windows:
- {
- var localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
- return Path.Combine(localAppData, @"JetBrains\Toolbox\apps\Rider");
- }
- case OperatingSystemFamily.MacOSX:
- {
- var home = Environment.GetEnvironmentVariable("HOME");
- if (!string.IsNullOrEmpty(home))
- {
- return Path.Combine(home, @"Library/Application Support/JetBrains/Toolbox/apps/Rider");
- }
- break;
- }
- case OperatingSystemFamily.Linux:
- {
- var home = Environment.GetEnvironmentVariable("HOME");
- if (!string.IsNullOrEmpty(home))
- {
- return Path.Combine(home, @".local/share/JetBrains/Toolbox/apps/Rider");
- }
- break;
- }
- }
- return string.Empty;
- }
-
- internal static string GetBuildNumber(string path)
- {
- var file = new FileInfo(Path.Combine(path, GetRelativePathToBuildTxt()));
- if (!file.Exists)
- return string.Empty;
- var text = File.ReadAllText(file.FullName);
- if (text.Length > 3)
- return text.Substring(3);
- return string.Empty;
- }
- internal static bool IsToolbox(string path)
- {
- return path.StartsWith(GetToolboxBaseDir());
- }
- private static string GetRelativePathToBuildTxt()
- {
- switch (SystemInfo.operatingSystemFamily)
- {
- case OperatingSystemFamily.Windows:
- case OperatingSystemFamily.Linux:
- return "../../build.txt";
- case OperatingSystemFamily.MacOSX:
- return "Contents/Resources/build.txt";
- }
- throw new Exception("Unknown OS");
- }
- private static void CollectPathsFromRegistry(string registryKey, List<string> installPaths)
- {
- using (var key = Registry.LocalMachine.OpenSubKey(registryKey))
- {
- if (key == null) return;
- foreach (var subkeyName in key.GetSubKeyNames().Where(a => a.Contains("Rider")))
- {
- using (var subkey = key.OpenSubKey(subkeyName))
- {
- var folderObject = subkey?.GetValue("InstallLocation");
- if (folderObject == null) continue;
- var folder = folderObject.ToString();
- var possiblePath = Path.Combine(folder, @"bin\rider64.exe");
- if (File.Exists(possiblePath))
- installPaths.Add(possiblePath);
- }
- }
- }
- }
- private static string[] CollectPathsFromToolbox(string toolboxRiderRootPath, string dirName, string searchPattern,
- bool isMac)
- {
- if (!Directory.Exists(toolboxRiderRootPath))
- return new string[0];
- var channelDirs = Directory.GetDirectories(toolboxRiderRootPath);
- var paths = channelDirs.SelectMany(channelDir =>
- {
- try
- {
- // use history.json - last entry stands for the active build https://jetbrains.slack.com/archives/C07KNP99D/p1547807024066500?thread_ts=1547731708.057700&cid=C07KNP99D
- var historyFile = Path.Combine(channelDir, ".history.json");
- if (File.Exists(historyFile))
- {
- var json = File.ReadAllText(historyFile);
- var build = ToolboxHistory.GetLatestBuildFromJson(json);
- if (build != null)
- {
- var buildDir = Path.Combine(channelDir, build);
- var executablePaths = GetExecutablePaths(dirName, searchPattern, isMac, buildDir);
- if (executablePaths.Any())
- return executablePaths;
- }
- }
- var channelFile = Path.Combine(channelDir, ".channel.settings.json");
- if (File.Exists(channelFile))
- {
- var json = File.ReadAllText(channelFile).Replace("active-application", "active_application");
- var build = ToolboxInstallData.GetLatestBuildFromJson(json);
- if (build != null)
- {
- var buildDir = Path.Combine(channelDir, build);
- var executablePaths = GetExecutablePaths(dirName, searchPattern, isMac, buildDir);
- if (executablePaths.Any())
- return executablePaths;
- }
- }
- // changes in toolbox json files format may brake the logic above, so return all found Rider installations
- return Directory.GetDirectories(channelDir)
- .SelectMany(buildDir => GetExecutablePaths(dirName, searchPattern, isMac, buildDir));
- }
- catch (Exception e)
- {
- // do not write to Debug.Log, just log it.
- Logger.Warn($"Failed to get RiderPath from {channelDir}", e);
- }
- return new string[0];
- })
- .Where(c => !string.IsNullOrEmpty(c))
- .ToArray();
- return paths;
- }
- private static string[] GetExecutablePaths(string dirName, string searchPattern, bool isMac, string buildDir)
- {
- var folder = new DirectoryInfo(Path.Combine(buildDir, dirName));
- if (!folder.Exists)
- return new string[0];
- if (!isMac)
- return new[] {Path.Combine(folder.FullName, searchPattern)}.Where(File.Exists).ToArray();
- return folder.GetDirectories(searchPattern).Select(f => f.FullName)
- .Where(Directory.Exists).ToArray();
- }
- // Disable the "field is never assigned" compiler warning. We never assign it, but Unity does.
- // Note that Unity disable this warning in the generated C# projects
- #pragma warning disable 0649
- [Serializable]
- class ToolboxHistory
- {
- public List<ItemNode> history;
- [CanBeNull]
- public static string GetLatestBuildFromJson(string json)
- {
- try
- {
- #if UNITY_4_7 || UNITY_5_5
- return JsonConvert.DeserializeObject<ToolboxHistory>(json).history.LastOrDefault()?.item.build;
- #else
- return JsonUtility.FromJson<ToolboxHistory>(json).history.LastOrDefault()?.item.build;
- #endif
- }
- catch (Exception)
- {
- Logger.Warn($"Failed to get latest build from json {json}");
- }
- return null;
- }
- }
- [Serializable]
- class ItemNode
- {
- public BuildNode item;
- }
- [Serializable]
- class BuildNode
- {
- public string build;
- }
- // ReSharper disable once ClassNeverInstantiated.Global
- [Serializable]
- class ToolboxInstallData
- {
- // ReSharper disable once InconsistentNaming
- public ActiveApplication active_application;
- [CanBeNull]
- public static string GetLatestBuildFromJson(string json)
- {
- try
- {
- #if UNITY_4_7 || UNITY_5_5
- var toolbox = JsonConvert.DeserializeObject<ToolboxInstallData>(json);
- #else
- var toolbox = JsonUtility.FromJson<ToolboxInstallData>(json);
- #endif
- var builds = toolbox.active_application.builds;
- if (builds != null && builds.Any())
- return builds.First();
- }
- catch (Exception)
- {
- Logger.Warn($"Failed to get latest build from json {json}");
- }
- return null;
- }
- }
- [Serializable]
- class ActiveApplication
- {
- // ReSharper disable once InconsistentNaming
- public List<string> builds;
- }
- #pragma warning restore 0649
- public struct RiderInfo
- {
- public bool IsToolbox;
- public string Presentation;
- public string BuildVersion;
- public string Path;
- public RiderInfo(string path, bool isToolbox)
- {
- if (path == RiderScriptEditor.CurrentEditor)
- {
- RiderScriptEditorData.instance.Init();
- BuildVersion = RiderScriptEditorData.instance.currentEditorVersion;
- }
- else
- BuildVersion = GetBuildNumber(path);
- Path = new FileInfo(path).FullName; // normalize separators
- var presentation = "Rider " + BuildVersion;
- if (isToolbox)
- presentation += " (JetBrains Toolbox)";
- Presentation = presentation;
- IsToolbox = isToolbox;
- }
- }
- private static class Logger
- {
- internal static void Warn(string message, Exception e = null)
- {
- #if RIDER_EDITOR_PLUGIN // can't be used in com.unity.ide.rider
- Log.GetLog(typeof(RiderPathLocator).Name).Warn(message);
- if (e != null)
- Log.GetLog(typeof(RiderPathLocator).Name).Warn(e);
- #else
- Debug.LogError(message);
- if (e != null)
- Debug.LogException(e);
- #endif
- }
- }
- }
- }
|