diff --git a/docs/installation/linux.md b/docs/installation/linux.md
index 2b9cf44b4..81806c59c 100644
--- a/docs/installation/linux.md
+++ b/docs/installation/linux.md
@@ -79,12 +79,13 @@ sudo installer -pkg powershell-6.0.0-alpha.8-osx.10.11-x64.pkg -target /
Paths
=====
-* User profiles will be read from `~/.config/powershell/profile.ps1`.
-* User modules will be read from `~/.local/share/powershell/Modules`
-* PSReadLine history will be recorded to `~/.local/share/powershell/PSReadLine/ConsoleHost_history.txt`
* `$PSHOME` is `/opt/microsoft/powershell/6.0.0-alpha.8/`
-* Default profiles will be read from `$PSHOME/profile.ps1`.
+* User profiles will be read from `~/.config/powershell/profile.ps1`
+* Default profiles will be read from `$PSHOME/profile.ps1`
+* User modules will be read from `~/.local/share/powershell/Modules`
+* Shared modules will be read from `/usr/local/share/powershell/Modules`
* Default modules will be read from `$PSHOME/Modules`
+* PSReadLine history will be recorded to `~/.local/share/powershell/PSReadLine/ConsoleHost_history.txt`
The profiles respect PowerShell's per-host configuration,
so the default host-specific profiles exists at `Microsoft.PowerShell_profile.ps1` in the same locations.
diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs
index 0c99e4767..da0df1159 100644
--- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs
+++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs
@@ -179,21 +179,17 @@ namespace Microsoft.PowerShell
try
{
string profileDir;
- if (Platform.IsWindows)
- {
- profileDir = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) +
- @"\Microsoft\Windows\PowerShell";
+#if UNIX
+ profileDir = Platform.SelectProductNameForDirectory(Platform.XDG_Type.CACHE);
+#else
+ profileDir = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) +
+ @"\Microsoft\Windows\PowerShell";
- if (!Directory.Exists(profileDir))
- {
- Directory.CreateDirectory(profileDir);
- }
- }
- else
+ if (!Directory.Exists(profileDir))
{
- profileDir = Platform.SelectProductNameForDirectory(Platform.XDG_Type.CACHE);
+ Directory.CreateDirectory(profileDir);
}
-
+#endif
ClrFacade.SetProfileOptimizationRoot(profileDir);
}
catch
diff --git a/src/System.Management.Automation/CoreCLR/CorePsExtensions.cs b/src/System.Management.Automation/CoreCLR/CorePsExtensions.cs
index 657475c06..d07a04d95 100644
--- a/src/System.Management.Automation/CoreCLR/CorePsExtensions.cs
+++ b/src/System.Management.Automation/CoreCLR/CorePsExtensions.cs
@@ -741,6 +741,7 @@ namespace System.Management.Automation
#endregion Reflection and Type related extensions
#region Environment Extensions
+
// TODO:CORECLR - Environment Extensions need serious work to refine.
internal enum EnvironmentVariableTarget
{
@@ -824,37 +825,7 @@ namespace System.Management.Automation
public static string GetEnvironmentVariable(string variable)
{
- string value = System.Environment.GetEnvironmentVariable(variable);
-
- // Porting note: if not otherwise defined, map Windows environment
- // variables to their corresponding Linux counterparts
- if (!Platform.IsWindows && String.IsNullOrEmpty(value))
- {
- switch (variable)
- {
- case "OS":
- return "Linux";
-
- case "COMPUTERNAME":
- return System.Environment.GetEnvironmentVariable("HOSTNAME");
-
- case "USERNAME":
- return System.Environment.GetEnvironmentVariable("USER");
-
- case "HOMEPATH":
- case "USERPROFILE":
- return System.Environment.GetEnvironmentVariable("HOME");
-
- case "TMP":
- case "TEMP":
- return System.Environment.GetEnvironmentVariable("TMPDIR");
-
- default:
- break;
- }
- }
-
- return value;
+ return System.Environment.GetEnvironmentVariable(variable);
}
public static IDictionary GetEnvironmentVariables()
@@ -896,7 +867,6 @@ namespace System.Management.Automation
#if UNIX
return null;
#else
-
if( target == EnvironmentVariableTarget.Machine)
{
using (RegistryKey environmentKey =
@@ -957,7 +927,6 @@ namespace System.Management.Automation
#if UNIX
return null;
#else
-
if (target == EnvironmentVariableTarget.Machine)
{
using (RegistryKey environmentKey =
@@ -986,31 +955,6 @@ namespace System.Management.Automation
#region Property_Extensions
- internal static string WinGetUserDomainName()
- {
- StringBuilder domainName = new StringBuilder(1024);
- uint domainNameLen = (uint)domainName.Capacity;
-
- byte ret = Win32Native.GetUserNameEx(Win32Native.NameSamCompatible, domainName, ref domainNameLen);
- if (ret == 1)
- {
- string samName = domainName.ToString();
- int index = samName.IndexOf('\\');
- if (index != -1)
- {
- return samName.Substring(0, index);
- }
- }
- else
- {
- int errorCode = Marshal.GetLastWin32Error();
- throw new InvalidOperationException(Win32Native.GetMessage(errorCode));
- }
-
- // Cannot use LookupAccountNameW to get DomainName because 'GetUserName' is not available in CSS and thus we cannot get the account.
- throw new InvalidOperationException(CoreClrStubResources.CannotGetDomainName);
- }
-
///
/// UserDomainName
///
@@ -1018,15 +962,11 @@ namespace System.Management.Automation
{
get
{
- if (Platform.IsWindows)
- {
- return WinGetUserDomainName();
- }
- else
- {
- return Platform.NonWindowsGetDomainName();
- }
-
+#if UNIX
+ return Platform.NonWindowsGetDomainName();
+#else
+ return WinGetUserDomainName();
+#endif
}
}
@@ -1040,20 +980,7 @@ namespace System.Management.Automation
#if UNIX
return Platform.Unix.UserName;
#else
- StringBuilder domainName = new StringBuilder(1024);
- uint domainNameLen = (uint)domainName.Capacity;
-
- byte ret = Win32Native.GetUserNameEx(Win32Native.NameSamCompatible, domainName, ref domainNameLen);
- if (ret == 1)
- {
- string samName = domainName.ToString();
- int index = samName.IndexOf('\\');
- if (index != -1)
- {
- return samName.Substring(index + 1);
- }
- }
- return string.Empty;
+ return WinGetUserName();
#endif
}
}
@@ -1078,45 +1005,19 @@ namespace System.Management.Automation
{
if (s_os == null)
{
- if (Platform.IsWindows)
- {
- s_os = WinOSVersion;
- }
- else
- {
- // TODO:PSL use P/Invoke to provide proper version
-
- // Porting note: cannot put this in CorePsPlatform since
- // System.Management.Automation.Environment only exists in CoreCLR
- // builds of monad.
- s_os = new Environment.OperatingSystem(new Version(1, 0, 0, 0), "");
- }
+#if UNIX
+ // TODO:PSL use P/Invoke to provide proper version
+ // OSVersion will be back in CoreCLR 1.1
+ s_os = new Environment.OperatingSystem(new Version(1, 0, 0, 0), "");
+#else
+ s_os = WinGetOSVersion();
+#endif
}
return s_os;
}
}
private static volatile OperatingSystem s_os;
- ///
- /// Windows OSVersion implementation
- ///
- private static OperatingSystem WinOSVersion
- {
- get
- {
- Win32Native.OSVERSIONINFOEX osviex = new Win32Native.OSVERSIONINFOEX();
- osviex.OSVersionInfoSize = Marshal.SizeOf(osviex);
- if (!Win32Native.GetVersionEx(ref osviex))
- {
- int errorCode = Marshal.GetLastWin32Error();
- throw new Win32Exception(errorCode);
- }
-
- Version v = new Version(osviex.MajorVersion, osviex.MinorVersion, osviex.BuildNumber, (osviex.ServicePackMajor << 16) | osviex.ServicePackMinor);
- return new OperatingSystem(v, osviex.CSDVersion);
- }
- }
-
#endregion Property_Extensions
#region SpecialFolder_Extensions
@@ -1242,13 +1143,79 @@ namespace System.Management.Automation
#endregion SpecialFolder_Extensions
- #region NativeMethods
+ #region WinPlatform_Specific_Methods
+#if !UNIX
+ ///
+ /// Windows UserDomainName implementation
+ ///
+ private static string WinGetUserDomainName()
+ {
+ StringBuilder domainName = new StringBuilder(1024);
+ uint domainNameLen = (uint)domainName.Capacity;
+
+ byte ret = Win32Native.GetUserNameEx(Win32Native.NameSamCompatible, domainName, ref domainNameLen);
+ if (ret == 1)
+ {
+ string samName = domainName.ToString();
+ int index = samName.IndexOf('\\');
+ if (index != -1)
+ {
+ return samName.Substring(0, index);
+ }
+ }
+ else
+ {
+ int errorCode = Marshal.GetLastWin32Error();
+ throw new InvalidOperationException(Win32Native.GetMessage(errorCode));
+ }
+
+ // Cannot use LookupAccountNameW to get DomainName because 'GetUserName' is not available in CSS and thus we cannot get the account.
+ throw new InvalidOperationException(CoreClrStubResources.CannotGetDomainName);
+ }
+
+ ///
+ /// Windows UserName implementation
+ ///
+ private static string WinGetUserName()
+ {
+ StringBuilder domainName = new StringBuilder(1024);
+ uint domainNameLen = (uint)domainName.Capacity;
+
+ byte ret = Win32Native.GetUserNameEx(Win32Native.NameSamCompatible, domainName, ref domainNameLen);
+ if (ret == 1)
+ {
+ string samName = domainName.ToString();
+ int index = samName.IndexOf('\\');
+ if (index != -1)
+ {
+ return samName.Substring(index + 1);
+ }
+ }
+ return string.Empty;
+ }
+
+ ///
+ /// Windows OSVersion implementation
+ ///
+ private static OperatingSystem WinGetOSVersion()
+ {
+ Win32Native.OSVERSIONINFOEX osviex = new Win32Native.OSVERSIONINFOEX();
+ osviex.OSVersionInfoSize = Marshal.SizeOf(osviex);
+ if (!Win32Native.GetVersionEx(ref osviex))
+ {
+ int errorCode = Marshal.GetLastWin32Error();
+ throw new Win32Exception(errorCode);
+ }
+
+ Version v = new Version(osviex.MajorVersion, osviex.MinorVersion, osviex.BuildNumber, (osviex.ServicePackMajor << 16) | osviex.ServicePackMinor);
+ return new OperatingSystem(v, osviex.CSDVersion);
+ }
///
/// DllImport uses the ApiSet dll that is available on CSS, since this code
/// will only be included when building targeting CoreCLR.
///
- internal static class Win32Native
+ private static class Win32Native
{
internal const int NameSamCompatible = 2; // EXTENDED_NAME_FORMAT - NameSamCompatible
@@ -1307,8 +1274,8 @@ namespace System.Management.Automation
}
}
}
-
- #endregion NativeMethods
+#endif
+ #endregion WinPlatform_Specific_Methods
#region NestedTypes
diff --git a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs
index 8c681ae25..1cccdabdc 100644
--- a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs
+++ b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs
@@ -2,13 +2,7 @@
Copyright (c) Microsoft Corporation. All rights reserved.
--********************************************************************/
-using System.Globalization;
-using System.Linq;
-using System.Reflection;
-using System.Collections;
using System.Collections.Generic;
-using System.Text;
-using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Microsoft.Win32;
using Microsoft.Win32.SafeHandles;
@@ -80,6 +74,105 @@ namespace System.Management.Automation
#endif
}
}
+
+ ///
+ /// True if the underlying system is NanoServer.
+ ///
+ public static bool IsNanoServer
+ {
+ get
+ {
+#if !CORECLR
+ return false;
+#elif UNIX
+ return false;
+#else
+ if (_isNanoServer.HasValue) { return _isNanoServer.Value; }
+
+ _isNanoServer = false;
+ using (RegistryKey regKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Server\ServerLevels"))
+ {
+ if (regKey != null)
+ {
+ object value = regKey.GetValue("NanoServer");
+ if (value != null && regKey.GetValueKind("NanoServer") == RegistryValueKind.DWord)
+ {
+ _isNanoServer = (int)value == 1;
+ }
+ }
+ }
+
+ return _isNanoServer.Value;
+#endif
+ }
+ }
+
+ ///
+ /// True if the underlying system is IoT.
+ ///
+ public static bool IsIoT
+ {
+ get
+ {
+#if !CORECLR
+ return false;
+#elif UNIX
+ return false;
+#else
+ if (_isIoT.HasValue) { return _isIoT.Value; }
+
+ _isIoT = false;
+ using (RegistryKey regKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion"))
+ {
+ if (regKey != null)
+ {
+ object value = regKey.GetValue("ProductName");
+ if (value != null && regKey.GetValueKind("ProductName") == RegistryValueKind.String)
+ {
+ _isIoT = string.Equals("IoTUAP", (string)value, StringComparison.OrdinalIgnoreCase);
+ }
+ }
+ }
+
+ return _isIoT.Value;
+#endif
+ }
+ }
+
+#if CORECLR
+ ///
+ /// True if it is the inbox powershell for NanoServer or IoT.
+ ///
+ internal static bool IsInbox
+ {
+ get
+ {
+#if UNIX
+ return false;
+#else
+ if (_isInbox.HasValue) { return _isInbox.Value; }
+
+ _isInbox = false;
+ if (IsNanoServer || IsIoT)
+ {
+ _isInbox = string.Equals(
+ Utils.GetApplicationBase(Utils.DefaultPowerShellShellID),
+ Utils.GetApplicationBaseFromRegistry(Utils.DefaultPowerShellShellID),
+ StringComparison.OrdinalIgnoreCase);
+ }
+
+ return _isInbox.Value;
+#endif
+ }
+ }
+
+#if !UNIX
+ private static bool? _isNanoServer = null;
+ private static bool? _isIoT = null;
+ private static bool? _isInbox = null;
+#endif
+
+#endif
// format files
internal static List FormatFileNames = new List
@@ -97,6 +190,20 @@ namespace System.Management.Automation
"WSMan.format.ps1xml"
};
+ ///
+ /// Some common environment variables used in PS have different
+ /// names in different OS platforms
+ ///
+ internal static class CommonEnvVariableNames
+ {
+#if UNIX
+ internal const string Home = "HOME";
+#else
+ internal const string Home = "USERPROFILE";
+#endif
+ }
+
+#if UNIX
///
/// X Desktop Group configuration type enum.
///
@@ -109,7 +216,9 @@ namespace System.Management.Automation
/// XDG_DATA_HOME/powershell
DATA,
/// XDG_DATA_HOME/powershell/Modules
- MODULES,
+ USER_MODULES,
+ /// /usr/local/share/powershell/Modules
+ SHARED_MODULES,
/// XDG_CONFIG_HOME/powershell
DEFAULT
}
@@ -160,7 +269,7 @@ namespace System.Management.Automation
return Path.Combine(xdgdatahome, "powershell");
}
- case Platform.XDG_Type.MODULES:
+ case Platform.XDG_Type.USER_MODULES:
//the user has set XDG_DATA_HOME corresponding to module path
if (String.IsNullOrEmpty(xdgdatahome))
{
@@ -176,6 +285,9 @@ namespace System.Management.Automation
return Path.Combine(xdgdatahome, "powershell", "Modules");
}
+ case Platform.XDG_Type.SHARED_MODULES:
+ return "/usr/local/share/powershell/Modules";
+
case Platform.XDG_Type.CACHE:
//the user has set XDG_CACHE_HOME
if (String.IsNullOrEmpty(xdgcachehome))
@@ -221,6 +333,7 @@ namespace System.Management.Automation
return xdgConfigHomeDefault;
}
}
+#endif
// Platform methods prefixed NonWindows are:
// - non-windows by the definition of the IsWindows method above
diff --git a/src/System.Management.Automation/engine/CommandDiscovery.cs b/src/System.Management.Automation/engine/CommandDiscovery.cs
index 0f2f24e73..4142b52a5 100644
--- a/src/System.Management.Automation/engine/CommandDiscovery.cs
+++ b/src/System.Management.Automation/engine/CommandDiscovery.cs
@@ -1548,7 +1548,7 @@ namespace System.Management.Automation
if (_pathCacheKey != null)
{
- string[] tokenizedPath = _pathCacheKey.Split(new char[] { System.IO.Path.PathSeparator }, StringSplitOptions.RemoveEmptyEntries);
+ string[] tokenizedPath = _pathCacheKey.Split(Utils.Separators.PathSeparator, StringSplitOptions.RemoveEmptyEntries);
_cachedPath = new Collection();
foreach (string directory in tokenizedPath)
@@ -1633,7 +1633,7 @@ namespace System.Management.Automation
lock (s_lockObject)
{
s_cachedPathExtCollection = pathExt != null
- ? pathExt.Split(new char[] { System.IO.Path.PathSeparator }, StringSplitOptions.RemoveEmptyEntries)
+ ? pathExt.Split(Utils.Separators.PathSeparator, StringSplitOptions.RemoveEmptyEntries)
: Utils.EmptyArray();
s_cachedPathExtCollectionWithPs1 = new string[s_cachedPathExtCollection.Length + 1];
s_cachedPathExtCollectionWithPs1[0] = StringLiterals.PowerShellScriptFileExtension;
diff --git a/src/System.Management.Automation/engine/Modules/AnalysisCache.cs b/src/System.Management.Automation/engine/Modules/AnalysisCache.cs
index d07bb0427..70ebe628c 100644
--- a/src/System.Management.Automation/engine/Modules/AnalysisCache.cs
+++ b/src/System.Management.Automation/engine/Modules/AnalysisCache.cs
@@ -1027,10 +1027,11 @@ namespace System.Management.Automation
{
s_cacheStoreLocation =
Environment.GetEnvironmentVariable("PSModuleAnalysisCachePath") ??
- (Platform.IsWindows
- ? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
- @"Microsoft\Windows\PowerShell\ModuleAnalysisCache")
- : Path.Combine(Platform.SelectProductNameForDirectory(Platform.XDG_Type.CACHE), "ModuleAnalysisCache"));
+#if UNIX
+ Path.Combine(Platform.SelectProductNameForDirectory(Platform.XDG_Type.CACHE), "ModuleAnalysisCache");
+#else
+ Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"Microsoft\Windows\PowerShell\ModuleAnalysisCache");
+#endif
}
}
diff --git a/src/System.Management.Automation/engine/Modules/ModuleIntrinsics.cs b/src/System.Management.Automation/engine/Modules/ModuleIntrinsics.cs
index 5f205bce6..0e96745c7 100644
--- a/src/System.Management.Automation/engine/Modules/ModuleIntrinsics.cs
+++ b/src/System.Management.Automation/engine/Modules/ModuleIntrinsics.cs
@@ -8,6 +8,7 @@ using System.Management.Automation.Internal;
using System.Management.Automation.Language;
using Microsoft.PowerShell.Commands;
using System.Linq;
+using System.Text;
using System.Threading;
using Dbg = System.Management.Automation.Diagnostics;
@@ -529,26 +530,15 @@ namespace System.Management.Automation
///
/// Gets the personal module path
- /// (i.e. C:\Users\lukasza\Documents\WindowsPowerShell\modules, or
- /// ~/.powershell/Modules on Linux)
///
/// personal module path
internal static string GetPersonalModulePath()
{
- if (Platform.IsWindows)
- {
- string personalModuleRoot = Path.Combine(
- Path.Combine(
- Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
- Utils.ProductNameForDirectory),
- Utils.ModuleDirectory);
- return personalModuleRoot;
- }
- else
- {
- string personalModuleRoot = Platform.SelectProductNameForDirectory(Platform.XDG_Type.MODULES);
- return personalModuleRoot;
- }
+#if UNIX
+ return Platform.SelectProductNameForDirectory(Platform.XDG_Type.USER_MODULES);
+#else
+ return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), Utils.ModuleDirectory);
+#endif
}
///
@@ -559,39 +549,26 @@ namespace System.Management.Automation
{
if (s_systemWideModulePath != null)
return s_systemWideModulePath;
-
- // There is no runspace config so we use the default string
- string shellId = Utils.DefaultPowerShellShellID;
-
- // Now figure out what $PSHOME is.
- // This depends on the shellId. If we cannot read the application base
- // registry key, set the variable to empty string
- string psHome = null;
+
try
{
- psHome = Utils.GetApplicationBase(shellId);
- }
- catch (System.Security.SecurityException)
- {
- }
-
- if (!string.IsNullOrEmpty(psHome))
- {
- // Win8: 584267 Powershell Modules are listed twice in x86, and cannot be removed
- // This happens because ModuleTable uses Path as the key and CBS installer
- // expands the path to include "SysWOW64" (for
- // HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\PowerShell\3\PowerShellEngine ApplicationBase).
- // Because of this, the module that is getting loaded during startup (through LocalRunspace)
- // is using "SysWow64" in the key. Later, when Import-Module is called, it loads the
- // module using ""System32" in the key.
-
- // Porting note: psHome cannot be lower-cased on case sensitive file systems
- if (Platform.IsWindows)
+ string psHome = Utils.GetApplicationBase(Utils.DefaultPowerShellShellID);
+ if (!string.IsNullOrEmpty(psHome))
{
+ // Win8: 584267 Powershell Modules are listed twice in x86, and cannot be removed
+ // This happens because ModuleTable uses Path as the key and CBS installer
+ // expands the path to include "SysWOW64" (for
+ // HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\PowerShell\3\PowerShellEngine ApplicationBase).
+ // Because of this, the module that is getting loaded during startup (through LocalRunspace)
+ // is using "SysWow64" in the key. Later, when Import-Module is called, it loads the
+ // module using ""System32" in the key.
+#if !UNIX
psHome = psHome.ToLowerInvariant().Replace("\\syswow64\\", "\\system32\\");
+#endif
+ Interlocked.CompareExchange(ref s_systemWideModulePath, Path.Combine(psHome, "Modules"), null);
}
- Interlocked.CompareExchange(ref s_systemWideModulePath, Path.Combine(psHome, Utils.ModuleDirectory), null);
}
+ catch (System.Security.SecurityException) { }
return s_systemWideModulePath;
}
@@ -604,18 +581,17 @@ namespace System.Management.Automation
///
internal static string GetDscModulePath()
{
- if (!Platform.IsWindows)
- {
- return string.Empty;
- }
-
+#if UNIX
+ return Platform.SelectProductNameForDirectory(Platform.XDG_Type.SHARED_MODULES);
+#else
string dscModulePath = null;
string programFilesPath = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
if (!string.IsNullOrEmpty(programFilesPath))
{
- dscModulePath = Path.Combine(programFilesPath, Utils.DscModuleDirectory);
+ dscModulePath = Path.Combine(programFilesPath, Utils.ModuleDirectory);
}
return dscModulePath;
+#endif
}
///
@@ -625,17 +601,20 @@ namespace System.Management.Automation
///
private static string CombineSystemModulePaths()
{
- string psSystemModulePath = GetSystemwideModulePath();
- string dscSystemModulePath = GetDscModulePath();
+ string systemModulePath = GetSystemwideModulePath();
+ string commonModulePath = GetDscModulePath();
- if (psSystemModulePath != null && dscSystemModulePath != null)
+ bool isSystemPathNullOrEmpty = string.IsNullOrEmpty(systemModulePath);
+ bool isCommonPathNullOrEmpty = string.IsNullOrEmpty(commonModulePath);
+
+ if (!isSystemPathNullOrEmpty && !isCommonPathNullOrEmpty)
{
- return (dscSystemModulePath + ";" + psSystemModulePath);
+ return (commonModulePath + Path.PathSeparator + systemModulePath);
}
- if (psSystemModulePath != null || dscSystemModulePath != null)
+ if (!isSystemPathNullOrEmpty || !isCommonPathNullOrEmpty)
{
- return (psSystemModulePath ?? dscSystemModulePath);
+ return isSystemPathNullOrEmpty ? commonModulePath : systemModulePath;
}
return null;
@@ -664,11 +643,11 @@ namespace System.Management.Automation
Diagnostics.Assert(pathToLookFor != null, "pathToLookFor should not be null according to contract of the function");
int pos = 0; // position of the current substring in pathToScan
- string[] substrings = pathToScan.Split(new char[] { ';' }, StringSplitOptions.None); // we want to process empty entries
- string goodPathToLookFor = pathToLookFor.Trim().TrimEnd('\\'); // trailing backslashes and white-spaces will mess up equality comparison
+ string[] substrings = pathToScan.Split(Utils.Separators.PathSeparator, StringSplitOptions.None); // we want to process empty entries
+ string goodPathToLookFor = pathToLookFor.Trim().TrimEnd(Path.DirectorySeparatorChar); // trailing backslashes and white-spaces will mess up equality comparison
foreach (string substring in substrings)
{
- string goodSubstring = substring.Trim().TrimEnd('\\'); // trailing backslashes and white-spaces will mess up equality comparison
+ string goodSubstring = substring.Trim().TrimEnd(Path.DirectorySeparatorChar); // trailing backslashes and white-spaces will mess up equality comparison
// We have to use equality comparison on individual substrings (as opposed to simple 'string.IndexOf' or 'string.Contains')
// because of cases like { pathToScan="C:\Temp\MyDir\MyModuleDir", pathToLookFor="C:\Temp" }
@@ -698,29 +677,28 @@ namespace System.Management.Automation
Diagnostics.Assert(basePath != null, "basePath should not be null according to contract of the function");
Diagnostics.Assert(pathToAdd != null, "pathToAdd should not be null according to contract of the function");
- System.Text.StringBuilder result = new System.Text.StringBuilder(basePath);
-
- char[] semicolonSeparator = new char[] { ';' };
+ StringBuilder result = new StringBuilder(basePath);
+
if (!string.IsNullOrEmpty(pathToAdd)) // we don't want to append empty paths
{
- foreach (string subPathToAdd in pathToAdd.Split(semicolonSeparator, StringSplitOptions.RemoveEmptyEntries)) // in case pathToAdd is a 'combined path' (semicolon-separated)
+ foreach (string subPathToAdd in pathToAdd.Split(Utils.Separators.PathSeparator, StringSplitOptions.RemoveEmptyEntries)) // in case pathToAdd is a 'combined path' (semicolon-separated)
{
int position = PathContainsSubstring(result.ToString(), subPathToAdd); // searching in effective 'result' value ensures that possible duplicates in pathsToAdd are handled correctly
if (-1 == position) // subPathToAdd not found - add it
{
if (-1 == insertPosition) // append subPathToAdd to the end
{
- bool resultHasEndingSemicolon = false;
- if (result.Length > 0) resultHasEndingSemicolon = (result[result.Length - 1] == ';');
+ bool endsWithPathSeparator = false;
+ if (result.Length > 0) endsWithPathSeparator = (result[result.Length - 1] == Path.PathSeparator);
- if (resultHasEndingSemicolon)
+ if (endsWithPathSeparator)
result.Append(subPathToAdd);
else
- result.Append(";" + subPathToAdd);
+ result.Append(Path.PathSeparator + subPathToAdd);
}
else // insert at the requested location (this is used by DSC ( location) and by 'user-specific location' (SpecialFolder.MyDocuments or EVT.User))
{
- result.Insert(insertPosition, subPathToAdd + ";");
+ result.Insert(insertPosition, subPathToAdd + Path.PathSeparator);
}
}
}
@@ -728,6 +706,94 @@ namespace System.Management.Automation
return result.ToString();
}
+
+ ///
+ /// Check if the current powershell is likely running in following scenarios:
+ /// - sxs ps started on windows [machine-wide env:psmodulepath will influence]
+ /// - sxs ps started from full ps
+ /// - sxs ps started from inbox nano/iot ps
+ /// - full ps started from sxs ps
+ /// - inbox nano/iot ps started from sxs ps
+ /// If it's likely one of them, then we need to clear the current process module path.
+ ///
+ private static bool NeedToClearProcessModulePath(string currentProcessModulePath, string personalModulePath, string programFilesModulePath, bool runningSxS)
+ {
+#if UNIX
+ return false;
+#else
+ Dbg.Assert(!string.IsNullOrEmpty(personalModulePath), "caller makes sure it's not null or empty");
+ Dbg.Assert(!string.IsNullOrEmpty(programFilesModulePath), "caller makes sure it's not null or empty");
+
+ const string winSxSModuleDirectory = @"PowerShell\Modules";
+ const string winLegacyModuleDirectory = @"WindowsPowerShell\Modules";
+
+ if (runningSxS)
+ {
+ // The machine-wide and user-wide environment variables are only meaningful for full ps,
+ // so if the current process module path contains any of them, it's likely that the sxs
+ // ps was started directly on windows, or from full ps. The same goes for the legacy personal
+ // and shared module paths.
+ string hklmModulePath = GetExpandedEnvironmentVariable("PSMODULEPATH", EnvironmentVariableTarget.Machine);
+ string hkcuModulePath = GetExpandedEnvironmentVariable("PSMODULEPATH", EnvironmentVariableTarget.User);
+ string legacyPersonalModulePath = personalModulePath.Replace(winSxSModuleDirectory, winLegacyModuleDirectory);
+ string legacyProgramFilesModulePath = programFilesModulePath.Replace(winSxSModuleDirectory, winLegacyModuleDirectory);
+
+ return (!string.IsNullOrEmpty(hklmModulePath) && currentProcessModulePath.IndexOf(hklmModulePath, StringComparison.OrdinalIgnoreCase) != -1) ||
+ (!string.IsNullOrEmpty(hkcuModulePath) && currentProcessModulePath.IndexOf(hkcuModulePath, StringComparison.OrdinalIgnoreCase) != -1) ||
+ currentProcessModulePath.IndexOf(legacyPersonalModulePath, StringComparison.OrdinalIgnoreCase) != -1 ||
+ currentProcessModulePath.IndexOf(legacyProgramFilesModulePath, StringComparison.OrdinalIgnoreCase) != -1;
+ }
+
+ // The sxs personal and shared module paths are only meaningful for sxs ps, so if they appear
+ // in the current process module path, it's likely the running ps was started from a sxs ps.
+ string sxsPersonalModulePath = personalModulePath.Replace(winLegacyModuleDirectory, winSxSModuleDirectory);
+ string sxsProgramFilesModulePath = programFilesModulePath.Replace(winLegacyModuleDirectory, winSxSModuleDirectory);
+
+ return currentProcessModulePath.IndexOf(sxsPersonalModulePath, StringComparison.OrdinalIgnoreCase) != -1 ||
+ currentProcessModulePath.IndexOf(sxsProgramFilesModulePath, StringComparison.OrdinalIgnoreCase) != -1;
+#endif
+ }
+
+ ///
+ /// When sxs ps instance B got started from sxs ps instance A, A's pshome module path might
+ /// show up in current process module path. It doesn't make sense for B to load modules from
+ /// A's pshome module path, so remove it in such case.
+ ///
+ private static string RemoveSxSPsHomeModulePath(string currentProcessModulePath)
+ {
+#if UNIX
+ const string powershellExeName = "powershell";
+#else
+ const string powershellExeName = "powershell.exe";
+#endif
+ StringBuilder modulePathString = new StringBuilder(currentProcessModulePath.Length);
+ char[] invalidPathChars = Path.GetInvalidPathChars();
+
+ foreach (var path in currentProcessModulePath.Split(Utils.Separators.PathSeparator, StringSplitOptions.RemoveEmptyEntries))
+ {
+ string trimedPath = path.Trim().TrimEnd(Path.DirectorySeparatorChar);
+ if (trimedPath == string.Empty || trimedPath.IndexOfAny(invalidPathChars) != -1)
+ {
+ // Path contains invalid characters. Ignore it.
+ continue;
+ }
+
+ string psExePath = Path.Combine(Path.GetDirectoryName(trimedPath), powershellExeName);
+ if (File.Exists(psExePath))
+ {
+ // Path is a PSHome module path. Ignore it.
+ continue;
+ }
+
+ if (modulePathString.Length > 0)
+ {
+ modulePathString.Append(Path.PathSeparator);
+ }
+ modulePathString.Append(trimedPath);
+ }
+
+ return modulePathString.ToString();
+ }
///
/// Checks the various PSModulePath environment string and returns PSModulePath string as appropriate. Note - because these
@@ -736,23 +802,42 @@ namespace System.Management.Automation
///
public static string GetModulePath(string currentProcessModulePath, string hklmMachineModulePath, string hkcuUserModulePath)
{
+ string personalModulePath = GetPersonalModulePath();
string programFilesModulePath = GetDscModulePath(); // aka location
- string psHomeModulePath = Environment.ExpandEnvironmentVariables(GetSystemwideModulePath()); // $PSHome\Modules location
+ string psHomeModulePath = GetSystemwideModulePath(); // $PSHome\Modules location
+
+#if CORECLR
+ bool runningSxS = Platform.IsInbox ? false : true;
+#else
+ bool runningSxS = false;
+#endif
+ if (!string.IsNullOrEmpty(currentProcessModulePath) &&
+ NeedToClearProcessModulePath(currentProcessModulePath, personalModulePath, programFilesModulePath, runningSxS))
+ {
+ // Clear the current process module path in the following cases
+ // - start sxs ps on windows [machine-wide env:psmodulepath will influence]
+ // - start sxs ps from full ps
+ // - start sxs ps from inbox nano/iot ps
+ // - start full ps from sxs ps
+ // - start inbox nano/iot ps from sxs ps
+ currentProcessModulePath = null;
+ }
+
// If the variable isn't set, then set it to the default value
if (currentProcessModulePath == null) // EVT.Process does Not exist - really corner case
{
// Handle the default case...
- if (String.IsNullOrEmpty(hkcuUserModulePath)) // EVT.User does Not exist -> set to location
+ if (string.IsNullOrEmpty(hkcuUserModulePath)) // EVT.User does Not exist -> set to location
{
- currentProcessModulePath = GetPersonalModulePath(); // = SpecialFolder.MyDocuments + Utils.ProductNameForDirectory + Utils.ModuleDirectory
+ currentProcessModulePath = personalModulePath; // = SpecialFolder.MyDocuments + Utils.ProductNameForDirectory + Utils.ModuleDirectory
}
else // EVT.User exists -> set to EVT.User
{
currentProcessModulePath = hkcuUserModulePath; // = EVT.User
}
- currentProcessModulePath += ';';
- if (String.IsNullOrEmpty(hklmMachineModulePath)) // EVT.Machine does Not exist
+ currentProcessModulePath += Path.PathSeparator;
+ if (string.IsNullOrEmpty(hklmMachineModulePath)) // EVT.Machine does Not exist
{
currentProcessModulePath += CombineSystemModulePaths(); // += (DscModulePath + $PSHome\Modules)
}
@@ -761,18 +846,21 @@ namespace System.Management.Automation
currentProcessModulePath += hklmMachineModulePath; // += EVT.Machine
}
}
- else // EVT.Process exists
+ // EVT.Process exists
+ // Now handle the case where the environment variable is already set.
+ else if (runningSxS) // The running powershell is an SxS PS instance
{
- // Now handle the case where the environment variable is already set.
+ // When SxS PS instance A starts SxS PS instance B, A's PSHome module path might be inherited by B. We need to remove that path from B
+ currentProcessModulePath = RemoveSxSPsHomeModulePath(currentProcessModulePath);
- // CoreCLR PowerShell on Windows has a Modules folder in the the application base
- // path which contains the built-in modules It must be in the front of the path no
- // matter what, regardless of inherited path.
-#if CORECLR && !UNIX
- // TODO: #1184 will resolve this work-around
- currentProcessModulePath = AddToPath(currentProcessModulePath, GetSystemwideModulePath(), 0);
-#endif
+ string personalModulePathToUse = string.IsNullOrEmpty(hkcuUserModulePath) ? personalModulePath : hkcuUserModulePath;
+ string systemModulePathToUse = string.IsNullOrEmpty(hklmMachineModulePath) ? psHomeModulePath : hklmMachineModulePath;
+ currentProcessModulePath = AddToPath(currentProcessModulePath, personalModulePathToUse, 0);
+ currentProcessModulePath = AddToPath(currentProcessModulePath, systemModulePathToUse, -1);
+ }
+ else // The running powershell is Full PS or inbox Core PS
+ {
// If there is no personal path key, then if the env variable doesn't match the system variable,
// the user modified it somewhere, else prepend the default personel module path
if (hklmMachineModulePath != null) // EVT.Machine exists
@@ -789,8 +877,7 @@ namespace System.Management.Automation
// for bug 6678623, if we are running wow64 process (x86 32-bit process on 64-bit (amd64) OS), then ensure that exists in currentProcessModulePath / return value
if (Environment.Is64BitOperatingSystem && !Environment.Is64BitProcess)
{
- string userModulePath = GetPersonalModulePath();
- currentProcessModulePath = AddToPath(currentProcessModulePath, userModulePath, psHomePosition);
+ currentProcessModulePath = AddToPath(currentProcessModulePath, personalModulePath, psHomePosition);
psHomePosition = PathContainsSubstring(currentProcessModulePath, psHomeModulePath);
}
#endif
@@ -799,12 +886,12 @@ namespace System.Management.Automation
return null;
}
- currentProcessModulePath = GetPersonalModulePath() + ';' + hklmMachineModulePath; // + EVT.Machine + inserted later in this function
+ currentProcessModulePath = personalModulePath + Path.PathSeparator + hklmMachineModulePath; // + EVT.Machine + inserted later in this function
}
else // EVT.User exists
{
// PSModulePath is designed to have behaviour like 'Path' var in a sense that EVT.User + EVT.Machine are merged to get final value of PSModulePath
- string combined = string.Concat(hkcuUserModulePath, ';', hklmMachineModulePath); // EVT.User + EVT.Machine
+ string combined = string.Concat(hkcuUserModulePath, Path.PathSeparator, hklmMachineModulePath); // EVT.User + EVT.Machine
if (!((combined).Equals(currentProcessModulePath, StringComparison.OrdinalIgnoreCase) ||
(hklmMachineModulePath).Equals(currentProcessModulePath, StringComparison.OrdinalIgnoreCase) ||
(hkcuUserModulePath).Equals(currentProcessModulePath, StringComparison.OrdinalIgnoreCase)))
@@ -829,7 +916,7 @@ namespace System.Management.Automation
{
if (hkcuUserModulePath.Equals(currentProcessModulePath, StringComparison.OrdinalIgnoreCase))
{
- currentProcessModulePath = hkcuUserModulePath + ';' + CombineSystemModulePaths(); // = EVT.User + (DscModulePath + $PSHome\Modules)
+ currentProcessModulePath = hkcuUserModulePath + Path.PathSeparator + CombineSystemModulePaths(); // = EVT.User + (DscModulePath + $PSHome\Modules)
}
else
{
@@ -923,13 +1010,13 @@ namespace System.Management.Automation
if (!string.IsNullOrWhiteSpace(modulePathString))
{
- foreach (string envPath in modulePathString.Split(Utils.Separators.Semicolon, StringSplitOptions.RemoveEmptyEntries))
+ foreach (string envPath in modulePathString.Split(Utils.Separators.PathSeparator, StringSplitOptions.RemoveEmptyEntries))
{
var processedPath = ProcessOneModulePath(context, envPath, processedPathSet);
if (processedPath != null)
yield return processedPath;
}
- }
+ }
if (includeSystemModulePath)
{
diff --git a/src/System.Management.Automation/engine/PropertyAccessor.cs b/src/System.Management.Automation/engine/PropertyAccessor.cs
index 15203fa24..7cdd7e3bb 100644
--- a/src/System.Management.Automation/engine/PropertyAccessor.cs
+++ b/src/System.Management.Automation/engine/PropertyAccessor.cs
@@ -36,17 +36,9 @@ namespace System.Management.Automation
static ConfigPropertyAccessor()
{
#if CORECLR
- // TODO: Update this for #1184
- //if (Platform.IsInbox())
- //{
- // Instance = new RegistryAccessor();
- //}
- //else
- //{
- // Instance = new JsonConfigFileAccessor();
- //}
- // Remove this following line:
- Instance = new JsonConfigFileAccessor();
+ Instance = Platform.IsInbox
+ ? (ConfigPropertyAccessor) new RegistryAccessor()
+ : new JsonConfigFileAccessor();
#else
Instance = new RegistryAccessor();
#endif
diff --git a/src/System.Management.Automation/engine/SessionState.cs b/src/System.Management.Automation/engine/SessionState.cs
index 9b8fd3bdb..5ded60804 100644
--- a/src/System.Management.Automation/engine/SessionState.cs
+++ b/src/System.Management.Automation/engine/SessionState.cs
@@ -303,8 +303,10 @@ namespace System.Management.Automation
RunspaceInit.PSHostDescription);
this.GlobalScope.SetVariable(v.Name, v, false, true, this, CommandOrigin.Internal, fastPath: true);
- // $HOME = %USERPROFILE% - indicate where a user's home directory is located in the file system.
- string home = Environment.GetEnvironmentVariable("USERPROFILE") ?? string.Empty;
+ // $HOME - indicate where a user's home directory is located in the file system.
+ // -- %USERPROFILE% on windows
+ // -- %HOME% on unix
+ string home = Environment.GetEnvironmentVariable(Platform.CommonEnvVariableNames.Home) ?? string.Empty;
v = new PSVariable(SpecialVariables.Home,
home,
ScopedItemOptions.ReadOnly | ScopedItemOptions.AllScope,
diff --git a/src/System.Management.Automation/engine/Utils.cs b/src/System.Management.Automation/engine/Utils.cs
index 3ba32c7bf..63c687050 100644
--- a/src/System.Management.Automation/engine/Utils.cs
+++ b/src/System.Management.Automation/engine/Utils.cs
@@ -216,6 +216,7 @@ namespace System.Management.Automation
if (engineKey != null)
{
var result = engineKey.GetValue(RegistryStrings.MonadEngine_ApplicationBase) as string;
+ result = Environment.ExpandEnvironmentVariables(result);
if (wantPsHome)
Interlocked.CompareExchange(ref s_pshome, null, result);
@@ -584,25 +585,22 @@ namespace System.Management.Automation
///
/// String representing the Default shellID.
///
- internal static string DefaultPowerShellShellID = "Microsoft.PowerShell";
- ///
- /// String used to control directory location for PowerShell
- ///
- ///
- /// Profile uses this to control profile loading.
- ///
- internal static string ProductNameForDirectory =
- Platform.IsWindows ? "WindowsPowerShell" : Platform.SelectProductNameForDirectory(Platform.XDG_Type.CONFIG);
+ internal const string DefaultPowerShellShellID = "Microsoft.PowerShell";
///
- /// The name of the subdirectory that contains packages.
+ /// This is used to construct the profile path.
///
- internal static string ModuleDirectory = "Modules";
+#if CORECLR
+ internal static string ProductNameForDirectory = Platform.IsInbox ? "WindowsPowerShell" : "PowerShell";
+#else
+ internal const string ProductNameForDirectory = "WindowsPowerShell";
+#endif
///
- /// The partial path to the DSC module directory
+ /// The subdirectory of module paths
+ /// e.g. ~\Documents\WindowsPowerShell\Modules and %ProgramFiles%\WindowsPowerShell\Modules
///
- internal static string DscModuleDirectory = Path.Combine("WindowsPowerShell", "Modules");
+ internal static string ModuleDirectory = Path.Combine(ProductNameForDirectory, "Modules");
internal static string GetRegistryConfigurationPrefix()
{
@@ -1519,6 +1517,7 @@ namespace System.Management.Automation
internal static readonly char[] Semicolon = new char[] { ';' };
internal static readonly char[] StarOrQuestion = new char[] { '*', '?' };
internal static readonly char[] ColonOrBackslash = new char[] { '\\', ':' };
+ internal static readonly char[] PathSeparator = new char[] { Path.PathSeparator };
internal static readonly char[] QuoteChars = new char[] { '\'', '"' };
internal static readonly char[] Space = new char[] { ' ' };
diff --git a/src/System.Management.Automation/engine/hostifaces/HostUtilities.cs b/src/System.Management.Automation/engine/hostifaces/HostUtilities.cs
index 1928eed5b..324a1d7ea 100644
--- a/src/System.Management.Automation/engine/hostifaces/HostUtilities.cs
+++ b/src/System.Management.Automation/engine/hostifaces/HostUtilities.cs
@@ -182,8 +182,12 @@ namespace System.Management.Automation
if (forCurrentUser)
{
+#if UNIX
+ basePath = Platform.SelectProductNameForDirectory(Platform.XDG_Type.CONFIG);
+#else
basePath = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
basePath = IO.Path.Combine(basePath, Utils.ProductNameForDirectory);
+#endif
}
else
{
diff --git a/src/System.Management.Automation/engine/remoting/fanin/InitialSessionStateProvider.cs b/src/System.Management.Automation/engine/remoting/fanin/InitialSessionStateProvider.cs
index 21c7261ca..494827dc9 100644
--- a/src/System.Management.Automation/engine/remoting/fanin/InitialSessionStateProvider.cs
+++ b/src/System.Management.Automation/engine/remoting/fanin/InitialSessionStateProvider.cs
@@ -1891,7 +1891,7 @@ namespace System.Management.Automation.Remoting
}
// Go through each directory in the module path
- string[] modulePaths = ModuleIntrinsics.GetModulePath().Split(Utils.Separators.Semicolon);
+ string[] modulePaths = ModuleIntrinsics.GetModulePath().Split(Utils.Separators.PathSeparator);
foreach (string path in modulePaths)
{
try
diff --git a/src/System.Management.Automation/namespaces/FileSystemProvider.cs b/src/System.Management.Automation/namespaces/FileSystemProvider.cs
index 1ae3130c5..e68a286c9 100644
--- a/src/System.Management.Automation/namespaces/FileSystemProvider.cs
+++ b/src/System.Management.Automation/namespaces/FileSystemProvider.cs
@@ -351,7 +351,7 @@ namespace Microsoft.PowerShell.Commands
if (providerInfo != null && string.IsNullOrEmpty(providerInfo.Home))
{
// %USERPROFILE% - indicate where a user's home directory is located in the file system.
- string homeDirectory = Environment.GetEnvironmentVariable("USERPROFILE");
+ string homeDirectory = Environment.GetEnvironmentVariable(Platform.CommonEnvVariableNames.Home);
if (!string.IsNullOrEmpty(homeDirectory))
{
diff --git a/src/System.Management.Automation/security/SecuritySupport.cs b/src/System.Management.Automation/security/SecuritySupport.cs
index f2ec5051a..ffaabf398 100644
--- a/src/System.Management.Automation/security/SecuritySupport.cs
+++ b/src/System.Management.Automation/security/SecuritySupport.cs
@@ -211,9 +211,9 @@ namespace System.Management.Automation.Internal
private static void CleanKeyParents(RegistryKey baseKey, string keyPath)
{
#if CORECLR
- // if ( ! Platform.Inbox) // Modify this to support registry checks for inbox CoreCLR
- return;
-#else // Windows && FullCLR
+ if (!Platform.IsInbox)
+ return;
+#endif
using (RegistryKey key = baseKey.OpenSubKey(keyPath, true))
{
// Verify the child key has no children
@@ -248,7 +248,6 @@ namespace System.Management.Automation.Internal
return;
}
}
-#endif
}
internal static ExecutionPolicy GetExecutionPolicy(string shellId)
diff --git a/test/powershell/Host/Base-Directory.Tests.ps1 b/test/powershell/Host/Base-Directory.Tests.ps1
index 11811fdb3..625c64b1d 100644
--- a/test/powershell/Host/Base-Directory.Tests.ps1
+++ b/test/powershell/Host/Base-Directory.Tests.ps1
@@ -10,9 +10,14 @@ Describe "Configuration file locations" -tags "CI","Slow" {
BeforeAll {
if ($IsWindows) {
+ $ProductName = "WindowsPowerShell"
+ if ($IsCoreCLR -and ($PSHOME -notlike "*Windows\System32\WindowsPowerShell\v1.0"))
+ {
+ $ProductName = "PowerShell"
+ }
$expectedCache = [IO.Path]::Combine($env:LOCALAPPDATA, "Microsoft", "Windows", "PowerShell", "StartupProfileData-NonInteractive")
- $expectedModule = [IO.Path]::Combine($env:USERPROFILE, "Documents", "WindowsPowerShell", "Modules")
- $expectedProfile = [io.path]::Combine($env:USERPROFILE, "Documents","WindowsPowerShell",$profileName)
+ $expectedModule = [IO.Path]::Combine($env:USERPROFILE, "Documents", $ProductName, "Modules")
+ $expectedProfile = [io.path]::Combine($env:USERPROFILE, "Documents", $ProductName, $profileName)
$expectedReadline = [IO.Path]::Combine($env:AppData, "Microsoft", "Windows", "PowerShell", "PSReadline", "ConsoleHost_history.txt")
} else {
$expectedCache = [IO.Path]::Combine($env:HOME, ".cache", "powershell", "StartupProfileData-NonInteractive")
diff --git a/test/powershell/Language/Classes/Scripting.Classes.Modules.Tests.ps1 b/test/powershell/Language/Classes/Scripting.Classes.Modules.Tests.ps1
index 5caca7d22..b9bc55132 100644
--- a/test/powershell/Language/Classes/Scripting.Classes.Modules.Tests.ps1
+++ b/test/powershell/Language/Classes/Scripting.Classes.Modules.Tests.ps1
@@ -26,7 +26,7 @@ Describe 'use of a module from two runspaces' -Tags "CI" {
$resolvedTestDrivePath = Split-Path ((get-childitem TestDrive:\)[0].FullName)
if (-not ($env:PSMODULEPATH -like "*$resolvedTestDrivePath*")) {
- $env:PSMODULEPATH += ";$resolvedTestDrivePath"
+ $env:PSMODULEPATH += "$([System.IO.Path]::PathSeparator)$resolvedTestDrivePath"
}
}
diff --git a/test/powershell/Language/Classes/scripting.Classes.NestedModules.tests.ps1 b/test/powershell/Language/Classes/scripting.Classes.NestedModules.tests.ps1
index 86ae42c12..886ea067f 100644
--- a/test/powershell/Language/Classes/scripting.Classes.NestedModules.tests.ps1
+++ b/test/powershell/Language/Classes/scripting.Classes.NestedModules.tests.ps1
@@ -31,7 +31,7 @@ Describe 'NestedModules' -Tags "CI" {
$resolvedTestDrivePath = Split-Path ((get-childitem TestDrive:\)[0].FullName)
if (-not ($env:PSMODULEPATH -like "*$resolvedTestDrivePath*")) {
- $env:PSMODULEPATH += ";$resolvedTestDrivePath"
+ $env:PSMODULEPATH += "$([System.IO.Path]::PathSeparator)$resolvedTestDrivePath"
}
}
diff --git a/test/powershell/Language/Classes/scripting.Classes.using.tests.ps1 b/test/powershell/Language/Classes/scripting.Classes.using.tests.ps1
index d4c6fc42f..82fd31780 100644
--- a/test/powershell/Language/Classes/scripting.Classes.using.tests.ps1
+++ b/test/powershell/Language/Classes/scripting.Classes.using.tests.ps1
@@ -24,7 +24,7 @@ Describe 'using module' -Tags "CI" {
$resolvedTestDrivePath = Split-Path ((get-childitem "${TestDrive}\$ModulePathPrefix")[0].FullName)
if (-not ($env:PSMODULEPATH -like "*$resolvedTestDrivePath*")) {
- $env:PSMODULEPATH += ";$resolvedTestDrivePath"
+ $env:PSMODULEPATH += "$([System.IO.Path]::PathSeparator)$resolvedTestDrivePath"
}
}
diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/Export-Csv.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/Export-Csv.Tests.ps1
index 0340ae3d4..f4c95abb4 100644
--- a/test/powershell/Modules/Microsoft.PowerShell.Utility/Export-Csv.Tests.ps1
+++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/Export-Csv.Tests.ps1
@@ -1,6 +1,6 @@
Describe "Export-Csv" -Tags "CI" {
$testObject = @("test","object","array")
- $testCsv = "output.csv"
+ $testCsv = Join-Path -Path $TestDrive -ChildPath "output.csv"
AfterEach {
Remove-Item $testCsv -Force -ErrorAction SilentlyContinue
@@ -51,7 +51,7 @@ Describe "Export-Csv" -Tags "CI" {
It "Should have the same information when using the alias vs the cmdlet" {
$testObject | Export-Csv -Path $testCsv
- $aliasObject = "alias.csv"
+ $aliasObject = Join-Path -Path $TestDrive -ChildPath "alias.csv"
$testObject | epcsv -Path $aliasObject
diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/Export-FormatData.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/Export-FormatData.Tests.ps1
index fc50695b8..7761cf4ac 100644
--- a/test/powershell/Modules/Microsoft.PowerShell.Utility/Export-FormatData.Tests.ps1
+++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/Export-FormatData.Tests.ps1
@@ -16,26 +16,29 @@ Describe "Export-FormatData DRT Unit Tests" -Tags "CI" {
Describe "Export-FormatData" -Tags "CI" {
+ $testOutput = Join-Path -Path $TestDrive -ChildPath "outputfile"
+
+ AfterEach {
+ Remove-Item $testOutput -Force -ErrorAction SilentlyContinue
+ }
+
Context "Check Export-FormatData can be called validly." {
- It "Should be able to be called without error" {
- { Get-FormatData | Export-FormatData -Path "outputfile" } | Should Not Throw
- Remove-Item "outputfile" -Force -ErrorAction SilentlyContinue
- }
+ It "Should be able to be called without error" {
+ { Get-FormatData | Export-FormatData -Path $testOutput } | Should Not Throw
+ }
}
Context "Check that the output is in the correct format" {
- It "Should not return an empty xml file" {
- Get-FormatData | Export-FormatData -Path "outputfile"
- $piped = Get-Content "outputfile"
- $piped | Should Not Be ""
- Remove-Item "outputfile" -Force -ErrorAction SilentlyContinue
- }
+ It "Should not return an empty xml file" {
+ Get-FormatData | Export-FormatData -Path $testOutput
+ $piped = Get-Content $testOutput
+ $piped | Should Not Be ""
+ }
- It "Should have a valid xml tag at the start of the file" {
- Get-FormatData | Export-FormatData -Path "outputfile"
- $piped = Get-Content "outputfile"
- $piped[0] | Should Be "<"
- Remove-Item "outputfile" -Force -ErrorAction SilentlyContinue
- }
+ It "Should have a valid xml tag at the start of the file" {
+ Get-FormatData | Export-FormatData -Path $testOutput
+ $piped = Get-Content $testOutput
+ $piped[0] | Should Be "<"
+ }
}
}
diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/Out-File.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/Out-File.Tests.ps1
index cf99491fc..70e4ce1cd 100644
--- a/test/powershell/Modules/Microsoft.PowerShell.Utility/Out-File.Tests.ps1
+++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/Out-File.Tests.ps1
@@ -1,6 +1,6 @@
Describe "Out-File DRT Unit Tests" -Tags "CI" {
It "Should be able to write the contens into a file with -pspath" {
- $tempFile = "ExposeBug928965"
+ $tempFile = Join-Path -Path $TestDrive -ChildPath "ExposeBug928965"
{ 1 | Out-File -PSPath $tempFile } | Should Not Throw
$fileContents = Get-Content $tempFile
$fileContents | Should be 1
@@ -8,7 +8,7 @@ Describe "Out-File DRT Unit Tests" -Tags "CI" {
}
It "Should be able to write the contens into a file with -pspath" {
- $tempFile = "outfileAppendTest.txt"
+ $tempFile = Join-Path -Path $TestDrive -ChildPath "outfileAppendTest.txt"
{ 'This is first line.' | out-file $tempFile } | Should Not Throw
{ 'This is second line.' | out-file -append $tempFile } | Should Not Throw
$tempFile |Should Contain "first"
diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/Write-Stream.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/Write-Stream.Tests.ps1
index 4c52d3e04..7df534ae1 100644
--- a/test/powershell/Modules/Microsoft.PowerShell.Utility/Write-Stream.Tests.ps1
+++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/Write-Stream.Tests.ps1
@@ -1,5 +1,5 @@
Describe "Stream writer tests" -Tags "CI" {
- $targetfile = "writeoutput.txt"
+ $targetfile = Join-Path -Path $TestDrive -ChildPath "writeoutput.txt"
# A custom function is defined here do handle the debug stream dealing with the confirm prompt
# that would normally
diff --git a/test/powershell/enginecore/ModulePath.Tests.ps1 b/test/powershell/enginecore/ModulePath.Tests.ps1
new file mode 100644
index 000000000..0c33e15ee
--- /dev/null
+++ b/test/powershell/enginecore/ModulePath.Tests.ps1
@@ -0,0 +1,45 @@
+Describe "SxS Module Path Basic Tests" -tags "CI" {
+
+ BeforeAll {
+
+ if ($IsWindows)
+ {
+ $powershell = "$PSHOME\powershell.exe"
+ $ProductName = "WindowsPowerShell"
+ if ($IsCoreCLR -and ($PSHOME -notlike "*Windows\System32\WindowsPowerShell\v1.0"))
+ {
+ $ProductName = "PowerShell"
+ }
+ $expectedUserPath = Join-Path -Path $HOME -ChildPath "Documents\$ProductName\Modules"
+ $expectedSharedPath = Join-Path -Path $env:ProgramFiles -ChildPath "$ProductName\Modules"
+ }
+ else
+ {
+ $powershell = "$PSHOME/powershell"
+ $expectedUserPath = [System.Management.Automation.Platform]::SelectProductNameForDirectory("USER_MODULES")
+ $expectedSharedPath = [System.Management.Automation.Platform]::SelectProductNameForDirectory("SHARED_MODULES")
+ }
+ $expectedSystemPath = Join-Path -Path $PSHOME -ChildPath 'Modules'
+ }
+
+ BeforeEach {
+ $originalModulePath = $env:PSMODULEPATH
+ }
+
+ AfterEach {
+ $env:PSMODULEPATH = $originalModulePath
+ }
+
+ It "validate sxs module path" {
+
+ $env:PSMODULEPATH = ""
+ $defaultModulePath = & $powershell -nopro -c '$env:PSMODULEPATH'
+
+ $paths = $defaultModulePath -split [System.IO.Path]::PathSeparator
+
+ $paths.Count | Should Be 3
+ $paths[0] | Should Be $expectedUserPath
+ $paths[1] | Should Be $expectedSharedPath
+ $paths[2] | Should Be $expectedSystemPath
+ }
+}