-
Notifications
You must be signed in to change notification settings - Fork 7.3k
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
Rework PSModulePath path building #24568
base: master
Are you sure you want to change the base?
Conversation
11ffe1d
to
231f0bd
Compare
Tests work locally on my macOS host so I need to figure out what the differences are in CI to cause it to fail. |
src/System.Management.Automation/engine/Modules/ModuleIntrinsics.cs
Outdated
Show resolved
Hide resolved
I've pushed a commit that removed the existing logic and reimplemented it in what I hope is a more sane manner. I'll have to sit down and write some more comprehensive tests for this API to test out edge cases like cases, inserting the system/shared path earlier and so on. I'll also need to look into things like trailing backslash and whitespace handling as I think before this was considered equal when compared. The only change that I can tell this makes is that the shared path |
As for tests, I'd consider xUnit tests for GetModulePath(). It saves a lot your time and makes design intentions explicit. |
2ee70e7
to
cbd1fd7
Compare
I've updated the PR title and summary to sum up the changes I've made here. The tests are passing except for the CodeFactor checks which is complaining about the lack of docs on the new unit tests added. No other tests have the docs so I'm just ignoring this failure. This is now ready for review. |
src/System.Management.Automation/engine/Modules/ModuleIntrinsics.cs
Outdated
Show resolved
Hide resolved
{ | ||
currentProcessModulePath += Path.PathSeparator; | ||
entry = entry.TrimEnd(dirSeparatorChars); |
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.
It seems there is very optimized overload for two arguments so we can eliminate dirSeparatorChars
.
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.
Fair point I’ll use the params overload which I think is the same one used but avoids the span allocation.
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.
Please see my comment about TrimEndingDirectorySeparator()
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.
Ah there reason why I did this is because the params
overload is for a string, we are operating on a ReadOnlySpan
and MemoryExtensions.TrimEnd doesn't have such an overload.
I've kept the current code because TrimEndingDirectorySeparator()
doesn't handle trimming more than 1 trailing separator.
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.
I think it makes no sense for us to try to fix user errors. In fact, there may be a lot of user errors that we don't fix. I suggest following the same logic as you suggested for duplicated paths - just ignore it.
The main thing for us here is to correctly add the necessary paths and no more.
I suggest to use more simple code with TrimEndingDirectorySeparator() here. It is well tested (I see one issue in your code for Windows).
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.
I suggest to use more simple code with TrimEndingDirectorySeparator() here. It is well tested (I see one issue in your code for Windows)
What's the issue for Windows?
I'll wait until the WG weighs in to see whether they prefer the current code that trims all trailing dir path separators or whether to swap to TrimEndingDirectorySeparator
which only trims the last.
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.
// On Windows we always want to trim '/' or '\'. Only exception
// is to keep one trailing char if the path is the drive root
// 'C:\+'.
entry = entry.Length > 1 && entry[1] == Path.VolumeSeparatorChar && trimmedEntry.Length == 2
? entry.Slice(0, 3)
: trimmedEntry;
The code is not correct.
You will wait indefinitely.
I suggest making the code as simple as possible and accepting it now.
Since we are talking about an extreme case, you can create an issue for it and make changes to the code after additional discussion if it really turns out to be useful for many users.
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.
You will wait indefinitely.
I'm assuming I'm waiting indefinitely to get this reviewed by the WG anyway :) I appreciate you reviewing these PRs though and I'm not trying to be obstructionist it's just if they have to review this anyway I prefer to wait for the final verdict before changing things.
I have picked up the bug if it's C:
then the Slice
will go out of bounds and will fix up. Not sure if that's the incorrect part you are referring to though.
src/System.Management.Automation/engine/Modules/ModuleIntrinsics.cs
Outdated
Show resolved
Hide resolved
} | ||
#if UNIX | ||
// Don't trim if it's just '/' as that's a valid entry on *nix. | ||
else if (!(entry.Length == 1 && entry[0] == 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.
Can we use [IO.Path.IsPathRooted](https://source.dot.net/#System.Private.CoreLib/src/libraries/System.Private.CoreLib/src/System/IO/Path.Unix.cs,132)?
And what about C:\
on Windows?
Also there is TrimEndingDirectorySeparator() - it checks for size and root.
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.
One downside of TrimEndingDirectorySeparator
is that it seems like it only trims 1 char versus TrimEnd
which trims them all if present. I don't think someone having C:\foo\\
would be common but it depends if we want to handle it or not (I think we should).
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.
I've updated the code to handle Windows absolute paths and their trailing chars and expanded the comments that explains what it does. I didn't use TrimEndingDirectorySeparator()
because it will only trim 1 char at the end.
src/System.Management.Automation/engine/Modules/ModuleIntrinsics.cs
Outdated
Show resolved
Hide resolved
src/System.Management.Automation/engine/Modules/ModuleIntrinsics.cs
Outdated
Show resolved
Hide resolved
5554bb1
to
e46c27b
Compare
Fixes the parsing of the powershell.config.json PSModulePath entry when it contains the path separator char. Instead of crashing this is able to handle entries with those characters and treat them as multiple entries. This also rewrites the logic when building the process PSModulePath to simplify the way entries were added and deal with canonicalisation of the path entries.
e46c27b
to
9019fe4
Compare
PR Summary
Fixes the parsing of the powershell.config.json PSModulePath entry when it contains the path separator char. Instead of crashing this is able to handle entries with those characters and treat them as multiple entries.
This also rewrites the logic when building the process PSModulePath to
simplify the way entries were added and deal with canonicalisation of
the path entries.
Fixes: #20706
PR Context
Using
powershell.config.json
with aPSModulePath
entry that contains thePath.Separator
char (;
for Windows and:
for *nix) crashes the process on startup. This is because the current logic adds the value provided as is to the path but then attempts to find the index for where it is located. This lookup splits the new path value by thePath.Separator
so the entry being added is never found (the raw value is never a split component of the new path).This new PR consolidates the various path addition logic and tries to make it more consistent in how it handles new values. It also attempts to remove as much string allocations by working on spans where possible to hopefully improve the memory allocation at startup.
Ultimately the behaviour of this logic when building the
PSModulePath
is.config.json
and falls back to the default user's modules dirC:\Program Files\PowerShell\Modules
$PSHome/powershell.config.json
entry and falls back to$PSHome/Modules
PSModulePath
if presentPSModulePath
(System32/Program Files\WindowsPowerShell)There are a few behavioural changes introduced by this PR which I think aren't breaking changes but are behaviour differences for the better.
/
or\
(on Windows) is removed/
is converted to\
PSModulePath
PSModulePath
is not set as a process env var (Linux mostly) and there is an explicitPSModulePath
in the$PSHome/powershell.config.json
the shared module path is now includedPR Checklist
.h
,.cpp
,.cs
,.ps1
and.psm1
files have the correct copyright headerWIP:
or[ WIP ]
to the beginning of the title (theWIP
bot will keep its status check atPending
while the prefix is present) and remove the prefix when the PR is ready.- [ ] Issue filed:
(which runs in a different PS Host).