diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/EnvironmentProvider.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/EnvironmentProvider.cs index 06c64b67110a..4048868bb7a1 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/EnvironmentProvider.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/EnvironmentProvider.cs @@ -39,7 +39,7 @@ private IEnumerable SearchPaths { if (_searchPaths == null) { - var searchPaths = new List { AppContext.BaseDirectory }; + var searchPaths = new List { SdkPaths.SdkDirectory }; searchPaths.AddRange(Environment .GetEnvironmentVariable("PATH")? diff --git a/src/Cli/dotnet/Commands/Format/FormatForwardingApp.cs b/src/Cli/dotnet/Commands/Format/FormatForwardingApp.cs index 22319d76f3f5..a6049bf47329 100644 --- a/src/Cli/dotnet/Commands/Format/FormatForwardingApp.cs +++ b/src/Cli/dotnet/Commands/Format/FormatForwardingApp.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using Microsoft.DotNet.Cli.Utils; + namespace Microsoft.DotNet.Cli.Commands.Format; public class FormatForwardingApp(IEnumerable argsToForward) @@ -11,11 +13,11 @@ public class FormatForwardingApp(IEnumerable argsToForward) runtimeConfig: GetRuntimeConfigPath()) { private static string GetForwardApplicationPath() - => Path.Combine(AppContext.BaseDirectory, "DotnetTools/dotnet-format/dotnet-format.dll"); + => Path.Combine(SdkPaths.SdkDirectory, "DotnetTools", "dotnet-format", "dotnet-format.dll"); private static string GetDepsFilePath() - => Path.Combine(AppContext.BaseDirectory, "DotnetTools/dotnet-format/dotnet-format.deps.json"); + => Path.Combine(SdkPaths.SdkDirectory, "DotnetTools", "dotnet-format", "dotnet-format.deps.json"); private static string GetRuntimeConfigPath() - => Path.Combine(AppContext.BaseDirectory, "DotnetTools/dotnet-format/dotnet-format.runtimeconfig.json"); + => Path.Combine(SdkPaths.SdkDirectory, "DotnetTools", "dotnet-format", "dotnet-format.runtimeconfig.json"); } diff --git a/src/Cli/dotnet/Commands/Fsi/FsiForwardingApp.cs b/src/Cli/dotnet/Commands/Fsi/FsiForwardingApp.cs index ff4e435de516..4fd2b0c0556e 100644 --- a/src/Cli/dotnet/Commands/Fsi/FsiForwardingApp.cs +++ b/src/Cli/dotnet/Commands/Fsi/FsiForwardingApp.cs @@ -9,8 +9,9 @@ namespace Microsoft.DotNet.Cli.Commands.Fsi; public class FsiForwardingApp(string[] arguments) : ForwardingApp(GetFsiAppPath(), processArguments(arguments)) { - private const string FsiDllName = @"FSharp/fsi.dll"; - private const string FsiExeName = @"FSharp/fsi.exe"; + private const string FsiDirectoryName = "FSharp"; + private const string FsiDllName = "fsi.dll"; + private const string FsiExeName = "fsi.exe"; static string[] processArguments(string[] args) { @@ -45,16 +46,16 @@ private static bool exists(string path) * So here we look for fsi.dll, if it's found then we will return the path to it, otherwise we return fsi.exe * the reason for using this bridging mechanism is to simplify the coordination between F#/VS and the dotnet sdk */ - private static string GetFsiAppPath() + internal static string GetFsiAppPath() { - var dllPath = Path.Combine(AppContext.BaseDirectory, FsiDllName); + var dllPath = Path.Combine(SdkPaths.SdkDirectory, FsiDirectoryName, FsiDllName); if (exists(dllPath)) { return dllPath; } else { - return Path.Combine(AppContext.BaseDirectory, FsiExeName); + return Path.Combine(SdkPaths.SdkDirectory, FsiDirectoryName, FsiExeName); } } } diff --git a/src/Cli/dotnet/Commands/Test/VSTest/VSTestForwardingApp.cs b/src/Cli/dotnet/Commands/Test/VSTest/VSTestForwardingApp.cs index 7f2773df467b..8ecc0b5dc7b7 100644 --- a/src/Cli/dotnet/Commands/Test/VSTest/VSTestForwardingApp.cs +++ b/src/Cli/dotnet/Commands/Test/VSTest/VSTestForwardingApp.cs @@ -24,7 +24,7 @@ public VSTestForwardingApp(IEnumerable argsToForward) VSTestTrace.SafeWriteTrace(() => $"Forwarding to '{GetVSTestExePath()}' with args \"{string.Join(" | ", argsToForward ?? [])}\""); } - private static string GetVSTestExePath() + internal static string GetVSTestExePath() { // Provide custom path to vstest.console.dll or exe to be able to test it against any version of // vstest.console. This is useful especially for our integration tests. @@ -35,7 +35,7 @@ private static string GetVSTestExePath() return vsTestConsolePath; } - return Path.Combine(AppContext.BaseDirectory, VstestAppName); + return Path.Combine(SdkPaths.SdkDirectory, VstestAppName); } internal static Dictionary GetVSTestRootVariables() diff --git a/src/Cli/dotnet/NuGetForwardingApp.cs b/src/Cli/dotnet/NuGetForwardingApp.cs index a8e99477dccf..a3aeae4d6da5 100644 --- a/src/Cli/dotnet/NuGetForwardingApp.cs +++ b/src/Cli/dotnet/NuGetForwardingApp.cs @@ -3,6 +3,8 @@ #nullable disable +using Microsoft.DotNet.Cli.Utils; + namespace Microsoft.DotNet.Cli; public class NuGetForwardingApp @@ -35,10 +37,10 @@ public NuGetForwardingApp WithEnvironmentVariable(string name, string value) return this; } - private static string GetNuGetExePath() + internal static string GetNuGetExePath() { return Path.Combine( - AppContext.BaseDirectory, + SdkPaths.SdkDirectory, s_nugetExeName); } } diff --git a/test/dotnet-aot.Tests/EnvironmentProviderTests.cs b/test/dotnet-aot.Tests/EnvironmentProviderTests.cs new file mode 100644 index 000000000000..ed14164d7b29 --- /dev/null +++ b/test/dotnet-aot.Tests/EnvironmentProviderTests.cs @@ -0,0 +1,33 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.DotNet.Cli.Utils; + +namespace Microsoft.DotNet.Cli.Tests; + +[TestClass] +public class EnvironmentProviderTests +{ + [TestMethod] + public void CommandSearchStartsInVersionedSdkDirectory() + { + string sdkDirectory = Path.Combine(Path.GetTempPath(), "dotnet", "sdk", $"test-version-{Guid.NewGuid():N}"); + string commandName = $"sdk-command-{Guid.NewGuid():N}"; + string commandPath = Path.Combine(sdkDirectory, commandName); + Directory.CreateDirectory(sdkDirectory); + File.WriteAllText(commandPath, string.Empty); + + try + { + using var _ = new SdkDirectoryScope(sdkDirectory); + + Assert.AreEqual( + commandPath, + new EnvironmentProvider().GetCommandPath(commandName, string.Empty)); + } + finally + { + Directory.Delete(sdkDirectory, recursive: true); + } + } +} diff --git a/test/dotnet-aot.Tests/FormatForwardingAppTests.cs b/test/dotnet-aot.Tests/FormatForwardingAppTests.cs new file mode 100644 index 000000000000..00e81d8afcee --- /dev/null +++ b/test/dotnet-aot.Tests/FormatForwardingAppTests.cs @@ -0,0 +1,25 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.DotNet.Cli.Commands.Format; + +namespace Microsoft.DotNet.Cli.Tests; + +[TestClass] +public class FormatForwardingAppTests +{ + [TestMethod] + public void FormatForwardingUsesVersionedSdkDirectory() + { + string sdkDirectory = Path.Combine(Path.GetTempPath(), "dotnet", "sdk", "test-version"); + using var _ = new SdkDirectoryScope(sdkDirectory + Path.DirectorySeparatorChar); + + string dotnetFormatDirectory = Path.Combine(sdkDirectory, "DotnetTools", "dotnet-format"); + string arguments = new FormatForwardingApp(["--help"]).GetProcessStartInfo().Arguments; + + arguments.Should().Contain(Path.Combine(dotnetFormatDirectory, "dotnet-format.deps.json")); + arguments.Should().Contain(Path.Combine(dotnetFormatDirectory, "dotnet-format.runtimeconfig.json")); + arguments.Should().Contain(Path.Combine(dotnetFormatDirectory, "dotnet-format.dll")); + arguments.Should().Contain("--help"); + } +} diff --git a/test/dotnet-aot.Tests/MSBuildEvaluationTests.cs b/test/dotnet-aot.Tests/MSBuildEvaluationTests.cs index 4ef6a2216dc5..3c0cf58988dd 100644 --- a/test/dotnet-aot.Tests/MSBuildEvaluationTests.cs +++ b/test/dotnet-aot.Tests/MSBuildEvaluationTests.cs @@ -15,23 +15,6 @@ namespace Microsoft.DotNet.Cli.Tests; [TestClass] public class MSBuildEvaluationTests { - private readonly struct SdkDirectoryScope : IDisposable - { - private readonly object? _previousSdkRoot = AppContext.GetData(SdkPaths.DataName); - - public SdkDirectoryScope(string sdkDirectory) - { - AppContext.SetData(SdkPaths.DataName, sdkDirectory); - SdkPaths.ClearSdkDirectoryCacheForTests(); - } - - public void Dispose() - { - AppContext.SetData(SdkPaths.DataName, _previousSdkRoot); - SdkPaths.ClearSdkDirectoryCacheForTests(); - } - } - public TestContext TestContext { get; set; } = null!; [TestMethod] diff --git a/test/dotnet-aot.Tests/SdkDirectoryScope.cs b/test/dotnet-aot.Tests/SdkDirectoryScope.cs new file mode 100644 index 000000000000..a85097544f67 --- /dev/null +++ b/test/dotnet-aot.Tests/SdkDirectoryScope.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.DotNet.Cli.Utils; + +namespace Microsoft.DotNet.Cli.Tests; + +internal readonly struct SdkDirectoryScope : IDisposable +{ + private readonly object? _previousSdkRoot = AppContext.GetData(SdkPaths.DataName); + + public SdkDirectoryScope(string sdkDirectory) + { + AppContext.SetData(SdkPaths.DataName, sdkDirectory); + SdkPaths.ClearSdkDirectoryCacheForTests(); + } + + public void Dispose() + { + AppContext.SetData(SdkPaths.DataName, _previousSdkRoot); + SdkPaths.ClearSdkDirectoryCacheForTests(); + } +} diff --git a/test/dotnet-aot.Tests/SdkForwardingAppTests.cs b/test/dotnet-aot.Tests/SdkForwardingAppTests.cs new file mode 100644 index 000000000000..96378d725a23 --- /dev/null +++ b/test/dotnet-aot.Tests/SdkForwardingAppTests.cs @@ -0,0 +1,56 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.DotNet.Cli.Commands.Fsi; +using Microsoft.DotNet.Cli.Commands.Test; + +namespace Microsoft.DotNet.Cli.Tests; + +[TestClass] +public class SdkForwardingAppTests +{ + [TestMethod] + public void NuGetForwardingUsesVersionedSdkDirectory() + { + string sdkDirectory = CreateSdkDirectory(); + using var _ = new SdkDirectoryScope(sdkDirectory); + + Assert.AreEqual( + Path.Combine(sdkDirectory, "NuGet.CommandLine.XPlat.dll"), + NuGetForwardingApp.GetNuGetExePath()); + } + + [TestMethod] + public void FsiForwardingUsesVersionedSdkDirectory() + { + string sdkDirectory = CreateSdkDirectory(); + using var _ = new SdkDirectoryScope(sdkDirectory); + + Assert.AreEqual( + Path.Combine(sdkDirectory, "FSharp", "fsi.exe"), + FsiForwardingApp.GetFsiAppPath()); + } + + [TestMethod] + public void VSTestForwardingUsesVersionedSdkDirectory() + { + string sdkDirectory = CreateSdkDirectory(); + using var _ = new SdkDirectoryScope(sdkDirectory); + string? previousVSTestConsolePath = Environment.GetEnvironmentVariable("VSTEST_CONSOLE_PATH"); + + try + { + Environment.SetEnvironmentVariable("VSTEST_CONSOLE_PATH", null); + Assert.AreEqual( + Path.Combine(sdkDirectory, "vstest.console.dll"), + VSTestForwardingApp.GetVSTestExePath()); + } + finally + { + Environment.SetEnvironmentVariable("VSTEST_CONSOLE_PATH", previousVSTestConsolePath); + } + } + + private static string CreateSdkDirectory() + => Path.Combine(Path.GetTempPath(), "dotnet", "sdk", $"test-version-{Guid.NewGuid():N}"); +}