Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/BuiltInTools/Watch/Context/EnvironmentOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ public TimeSpan GetProcessCleanupTimeout(bool isHotReloadEnabled)

private static string ValidateMuxerPath(string path)
{
Debug.Assert(Path.GetFileNameWithoutExtension(path) == "dotnet");
Debug.Assert(Path.GetFileName(path) == $"dotnet{PathUtilities.ExecutableExtension}");
return path;
}

Expand Down
11 changes: 7 additions & 4 deletions src/Cli/Microsoft.DotNet.Cli.Utils/Muxer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,14 @@ public string MuxerPath

public Muxer()
{
string muxerFileName = MuxerName + Constants.ExeSuffix;

// Most scenarios are running dotnet.dll as the app
// Root directory with muxer should be two above app base: <root>/sdk/<version>
string? rootPath = Path.GetDirectoryName(Path.GetDirectoryName(AppContext.BaseDirectory.TrimEnd(Path.DirectorySeparatorChar)));
if (rootPath is not null)
{
string muxerPathMaybe = Path.Combine(rootPath, $"{MuxerName}{FileNameSuffixes.CurrentPlatform.Exe}");
string muxerPathMaybe = Path.Combine(rootPath, muxerFileName);
if (File.Exists(muxerPathMaybe))
{
_muxerPath = muxerPathMaybe;
Expand All @@ -58,8 +60,9 @@ public Muxer()
string processPath = Process.GetCurrentProcess().MainModule.FileName;
#endif

// The current process should be dotnet in most normal scenarios except when dotnet.dll is loaded in a custom host like the testhost
if (processPath is not null && !Path.GetFileNameWithoutExtension(processPath).Equals("dotnet", StringComparison.OrdinalIgnoreCase))
// The current process should be dotnet in most normal scenarios except when dotnet.dll is loaded in a custom host like the testhost.
// Use GetFileName (not GetFileNameWithoutExtension) to avoid false matches with dotnet-prefixed names like "dotnet.Tests".
if (processPath is not null && !Path.GetFileName(processPath).Equals(muxerFileName, StringComparison.OrdinalIgnoreCase))
{
// SDK sets DOTNET_HOST_PATH as absolute path to current dotnet executable
processPath = Environment.GetEnvironmentVariable("DOTNET_HOST_PATH");
Expand All @@ -69,7 +72,7 @@ public Muxer()
var root = Environment.GetEnvironmentVariable("DOTNET_ROOT");
if (root is not null)
{
processPath = Path.Combine(root, $"dotnet{Constants.ExeSuffix}");
processPath = Path.Combine(root, muxerFileName);
}
}
}
Expand Down
5 changes: 3 additions & 2 deletions src/RazorSdk/Tool/ServerProtocol/ServerConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -290,14 +290,15 @@ private static string FindDotNetExecutable()
expectedPath = Process.GetCurrentProcess().MainModule.FileName;
#endif

if ("dotnet".Equals(Path.GetFileNameWithoutExtension(expectedPath), StringComparison.Ordinal))
// Use GetFileName (not GetFileNameWithoutExtension) to avoid false matches with dotnet-prefixed names like "dotnet.Tests".
var exeName = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "dotnet.exe" : "dotnet";
if (exeName.Equals(Path.GetFileName(expectedPath), StringComparison.Ordinal))
{
return expectedPath;
}

// We were probably running from Visual Studio or Build Tools and found MSBuild instead of dotnet. Use the PATH...
var paths = Environment.GetEnvironmentVariable("PATH").Split(Path.PathSeparator);
var exeName = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "dotnet.exe" : "dotnet";
foreach (string path in paths)
{
var dotnetPath = Path.Combine(path, exeName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -234,9 +234,7 @@ private sealed class CachedState
minimumVSDefinedSDKVersion);
}

string? fullPathToMuxer =
TryResolveMuxerFromSdkResolution(dotnetSdkDir)
?? Path.Combine(dotnetRoot, RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? Constants.DotNetExe : Constants.DotNet);
string? fullPathToMuxer = TryResolveMuxerFromSdkResolution(dotnetSdkDir) ?? Path.Combine(dotnetRoot, Constants.DotNetFileName);
if (File.Exists(fullPathToMuxer))
{
// keeping this in until this component no longer needs to handle 17.14.
Expand Down Expand Up @@ -349,10 +347,9 @@ private sealed class CachedState
/// </remarks>
private static string? TryResolveMuxerFromSdkResolution(string resolvedSdkDirectory)
{
var expectedFileName = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? Constants.DotNetExe : Constants.DotNet;
var currentDir = resolvedSdkDirectory;
var expectedDotnetRoot = Path.GetDirectoryName(Path.GetDirectoryName(currentDir));
var expectedMuxerPath = Path.Combine(expectedDotnetRoot, expectedFileName);
var expectedMuxerPath = Path.Combine(expectedDotnetRoot, Constants.DotNetFileName);
if (File.Exists(expectedMuxerPath))
{
return expectedMuxerPath;
Expand Down
2 changes: 1 addition & 1 deletion src/Resolvers/Microsoft.DotNet.NativeWrapper/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ internal static class Constants
{
public const string HostFxr = "hostfxr";
public const string DotNet = "dotnet";
public const string DotNetExe = "dotnet.exe";
public const string PATH = "PATH";
public const string DOTNET_MSBUILD_SDK_RESOLVER_CLI_DIR = "DOTNET_MSBUILD_SDK_RESOLVER_CLI_DIR";

public static readonly string ExeSuffix =
RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ".exe" : string.Empty;

public static readonly string DotNetFileName = DotNet + ExeSuffix;
public static class RuntimeProperty
{
public const string HostFxrPath = "HOSTFXR_PATH";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ private IEnumerable<string> SearchPaths
// the current process path on .NET Framework. We are expected to find dotnet on PATH.
dotnetExe = _getCurrentProcessPath();

if (string.IsNullOrEmpty(dotnetExe) || !Path.GetFileNameWithoutExtension(dotnetExe)
.Equals(Constants.DotNet, StringComparison.InvariantCultureIgnoreCase))
if (string.IsNullOrEmpty(dotnetExe) || !Path.GetFileName(dotnetExe)
.Equals(Constants.DotNetFileName, StringComparison.InvariantCultureIgnoreCase))
#endif
{
string? dotnetExeFromPath = GetCommandPath(Constants.DotNet);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@

namespace Microsoft.DotNet.Cli.Utils.Tests
{
public class GivenAnEnvironmentForResolution
public class GivenAnEnvironmentForResolution : SdkTest
{
public GivenAnEnvironmentForResolution(ITestOutputHelper log) : base(log)
{
}

[Fact]
public void ItIgnoresInvalidPath()
{
Expand All @@ -22,5 +26,26 @@ public void ItDoesNotReturnNullDotnetRootOnExtraPathSeparator()
var result = NativeWrapper.EnvironmentProvider.GetDotnetExeDirectory(getPathEnvVarFunc);
result.Should().NotBeNullOrWhiteSpace();
}

[Fact]
public void ItDoesNotMistakeDotnetPrefixedProcessForDotnetHost()
{
// Use separate directories for the dotnet host and the dotnet-prefixed process
// to verify the resolver finds dotnet via PATH, not from the process path.
var dotnetDir = TestAssetsManager.CreateTestDirectory(identifier: "dotnetHost").Path;
var processDir = TestAssetsManager.CreateTestDirectory(identifier: "processDir").Path;

var dotnetFileName = "dotnet" + (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ".exe" : string.Empty);
File.Create(Path.Combine(dotnetDir, dotnetFileName)).Close();

// Simulate a dotnet-prefixed process (e.g. dotnet.Tests from xunit v3)
Func<string, string?> getEnvVar = (input) => input.Equals("PATH") ? dotnetDir : null;
Func<string?> getProcessPath = () => Path.Combine(processDir, "dotnet.Tests");

var result = NativeWrapper.EnvironmentProvider.GetDotnetExeDirectory(getEnvVar, getProcessPath);

// Should resolve via PATH to the real dotnet directory, not the process directory
result.Should().Be(dotnetDir);
}
}
}
Loading