Skip to content

Add support for dotnet file.cs (without explicit run subcommand) #48387

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
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
16 changes: 7 additions & 9 deletions documentation/general/dotnet-run-file.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ File-based programs are processed by `dotnet run` equivalently to project-based
For example, the remaining command-line arguments after the first argument (the target path) are passed through to the target app
(except for the arguments recognized by `dotnet run` unless they are after the `--` separator).

`dotnet path.cs` is a shortcut for `dotnet run path.cs` provided that `path.cs` exists and has `.cs` file extension.

## Entry points

If a file is given to `dotnet run`, it has to be an *entry-point file*, otherwise an error is reported.
Expand Down Expand Up @@ -233,28 +235,24 @@ Along with `#:`, the language also ignores `#!` which could be then used for [sh
Console.WriteLine("Hello");
```

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

```cs
#!/usr/bin/env dotnet run
// ^ Might not work in all shells. "dotnet run" might be passed as a single argument to "env".
```
```cs
#!/usr/bin/env dotnet-run
#!/usr/bin/env dotnet
// ^ Should work in all shells.
```
```cs
#!/usr/bin/env -S dotnet run
// ^ Workaround in some shells.
// ^ Works in some shells.
```

We could also consider making `dotnet file.cs` work because `dotnet file.dll` also works today
but that would require changes to the native dotnet host.

## Other commands

We can consider supporting other commands like `dotnet build`, `dotnet pack`, `dotnet watch`.
Expand Down
6 changes: 5 additions & 1 deletion src/Cli/dotnet/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.CommandLine;
using System.Diagnostics;
using Microsoft.DotNet.Cli.CommandFactory;
using Microsoft.DotNet.Cli.Commands.Run;
using Microsoft.DotNet.Cli.Commands.Workload;
using Microsoft.DotNet.Cli.Extensions;
using Microsoft.DotNet.Cli.ShellShim;
Expand Down Expand Up @@ -123,7 +124,10 @@ internal static int ProcessArgs(string[] args, TimeSpan startupTime, ITelemetry
ParseResult parseResult;
using (new PerformanceMeasurement(performanceData, "Parse Time"))
{
parseResult = Parser.Instance.Parse(args);
// If we get C# file path as the first argument, parse as `dotnet run file.cs`.
parseResult = args is [{ } filePath, ..] && VirtualProjectBuildingCommand.IsValidEntryPointPath(filePath)
? Parser.Instance.Parse(["run", .. args])
: Parser.Instance.Parse(args);

// Avoid create temp directory with root permission and later prevent access in non sudo
// This method need to be run very early before temp folder get created
Expand Down
16 changes: 16 additions & 0 deletions test/dotnet.Tests/CommandTests/Run/RunFileTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,22 @@ public void FilePath(string? path, bool differentCasing)
}
}

/// <summary>
/// <c>dotnet file.cs</c> is equivalent to <c>dotnet run file.cs</c>.
/// </summary>
[Fact]
public void FilePath_WithoutRun()
{
var testInstance = _testAssetsManager.CreateTestDirectory();
File.WriteAllText(Path.Join(testInstance.Path, "Program.cs"), s_program);

new DotnetCommand(Log, "Program.cs")
.WithWorkingDirectory(testInstance.Path)
.Execute()
.Should().Pass()
.And.HaveStdOut("Hello from Program");
}

/// <summary>
/// Casing of the argument is used for the output binary name.
/// </summary>
Expand Down