-
Notifications
You must be signed in to change notification settings - Fork 32
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
Close #32 Searching for a file on the system path #111
base: release-1.7
Are you sure you want to change the base?
Close #32 Searching for a file on the system path #111
Conversation
MedallionShell.Tests/GeneralTest.cs
Outdated
public void TestGetFullPathOnWindows(string executable, string? expected) | ||
{ | ||
StringAssert.AreEqualIgnoringCase(expected, Shell.GetFullPathUsingSystemPathOrDefault(executable)); | ||
var command = Command.Run("where", executable); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure if this part adds any value here and in the other test.
MedallionShell/Shell.cs
Outdated
@@ -36,12 +38,17 @@ public Command Run(string executable, IEnumerable<object>? arguments = null, Act | |||
{ | |||
Throw.If(string.IsNullOrEmpty(executable), "executable is required"); | |||
|
|||
var executablePath = !executable.Contains(Path.DirectorySeparatorChar) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This changes the behavior, but my understanding is that using dotnet
(or dotnet.exe
) wouldn't have worked in the first place. Is this still a breaking change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The proposal wasn't to make this default behavior. This should be opt-in behavior.
When this behavior is enabled, it should behave like a shell would.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@madelson What is the expected behavior if o => o.SearchSystemPath()
was used but the fully qualified path was passed in as the first argument?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the expected behavior if o => o.SearchSystemPath() was used but the fully qualified path was passed in as the first argument?
We want to follow the actual behavior on the command line for guidance. I believe that the current directory is searched before the system path, but I could have that backwards.
Certainly a fully-qualified path should work without a search.
There are also relative paths like ./foo.exe
or ../bar/foo.exe
. I believe these work today (relative to the CWD); they should continue to work just like they do in a shell.
MedallionShell/Shell.cs
Outdated
{ | ||
var pathExtensions = Environment.GetEnvironmentVariable("PATHEXT")! | ||
.Split(Path.PathSeparator) | ||
.Concat(new[] { string.Empty }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The user may have included the extension, in which case we shouldn't be adding more.
Given that there may be an executable like myExe.exe.exe
, we shouldn't be checking whether an extension already exists in the executable
variable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the OS precedence behavior here?
E.g. if I type dotnet.exe
and there is a file dotnet.exe
and dotnet.exe.exe
on my path, which one gets executed?
What if dotnet.exe.exe
is in a folder which is higher precedence?
What if there is a file dotnet
and dotnet.exe
?
We should strive to match the OS behavior exactly, documenting and testing each decision point.
@madelson The test failures don't seem related to the updated code path, so requesting review! |
@madelson Any feedback here? |
MedallionShell/Shell.cs
Outdated
.FirstOrDefault(File.Exists); | ||
} | ||
|
||
return paths.Select(path => Path.Combine(path, executable)).FirstOrDefault(File.Exists); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By checking File.Exists
, that means we'll skip a directory that matches. Is that the OS behavior?
For example, I type dotnet
and there is a directory dotnet
or dotnet.exe
. What does the OS do?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like a good start! We need to be super careful implementing this to match the OS behavior. I don't want something that kinda-sorta matches.
We also need to make this an opt-in behavior that can be enabled via an option. Something like:
o => o.SearchSystemPath()
Note to self: Windows
// dotnet new -f net8.0
// dotnet publish -r win-x64 -p:PublishTrimmed=true -p:PublishSingleFile=true
Console.WriteLine("exe");
@echo off
echo bat
@echo cmd
WScript.Echo "vbs"
WScript.Echo("js");
|
@madelson I'm a bit confused. I just ran the following script on LINQPad 7, it worked as expected. async Task Main()
{
var command = Command.Run("dotnet", new[] { "tool", "list", "-g" });
await command.Task;
command.GetOutputAndErrorLines().Dump();
}
|
Never mind; I see that |
@madelson Should we continue to test against Mono? I went down the rabbit hole of testing my changes on AppVeyor and concluded that this is the question I need to answer. Context
The reason we're thinking about this in the first place is that we use I also realized .NET 6 may not be compatible with Mono, which made me wonder if we really need to support Mono use case in the first place. |
I've also observed things like this, and have never gone through the process of tracking down exactly what is happening with the built-in behavior. However, figuring that out is pretty essential for building this feature (and part of why I hadn't built it yet was because of all the nuance and complexity around that!). |
@madelson Thanks for the input. What do you think about the Mono comment above then? |
@Bartleby2718 Thanks for the detailed investigation on this. Mono is still a thing because it is used as the runtime for Blazor and I maybe for Xamarin. That said, I don't think we were actually testing against that version of Mono. Originally, the key reason for Mono testing was really about Linux testing. I wouldn't want to lose that since cross-platform bugs are a serious potential issue for a library like this. If we remove Mono testing, I'd want to take a serious look at any remaining Mono code forks and ask whether we can remove them since I want to keep the number of untested code branches to an absolute minimum, ideally zero. Looking at the code, it seems like we only branch on Mono for a small number of things, one of which seems related to #117. If we're going to invest in cleaning up the Mono support and replacing it with robust .NET 8 support, I'd want that to happen in a separate PR. |
Notes
Picked up this ticket because it seemed interesting, even though it was not in the 1.7 milestone.
Context
There are a few different suggested approaches in this StackOverflow thread.
Process.Start
on the executable itself (or callingwhere
on Windows,which
on Linux) is probably too expensive.PathFindOnPath
inshlwapi.dll
, but I wasn't sure if it was reliable.Open Questions
PathFindOnPath
, or is searching forPATH
suffice?executable
?