Skip to content
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

Getting started tutorial does not work with dotnet run using .NET 8.0 #2427

Open
frankbuckley opened this issue May 13, 2024 · 1 comment
Open

Comments

@frankbuckley
Copy link

frankbuckley commented May 13, 2024

Following the getting started tutorial:

using System.CommandLine;

namespace CommandLineTest;

internal class Program
{
    private static async Task<int> Main(string[] args)
    {
        var fileOption = new Option<FileInfo?>(
            name: "--file",
            description: "The file to read and display on the console.");

        var rootCommand = new RootCommand("Sample app for System.CommandLine");
        rootCommand.AddOption(fileOption);

        rootCommand.SetHandler((file) => ReadFile(file!),
            fileOption);

        return await rootCommand.InvokeAsync(args);
    }

    private static void ReadFile(FileInfo file)
    {
        File.ReadLines(file.FullName)
            .ToList()
            .ForEach(Console.WriteLine);
    }
}

and with a launchSettings.json:

{
  "profiles": {
    "CommandLineTest": {
      "commandName": "Project",
      "commandLineArgs": "--file CommandLineTest.runtimeconfig.json"
    }
  }
}

If you start the project from Visual Studio, then it works as expected and writes the contents of the file to the console.

If you start the built project from the bin\Debug\net8.0 directory*, then it works as expected and writes the contents of the file to the console.

If you start the project using dotnet run -- --file CommandLineTest.runtimeconfig.json, then you get an exception:

Unhandled exception: System.IO.FileNotFoundException: Could not find file 'V:\dev\testing\CommandLineTest\CommandLineTest\CommandLineTest.runtimeconfig.json'.
File name: 'V:\dev\testing\CommandLineTest\CommandLineTest\CommandLineTest.runtimeconfig.json'
   at Microsoft.Win32.SafeHandles.SafeFileHandle.CreateFile(String fullPath, FileMode mode, FileAccess access, FileShare share, FileOptions options)
   at Microsoft.Win32.SafeHandles.SafeFileHandle.Open(String fullPath, FileMode mode, FileAccess access, FileShare share, FileOptions options, Int64 preallocationSize, Nullable`1 unixCreateMode)
   at System.IO.Strategies.OSFileStreamStrategy..ctor(String path, FileMode mode, FileAccess access, FileShare share, FileOptions options, Int64 preallocationSize, Nullable`1 unixCreateMode)
   at System.IO.Strategies.FileStreamHelpers.ChooseStrategyCore(String path, FileMode mode, FileAccess access, FileShare share, FileOptions options, Int64 preallocationSize, Nullable`1 unixCreateMode)
   at System.IO.StreamReader.ValidateArgsAndOpenPath(String path, Encoding encoding, Int32 bufferSize)
   at System.IO.File.ReadLines(String path)
   at CommandLineTest.Program.ReadFile(FileInfo file) in V:\dev\testing\CommandLineTest\CommandLineTest\Program.cs:line 24
   at CommandLineTest.Program.<>c.<Main>b__0_0(FileInfo file) in V:\dev\testing\CommandLineTest\CommandLineTest\Program.cs:line 16
   at System.CommandLine.Handler.<>c__DisplayClass2_0`1.<SetHandler>b__0(InvocationContext context)
   at System.CommandLine.Invocation.AnonymousCommandHandler.Invoke(InvocationContext context)
   at System.CommandLine.Invocation.AnonymousCommandHandler.InvokeAsync(InvocationContext context)
   at System.CommandLine.Invocation.InvocationPipeline.<>c__DisplayClass4_0.<<BuildInvocationChain>b__0>d.MoveNext()

This is due to difference in how current directory is set when running from Visual Studio vs. CLI.

FileInfo (and DirectoryInfo) end up using current directory to normalize relative paths.

If you add the following lines to Main() and run from CLI and Visual Studio you can observe the differences:

        Console.WriteLine($"BaseDirectory: {AppContext.BaseDirectory}");
        Console.WriteLine($"CurrentDirectory: {Environment.CurrentDirectory}");
        Console.WriteLine($"Path.GetFullPath(\"./\"): {Path.GetFullPath("./")}");

You might consider updating the getting started docs to note the problem and/or update the path used for the dotnet run example - for example: dotnet run -- --file .\bin\Debug\net8.0\CommandLineTest.runtimeconfig.json

Related:

@frankbuckley
Copy link
Author

Workaround to get Visual Studio to behave the same as dotnet run from the project root is to add "workingDirectory": "./" to launchSettings.json:

{
  "profiles": {
    "CommandLineTest": {
      "commandName": "Project",
      "commandLineArgs": "--file ./bin/Debug/net8.0/CommandLineTest.runtimeconfig.json",
      "workingDirectory": "./"
    }
  }
}

This works the same in Visual Studio as:

dotnet run -- --file ./bin/Debug/net8.0/CommandLineTest.runtimeconfig.json

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant