-
Notifications
You must be signed in to change notification settings - Fork 8.1k
Change detection of home directory to fallback to host if SMA.dll location is not found #18674
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
Conversation
|
Perhaps we could use PowerShell/src/System.Management.Automation/engine/Utils.cs Lines 483 to 488 in 8686612
|
|
@iSazonov |
I mean something like |
|
In this case, I don't think the result will be any different. The fundamental problem seems to be that in some hosted scenarios, SMA.dll's location is empty. |
|
Maybe @jborean93 and @SeeminglyScience have ideas. |
|
I think we need to fix We need to try reproducing this in a Windows Background Service to better understand the problem, and then contact the .NET team to see how to get the assembly location in that environment. |
|
Perhaps it is more reliable to use From https://learn.microsoft.com/en-us/dotnet/api/system.appcontext.basedirectory?view=net-7.0#remarks
|
|
@daxian-dbw I don't think we can rely on SMA.dll location as if we can get singleexe working, there isn't a SMA.dll as it's part of the exe. Perhaps @iSazonov's suggestion on base exe location is a better all around solution as long as hosted scenarios keep PowerShell assemblies in the same location as the host exe (which might not always be the case). |
To be clear - |
|
For an application that hosts PowerShell and build using In the singleexe scenario, does Also, does At any rates, simply handling the null case in |
|
I have a branch that can build singleexe I can test this on. Note that singleexe currently is impossible to debug other than writing traces to the console. Here's the results: Assembly.FullName = System.Management.Automation, Version=7.3.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
Assembly.Location =
AppContext.BaseDirectory = /Users/steve/repos/PowerShell/src/powershell-unix/bin/Debug/net7.0/osx-arm64/publish/So it seems that |
|
Looks good. Let's change to use The only thing is we cannot verify whether |
|
So SingleExe build does work as long as you don't use trimming. But the result is a 112MB executable. I think for a Windows Service, we'll have to wait for user feedback. |
4946e69 to
38e1c0c
Compare
daxian-dbw
left a comment
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.
LGTM
daxian-dbw
left a comment
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.
@SteveL-MSFT Can you please verify what AppContext.BaseDirectory returns in a remote session?
I found the following comments in https://github.com/PowerShell/PowerShell/blob/master/src/Microsoft.PowerShell.Commands.Utility/commands/utility/AddType.cs#LL628-L630C61
// However, 'Assembly.GetEntryAssembly()' returns null when the managed code is called from // unmanaged code (PowerShell WSMan remoting scenario), so in that case, we continue to use // the location of 'System.Management.Automation.dll'.
Since AppContext.BaseDirectory uses Assembly.GetEntryAssembly(), I'm afraid it may not work a remote session.
38e1c0c to
958ed20
Compare
|
@daxian-dbw unfortunately I don't have a system where I can do WinRM remoting (it doesn't work on win-arm64 due to the msix install sandbox) |
|
@daxian-dbw I think we have our answer: https://dev.azure.com/powershell/PowerShell/_build/results?buildId=115792&view=logs&j=99798060-e5bf-55b9-b3b5-109e6acacce3&t=0050f0b1-14ee-5c4d-b4b0-da6405e3eaf0&l=413. So it won't work with remoting or hosted in unmanaged host. |
|
What if we use |
5e8aa23 to
5c56e31
Compare
|
This PR has Quantification details
Why proper sizing of changes matters
Optimal pull request sizes drive a better predictable PR flow as they strike a
What can I do to optimize my changes
How to interpret the change counts in git diff output
Was this comment helpful? 👍 :ok_hand: :thumbsdown: (Email) |
| Assembly assembly = typeof(PSObject).Assembly; | ||
| return Path.GetDirectoryName(assembly.Location); | ||
| // AppContext is needed where PS7 may be compiled as a single exe | ||
| // Environment.ProcessPath covers the general case |
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 general case that Environment.ProcessPath is going to cover?
AppContext.BaseDirectory calls Environment.ProcessPath in case of NativeAOT, so I think that already covers it, isn't it?
| if (!string.IsNullOrEmpty(AppContext.BaseDirectory)) | ||
| { | ||
| return Path.TrimEndingDirectorySeparator(AppContext.BaseDirectory); | ||
| } | ||
| else if (!string.IsNullOrEmpty(Path.GetDirectoryName(typeof(PSObject).Assembly.Location))) | ||
| { | ||
| return Path.GetDirectoryName(typeof(PSObject).Assembly.Location); | ||
| } |
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.
We should use a static variable to hold the resulted application base path, instead of running this every time.
Also, users can change the application path by AppContext.SetData("APP_CONTEXT_BASE_DIRECTORY", <new-path>), which may completely break PowerShell, so we have to cache the original value on the first call to GetApplicationBase.
| #endif | ||
|
|
||
| internal static string DefaultPowerShellAppBase => GetApplicationBase(DefaultPowerShellShellID); | ||
| internal static string DefaultPowerShellAppBase => GetApplicationBase(); |
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 AddType.cs needs to be updated too:
PowerShell/src/Microsoft.PowerShell.Commands.Utility/commands/utility/AddType.cs
Lines 634 to 640 in 8f0cd65
| private static readonly string s_netcoreAppRefFolder = PathType.Combine( | |
| PathType.GetDirectoryName( | |
| (Assembly.GetEntryAssembly() ?? typeof(PSObject).Assembly).Location), | |
| "ref"); | |
| // Path to the folder where .NET Core runtime assemblies are located. | |
| private static readonly string s_frameworkFolder = PathType.GetDirectoryName(typeof(object).Assembly.Location); |
s_netcoreAppRefFolder should be just using GetApplicationBase(). As for s_frameworkFolder, the reference of this variable can be replaced by GetApplicationBase() directly I think.
| } | ||
| #endif | ||
|
|
||
| internal static string DefaultPowerShellAppBase => GetApplicationBase(DefaultPowerShellShellID); |
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 guess we can remove all the reference to DefaultPowerShellAppBase now, and just GetApplicationBase() directly instead.
| { | ||
| var results = new List<CompletionResult>(); | ||
| string userHelpDir = HelpUtils.GetUserHomeHelpSearchPath(); | ||
| string appHelpDir = Utils.GetApplicationBase(Utils.DefaultPowerShellShellID); |
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 remove Utils.DefaultPowerShellID now?
|
This change will cause a problem to the Today, The way we ship the built-in modules in After this change, the |
|
This pull request has been automatically marked as stale because it has been marked as requiring author feedback but has not had any activity for 15 days. It will be closed if no further activity occurs within 10 days of this comment. |
|
@adityapatwardhan Please take a look at #18674 (comment). We may need to brainstorm on this NuGet packaging issue. |
|
This pull request has been automatically marked as stale because it has been marked as requiring author feedback but has not had any activity for 15 days. It will be closed if no further activity occurs within 10 days of this comment. |
|
This is a huge thorn in our side. As long as there are vital configuration functions which are only available via PowerShell (such as the enabling/disabling of server roles and features), which our application must be able to call via C#, this is going to continue to be a headache for us and our deployment requirements. The .NET Core 3.1 version of single file deployment (extract all files to /temp) is simply not acceptable to our customer base for security reasons. |
PR Summary
The path to the system wide
powershell.config.jsonfile depends on the location of SMA.dll. However, for some uses like hosting in a Windows Service, the .NET call to get the path to SMA.dll returns null which then subsequently fails aPath.Join()call. The change is to use AppContext.BaseDirectory instead of location of SMA.dll. In addition, the internal method takes a parameter that hasn't been used and has been removed.I originally tried making the members nullable, however, this brought in a cascade of other changes needed to make it work across multiple files and also adds more regression risk since the behavior changes being nullable. Instead, I opened for this current approach which is more straight forward.
Also, this change is needed to experiment with building a single exe pwsh where SMA.dll won't be found.
PR Context
Fix #18109
PR Checklist
.h,.cpp,.cs,.ps1and.psm1files have the correct copyright headerWIP:or[ WIP ]to the beginning of the title (theWIPbot will keep its status check atPendingwhile the prefix is present) and remove the prefix when the PR is ready.(which runs in a different PS Host).