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)