Skip to content

Commit a0515ab

Browse files
authored
Add support for dotnet file.cs (without explicit run subcommand) (#48387)
1 parent f058035 commit a0515ab

File tree

3 files changed

+52
-10
lines changed

3 files changed

+52
-10
lines changed

documentation/general/dotnet-run-file.md

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ For example, the remaining command-line arguments after the first argument (the
6565
(except for the arguments recognized by `dotnet run` unless they are after the `--` separator)
6666
and working directory is not changed (e.g., `cd /x/ && dotnet run /y/file.cs` runs the program in directory `/x/`).
6767

68+
`dotnet path.cs` is a shortcut for `dotnet run path.cs` provided that `path.cs` is a valid [target path](#target-path).
69+
6870
### Other commands
6971

7072
Commands `dotnet restore file.cs` and `dotnet build file.cs` are needed for IDE support and hence work for file-based programs.
@@ -294,28 +296,24 @@ Also, `InternalsVisibleTo` needs to be added into a C# file as an attribute, or
294296

295297
### Shebang support
296298

297-
It might be beneficial to also ship `dotnet-run` binary
298-
(or `dotnet-run-file` that would only work with file-based programs, not project-based ones, perhaps simply named `cs`)
299-
because some shells do not support multiple command-line arguments in the shebang
299+
Some shells do not support multiple command-line arguments in the shebang
300300
which is needed if one wants to use `/usr/bin/env` to find the `dotnet` executable
301-
(although `-S` argument can be sometimes used to enable multiple argument support):
301+
(although `-S` argument can be sometimes used to enable multiple argument support),
302+
so `dotnet file.cs` instead of `dotnet run file.cs` should be used in shebangs:
302303

303304
```cs
304305
#!/usr/bin/env dotnet run
305306
// ^ Might not work in all shells. "dotnet run" might be passed as a single argument to "env".
306307
```
307308
```cs
308-
#!/usr/bin/env dotnet-run
309+
#!/usr/bin/env dotnet
309310
// ^ Should work in all shells.
310311
```
311312
```cs
312313
#!/usr/bin/env -S dotnet run
313-
// ^ Workaround in some shells.
314+
// ^ Works in some shells.
314315
```
315316

316-
We could also consider making `dotnet file.cs` work because `dotnet file.dll` also works today
317-
but that would require changes to the native dotnet host.
318-
319317
### Other possible commands
320318

321319
We can consider supporting other commands like `dotnet pack`, `dotnet watch`,

src/Cli/dotnet/Program.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.CommandLine;
77
using System.Diagnostics;
88
using Microsoft.DotNet.Cli.CommandFactory;
9+
using Microsoft.DotNet.Cli.Commands.Run;
910
using Microsoft.DotNet.Cli.Commands.Workload;
1011
using Microsoft.DotNet.Cli.Extensions;
1112
using Microsoft.DotNet.Cli.ShellShim;
@@ -125,7 +126,10 @@ internal static int ProcessArgs(string[] args, TimeSpan startupTime, ITelemetry
125126
ParseResult parseResult;
126127
using (new PerformanceMeasurement(performanceData, "Parse Time"))
127128
{
128-
parseResult = Parser.Instance.Parse(args);
129+
// If we get C# file path as the first argument, parse as `dotnet run file.cs`.
130+
parseResult = args is [{ } filePath, ..] && VirtualProjectBuildingCommand.IsValidEntryPointPath(filePath)
131+
? Parser.Instance.Parse(["run", .. args])
132+
: Parser.Instance.Parse(args);
129133

130134
// Avoid create temp directory with root permission and later prevent access in non sudo
131135
// This method need to be run very early before temp folder get created

test/dotnet.Tests/CommandTests/Run/RunFileTests.cs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,46 @@ public void FilePath(string? path, bool differentCasing)
118118
}
119119
}
120120

121+
/// <summary>
122+
/// <c>dotnet file.cs</c> is equivalent to <c>dotnet run file.cs</c>.
123+
/// </summary>
124+
[Fact]
125+
public void FilePath_WithoutRun()
126+
{
127+
var testInstance = _testAssetsManager.CreateTestDirectory();
128+
File.WriteAllText(Path.Join(testInstance.Path, "Program.cs"), s_program);
129+
130+
new DotnetCommand(Log, "Program.cs")
131+
.WithWorkingDirectory(testInstance.Path)
132+
.Execute()
133+
.Should().Pass()
134+
.And.HaveStdOut("""
135+
Hello from Program
136+
""");
137+
138+
File.WriteAllText(Path.Join(testInstance.Path, "Program.cs"), $"""
139+
#:property Configuration=Release
140+
{s_program}
141+
""");
142+
143+
new DotnetCommand(Log, "Program.cs")
144+
.WithWorkingDirectory(testInstance.Path)
145+
.Execute()
146+
.Should().Pass()
147+
.And.HaveStdOut("""
148+
Hello from Program
149+
Release config
150+
""");
151+
152+
new DotnetCommand(Log, "Program.cs", "-c", "Debug")
153+
.WithWorkingDirectory(testInstance.Path)
154+
.Execute()
155+
.Should().Pass()
156+
.And.HaveStdOut("""
157+
Hello from Program
158+
""");
159+
}
160+
121161
/// <summary>
122162
/// Casing of the argument is used for the output binary name.
123163
/// </summary>

0 commit comments

Comments
 (0)