diff --git a/src/Cli/dotnet/Commands/Run/RunCommand.cs b/src/Cli/dotnet/Commands/Run/RunCommand.cs index ba57cb7e44f4..95a5182e7319 100644 --- a/src/Cli/dotnet/Commands/Run/RunCommand.cs +++ b/src/Cli/dotnet/Commands/Run/RunCommand.cs @@ -470,8 +470,7 @@ static ICommand CreateCommandForCscBuiltProgram(string entryPointFileFullPath, s var artifactsPath = VirtualProjectBuildingCommand.GetArtifactsPath(entryPointFileFullPath); var exePath = Path.Join(artifactsPath, "bin", "debug", Path.GetFileNameWithoutExtension(entryPointFileFullPath) + FileNameSuffixes.CurrentPlatform.Exe); var commandSpec = new CommandSpec(path: exePath, args: ArgumentEscaper.EscapeAndConcatenateArgArrayForProcessStart(args)); - var command = CommandFactoryUsingResolver.Create(commandSpec) - .WorkingDirectory(Path.GetDirectoryName(entryPointFileFullPath)); + var command = CommandFactoryUsingResolver.Create(commandSpec); SetRootVariableName( command, diff --git a/test/dotnet.Tests/CommandTests/Run/RunFileTests.cs b/test/dotnet.Tests/CommandTests/Run/RunFileTests.cs index 1eebfddaef95..d51b29c46661 100644 --- a/test/dotnet.Tests/CommandTests/Run/RunFileTests.cs +++ b/test/dotnet.Tests/CommandTests/Run/RunFileTests.cs @@ -856,6 +856,124 @@ public void EmptyFile() .And.HaveStdOutContaining("error CS5001:"); // Program does not contain a static 'Main' method suitable for an entry point } + /// + /// See . + /// + [Theory, CombinatorialData] + public void WorkingDirectory(bool cscOnly) + { + var testInstance = _testAssetsManager.CreateTestDirectory(baseDirectory: cscOnly ? OutOfTreeBaseDirectory : null); + var programPath = Path.Join(testInstance.Path, "Program.cs"); + + var code = """ + Console.WriteLine("v1"); + Console.WriteLine(Environment.CurrentDirectory); + Console.WriteLine(Directory.GetCurrentDirectory()); + Console.WriteLine(new DirectoryInfo(".").FullName); + Console.WriteLine(AppContext.GetData("EntryPointFileDirectoryPath")); + """; + + File.WriteAllText(programPath, code); + + var workDir = TestPathUtility.ResolveTempPrefixLink(Path.GetTempPath()).TrimEnd(Path.DirectorySeparatorChar); + + var artifactsDir = VirtualProjectBuildingCommand.GetArtifactsPath(programPath); + if (Directory.Exists(artifactsDir)) Directory.Delete(artifactsDir, recursive: true); + + Build(testInstance, + expectedLevel: cscOnly ? BuildLevel.Csc : BuildLevel.All, + programFileName: programPath, + workDir: workDir, + expectedOutput: GetExpectedOutput("v1", workDir)); + + code = code.Replace("v1", "v2"); + File.WriteAllText(programPath, code); + + Build(testInstance, + expectedLevel: BuildLevel.Csc, + programFileName: programPath, + workDir: workDir, + expectedOutput: GetExpectedOutput("v2", workDir)); + + string GetExpectedOutput(string version, string workDir) => $""" + {version} + {workDir} + {workDir} + {workDir} + {Path.GetDirectoryName(programPath)} + """; + } + + /// + /// Combination of and . + /// + [Fact] + public void WorkingDirectory_CscOnly_AfterMSBuild() + { + var testInstance = _testAssetsManager.CreateTestDirectory(baseDirectory: OutOfTreeBaseDirectory); + var programPath = Path.Join(testInstance.Path, "Program.cs"); + + var code = """ + #:property Configuration=Release + Console.WriteLine("v1"); + Console.WriteLine(Environment.CurrentDirectory); + Console.WriteLine(Directory.GetCurrentDirectory()); + Console.WriteLine(new DirectoryInfo(".").FullName); + Console.WriteLine(AppContext.GetData("EntryPointFileDirectoryPath")); + """; + + File.WriteAllText(programPath, code); + + var workDir = TestPathUtility.ResolveTempPrefixLink(Path.GetTempPath()).TrimEnd(Path.DirectorySeparatorChar); + + var artifactsDir = VirtualProjectBuildingCommand.GetArtifactsPath(programPath); + if (Directory.Exists(artifactsDir)) Directory.Delete(artifactsDir, recursive: true); + + Build(testInstance, + expectedLevel: BuildLevel.All, + programFileName: programPath, + workDir: workDir, + expectedOutput: GetExpectedOutput("v1", workDir)); + + Build(testInstance, + expectedLevel: BuildLevel.None, + programFileName: programPath, + workDir: workDir, + expectedOutput: GetExpectedOutput("v1", workDir)); + + code = code.Replace("v1", "v2"); + File.WriteAllText(programPath, code); + + Build(testInstance, + expectedLevel: BuildLevel.Csc, + programFileName: programPath, + workDir: workDir, + expectedOutput: GetExpectedOutput("v2", workDir)); + + // Can be overridden with a #:property. + var workDir2 = Path.Join(testInstance.Path, "dir2"); + Directory.CreateDirectory(workDir2); + code = $""" + #:property RunWorkingDirectory={workDir2} + {code} + """; + File.WriteAllText(programPath, code); + + Build(testInstance, + expectedLevel: BuildLevel.All, + programFileName: programPath, + workDir: workDir, + expectedOutput: GetExpectedOutput("v2", workDir2)); + + string GetExpectedOutput(string version, string workDir) => $""" + {version} + {workDir} + {workDir} + {workDir} + {Path.GetDirectoryName(programPath)} + """; + } + /// /// Implicit build files have an effect. /// @@ -3214,7 +3332,7 @@ Release config Build(testInstance, BuildLevel.Csc); } - private void Build(TestDirectory testInstance, BuildLevel expectedLevel, ReadOnlySpan args = default, string expectedOutput = "Hello from Program", string programFileName = "Program.cs") + private void Build(TestDirectory testInstance, BuildLevel expectedLevel, ReadOnlySpan args = default, string expectedOutput = "Hello from Program", string programFileName = "Program.cs", string? workDir = null) { string prefix = expectedLevel switch { @@ -3225,12 +3343,12 @@ private void Build(TestDirectory testInstance, BuildLevel expectedLevel, ReadOnl }; new DotnetCommand(Log, ["run", programFileName, "-bl", .. args]) - .WithWorkingDirectory(testInstance.Path) + .WithWorkingDirectory(workDir ?? testInstance.Path) .Execute() .Should().Pass() .And.HaveStdOut(prefix + expectedOutput); - var binlogs = new DirectoryInfo(testInstance.Path) + var binlogs = new DirectoryInfo(workDir ?? testInstance.Path) .EnumerateFiles("*.binlog", SearchOption.TopDirectoryOnly); binlogs.Select(f => f.Name)