From 3c601c10e49b6908e0f017498630c79eb81c3064 Mon Sep 17 00:00:00 2001 From: Dustin Campbell Date: Thu, 13 Jul 2017 12:32:35 -0700 Subject: [PATCH 1/3] Use p/invoke rather than ugly bash commands to locate Mono on Unix --- .../Utilities/PlatformHelper.cs | 117 ++++-------------- 1 file changed, 21 insertions(+), 96 deletions(-) diff --git a/src/OmniSharp.Abstractions/Utilities/PlatformHelper.cs b/src/OmniSharp.Abstractions/Utilities/PlatformHelper.cs index 387d2d0e15..ef382f703f 100644 --- a/src/OmniSharp.Abstractions/Utilities/PlatformHelper.cs +++ b/src/OmniSharp.Abstractions/Utilities/PlatformHelper.cs @@ -1,6 +1,6 @@ using System; -using System.Collections.Generic; using System.IO; +using System.Runtime.InteropServices; namespace OmniSharp.Utilities { @@ -16,29 +16,35 @@ public static class PlatformHelper public static bool IsWindows => Path.DirectorySeparatorChar == '\\'; - private static string FindMonoPath() - { - // To locate Mono on unix, we use the 'which' command (https://en.wikipedia.org/wiki/Which_(Unix)) - var monoFilePath = RunOnBashAndCaptureOutput("which", "mono"); + // CharSet.Ansi is UTF8 on Unix + [DllImport("libc", EntryPoint = "realpath", CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr Unix_realpath(string path, IntPtr buffer); - if (string.IsNullOrEmpty(monoFilePath)) + [DllImport("libc", EntryPoint = "free", ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] + private static extern void Unix_free(IntPtr ptr); + + private static string RealPath(string path) + { + if (IsWindows) { - return null; + throw new PlatformNotSupportedException($"{nameof(RealPath)} can only be called on Unix."); } - Console.WriteLine($"Discovered Mono file path: {monoFilePath}"); + var ptr = Unix_realpath(path, IntPtr.Zero); + var result = Marshal.PtrToStringAnsi(ptr); // uses UTF8 on Unix + Unix_free(ptr); - // 'mono' is likely a symbolic link. Try to resolve it. - var resolvedMonoFilePath = ResolveSymbolicLink(monoFilePath); + return result; + } - if (StringComparer.OrdinalIgnoreCase.Compare(monoFilePath, resolvedMonoFilePath) == 0) + private static string FindMonoPath() + { + if (IsWindows) { - return monoFilePath; + throw new PlatformNotSupportedException($"{nameof(FindMonoPath)} can only be called on Unix."); } - Console.WriteLine($"Resolved symbolic link for Mono file path: {resolvedMonoFilePath}"); - - return resolvedMonoFilePath; + return RealPath("mono"); } private static string FindMonoXBuildFrameworksDirPath() @@ -72,86 +78,5 @@ private static string FindMonoXBuildFrameworksDirPath() ? monoXBuildFrameworksDirPath : null; } - - private static string ResolveSymbolicLink(string path) - { - var result = ResolveSymbolicLink(new List { path }); - - return CanonicalizePath(result); - } - - private static string ResolveSymbolicLink(List paths) - { - while (!HasCycle(paths)) - { - // We use 'readlink' to resolve symbolic links on unix. Note that OSX does not - // support the -f flag for recursively resolving symbolic links and canonicalzing - // the final path. - - var originalPath = paths[paths.Count - 1]; - var newPath = RunOnBashAndCaptureOutput("readlink", $"{originalPath}"); - - if (string.IsNullOrEmpty(newPath) || - string.CompareOrdinal(originalPath, newPath) == 0) - { - return originalPath; - } - - if (!newPath.StartsWith("/")) - { - var dir = File.Exists(originalPath) - ? Path.GetDirectoryName(originalPath) - : originalPath; - - newPath = Path.Combine(dir, newPath); - newPath = Path.GetFullPath(newPath); - } - - paths.Add(newPath); - } - - return null; - } - - private static bool HasCycle(List paths) - { - var target = paths[0]; - for (var i = 1; i < paths.Count; i++) - { - var path = paths[i]; - - if (string.CompareOrdinal(target, path) == 0) - { - return true; - } - } - - return false; - } - - private static string CanonicalizePath(string path) - { - if (File.Exists(path)) - { - return Path.Combine( - CanonicalizeDirectory(Path.GetDirectoryName(path)), - Path.GetFileName(path)); - } - else - { - return CanonicalizeDirectory(path); - } - } - - private static string CanonicalizeDirectory(string directoryName) - { - // Use "pwd -P" to get the directory name with all symbolic links on Unix. - return RunOnBashAndCaptureOutput("pwd", "-P", directoryName); - } - - private static string RunOnBashAndCaptureOutput(string fileName, string arguments, string workingDirectory = null) - { - return ProcessHelper.RunAndCaptureOutput("/bin/bash", $"-c '{fileName} {arguments}'", workingDirectory); - } } } From 9fdc48a136f4d3d5c81656cd2a0a2332e5874c3d Mon Sep 17 00:00:00 2001 From: Dustin Campbell Date: Thu, 13 Jul 2017 12:37:07 -0700 Subject: [PATCH 2/3] Add some documentation to Unix p/invokes and RealPath(...) function --- src/OmniSharp.Abstractions/Utilities/PlatformHelper.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/OmniSharp.Abstractions/Utilities/PlatformHelper.cs b/src/OmniSharp.Abstractions/Utilities/PlatformHelper.cs index ef382f703f..4485e9ca8b 100644 --- a/src/OmniSharp.Abstractions/Utilities/PlatformHelper.cs +++ b/src/OmniSharp.Abstractions/Utilities/PlatformHelper.cs @@ -16,13 +16,19 @@ public static class PlatformHelper public static bool IsWindows => Path.DirectorySeparatorChar == '\\'; + // http://man7.org/linux/man-pages/man3/realpath.3.html // CharSet.Ansi is UTF8 on Unix [DllImport("libc", EntryPoint = "realpath", CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr Unix_realpath(string path, IntPtr buffer); + // http://man7.org/linux/man-pages/man3/free.3.html [DllImport("libc", EntryPoint = "free", ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] private static extern void Unix_free(IntPtr ptr); + /// + /// Returns the conanicalized absolute path from a given path, expanding symbolic links and resolving + /// references to /./, /../ and extra '/' path characters. + /// private static string RealPath(string path) { if (IsWindows) From 10281bb7f4e4546c741f2634d0676fceeaf3cb1c Mon Sep 17 00:00:00 2001 From: Dustin Campbell Date: Thu, 13 Jul 2017 12:39:12 -0700 Subject: [PATCH 3/3] Return null for Mono paths on Windows --- .../Utilities/PlatformHelper.cs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/OmniSharp.Abstractions/Utilities/PlatformHelper.cs b/src/OmniSharp.Abstractions/Utilities/PlatformHelper.cs index 4485e9ca8b..557e6ed565 100644 --- a/src/OmniSharp.Abstractions/Utilities/PlatformHelper.cs +++ b/src/OmniSharp.Abstractions/Utilities/PlatformHelper.cs @@ -45,16 +45,18 @@ private static string RealPath(string path) private static string FindMonoPath() { - if (IsWindows) - { - throw new PlatformNotSupportedException($"{nameof(FindMonoPath)} can only be called on Unix."); - } - - return RealPath("mono"); + return !IsWindows + ? RealPath("mono") + : null; } private static string FindMonoXBuildFrameworksDirPath() { + if (IsWindows) + { + return null; + } + const string defaultXBuildFrameworksDirPath = "/usr/lib/mono/xbuild-frameworks"; if (Directory.Exists(defaultXBuildFrameworksDirPath)) {