From 5c6b7e85f55e35278b1f088dd305b69d72950803 Mon Sep 17 00:00:00 2001 From: kasperk81 <83082615+kasperk81@users.noreply.github.com> Date: Fri, 27 Dec 2024 13:26:19 +0000 Subject: [PATCH 1/2] realpath in c# --- src/Cli/dotnet/StatInterop.cs | 71 ------------------- .../EnvironmentProvider.cs | 17 +++-- .../Microsoft.DotNet.NativeWrapper/Interop.cs | 16 +---- 3 files changed, 13 insertions(+), 91 deletions(-) delete mode 100644 src/Cli/dotnet/StatInterop.cs diff --git a/src/Cli/dotnet/StatInterop.cs b/src/Cli/dotnet/StatInterop.cs deleted file mode 100644 index c95d52362764..000000000000 --- a/src/Cli/dotnet/StatInterop.cs +++ /dev/null @@ -1,71 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#nullable disable - -namespace Microsoft.DotNet.Cli; - -// https://github.com/dotnet/runtime/blob/main/src/libraries/Common/src/Interop/Unix/System.Native/Interop.Stat.cs - -internal static class StatInterop -{ - // Even though csc will by default use a sequential layout, a CS0649 warning as error - // is produced for un-assigned fields when no StructLayout is specified. - // - // Explicitly saying Sequential disables that warning/error for consumers which only - // use Stat in debug builds. - [StructLayout(LayoutKind.Sequential)] - internal struct FileStatus - { - internal FileStatusFlags Flags; - internal int Mode; - internal uint Uid; - internal uint Gid; - internal long Size; - internal long ATime; - internal long ATimeNsec; - internal long MTime; - internal long MTimeNsec; - internal long CTime; - internal long CTimeNsec; - internal long BirthTime; - internal long BirthTimeNsec; - internal long Dev; - internal long RDev; - internal long Ino; - internal uint UserFlags; - } - - [Flags] - internal enum Permissions - { - Mask = S_IRWXU | S_IRWXG | S_IRWXO, - - S_IRWXU = S_IRUSR | S_IWUSR | S_IXUSR, - S_IRUSR = 0x100, - S_IWUSR = 0x80, - S_IXUSR = 0x40, - - S_IRWXG = S_IRGRP | S_IWGRP | S_IXGRP, - S_IRGRP = 0x20, - S_IWGRP = 0x10, - S_IXGRP = 0x8, - - S_IRWXO = S_IROTH | S_IWOTH | S_IXOTH, - S_IROTH = 0x4, - S_IWOTH = 0x2, - S_IXOTH = 0x1, - - S_IXUGO = S_IXUSR | S_IXGRP | S_IXOTH, - } - - [Flags] - internal enum FileStatusFlags - { - None = 0, - HasBirthTime = 1, - } - - [DllImport("libSystem.Native", EntryPoint = "SystemNative_LStat", SetLastError = true)] - internal static extern int LStat(string path, out FileStatus output); -} diff --git a/src/Resolvers/Microsoft.DotNet.NativeWrapper/EnvironmentProvider.cs b/src/Resolvers/Microsoft.DotNet.NativeWrapper/EnvironmentProvider.cs index 96c203cc3f4d..487bc8f39a3d 100644 --- a/src/Resolvers/Microsoft.DotNet.NativeWrapper/EnvironmentProvider.cs +++ b/src/Resolvers/Microsoft.DotNet.NativeWrapper/EnvironmentProvider.cs @@ -59,7 +59,7 @@ private IEnumerable SearchPaths } string? dotnetExe; -#if NETCOREAPP +#if NET // The dotnet executable is loading only the .NET version of this code so there is no point checking // the current process path on .NET Framework. We are expected to find dotnet on PATH. dotnetExe = _getCurrentProcessPath(); @@ -70,12 +70,19 @@ private IEnumerable SearchPaths { string? dotnetExeFromPath = GetCommandPath(Constants.DotNet); - if (dotnetExeFromPath != null && !Interop.RunningOnWindows) +#if NET + if (dotnetExeFromPath != null && !OperatingSystem.IsWindows()) { // e.g. on Linux the 'dotnet' command from PATH is a symlink so we need to // resolve it to get the actual path to the binary - dotnetExeFromPath = Interop.Unix.realpath(dotnetExeFromPath) ?? dotnetExeFromPath; + FileSystemInfo fileInfo = new FileInfo(dotnetExeFromPath); + if ((fileInfo.Attributes & FileAttributes.ReparsePoint) != 0) + { + fileInfo = fileInfo.ResolveLinkTarget(returnFinalTarget: true) ?? fileInfo; + } + dotnetExeFromPath = fileInfo.FullName; } +#endif if (!string.IsNullOrWhiteSpace(dotnetExeFromPath)) { @@ -86,7 +93,7 @@ private IEnumerable SearchPaths log?.Invoke($"GetDotnetExeDirectory: dotnet command path not found. Using current process"); log?.Invoke($"GetDotnetExeDirectory: Path variable: {_getEnvironmentVariable(Constants.PATH)}"); -#if !NETCOREAPP +#if !NET // If we failed to find dotnet on PATH, we revert to the old behavior of returning the current process // path. This is really an error state but we're keeping the contract of always returning a non-empty // path for backward compatibility. @@ -123,7 +130,7 @@ private IEnumerable SearchPaths private static string? GetCurrentProcessPath() { string? currentProcessPath; -#if NET6_0_OR_GREATER +#if NET currentProcessPath = Environment.ProcessPath; #else currentProcessPath = Process.GetCurrentProcess().MainModule.FileName; diff --git a/src/Resolvers/Microsoft.DotNet.NativeWrapper/Interop.cs b/src/Resolvers/Microsoft.DotNet.NativeWrapper/Interop.cs index 678577e9053f..7bce721fc98d 100644 --- a/src/Resolvers/Microsoft.DotNet.NativeWrapper/Interop.cs +++ b/src/Resolvers/Microsoft.DotNet.NativeWrapper/Interop.cs @@ -28,7 +28,7 @@ static Interop() } // MSBuild SDK resolvers are required to be AnyCPU, but we have a native dependency and .NETFramework does not - // have a built-in facility for dynamically loading user native dlls for the appropriate platform. We therefore + // have a built-in facility for dynamically loading user native dlls for the appropriate platform. We therefore // preload the version with the correct architecture (from a corresponding sub-folder relative to us) on static // construction so that subsequent P/Invokes can find it. private static void PreloadWindowsLibrary(string dllFileName) @@ -180,20 +180,6 @@ internal delegate void hostfxr_get_available_sdks_result_fn( internal static extern int hostfxr_get_available_sdks( string? exe_dir, hostfxr_get_available_sdks_result_fn result); - - [DllImport("libc", CharSet = UTF8, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr realpath(string path, IntPtr buffer); - - [DllImport("libc", ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] - private static extern void free(IntPtr ptr); - - public static string? realpath(string path) - { - var ptr = realpath(path, IntPtr.Zero); - var result = PtrToStringUTF8(ptr); - free(ptr); - return result; - } } } } From 13e1e4ad51514632959b686b5c48ab91b55cff6f Mon Sep 17 00:00:00 2001 From: kasperk81 <83082615+kasperk81@users.noreply.github.com> Date: Sun, 26 Oct 2025 11:41:28 +0200 Subject: [PATCH 2/2] update --- src/Resolvers/Microsoft.DotNet.NativeWrapper/Interop.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Resolvers/Microsoft.DotNet.NativeWrapper/Interop.cs b/src/Resolvers/Microsoft.DotNet.NativeWrapper/Interop.cs index 7bce721fc98d..1c57fc445757 100644 --- a/src/Resolvers/Microsoft.DotNet.NativeWrapper/Interop.cs +++ b/src/Resolvers/Microsoft.DotNet.NativeWrapper/Interop.cs @@ -8,7 +8,7 @@ namespace Microsoft.DotNet.NativeWrapper public static partial class Interop { public static readonly bool RunningOnWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); -#if NETCOREAPP +#if NET private static readonly string? HostFxrPath; #endif @@ -18,7 +18,7 @@ static Interop() { PreloadWindowsLibrary(Constants.HostFxr); } -#if NETCOREAPP +#if NET else { HostFxrPath = (string)AppContext.GetData(Constants.RuntimeProperty.HostFxrPath)!; @@ -41,7 +41,7 @@ private static void PreloadWindowsLibrary(string dllFileName) LoadLibraryExW(dllPath, IntPtr.Zero, LOAD_WITH_ALTERED_SEARCH_PATH); } -#if NETCOREAPP +#if NET private static IntPtr HostFxrResolver(Assembly assembly, string libraryName) { if (libraryName != Constants.HostFxr)