From 54c64c9db651b1be5e13c080ae355d887a203ddb Mon Sep 17 00:00:00 2001 From: v-alexjo Date: Tue, 24 May 2016 14:29:00 -0700 Subject: [PATCH 01/17] Initial XDG support and folder default changes Conflicts: src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs --- .../host/msh/ConsoleHost.cs | 15 +++--- .../Cmdlets.cs | 21 ++++++-- .../CoreCLR/CorePsPlatform.cs | 53 ++++++++++++++++++- .../engine/Modules/ModuleIntrinsics.cs | 8 +-- .../engine/Utils.cs | 4 +- .../engine/hostifaces/HostUtilities.cs | 9 +++- 6 files changed, 88 insertions(+), 22 deletions(-) diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs index 61a4012d6..8c04e0fcb 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs @@ -185,14 +185,18 @@ namespace Microsoft.PowerShell { profileDir = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + @"\Microsoft\Windows\PowerShell"; - } else + } + + else { - profileDir = System.IO.Path.Combine(Environment.GetEnvironmentVariable("HOME"), ".powershell"); + //check if the user has set an XDG path in their environment variables + profileDir = Platform.SelectProductNameForDirectory("profile"); } - - if (!Directory.Exists(profileDir)) + + if (!Directory.Exists(profileDir)) //xdg value may have been set but not a valid directory { - Directory.CreateDirectory(profileDir); + Console.WriteLine("The selected directory for the profile does not exist. Using the default path."); + profileDir = Platform.SelectProductNameForDirectory("default"); } ClrFacade.SetProfileOptimizationRoot(profileDir); } @@ -268,7 +272,6 @@ namespace Microsoft.PowerShell : "StartupProfileData-NonInteractive"); exitCode = theConsoleHost.Run(cpp, !string.IsNullOrEmpty(preStartWarning)); } - } finally { diff --git a/src/Microsoft.PowerShell.PSReadLine/Cmdlets.cs b/src/Microsoft.PowerShell.PSReadLine/Cmdlets.cs index 3210a8965..fb9145314 100644 --- a/src/Microsoft.PowerShell.PSReadLine/Cmdlets.cs +++ b/src/Microsoft.PowerShell.PSReadLine/Cmdlets.cs @@ -178,14 +178,25 @@ namespace Microsoft.PowerShell } else { + //PSReadline does not have access to Utils.CorePSPlatform. Must set PSReadline path seperately + string modulepath = System.Environment.GetEnvironmentVariable("XDG_CACHE_HOME"); + + if (String.IsNullOrEmpty(modulepath)) + { + modulepath = System.IO.Path.Combine( + Environment.GetEnvironmentVariable("HOME"), + ".config/powershell/modules"); + } + HistorySavePath = System.IO.Path.Combine( Environment.GetEnvironmentVariable("HOME"), - ".powershell", + modulepath, "PSReadLine", hostName + "_history.txt"); - } -#else - HistorySavePath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + + } + #else + HistorySavePath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + @"\Microsoft\Windows\PowerShell\PSReadline\" + hostName + "_history.txt"; #endif CommandValidationHandler = null; @@ -205,7 +216,7 @@ namespace Microsoft.PowerShell "Where-Object", "?", "where", }; } - + public EditMode EditMode { get; set; } public string ContinuationPrompt { get; set; } diff --git a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs index 34623fc1b..9e2d40541 100644 --- a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs +++ b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs @@ -96,8 +96,57 @@ namespace System.Management.Automation "WSMan.format.ps1xml" }; - // directory location of PowerShell for profile loading - public static string ProductNameForDirectory = ".powershell"; + // function for choosing directory location of PowerShell for profile loading + public static string SelectProductNameForDirectory (string dir){ + + string xdgconfighome = System.Environment.GetEnvironmentVariable("XDG_CONFIG_HOME"); + string xdgdatahome = System.Environment.GetEnvironmentVariable("XDG_DATA_HOME"); + string xdgcachehome = System.Environment.GetEnvironmentVariable("XDG_CACHE_HOME"); + + + //the user has set XDG_CONFIG_HOME corrresponding to profile path + if (!String.IsNullOrEmpty(xdgconfighome) && dir == "profile") + { + return xdgconfighome; + } + + //the user has set XDG_DATA_HOME corresponding to module path + if (!String.IsNullOrEmpty(xdgdatahome) && dir == "modules") + { + return xdgdatahome; + } + + //the user has set XDG_CACHE_HOME + if (!String.IsNullOrEmpty(xdgcachehome) && dir == "history") + { + return xdgcachehome; + } + + if (dir == "default") + { + return @".config/powershell"; + } + //the user has set XDG_DATA_HOME + else //xdg values have not been set + { + if (dir == "profile" || dir == "history") + { + return @".config/powershell"; //default on Linux + } + if (dir == "modules") + { + if (!Directory.Exists(@".config/powershell/modules")) + { + Directory.CreateDirectory(@".config/powershell/modules"); + + } + + return @".config/powershell/modules"; //default on Linux + } + + return @".config/powershell"; + } + } // ComObjectType is null on CoreCLR for Linux since there is // no COM support on Linux diff --git a/src/System.Management.Automation/engine/Modules/ModuleIntrinsics.cs b/src/System.Management.Automation/engine/Modules/ModuleIntrinsics.cs index 129fd8db5..46bd51a21 100644 --- a/src/System.Management.Automation/engine/Modules/ModuleIntrinsics.cs +++ b/src/System.Management.Automation/engine/Modules/ModuleIntrinsics.cs @@ -548,12 +548,7 @@ namespace System.Management.Automation /// personal module path internal static string GetPersonalModulePath() { - string personalModuleRoot = Path.Combine( - Path.Combine( - Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), - Utils.ProductNameForDirectory), - Utils.ModuleDirectory); - + string personalModuleRoot = Platform.SelectProductNameForDirectory("modules"); return personalModuleRoot; } @@ -597,6 +592,7 @@ namespace System.Management.Automation psHome = psHome.ToLowerInvariant().Replace("\\syswow64\\", "\\system32\\"); } Interlocked.CompareExchange(ref SystemWideModulePath, Path.Combine(psHome, Utils.ModuleDirectory), null); + } return SystemWideModulePath; diff --git a/src/System.Management.Automation/engine/Utils.cs b/src/System.Management.Automation/engine/Utils.cs index 8c4f55e7d..3e6857d1b 100644 --- a/src/System.Management.Automation/engine/Utils.cs +++ b/src/System.Management.Automation/engine/Utils.cs @@ -606,13 +606,13 @@ namespace System.Management.Automation /// Profile uses this to control profile loading. /// internal static string ProductNameForDirectory = - Platform.IsWindows ? "WindowsPowerShell" : Platform.ProductNameForDirectory; + Platform.IsWindows ? "WindowsPowerShell" : Platform.SelectProductNameForDirectory("profile"); /// /// The name of the subdirectory that contains packages. /// internal static string ModuleDirectory = "Modules"; - + /// /// The partial path to the DSC module directory /// diff --git a/src/System.Management.Automation/engine/hostifaces/HostUtilities.cs b/src/System.Management.Automation/engine/hostifaces/HostUtilities.cs index 8783240ec..aef980161 100644 --- a/src/System.Management.Automation/engine/hostifaces/HostUtilities.cs +++ b/src/System.Management.Automation/engine/hostifaces/HostUtilities.cs @@ -152,6 +152,7 @@ namespace System.Management.Automation { continue; } + command = new PSCommand(); command.AddCommand(profilePath, false); commands.Add(command); @@ -186,7 +187,14 @@ namespace System.Management.Automation { basePath = Environment.GetFolderPath(Environment.SpecialFolder.Personal); basePath = IO.Path.Combine(basePath, Utils.ProductNameForDirectory); + + //If the profile path doesn't exist, create it. + if (!System.IO.Directory.Exists(basePath)) + { + System.IO.Directory.CreateDirectory(basePath); + } } + else { basePath = GetAllUsersFolderPath(shellId); @@ -198,7 +206,6 @@ namespace System.Management.Automation string profileName = useTestProfile ? "profile_test.ps1" : "profile.ps1"; - if (!string.IsNullOrEmpty(shellId)) { profileName = shellId + "_" + profileName; From 29e97bf1aa0f9d3f0b00132a469e8c3d2050467d Mon Sep 17 00:00:00 2001 From: v-alexjo Date: Wed, 1 Jun 2016 11:03:05 -0700 Subject: [PATCH 02/17] Code review changes to XDG support --- .../host/msh/ConsoleHost.cs | 5 +- .../Cmdlets.cs | 12 ++- .../CoreCLR/CorePsPlatform.cs | 102 ++++++++++-------- .../engine/Modules/ModuleIntrinsics.cs | 2 +- .../engine/Utils.cs | 2 +- 5 files changed, 75 insertions(+), 48 deletions(-) diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs index 8c04e0fcb..cd25cf24f 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs @@ -190,14 +190,15 @@ namespace Microsoft.PowerShell else { //check if the user has set an XDG path in their environment variables - profileDir = Platform.SelectProductNameForDirectory("profile"); + profileDir = Platform.SelectProductNameForDirectory(Platform.XDG_Type.PROFILE); } if (!Directory.Exists(profileDir)) //xdg value may have been set but not a valid directory { Console.WriteLine("The selected directory for the profile does not exist. Using the default path."); - profileDir = Platform.SelectProductNameForDirectory("default"); + profileDir = Platform.SelectProductNameForDirectory(Platform.XDG_Type.DEFAULT); } + ClrFacade.SetProfileOptimizationRoot(profileDir); } catch diff --git a/src/Microsoft.PowerShell.PSReadLine/Cmdlets.cs b/src/Microsoft.PowerShell.PSReadLine/Cmdlets.cs index fb9145314..ff6eb9dd0 100644 --- a/src/Microsoft.PowerShell.PSReadLine/Cmdlets.cs +++ b/src/Microsoft.PowerShell.PSReadLine/Cmdlets.cs @@ -176,6 +176,7 @@ namespace Microsoft.PowerShell @"\Microsoft\Windows\PowerShell\PSReadline\", hostName + "_history.txt"); } + else { //PSReadline does not have access to Utils.CorePSPlatform. Must set PSReadline path seperately @@ -186,13 +187,20 @@ namespace Microsoft.PowerShell modulepath = System.IO.Path.Combine( Environment.GetEnvironmentVariable("HOME"), ".config/powershell/modules"); - } - HistorySavePath = System.IO.Path.Combine( + HistorySavePath = System.IO.Path.Combine( Environment.GetEnvironmentVariable("HOME"), modulepath, "PSReadLine", hostName + "_history.txt"); + } + + else { + + HistorySavePath = modulepath; + + } + } #else diff --git a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs index 9e2d40541..cd948ae43 100644 --- a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs +++ b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs @@ -21,7 +21,6 @@ using SpecialFolder = System.Management.Automation.Environment.SpecialFolder; namespace System.Management.Automation { - /// /// These are platform abstractions and platform specific implementations /// @@ -43,6 +42,16 @@ namespace System.Management.Automation #endif } } + + //enum for selecting the xdgpaths + public enum XDG_Type + { + PROFILE, + MODULES, + HISTORY, + DEFAULT + + } public static bool IsOSX { @@ -97,55 +106,64 @@ namespace System.Management.Automation }; // function for choosing directory location of PowerShell for profile loading - public static string SelectProductNameForDirectory (string dir){ + public static string SelectProductNameForDirectory (Platform.XDG_Type dirpath){ string xdgconfighome = System.Environment.GetEnvironmentVariable("XDG_CONFIG_HOME"); string xdgdatahome = System.Environment.GetEnvironmentVariable("XDG_DATA_HOME"); string xdgcachehome = System.Environment.GetEnvironmentVariable("XDG_CACHE_HOME"); + string profileDefault = Path.Combine ( System.Environment.GetEnvironmentVariable("HOME"), ".config/powershell"); + string moduleDefault = Path.Combine ( System.Environment.GetEnvironmentVariable("HOME"), ".config/powershell/modules"); - - //the user has set XDG_CONFIG_HOME corrresponding to profile path - if (!String.IsNullOrEmpty(xdgconfighome) && dir == "profile") - { - return xdgconfighome; - } - - //the user has set XDG_DATA_HOME corresponding to module path - if (!String.IsNullOrEmpty(xdgdatahome) && dir == "modules") - { - return xdgdatahome; - } - - //the user has set XDG_CACHE_HOME - if (!String.IsNullOrEmpty(xdgcachehome) && dir == "history") - { - return xdgcachehome; - } - - if (dir == "default") - { - return @".config/powershell"; - } - //the user has set XDG_DATA_HOME - else //xdg values have not been set - { - if (dir == "profile" || dir == "history") - { - return @".config/powershell"; //default on Linux - } - if (dir == "modules") - { - if (!Directory.Exists(@".config/powershell/modules")) - { - Directory.CreateDirectory(@".config/powershell/modules"); - + switch (dirpath){ + case Platform.XDG_Type.PROFILE: + //the user has set XDG_CONFIG_HOME corrresponding to profile path + if (!String.IsNullOrEmpty(xdgconfighome)){ + + return xdgconfighome; + } + + else { + //xdg values have not been set + return profileDefault; } - return @".config/powershell/modules"; //default on Linux - } - - return @".config/powershell"; + case Platform.XDG_Type.MODULES: + //the user has set XDG_DATA_HOME corresponding to module path + if (!String.IsNullOrEmpty(xdgdatahome)){ + return xdgdatahome; + } + + else{ + //xdg values have not been set + if (!Directory.Exists(profileDefault)) //module folder not always guaranteed to exist + { + Directory.CreateDirectory(moduleDefault); + } + + return profileDefault; + } + + case Platform.XDG_Type.HISTORY: + //the user has set XDG_CACHE_HOME + if (!String.IsNullOrEmpty(xdgcachehome)){ + return xdgcachehome; + } + else{ + return profileDefault; + } + + case Platform.XDG_Type.DEFAULT: + //default for profile location + return profileDefault; + + default: + if (!Directory.Exists(profileDefault)) + { + Directory.CreateDirectory(profileDefault); + } + return profileDefault; } + } // ComObjectType is null on CoreCLR for Linux since there is diff --git a/src/System.Management.Automation/engine/Modules/ModuleIntrinsics.cs b/src/System.Management.Automation/engine/Modules/ModuleIntrinsics.cs index 46bd51a21..ca976181c 100644 --- a/src/System.Management.Automation/engine/Modules/ModuleIntrinsics.cs +++ b/src/System.Management.Automation/engine/Modules/ModuleIntrinsics.cs @@ -548,7 +548,7 @@ namespace System.Management.Automation /// personal module path internal static string GetPersonalModulePath() { - string personalModuleRoot = Platform.SelectProductNameForDirectory("modules"); + string personalModuleRoot = Platform.SelectProductNameForDirectory(Platform.XDG_Type.MODULES); return personalModuleRoot; } diff --git a/src/System.Management.Automation/engine/Utils.cs b/src/System.Management.Automation/engine/Utils.cs index 3e6857d1b..d499f3ad3 100644 --- a/src/System.Management.Automation/engine/Utils.cs +++ b/src/System.Management.Automation/engine/Utils.cs @@ -606,7 +606,7 @@ namespace System.Management.Automation /// Profile uses this to control profile loading. /// internal static string ProductNameForDirectory = - Platform.IsWindows ? "WindowsPowerShell" : Platform.SelectProductNameForDirectory("profile"); + Platform.IsWindows ? "WindowsPowerShell" : Platform.SelectProductNameForDirectory(Platform.XDG_Type.PROFILE); /// /// The name of the subdirectory that contains packages. From 237c659b5ec701e583e0ad69b4cfc6d567dcf8dc Mon Sep 17 00:00:00 2001 From: v-alexjo Date: Thu, 2 Jun 2016 10:14:51 -0700 Subject: [PATCH 03/17] Changes to xdg to append powershell path --- .../host/msh/ConsoleHost.cs | 6 ++-- .../Cmdlets.cs | 34 +++++++++---------- .../CoreCLR/CorePsPlatform.cs | 21 ++++++++---- 3 files changed, 33 insertions(+), 28 deletions(-) diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs index cd25cf24f..0071b1aad 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs @@ -192,13 +192,13 @@ namespace Microsoft.PowerShell //check if the user has set an XDG path in their environment variables profileDir = Platform.SelectProductNameForDirectory(Platform.XDG_Type.PROFILE); } - + /* if (!Directory.Exists(profileDir)) //xdg value may have been set but not a valid directory { - Console.WriteLine("The selected directory for the profile does not exist. Using the default path."); + Console.WriteLine("The selected directory (" + profileDir +") for the profile does not exist. Using the default path."); profileDir = Platform.SelectProductNameForDirectory(Platform.XDG_Type.DEFAULT); } - + */ ClrFacade.SetProfileOptimizationRoot(profileDir); } catch diff --git a/src/Microsoft.PowerShell.PSReadLine/Cmdlets.cs b/src/Microsoft.PowerShell.PSReadLine/Cmdlets.cs index ff6eb9dd0..f32595182 100644 --- a/src/Microsoft.PowerShell.PSReadLine/Cmdlets.cs +++ b/src/Microsoft.PowerShell.PSReadLine/Cmdlets.cs @@ -181,29 +181,27 @@ namespace Microsoft.PowerShell { //PSReadline does not have access to Utils.CorePSPlatform. Must set PSReadline path seperately string modulepath = System.Environment.GetEnvironmentVariable("XDG_CACHE_HOME"); - - if (String.IsNullOrEmpty(modulepath)) - { - modulepath = System.IO.Path.Combine( - Environment.GetEnvironmentVariable("HOME"), - ".config/powershell/modules"); - - HistorySavePath = System.IO.Path.Combine( - Environment.GetEnvironmentVariable("HOME"), - modulepath, - "PSReadLine", - hostName + "_history.txt"); - } - - else { + if (!String.IsNullOrEmpty(modulepath)) + { + modulepath = System.IO.Path.Combine(modulepath, "powershell"); HistorySavePath = modulepath; - } - + else + { + modulepath = System.IO.Path.Combine( + Environment.GetEnvironmentVariable("HOME"), + ".config/powershell/modules"); + + HistorySavePath = System.IO.Path.Combine( + Environment.GetEnvironmentVariable("HOME"), + modulepath, + "PSReadLine", + hostName + "_history.txt"); } - #else + } +#else HistorySavePath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + @"\Microsoft\Windows\PowerShell\PSReadline\" + hostName + "_history.txt"; #endif diff --git a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs index cd948ae43..7865c05ab 100644 --- a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs +++ b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs @@ -106,10 +106,11 @@ namespace System.Management.Automation }; // function for choosing directory location of PowerShell for profile loading - public static string SelectProductNameForDirectory (Platform.XDG_Type dirpath){ + public static string SelectProductNameForDirectory (Platform.XDG_Type dirpath) + { string xdgconfighome = System.Environment.GetEnvironmentVariable("XDG_CONFIG_HOME"); - string xdgdatahome = System.Environment.GetEnvironmentVariable("XDG_DATA_HOME"); + string xdgdatahome = System.Environment.GetEnvironmentVariable("XDG_DATA_HOME"); string xdgcachehome = System.Environment.GetEnvironmentVariable("XDG_CACHE_HOME"); string profileDefault = Path.Combine ( System.Environment.GetEnvironmentVariable("HOME"), ".config/powershell"); string moduleDefault = Path.Combine ( System.Environment.GetEnvironmentVariable("HOME"), ".config/powershell/modules"); @@ -117,12 +118,14 @@ namespace System.Management.Automation switch (dirpath){ case Platform.XDG_Type.PROFILE: //the user has set XDG_CONFIG_HOME corrresponding to profile path - if (!String.IsNullOrEmpty(xdgconfighome)){ - + if (!String.IsNullOrEmpty(xdgconfighome)) + { + xdgconfighome = Path.Combine(xdgconfighome, "powershell"); return xdgconfighome; } - else { + else + { //xdg values have not been set return profileDefault; } @@ -130,10 +133,12 @@ namespace System.Management.Automation case Platform.XDG_Type.MODULES: //the user has set XDG_DATA_HOME corresponding to module path if (!String.IsNullOrEmpty(xdgdatahome)){ + xdgdatahome = Path.Combine(xdgdatahome, "powershell"); return xdgdatahome; } - else{ + else + { //xdg values have not been set if (!Directory.Exists(profileDefault)) //module folder not always guaranteed to exist { @@ -146,9 +151,11 @@ namespace System.Management.Automation case Platform.XDG_Type.HISTORY: //the user has set XDG_CACHE_HOME if (!String.IsNullOrEmpty(xdgcachehome)){ + xdgcachehome = Path.Combine(xdgcachehome, "powershell"); return xdgcachehome; } - else{ + else + { return profileDefault; } From 4c1f9f9c79e013e45e0f16307147accea2020f80 Mon Sep 17 00:00:00 2001 From: v-alexjo Date: Thu, 2 Jun 2016 15:11:11 -0700 Subject: [PATCH 04/17] Adding XDG tests --- test/powershell/Profile.Tests.ps1 | 64 +++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 test/powershell/Profile.Tests.ps1 diff --git a/test/powershell/Profile.Tests.ps1 b/test/powershell/Profile.Tests.ps1 new file mode 100644 index 000000000..701d6b623 --- /dev/null +++ b/test/powershell/Profile.Tests.ps1 @@ -0,0 +1,64 @@ +Describe "XDG Base Directory Specification" { + + BeforeAll { + $powershell = Join-Path -Path $PsHome -ChildPath "powershell" + $profileName = "Microsoft.PowerShell_profile.ps1" + } + + BeforeEach { + $original_XDG_CONFIG_HOME = $env:XDG_CONFIG_HOME + $original_XDG_CACHE_HOME = $env:XDG_CACHE_HOME + $original_XDG_DATA_HOME = $env:XDG_DATA_HOME + + } + + AfterEach { + $env:XDG_CONFIG_HOME = $original_XDG_CONFIG_HOME + $env:XDG_CACHE_HOME = $original_XDG_CACHE_HOME + $env:XDG_DATA_HOME = $original_XDG_DATA_HOME + } + + Context "Profile" { + + It "Should not change Windows behavior" -Skip:($IsLinux -or $IsOSX) { + # Assuming this is run outside the OPS host on Windows + & $powershell -noprofile `$PROFILE | Should Be $PROFILE + } + + It "Should start with the default profile" -Skip:$IsWindows { + $expected = [IO.Path]::Combine($env:HOME, ".config/powershell", $profileName) + # Escape variable with backtick so new process interpolates it + & $powershell -noprofile `$PROFILE | Should Be $expected + } + + It "Should respect XDG_CONFIG_HOME" -Skip:$IsWindows { + $env:XDG_CONFIG_HOME = [IO.Path]::Combine($pwd, "test", "path") + # Escape variable with backtick so new process interpolates it + $profileDir = [IO.Path]::Combine($env:XDG_CONFIG_HOME, "powershell", $profileName) + & $powershell -noprofile `$PROFILE | Should Be $profileDir + } +} + + Context "Modules" { + + It "Should start with the default module path" -Skip:$IsWindows { + $expected = [IO.Path]::Combine($env:HOME, ".config", "powershell") + $modulepath = & $powershell -noprofile `$env:PSMODULEPATH + $modulepath = $modulepath.split(';')[0] + $modulepath | Should Be $expected + } + + It "Should respect XDG_CACHE_HOME" -Skip:$IsWindows { + $env:XDG_CACHE_HOME = [IO.Path]::Combine($pwd, "test", "path") + $expected = [IO.Path]::Combine($HOME, "Powershell", "test", "path") + & $powershell -noprofile `$env:XDG_CACHE_HOME | Should Be $expected + } + + It "Should respect XDG_DATA_HOME" -Skip:$IsWindows { + $env:XDG_DATA_HOME = [IO.Path]::Combine($pwd, "test", "path") + $datahomeDir = [IO.Path]::Combine($env:XDG_DATA_HOME, "ConsoleHost_history.txt") + $expected = [IO.Path]::Combine($HOME, "Powershell", "test", "path", "ConsoleHost_history.txt") + $datahomeDir | Should Be $expected + } + } +} From c2527199a66a6dc0be94a46cc080b3dac93fed6f Mon Sep 17 00:00:00 2001 From: v-alexjo Date: Fri, 3 Jun 2016 03:15:13 -0700 Subject: [PATCH 05/17] Further code review changes --- .../host/msh/ConsoleHost.cs | 4 +- .../Cmdlets.cs | 14 +++--- .../CoreCLR/CorePsPlatform.cs | 48 +++++++++---------- .../engine/Modules/ModuleIntrinsics.cs | 16 ++++++- test/powershell/Profile.Tests.ps1 | 18 ++++--- 5 files changed, 55 insertions(+), 45 deletions(-) diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs index 0071b1aad..ee8e2d587 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs @@ -192,13 +192,13 @@ namespace Microsoft.PowerShell //check if the user has set an XDG path in their environment variables profileDir = Platform.SelectProductNameForDirectory(Platform.XDG_Type.PROFILE); } - /* + if (!Directory.Exists(profileDir)) //xdg value may have been set but not a valid directory { Console.WriteLine("The selected directory (" + profileDir +") for the profile does not exist. Using the default path."); profileDir = Platform.SelectProductNameForDirectory(Platform.XDG_Type.DEFAULT); } - */ + ClrFacade.SetProfileOptimizationRoot(profileDir); } catch diff --git a/src/Microsoft.PowerShell.PSReadLine/Cmdlets.cs b/src/Microsoft.PowerShell.PSReadLine/Cmdlets.cs index f32595182..d3fae3ee6 100644 --- a/src/Microsoft.PowerShell.PSReadLine/Cmdlets.cs +++ b/src/Microsoft.PowerShell.PSReadLine/Cmdlets.cs @@ -180,23 +180,23 @@ namespace Microsoft.PowerShell else { //PSReadline does not have access to Utils.CorePSPlatform. Must set PSReadline path seperately - string modulepath = System.Environment.GetEnvironmentVariable("XDG_CACHE_HOME"); + string historypath = System.Environment.GetEnvironmentVariable("XDG_DATA_HOME"); - if (!String.IsNullOrEmpty(modulepath)) + if (!String.IsNullOrEmpty(historypath)) { - modulepath = System.IO.Path.Combine(modulepath, "powershell"); - HistorySavePath = modulepath; + historypath = System.IO.Path.Combine(historypath, "powershell"); + HistorySavePath = historypath; } else { - modulepath = System.IO.Path.Combine( + historypath = System.IO.Path.Combine( Environment.GetEnvironmentVariable("HOME"), - ".config/powershell/modules"); + ".local/share/powershell/modules"); HistorySavePath = System.IO.Path.Combine( Environment.GetEnvironmentVariable("HOME"), - modulepath, + historypath, "PSReadLine", hostName + "_history.txt"); } diff --git a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs index 7865c05ab..ecf7d8b2a 100644 --- a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs +++ b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs @@ -109,66 +109,66 @@ namespace System.Management.Automation public static string SelectProductNameForDirectory (Platform.XDG_Type dirpath) { + //TODO: XDG_DATA_DIRS implementation as per GitHub issue #1060 + string xdgconfighome = System.Environment.GetEnvironmentVariable("XDG_CONFIG_HOME"); string xdgdatahome = System.Environment.GetEnvironmentVariable("XDG_DATA_HOME"); string xdgcachehome = System.Environment.GetEnvironmentVariable("XDG_CACHE_HOME"); - string profileDefault = Path.Combine ( System.Environment.GetEnvironmentVariable("HOME"), ".config/powershell"); - string moduleDefault = Path.Combine ( System.Environment.GetEnvironmentVariable("HOME"), ".config/powershell/modules"); + string xdgConfigHomeDefault = Path.Combine ( System.Environment.GetEnvironmentVariable("HOME"), ".config/powershell"); + string moduleDefault = Path.Combine ( System.Environment.GetEnvironmentVariable("HOME"), ".local/share/powershell/modules"); switch (dirpath){ case Platform.XDG_Type.PROFILE: //the user has set XDG_CONFIG_HOME corrresponding to profile path - if (!String.IsNullOrEmpty(xdgconfighome)) + if (String.IsNullOrEmpty(xdgconfighome)) { - xdgconfighome = Path.Combine(xdgconfighome, "powershell"); - return xdgconfighome; + //xdg values have not been set + return xdgConfigHomeDefault; } else { - //xdg values have not been set - return profileDefault; + return Path.Combine(xdgconfighome, "powershell"); } case Platform.XDG_Type.MODULES: //the user has set XDG_DATA_HOME corresponding to module path - if (!String.IsNullOrEmpty(xdgdatahome)){ - xdgdatahome = Path.Combine(xdgdatahome, "powershell"); - return xdgdatahome; - } - - else - { + if (String.IsNullOrEmpty(xdgdatahome)){ //xdg values have not been set - if (!Directory.Exists(profileDefault)) //module folder not always guaranteed to exist + if (!Directory.Exists(moduleDefault)) //module folder not always guaranteed to exist { Directory.CreateDirectory(moduleDefault); } - return profileDefault; + return xdgConfigHomeDefault; + } + + else + { + return Path.Combine(xdgdatahome, "powershell"); } case Platform.XDG_Type.HISTORY: //the user has set XDG_CACHE_HOME - if (!String.IsNullOrEmpty(xdgcachehome)){ - xdgcachehome = Path.Combine(xdgcachehome, "powershell"); - return xdgcachehome; + if (String.IsNullOrEmpty(xdgcachehome)){ + return xdgConfigHomeDefault; } else { - return profileDefault; + return Path.Combine(xdgcachehome, "powershell"); + } case Platform.XDG_Type.DEFAULT: //default for profile location - return profileDefault; + return xdgConfigHomeDefault; default: - if (!Directory.Exists(profileDefault)) + if (!Directory.Exists(xdgConfigHomeDefault)) { - Directory.CreateDirectory(profileDefault); + Directory.CreateDirectory(xdgConfigHomeDefault); } - return profileDefault; + return xdgConfigHomeDefault; } } diff --git a/src/System.Management.Automation/engine/Modules/ModuleIntrinsics.cs b/src/System.Management.Automation/engine/Modules/ModuleIntrinsics.cs index ca976181c..12a9c81f7 100644 --- a/src/System.Management.Automation/engine/Modules/ModuleIntrinsics.cs +++ b/src/System.Management.Automation/engine/Modules/ModuleIntrinsics.cs @@ -548,8 +548,20 @@ namespace System.Management.Automation /// personal module path internal static string GetPersonalModulePath() { - string personalModuleRoot = Platform.SelectProductNameForDirectory(Platform.XDG_Type.MODULES); - return personalModuleRoot; + 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; + } } /// diff --git a/test/powershell/Profile.Tests.ps1 b/test/powershell/Profile.Tests.ps1 index 701d6b623..2722d8f82 100644 --- a/test/powershell/Profile.Tests.ps1 +++ b/test/powershell/Profile.Tests.ps1 @@ -26,9 +26,9 @@ Describe "XDG Base Directory Specification" { } It "Should start with the default profile" -Skip:$IsWindows { - $expected = [IO.Path]::Combine($env:HOME, ".config/powershell", $profileName) + $expected = [IO.Path]::Combine($env:HOME, "Documents", "WindowsPowerShell", $PROFILE) # Escape variable with backtick so new process interpolates it - & $powershell -noprofile `$PROFILE | Should Be $expected + & $powershell -noprofile `$PROFILE | Should Be $expected } It "Should respect XDG_CONFIG_HOME" -Skip:$IsWindows { @@ -41,11 +41,11 @@ Describe "XDG Base Directory Specification" { Context "Modules" { - It "Should start with the default module path" -Skip:$IsWindows { - $expected = [IO.Path]::Combine($env:HOME, ".config", "powershell") + It "Should respect XDG_DATA_HOME" -Skip:$IsWindows { + $env:XDG_DATA_HOME = [IO.Path]::Combine($env:HOME, ".local", "share", "powershell", "Modules") $modulepath = & $powershell -noprofile `$env:PSMODULEPATH $modulepath = $modulepath.split(';')[0] - $modulepath | Should Be $expected + $modulepath | Should Be $env:XDG_DATA_HOME } It "Should respect XDG_CACHE_HOME" -Skip:$IsWindows { @@ -54,11 +54,9 @@ Describe "XDG Base Directory Specification" { & $powershell -noprofile `$env:XDG_CACHE_HOME | Should Be $expected } - It "Should respect XDG_DATA_HOME" -Skip:$IsWindows { - $env:XDG_DATA_HOME = [IO.Path]::Combine($pwd, "test", "path") - $datahomeDir = [IO.Path]::Combine($env:XDG_DATA_HOME, "ConsoleHost_history.txt") - $expected = [IO.Path]::Combine($HOME, "Powershell", "test", "path", "ConsoleHost_history.txt") - $datahomeDir | Should Be $expected + It "Should respect PSReadLine history" -Skip:$IsWindows { + $env:XDG_DATA_HOME = [IO.Path]::Combine($env:HOME, ".local", "share", "powershell", "PSReadLine", "ConsoleHost_history.txt") + "a" | Should Be $expected } } } From caa4c3f1634ecdf5bbec3fb0698d57c8ff6a021c Mon Sep 17 00:00:00 2001 From: v-alexjo Date: Fri, 3 Jun 2016 13:55:23 -0700 Subject: [PATCH 06/17] Changing history to cache, removed redundant code --- .../host/msh/ConsoleHost.cs | 23 +++++---- .../Cmdlets.cs | 11 ++-- .../CoreCLR/CorePsPlatform.cs | 51 ++++++++++++------- .../engine/hostifaces/HostUtilities.cs | 5 -- 4 files changed, 49 insertions(+), 41 deletions(-) diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs index ee8e2d587..5cfdb0676 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs @@ -187,17 +187,18 @@ namespace Microsoft.PowerShell @"\Microsoft\Windows\PowerShell"; } - else - { - //check if the user has set an XDG path in their environment variables - profileDir = Platform.SelectProductNameForDirectory(Platform.XDG_Type.PROFILE); - } - - if (!Directory.Exists(profileDir)) //xdg value may have been set but not a valid directory - { - Console.WriteLine("The selected directory (" + profileDir +") for the profile does not exist. Using the default path."); - profileDir = Platform.SelectProductNameForDirectory(Platform.XDG_Type.DEFAULT); - } + else + { + //check if the user has set an XDG path in their environment variables + profileDir = Platform.SelectProductNameForDirectory(Platform.XDG_Type.CACHE); + + if (!Directory.Exists(profileDir)) //xdg value may have been set but not a valid directory + { + Console.Error.WriteLine("The selected directory (" + profileDir +") for the profile does not exist. Using the default path."); + profileDir = Platform.SelectProductNameForDirectory(Platform.XDG_Type.DEFAULT); + } + } + ClrFacade.SetProfileOptimizationRoot(profileDir); } diff --git a/src/Microsoft.PowerShell.PSReadLine/Cmdlets.cs b/src/Microsoft.PowerShell.PSReadLine/Cmdlets.cs index d3fae3ee6..cd00286ac 100644 --- a/src/Microsoft.PowerShell.PSReadLine/Cmdlets.cs +++ b/src/Microsoft.PowerShell.PSReadLine/Cmdlets.cs @@ -180,23 +180,20 @@ namespace Microsoft.PowerShell else { //PSReadline does not have access to Utils.CorePSPlatform. Must set PSReadline path seperately - string historypath = System.Environment.GetEnvironmentVariable("XDG_DATA_HOME"); + string historypath = System.Environment.GetEnvironmentVariable("XDG_CACHE_HOME"); if (!String.IsNullOrEmpty(historypath)) { - historypath = System.IO.Path.Combine(historypath, "powershell"); + historypath = System.IO.Path.Combine(historypath, "powershell", "PSReadLine", hostName + "_history.txt"); HistorySavePath = historypath; } else { - historypath = System.IO.Path.Combine( - Environment.GetEnvironmentVariable("HOME"), - ".local/share/powershell/modules"); - HistorySavePath = System.IO.Path.Combine( Environment.GetEnvironmentVariable("HOME"), - historypath, + ".cache", + "powershell", "PSReadLine", hostName + "_history.txt"); } diff --git a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs index ecf7d8b2a..f2bfb4d9c 100644 --- a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs +++ b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs @@ -48,7 +48,7 @@ namespace System.Management.Automation { PROFILE, MODULES, - HISTORY, + CACHE, DEFAULT } @@ -115,7 +115,8 @@ namespace System.Management.Automation string xdgdatahome = System.Environment.GetEnvironmentVariable("XDG_DATA_HOME"); string xdgcachehome = System.Environment.GetEnvironmentVariable("XDG_CACHE_HOME"); string xdgConfigHomeDefault = Path.Combine ( System.Environment.GetEnvironmentVariable("HOME"), ".config/powershell"); - string moduleDefault = Path.Combine ( System.Environment.GetEnvironmentVariable("HOME"), ".local/share/powershell/modules"); + string xdgModuleDefault = Path.Combine ( System.Environment.GetEnvironmentVariable("HOME"), ".local/share/powershell/modules"); + string xdgCacheDefault = Path.Combine (System.Environment.GetEnvironmentVariable("HOME"), ".cache/powershell"); switch (dirpath){ case Platform.XDG_Type.PROFILE: @@ -134,40 +135,54 @@ namespace System.Management.Automation case Platform.XDG_Type.MODULES: //the user has set XDG_DATA_HOME corresponding to module path if (String.IsNullOrEmpty(xdgdatahome)){ - //xdg values have not been set - if (!Directory.Exists(moduleDefault)) //module folder not always guaranteed to exist - { - Directory.CreateDirectory(moduleDefault); - } - - return xdgConfigHomeDefault; + + //xdg values have not been set + if (!Directory.Exists(xdgModuleDefault)) //module folder not always guaranteed to exist + { + Directory.CreateDirectory(xdgModuleDefault); + } + return xdgConfigHomeDefault; } - else { return Path.Combine(xdgdatahome, "powershell"); } - - case Platform.XDG_Type.HISTORY: + + case Platform.XDG_Type.CACHE: //the user has set XDG_CACHE_HOME - if (String.IsNullOrEmpty(xdgcachehome)){ - return xdgConfigHomeDefault; + if (String.IsNullOrEmpty(xdgcachehome)) + { + //xdg values have not been set + if (!Directory.Exists(xdgCacheDefault)) //module folder not always guaranteed to exist + { + Directory.CreateDirectory(xdgCacheDefault); + } + + return xdgCacheDefault; } else { return Path.Combine(xdgcachehome, "powershell"); - - } - + } + case Platform.XDG_Type.DEFAULT: //default for profile location return xdgConfigHomeDefault; default: + //xdgConfigHomeDefault needs to be created in the edge case that we do not have the folder or it was deleted + //This folder is the default in the event of all other failures for data storage if (!Directory.Exists(xdgConfigHomeDefault)) { - Directory.CreateDirectory(xdgConfigHomeDefault); + try { + Directory.CreateDirectory(xdgConfigHomeDefault); + } + catch{ + + Console.Error.WriteLine("Failed to create default data directory: " + xdgConfigHomeDefault); + } } + return xdgConfigHomeDefault; } diff --git a/src/System.Management.Automation/engine/hostifaces/HostUtilities.cs b/src/System.Management.Automation/engine/hostifaces/HostUtilities.cs index aef980161..592c0dcae 100644 --- a/src/System.Management.Automation/engine/hostifaces/HostUtilities.cs +++ b/src/System.Management.Automation/engine/hostifaces/HostUtilities.cs @@ -188,11 +188,6 @@ namespace System.Management.Automation basePath = Environment.GetFolderPath(Environment.SpecialFolder.Personal); basePath = IO.Path.Combine(basePath, Utils.ProductNameForDirectory); - //If the profile path doesn't exist, create it. - if (!System.IO.Directory.Exists(basePath)) - { - System.IO.Directory.CreateDirectory(basePath); - } } else From 41c7f9e469be42364eece070958e628bdb8c0dc8 Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Fri, 3 Jun 2016 16:39:58 -0700 Subject: [PATCH 07/17] Update XDG BDS tests to specifications This renames the tests and updates for better requirement specifications. The profile, modules, history, and startup cache data are tested for not breaking Windows behavior, new Linux behavior, and respecting set variables on launch. This also corrects some erroneous assumptions I had previously made. The ModuleAnalysisCache cannot be tested, and the startup cache data might not be testable on Windows. --- test/powershell/Profile.Tests.ps1 | 62 ----------------- test/powershell/XDGBDS.Tests.ps1 | 112 ++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+), 62 deletions(-) delete mode 100644 test/powershell/Profile.Tests.ps1 create mode 100644 test/powershell/XDGBDS.Tests.ps1 diff --git a/test/powershell/Profile.Tests.ps1 b/test/powershell/Profile.Tests.ps1 deleted file mode 100644 index 2722d8f82..000000000 --- a/test/powershell/Profile.Tests.ps1 +++ /dev/null @@ -1,62 +0,0 @@ -Describe "XDG Base Directory Specification" { - - BeforeAll { - $powershell = Join-Path -Path $PsHome -ChildPath "powershell" - $profileName = "Microsoft.PowerShell_profile.ps1" - } - - BeforeEach { - $original_XDG_CONFIG_HOME = $env:XDG_CONFIG_HOME - $original_XDG_CACHE_HOME = $env:XDG_CACHE_HOME - $original_XDG_DATA_HOME = $env:XDG_DATA_HOME - - } - - AfterEach { - $env:XDG_CONFIG_HOME = $original_XDG_CONFIG_HOME - $env:XDG_CACHE_HOME = $original_XDG_CACHE_HOME - $env:XDG_DATA_HOME = $original_XDG_DATA_HOME - } - - Context "Profile" { - - It "Should not change Windows behavior" -Skip:($IsLinux -or $IsOSX) { - # Assuming this is run outside the OPS host on Windows - & $powershell -noprofile `$PROFILE | Should Be $PROFILE - } - - It "Should start with the default profile" -Skip:$IsWindows { - $expected = [IO.Path]::Combine($env:HOME, "Documents", "WindowsPowerShell", $PROFILE) - # Escape variable with backtick so new process interpolates it - & $powershell -noprofile `$PROFILE | Should Be $expected - } - - It "Should respect XDG_CONFIG_HOME" -Skip:$IsWindows { - $env:XDG_CONFIG_HOME = [IO.Path]::Combine($pwd, "test", "path") - # Escape variable with backtick so new process interpolates it - $profileDir = [IO.Path]::Combine($env:XDG_CONFIG_HOME, "powershell", $profileName) - & $powershell -noprofile `$PROFILE | Should Be $profileDir - } -} - - Context "Modules" { - - It "Should respect XDG_DATA_HOME" -Skip:$IsWindows { - $env:XDG_DATA_HOME = [IO.Path]::Combine($env:HOME, ".local", "share", "powershell", "Modules") - $modulepath = & $powershell -noprofile `$env:PSMODULEPATH - $modulepath = $modulepath.split(';')[0] - $modulepath | Should Be $env:XDG_DATA_HOME - } - - It "Should respect XDG_CACHE_HOME" -Skip:$IsWindows { - $env:XDG_CACHE_HOME = [IO.Path]::Combine($pwd, "test", "path") - $expected = [IO.Path]::Combine($HOME, "Powershell", "test", "path") - & $powershell -noprofile `$env:XDG_CACHE_HOME | Should Be $expected - } - - It "Should respect PSReadLine history" -Skip:$IsWindows { - $env:XDG_DATA_HOME = [IO.Path]::Combine($env:HOME, ".local", "share", "powershell", "PSReadLine", "ConsoleHost_history.txt") - "a" | Should Be $expected - } - } -} diff --git a/test/powershell/XDGBDS.Tests.ps1 b/test/powershell/XDGBDS.Tests.ps1 new file mode 100644 index 000000000..cecbe6070 --- /dev/null +++ b/test/powershell/XDGBDS.Tests.ps1 @@ -0,0 +1,112 @@ +Describe "XDG Base Directory Specification" { + + BeforeAll { + $powershell = Join-Path -Path $PsHome -ChildPath "powershell" + $profileName = "Microsoft.PowerShell_profile.ps1" + } + + BeforeEach { + $original_XDG_CONFIG_HOME = $env:XDG_CONFIG_HOME + $original_XDG_CACHE_HOME = $env:XDG_CACHE_HOME + $original_XDG_DATA_HOME = $env:XDG_DATA_HOME + + } + + AfterEach { + $env:XDG_CONFIG_HOME = $original_XDG_CONFIG_HOME + $env:XDG_CACHE_HOME = $original_XDG_CACHE_HOME + $env:XDG_DATA_HOME = $original_XDG_DATA_HOME + } + + Context "Profile" { + + It "Should not change Windows behavior" -Skip:($IsLinux -or $IsOSX) { + $expected = [IO.Path]::Combine($env:HOME, "Documents", "WindowsPowerShell", $profileName) + & $powershell -noprofile `$PROFILE | Should Be $expected + } + + It "Should start with the default profile on Linux" -Skip:($IsWindows) { + $expected = [IO.Path]::Combine($env:HOME, ".config", "powershell", $profileName) + & $powershell -noprofile `$PROFILE | Should Be $expected + } + + It "Should respect XDG_CONFIG_HOME on Linux" -Skip:$IsWindows { + $env:XDG_CONFIG_HOME = $TestDrive + $expected = [IO.Path]::Combine($TestDrive, "powershell", $profileName) + & $powershell -noprofile `$PROFILE | Should Be $expected + } + } + + Context "Modules" { + + It "Should not change Windows behavior" -Skip:($IsLinux -or $IsOSX) { + $expected = [IO.Path]::Combine($env:HOME, "Documents", "WindowsPowerShell", "Modules") + $actual = & $powershell -noprofile `$env:PSMODULEPATH + $actual.split(';')[0] | Should Be $expected + } + + It "Should start with the default module path on Linux" -Skip:$IsWindows { + $env:PSMODULEPATH = "" # must not be sent to child process + $expected = [IO.Path]::Combine($env:HOME, ".local", "share", "powershell", "Modules") + $actual = & $powershell -noprofile `$env:PSMODULEPATH + $actual.split(';')[0] | Should Be $expected + } + + It "Should respect XDG_DATA_HOME on Linux" -Skip:$IsWindows { + $env:PSMODULEPATH = "" # must not be sent to child process + $env:XDG_DATA_HOME = $TestDrive + $expected = [IO.Path]::Combine($TestDrive, "powershell", "Modules") + $actual = & $powershell -noprofile `$env:PSMODULEPATH + $actual.split(';')[0] | Should Be $expected + } + + } + + Context "PSReadLine" { + + It "Should not change Windows behavior" -Skip:($IsLinux -or $IsOSX) { + $expected = [IO.Path]::Combine($env:AppData, "Microsoft", "Windows", "PowerShell", "PSReadline", "ConsoleHost_history.txt") + & $powershell -noprofile { (Get-PSReadlineOption).HistorySavePath } | Should Be $expected + } + + It "Should start with the default history save path on Linux" -Skip:$IsWindows { + $expected = [IO.Path]::Combine($env:HOME, ".local", "share", "powershell", "PSReadLine", "ConsoleHost_history.txt") + & $powershell -noprofile { (Get-PSReadlineOption).HistorySavePath } | Should Be $expected + } + + It "Should respect XDG_DATA_HOME on Linux" -Skip:$IsWindows { + $env:XDG_DATA_HOME = $TestDrive + $expected = [IO.Path]::Combine($TestDrive, "powershell", "PSReadLine", "ConsoleHost_history.txt") + & $powershell -noprofile { (Get-PSReadlineOption).HistorySavePath } | Should Be $expected + } + + } + + Context "Cache" { + + It "Should not change Windows behavior" -Skip:($IsLinux -or $IsOSX) { + $expected = [IO.Path]::Combine($env:HOME, "Documents", "WindowsPowerShell", "StartupProfileData-NonInteractive") + Remove-Item -ErrorAction SilentlyContinue $expected + & $powershell -noprofile { exit } + $expected | Should Exist + } + + It "Should start with the default StartupProfileData on Linux" -Skip:$IsWindows { + $expected = [IO.Path]::Combine($env:HOME, ".cache", "powershell", "StartupProfileData-NonInteractive") + Remove-Item -ErrorAction SilentlyContinue $expected + & $powershell -noprofile { exit } + $expected | Should Exist + } + + It "Should respect XDG_CACHE_HOME on Linux" -Skip:$IsWindows { + $env:XDG_CACHE_HOME = $TestDrive + $expected = [IO.Path]::Combine($TestDrive, "powershell", "StartupProfileData-NonInteractive") + Remove-Item -ErrorAction SilentlyContinue $expected + & $powershell -noprofile { exit } + $expected | Should Exist + } + + # The ModuleAnalysisCache cannot be forced to exist, thus we cannot test it + + } +} From 42c0b9a5f72b725842d047efbbc835e45ae87933 Mon Sep 17 00:00:00 2001 From: v-alexjo Date: Sun, 5 Jun 2016 23:50:35 -0700 Subject: [PATCH 08/17] Style changes and psreadline path change --- src/Microsoft.PowerShell.PSReadLine/Cmdlets.cs | 7 ++++--- src/System.Management.Automation/CoreCLR/CorePsPlatform.cs | 6 +++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/Microsoft.PowerShell.PSReadLine/Cmdlets.cs b/src/Microsoft.PowerShell.PSReadLine/Cmdlets.cs index cd00286ac..6688d9ceb 100644 --- a/src/Microsoft.PowerShell.PSReadLine/Cmdlets.cs +++ b/src/Microsoft.PowerShell.PSReadLine/Cmdlets.cs @@ -190,11 +190,12 @@ namespace Microsoft.PowerShell else { + //While a module, history save path goes into the larger .local/share/powershell folder HistorySavePath = System.IO.Path.Combine( Environment.GetEnvironmentVariable("HOME"), - ".cache", - "powershell", - "PSReadLine", + ".local", + "share", + "powershell", hostName + "_history.txt"); } } diff --git a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs index f2bfb4d9c..104ffdf7d 100644 --- a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs +++ b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs @@ -114,9 +114,9 @@ namespace System.Management.Automation string xdgconfighome = System.Environment.GetEnvironmentVariable("XDG_CONFIG_HOME"); string xdgdatahome = System.Environment.GetEnvironmentVariable("XDG_DATA_HOME"); string xdgcachehome = System.Environment.GetEnvironmentVariable("XDG_CACHE_HOME"); - string xdgConfigHomeDefault = Path.Combine ( System.Environment.GetEnvironmentVariable("HOME"), ".config/powershell"); - string xdgModuleDefault = Path.Combine ( System.Environment.GetEnvironmentVariable("HOME"), ".local/share/powershell/modules"); - string xdgCacheDefault = Path.Combine (System.Environment.GetEnvironmentVariable("HOME"), ".cache/powershell"); + string xdgConfigHomeDefault = Path.Combine ( System.Environment.GetEnvironmentVariable("HOME"), ".config", "powershell"); + string xdgModuleDefault = Path.Combine ( System.Environment.GetEnvironmentVariable("HOME"), ".local", "share", "powershell", "modules"); + string xdgCacheDefault = Path.Combine (System.Environment.GetEnvironmentVariable("HOME"), ".cache", "powershell"); switch (dirpath){ case Platform.XDG_Type.PROFILE: From f46aa8d1957184cc15f6fd517fa7f4d239f20aec Mon Sep 17 00:00:00 2001 From: v-alexjo Date: Mon, 6 Jun 2016 00:36:15 -0700 Subject: [PATCH 09/17] configdefault missed in refactoring --- src/Microsoft.PowerShell.PSReadLine/Cmdlets.cs | 5 +++-- src/System.Management.Automation/CoreCLR/CorePsPlatform.cs | 7 ++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Microsoft.PowerShell.PSReadLine/Cmdlets.cs b/src/Microsoft.PowerShell.PSReadLine/Cmdlets.cs index 6688d9ceb..7a72cee73 100644 --- a/src/Microsoft.PowerShell.PSReadLine/Cmdlets.cs +++ b/src/Microsoft.PowerShell.PSReadLine/Cmdlets.cs @@ -180,7 +180,7 @@ namespace Microsoft.PowerShell else { //PSReadline does not have access to Utils.CorePSPlatform. Must set PSReadline path seperately - string historypath = System.Environment.GetEnvironmentVariable("XDG_CACHE_HOME"); + string historypath = System.Environment.GetEnvironmentVariable("XDG_DATA_HOME"); if (!String.IsNullOrEmpty(historypath)) { @@ -195,7 +195,8 @@ namespace Microsoft.PowerShell Environment.GetEnvironmentVariable("HOME"), ".local", "share", - "powershell", + "powershell", + "PSReadLine", hostName + "_history.txt"); } } diff --git a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs index 104ffdf7d..c2e539662 100644 --- a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs +++ b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs @@ -115,7 +115,7 @@ namespace System.Management.Automation string xdgdatahome = System.Environment.GetEnvironmentVariable("XDG_DATA_HOME"); string xdgcachehome = System.Environment.GetEnvironmentVariable("XDG_CACHE_HOME"); string xdgConfigHomeDefault = Path.Combine ( System.Environment.GetEnvironmentVariable("HOME"), ".config", "powershell"); - string xdgModuleDefault = Path.Combine ( System.Environment.GetEnvironmentVariable("HOME"), ".local", "share", "powershell", "modules"); + string xdgModuleDefault = Path.Combine ( System.Environment.GetEnvironmentVariable("HOME"), ".local", "share", "powershell", "Modules"); string xdgCacheDefault = Path.Combine (System.Environment.GetEnvironmentVariable("HOME"), ".cache", "powershell"); switch (dirpath){ @@ -141,11 +141,11 @@ namespace System.Management.Automation { Directory.CreateDirectory(xdgModuleDefault); } - return xdgConfigHomeDefault; + return xdgModuleDefault; } else { - return Path.Combine(xdgdatahome, "powershell"); + return Path.Combine(xdgdatahome, "powershell", "Modules"); } case Platform.XDG_Type.CACHE: @@ -160,6 +160,7 @@ namespace System.Management.Automation return xdgCacheDefault; } + else { return Path.Combine(xdgcachehome, "powershell"); From a293707d71a29d5baffe2ab091e60a3d79bbfa63 Mon Sep 17 00:00:00 2001 From: v-alexjo Date: Mon, 6 Jun 2016 09:29:30 -0700 Subject: [PATCH 10/17] XDG cache home now will create powershell child folder --- src/System.Management.Automation/CoreCLR/CorePsPlatform.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs index c2e539662..b7748cd97 100644 --- a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs +++ b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs @@ -163,6 +163,11 @@ namespace System.Management.Automation else { + if (!Directory.Exists(Path.Combine(xdgcachehome, "powershell"))) + { + Directory.CreateDirectory(Path.Combine(xdgcachehome, "powershell")); + } + return Path.Combine(xdgcachehome, "powershell"); } From 140d452ba2bfa0979f62c12b554157c4e4e1ee8a Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Mon, 6 Jun 2016 13:37:03 -0700 Subject: [PATCH 11/17] Fix PSReadLine history path on Windows Bug introduced in initial port of PSReadLine. `Path.Combine` sees the initial backslash as an absolute path, thus discarding `AppData`. --- src/Microsoft.PowerShell.PSReadLine/Cmdlets.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.PowerShell.PSReadLine/Cmdlets.cs b/src/Microsoft.PowerShell.PSReadLine/Cmdlets.cs index 7a72cee73..ffb40e744 100644 --- a/src/Microsoft.PowerShell.PSReadLine/Cmdlets.cs +++ b/src/Microsoft.PowerShell.PSReadLine/Cmdlets.cs @@ -172,8 +172,8 @@ namespace Microsoft.PowerShell #if CORECLR if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) // MS Windows { - HistorySavePath = System.IO.Path.Combine(Environment.ExpandEnvironmentVariables("%AppData%"), - @"\Microsoft\Windows\PowerShell\PSReadline\", + HistorySavePath = System.IO.Path.Combine(Environment.GetEnvironmentVariable("APPDATA"), + @"Microsoft\Windows\PowerShell\PSReadline\", hostName + "_history.txt"); } From 0e10a51802cd4e1e2d3afa3d96d0d0daff200ecf Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Mon, 6 Jun 2016 13:37:27 -0700 Subject: [PATCH 12/17] Whitespace and formatting cleanups --- .../Cmdlets.cs | 35 ++++---- .../CoreCLR/CorePsPlatform.cs | 90 +++++++++---------- .../engine/Modules/ModuleIntrinsics.cs | 1 - .../engine/Utils.cs | 2 +- .../engine/hostifaces/HostUtilities.cs | 3 - 5 files changed, 63 insertions(+), 68 deletions(-) diff --git a/src/Microsoft.PowerShell.PSReadLine/Cmdlets.cs b/src/Microsoft.PowerShell.PSReadLine/Cmdlets.cs index ffb40e744..8c2f37004 100644 --- a/src/Microsoft.PowerShell.PSReadLine/Cmdlets.cs +++ b/src/Microsoft.PowerShell.PSReadLine/Cmdlets.cs @@ -169,40 +169,39 @@ namespace Microsoft.PowerShell WordDelimiters = DefaultWordDelimiters; HistorySearchCaseSensitive = DefaultHistorySearchCaseSensitive; HistorySaveStyle = DefaultHistorySaveStyle; + + string historyFileName = hostName + "_history.txt"; #if CORECLR if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) // MS Windows { HistorySavePath = System.IO.Path.Combine(Environment.GetEnvironmentVariable("APPDATA"), @"Microsoft\Windows\PowerShell\PSReadline\", - hostName + "_history.txt"); + historyFileName); } - else { - //PSReadline does not have access to Utils.CorePSPlatform. Must set PSReadline path seperately - string historypath = System.Environment.GetEnvironmentVariable("XDG_DATA_HOME"); - - if (!String.IsNullOrEmpty(historypath)) - { - historypath = System.IO.Path.Combine(historypath, "powershell", "PSReadLine", hostName + "_history.txt"); - HistorySavePath = historypath; - } + // PSReadline does not have access to Utils.CorePSPlatform. Must set PSReadline path separately + string historyPath = System.Environment.GetEnvironmentVariable("XDG_DATA_HOME"); + if (!String.IsNullOrEmpty(historyPath)) + { + historyPath = System.IO.Path.Combine(historyPath, "powershell", "PSReadLine", historyFileName); + HistorySavePath = historyPath; + } else { - //While a module, history save path goes into the larger .local/share/powershell folder - HistorySavePath = System.IO.Path.Combine( - Environment.GetEnvironmentVariable("HOME"), + // History is data, so it goes into .local/share/powershell folder + HistorySavePath = System.IO.Path.Combine(Environment.GetEnvironmentVariable("HOME"), ".local", "share", - "powershell", + "powershell", "PSReadLine", - hostName + "_history.txt"); + historyFileName); } } #else - HistorySavePath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) - + @"\Microsoft\Windows\PowerShell\PSReadline\" + hostName + "_history.txt"; + HistorySavePath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + + @"\Microsoft\Windows\PowerShell\PSReadline\" + historyFileName; #endif CommandValidationHandler = null; CommandsToValidateScriptBlockArguments = new HashSet(StringComparer.OrdinalIgnoreCase) @@ -221,7 +220,7 @@ namespace Microsoft.PowerShell "Where-Object", "?", "where", }; } - + public EditMode EditMode { get; set; } public string ContinuationPrompt { get; set; } diff --git a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs index b7748cd97..1110b2623 100644 --- a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs +++ b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs @@ -21,6 +21,7 @@ using SpecialFolder = System.Management.Automation.Environment.SpecialFolder; namespace System.Management.Automation { + /// /// These are platform abstractions and platform specific implementations /// @@ -42,15 +43,14 @@ namespace System.Management.Automation #endif } } - + //enum for selecting the xdgpaths public enum XDG_Type { PROFILE, MODULES, - CACHE, + CACHE, DEFAULT - } public static bool IsOSX @@ -105,7 +105,7 @@ namespace System.Management.Automation "WSMan.format.ps1xml" }; - // function for choosing directory location of PowerShell for profile loading + // function for choosing directory location of PowerShell for profile loading public static string SelectProductNameForDirectory (Platform.XDG_Type dirpath) { @@ -116,49 +116,49 @@ namespace System.Management.Automation string xdgcachehome = System.Environment.GetEnvironmentVariable("XDG_CACHE_HOME"); string xdgConfigHomeDefault = Path.Combine ( System.Environment.GetEnvironmentVariable("HOME"), ".config", "powershell"); string xdgModuleDefault = Path.Combine ( System.Environment.GetEnvironmentVariable("HOME"), ".local", "share", "powershell", "Modules"); - string xdgCacheDefault = Path.Combine (System.Environment.GetEnvironmentVariable("HOME"), ".cache", "powershell"); - + string xdgCacheDefault = Path.Combine (System.Environment.GetEnvironmentVariable("HOME"), ".cache", "powershell"); + switch (dirpath){ - case Platform.XDG_Type.PROFILE: + case Platform.XDG_Type.PROFILE: //the user has set XDG_CONFIG_HOME corrresponding to profile path if (String.IsNullOrEmpty(xdgconfighome)) { //xdg values have not been set - return xdgConfigHomeDefault; + return xdgConfigHomeDefault; } else { - return Path.Combine(xdgconfighome, "powershell"); + return Path.Combine(xdgconfighome, "powershell"); } - - case Platform.XDG_Type.MODULES: - //the user has set XDG_DATA_HOME corresponding to module path + + case Platform.XDG_Type.MODULES: + //the user has set XDG_DATA_HOME corresponding to module path if (String.IsNullOrEmpty(xdgdatahome)){ - - //xdg values have not been set + + //xdg values have not been set if (!Directory.Exists(xdgModuleDefault)) //module folder not always guaranteed to exist - { - Directory.CreateDirectory(xdgModuleDefault); + { + Directory.CreateDirectory(xdgModuleDefault); } - return xdgModuleDefault; + return xdgModuleDefault; } else { - return Path.Combine(xdgdatahome, "powershell", "Modules"); - } - + return Path.Combine(xdgdatahome, "powershell", "Modules"); + } + case Platform.XDG_Type.CACHE: //the user has set XDG_CACHE_HOME if (String.IsNullOrEmpty(xdgcachehome)) { - //xdg values have not been set + //xdg values have not been set if (!Directory.Exists(xdgCacheDefault)) //module folder not always guaranteed to exist - { - Directory.CreateDirectory(xdgCacheDefault); + { + Directory.CreateDirectory(xdgCacheDefault); } - - return xdgCacheDefault; + + return xdgCacheDefault; } else @@ -168,30 +168,30 @@ namespace System.Management.Automation Directory.CreateDirectory(Path.Combine(xdgcachehome, "powershell")); } - return Path.Combine(xdgcachehome, "powershell"); - } - - case Platform.XDG_Type.DEFAULT: + return Path.Combine(xdgcachehome, "powershell"); + } + + case Platform.XDG_Type.DEFAULT: //default for profile location return xdgConfigHomeDefault; - default: + default: //xdgConfigHomeDefault needs to be created in the edge case that we do not have the folder or it was deleted //This folder is the default in the event of all other failures for data storage - if (!Directory.Exists(xdgConfigHomeDefault)) - { - try { + if (!Directory.Exists(xdgConfigHomeDefault)) + { + try { Directory.CreateDirectory(xdgConfigHomeDefault); - } + } catch{ - + Console.Error.WriteLine("Failed to create default data directory: " + xdgConfigHomeDefault); - } + } } - - return xdgConfigHomeDefault; + + return xdgConfigHomeDefault; } - + } // ComObjectType is null on CoreCLR for Linux since there is @@ -458,7 +458,7 @@ namespace System.Management.Automation throw new InvalidOperationException("LinuxPlatform.NonWindowsHostName error: " + lastError); } return hostName; - + } else { @@ -481,7 +481,7 @@ namespace System.Management.Automation // TODO:PSL clean this up return 0; } - + /// /// This exception is meant to be thrown if a code path is not supported due /// to platform restrictions @@ -665,7 +665,7 @@ namespace System.Management.Automation int ret = Native.IsSymLink(filePath); switch(ret) { - case 1: + case 1: return true; case 0: return false; @@ -685,7 +685,7 @@ namespace System.Management.Automation int ret = Native.IsExecutable(filePath); switch(ret) { - case 1: + case 1: return true; case 0: return false; @@ -780,11 +780,11 @@ namespace System.Management.Automation internal static extern int SetDate(SetDateInfoInternal info); [DllImport(psLib, CharSet = CharSet.Ansi, SetLastError = true)] - internal static extern int CreateSymLink([MarshalAs(UnmanagedType.LPStr)]string filePath, + internal static extern int CreateSymLink([MarshalAs(UnmanagedType.LPStr)]string filePath, [MarshalAs(UnmanagedType.LPStr)]string target); [DllImport(psLib, CharSet = CharSet.Ansi, SetLastError = true)] - internal static extern int CreateHardLink([MarshalAs(UnmanagedType.LPStr)]string filePath, + internal static extern int CreateHardLink([MarshalAs(UnmanagedType.LPStr)]string filePath, [MarshalAs(UnmanagedType.LPStr)]string target); [DllImport(psLib, CharSet = CharSet.Ansi, SetLastError = true)] diff --git a/src/System.Management.Automation/engine/Modules/ModuleIntrinsics.cs b/src/System.Management.Automation/engine/Modules/ModuleIntrinsics.cs index 12a9c81f7..66f9e5ede 100644 --- a/src/System.Management.Automation/engine/Modules/ModuleIntrinsics.cs +++ b/src/System.Management.Automation/engine/Modules/ModuleIntrinsics.cs @@ -604,7 +604,6 @@ namespace System.Management.Automation psHome = psHome.ToLowerInvariant().Replace("\\syswow64\\", "\\system32\\"); } Interlocked.CompareExchange(ref SystemWideModulePath, Path.Combine(psHome, Utils.ModuleDirectory), null); - } return SystemWideModulePath; diff --git a/src/System.Management.Automation/engine/Utils.cs b/src/System.Management.Automation/engine/Utils.cs index d499f3ad3..ac23e8014 100644 --- a/src/System.Management.Automation/engine/Utils.cs +++ b/src/System.Management.Automation/engine/Utils.cs @@ -612,7 +612,7 @@ namespace System.Management.Automation /// The name of the subdirectory that contains packages. /// internal static string ModuleDirectory = "Modules"; - + /// /// The partial path to the DSC module directory /// diff --git a/src/System.Management.Automation/engine/hostifaces/HostUtilities.cs b/src/System.Management.Automation/engine/hostifaces/HostUtilities.cs index 592c0dcae..32d415965 100644 --- a/src/System.Management.Automation/engine/hostifaces/HostUtilities.cs +++ b/src/System.Management.Automation/engine/hostifaces/HostUtilities.cs @@ -152,7 +152,6 @@ namespace System.Management.Automation { continue; } - command = new PSCommand(); command.AddCommand(profilePath, false); commands.Add(command); @@ -187,9 +186,7 @@ namespace System.Management.Automation { basePath = Environment.GetFolderPath(Environment.SpecialFolder.Personal); basePath = IO.Path.Combine(basePath, Utils.ProductNameForDirectory); - } - else { basePath = GetAllUsersFolderPath(shellId); From 4b0c91664617d5ab4b1c899845102df4773d7965 Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Mon, 6 Jun 2016 13:44:33 -0700 Subject: [PATCH 13/17] Skip tests which can't be run on Travis OS X These should be marked as `-Pending` but Pester doesn't accept both `-Skip` and `-Pending` parameters, so they had to be combined to support skipping on Windows. --- test/powershell/XDGBDS.Tests.ps1 | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/test/powershell/XDGBDS.Tests.ps1 b/test/powershell/XDGBDS.Tests.ps1 index cecbe6070..ac906443f 100644 --- a/test/powershell/XDGBDS.Tests.ps1 +++ b/test/powershell/XDGBDS.Tests.ps1 @@ -25,12 +25,12 @@ Describe "XDG Base Directory Specification" { & $powershell -noprofile `$PROFILE | Should Be $expected } - It "Should start with the default profile on Linux" -Skip:($IsWindows) { + It "Should start with the default profile on Linux" -Skip:($IsWindows -or ($env:TRAVIS_OS_NAME -eq "osx")) { $expected = [IO.Path]::Combine($env:HOME, ".config", "powershell", $profileName) & $powershell -noprofile `$PROFILE | Should Be $expected } - It "Should respect XDG_CONFIG_HOME on Linux" -Skip:$IsWindows { + It "Should respect XDG_CONFIG_HOME on Linux" -Skip:($IsWindows -or ($env:TRAVIS_OS_NAME -eq "osx")) { $env:XDG_CONFIG_HOME = $TestDrive $expected = [IO.Path]::Combine($TestDrive, "powershell", $profileName) & $powershell -noprofile `$PROFILE | Should Be $expected @@ -42,24 +42,24 @@ Describe "XDG Base Directory Specification" { It "Should not change Windows behavior" -Skip:($IsLinux -or $IsOSX) { $expected = [IO.Path]::Combine($env:HOME, "Documents", "WindowsPowerShell", "Modules") $actual = & $powershell -noprofile `$env:PSMODULEPATH - $actual.split(';')[0] | Should Be $expected + # Windows prepends the system path, so the user path is second + $actual.split(';')[1] | Should Be $expected } - It "Should start with the default module path on Linux" -Skip:$IsWindows { + It "Should start with the default module path on Linux" -Skip:($IsWindows -or ($env:TRAVIS_OS_NAME -eq "osx")) { $env:PSMODULEPATH = "" # must not be sent to child process $expected = [IO.Path]::Combine($env:HOME, ".local", "share", "powershell", "Modules") $actual = & $powershell -noprofile `$env:PSMODULEPATH $actual.split(';')[0] | Should Be $expected } - It "Should respect XDG_DATA_HOME on Linux" -Skip:$IsWindows { + It "Should respect XDG_DATA_HOME on Linux" -Skip:($IsWindows -or ($env:TRAVIS_OS_NAME -eq "osx")) { $env:PSMODULEPATH = "" # must not be sent to child process $env:XDG_DATA_HOME = $TestDrive $expected = [IO.Path]::Combine($TestDrive, "powershell", "Modules") $actual = & $powershell -noprofile `$env:PSMODULEPATH $actual.split(';')[0] | Should Be $expected } - } Context "PSReadLine" { @@ -69,12 +69,12 @@ Describe "XDG Base Directory Specification" { & $powershell -noprofile { (Get-PSReadlineOption).HistorySavePath } | Should Be $expected } - It "Should start with the default history save path on Linux" -Skip:$IsWindows { + It "Should start with the default history save path on Linux" -Skip:($IsWindows -or ($env:TRAVIS_OS_NAME -eq "osx")) { $expected = [IO.Path]::Combine($env:HOME, ".local", "share", "powershell", "PSReadLine", "ConsoleHost_history.txt") & $powershell -noprofile { (Get-PSReadlineOption).HistorySavePath } | Should Be $expected } - It "Should respect XDG_DATA_HOME on Linux" -Skip:$IsWindows { + It "Should respect XDG_DATA_HOME on Linux" -Skip:($IsWindows -or ($env:TRAVIS_OS_NAME -eq "osx")) { $env:XDG_DATA_HOME = $TestDrive $expected = [IO.Path]::Combine($TestDrive, "powershell", "PSReadLine", "ConsoleHost_history.txt") & $powershell -noprofile { (Get-PSReadlineOption).HistorySavePath } | Should Be $expected @@ -91,14 +91,14 @@ Describe "XDG Base Directory Specification" { $expected | Should Exist } - It "Should start with the default StartupProfileData on Linux" -Skip:$IsWindows { + It "Should start with the default StartupProfileData on Linux" -Skip:($IsWindows -or ($env:TRAVIS_OS_NAME -eq "osx")) { $expected = [IO.Path]::Combine($env:HOME, ".cache", "powershell", "StartupProfileData-NonInteractive") Remove-Item -ErrorAction SilentlyContinue $expected & $powershell -noprofile { exit } $expected | Should Exist } - It "Should respect XDG_CACHE_HOME on Linux" -Skip:$IsWindows { + It "Should respect XDG_CACHE_HOME on Linux" -Skip:($IsWindows -or ($env:TRAVIS_OS_NAME -eq "osx")) { $env:XDG_CACHE_HOME = $TestDrive $expected = [IO.Path]::Combine($TestDrive, "powershell", "StartupProfileData-NonInteractive") Remove-Item -ErrorAction SilentlyContinue $expected From d5f6295042b3aafb241828f20f85a1666602c4dd Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Mon, 6 Jun 2016 14:26:13 -0700 Subject: [PATCH 14/17] Update ModuleAnalysisCache to use XDGBDS on Linux --- .../engine/Modules/AnalysisCache.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/System.Management.Automation/engine/Modules/AnalysisCache.cs b/src/System.Management.Automation/engine/Modules/AnalysisCache.cs index bcdcbe496..af4acc2bf 100644 --- a/src/System.Management.Automation/engine/Modules/AnalysisCache.cs +++ b/src/System.Management.Automation/engine/Modules/AnalysisCache.cs @@ -1015,8 +1015,10 @@ namespace System.Management.Automation { cacheStoreLocation = Environment.GetEnvironmentVariable("PSModuleAnalysisCachePath") ?? - Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), - @"Microsoft\Windows\PowerShell\ModuleAnalysisCache"); + (Platform.IsWindows + ? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), + @"Microsoft\Windows\PowerShell\ModuleAnalysisCache") + : Path.Combine(Platform.SelectProductNameForDirectory(Platform.XDG_Type.CACHE), "ModuleAnalysisCache")); } } From 539f8696e5c0070b67ec7ca8632294a10f2ef937 Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Mon, 6 Jun 2016 14:32:03 -0700 Subject: [PATCH 15/17] XDG cache directory now guaranteed to exist --- .../host/msh/ConsoleHost.cs | 24 ++++++++----------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs index 5cfdb0676..6148fbb3d 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs @@ -185,21 +185,17 @@ namespace Microsoft.PowerShell { profileDir = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + @"\Microsoft\Windows\PowerShell"; - } - - else + + if (!Directory.Exists(profileDir)) { - //check if the user has set an XDG path in their environment variables - profileDir = Platform.SelectProductNameForDirectory(Platform.XDG_Type.CACHE); - - if (!Directory.Exists(profileDir)) //xdg value may have been set but not a valid directory - { - Console.Error.WriteLine("The selected directory (" + profileDir +") for the profile does not exist. Using the default path."); - profileDir = Platform.SelectProductNameForDirectory(Platform.XDG_Type.DEFAULT); - } - } - - + Directory.CreateDirectory(profileDir); + } + } + else + { + profileDir = Platform.SelectProductNameForDirectory(Platform.XDG_Type.CACHE); + } + ClrFacade.SetProfileOptimizationRoot(profileDir); } catch From a48c5281a55495a1c0bff60d74a7d3da7b4c1609 Mon Sep 17 00:00:00 2001 From: Andy Schwartzmeyer Date: Mon, 6 Jun 2016 15:23:03 -0700 Subject: [PATCH 16/17] Fix Windows consistency tests Use `USERPROFILE`, not `HOME`. The latter doesn't exist on Windows, and `HOMEPATH` strips the drive. The former provides `C:\Users\`. Fix location tested for JIT cache location on Windows. --- test/powershell/XDGBDS.Tests.ps1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/powershell/XDGBDS.Tests.ps1 b/test/powershell/XDGBDS.Tests.ps1 index ac906443f..84a7c054b 100644 --- a/test/powershell/XDGBDS.Tests.ps1 +++ b/test/powershell/XDGBDS.Tests.ps1 @@ -21,7 +21,7 @@ Describe "XDG Base Directory Specification" { Context "Profile" { It "Should not change Windows behavior" -Skip:($IsLinux -or $IsOSX) { - $expected = [IO.Path]::Combine($env:HOME, "Documents", "WindowsPowerShell", $profileName) + $expected = [IO.Path]::Combine($env:USERPROFILE, "Documents", "WindowsPowerShell", $profileName) & $powershell -noprofile `$PROFILE | Should Be $expected } @@ -40,7 +40,7 @@ Describe "XDG Base Directory Specification" { Context "Modules" { It "Should not change Windows behavior" -Skip:($IsLinux -or $IsOSX) { - $expected = [IO.Path]::Combine($env:HOME, "Documents", "WindowsPowerShell", "Modules") + $expected = [IO.Path]::Combine($env:USERPROFILE, "Documents", "WindowsPowerShell", "Modules") $actual = & $powershell -noprofile `$env:PSMODULEPATH # Windows prepends the system path, so the user path is second $actual.split(';')[1] | Should Be $expected @@ -85,7 +85,7 @@ Describe "XDG Base Directory Specification" { Context "Cache" { It "Should not change Windows behavior" -Skip:($IsLinux -or $IsOSX) { - $expected = [IO.Path]::Combine($env:HOME, "Documents", "WindowsPowerShell", "StartupProfileData-NonInteractive") + $expected = [IO.Path]::Combine($env:LOCALAPPDATA, "Microsoft", "Windows", "PowerShell", "StartupProfileData-NonInteractive") Remove-Item -ErrorAction SilentlyContinue $expected & $powershell -noprofile { exit } $expected | Should Exist From 560bcaf736fa46a99cb202f03d0517e459cf1605 Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Thu, 9 Jun 2016 10:41:30 -0700 Subject: [PATCH 17/17] Refactor base directory tests --- test/powershell/Base-Directory.Tests.ps1 | 116 +++++++++++++++++++++++ test/powershell/XDGBDS.Tests.ps1 | 112 ---------------------- 2 files changed, 116 insertions(+), 112 deletions(-) create mode 100644 test/powershell/Base-Directory.Tests.ps1 delete mode 100644 test/powershell/XDGBDS.Tests.ps1 diff --git a/test/powershell/Base-Directory.Tests.ps1 b/test/powershell/Base-Directory.Tests.ps1 new file mode 100644 index 000000000..7e517fa8c --- /dev/null +++ b/test/powershell/Base-Directory.Tests.ps1 @@ -0,0 +1,116 @@ +Describe "Configuration file locations" { + + BeforeAll { + $powershell = Join-Path -Path $PsHome -ChildPath "powershell" + $profileName = "Microsoft.PowerShell_profile.ps1" + } + + Context "Default configuration file locations" { + + BeforeAll { + + if ($IsWindows) { + $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) + $expectedReadline = [IO.Path]::Combine($env:AppData, "Microsoft", "Windows", "PowerShell", "PSReadline", "ConsoleHost_history.txt") + } else { + $expectedCache = [IO.Path]::Combine($env:HOME, ".cache", "powershell", "StartupProfileData-NonInteractive") + $expectedModule = [IO.Path]::Combine($env:HOME, ".local", "share", "powershell", "Modules") + $expectedProfile = [io.path]::Combine($env:HOME,".config","powershell",$profileName) + $expectedReadline = [IO.Path]::Combine($env:HOME, ".local", "share", "powershell", "PSReadLine", "ConsoleHost_history.txt") + } + + if ($env:TRAVIS_OS_NAME -eq "osx") { + $ItArgs = @{ pending = $true } + } else { + $ItArgs = @{} + } + } + + BeforeEach { + $original_PSMODULEPATH = $env:PSMODULEPATH + } + + AfterEach { + $env:PSMODULEPATH = $original_PSMODULEPATH + } + + It @ItArgs "Profile location should be correct" { + & $powershell -noprofile `$PROFILE | Should Be $expectedProfile + } + + It @ItArgs "PSMODULEPATH should contain the correct path" { + $env:PSMODULEPATH = "" + $actual = & $powershell -noprofile `$env:PSMODULEPATH + $actual | Should Match ([regex]::Escape($expectedModule)) + } + + It @ItArgs "PSReadLine history save location should be correct" { + & $powershell -noprofile { (Get-PSReadlineOption).HistorySavePath } | Should Be $expectedReadline + } + + It @ItArgs "JIT cache should be created correctly" { + Remove-Item -ErrorAction SilentlyContinue $expectedCache + & $powershell -noprofile { exit } + $expectedCache | Should Exist + } + + # The ModuleAnalysisCache cannot be forced to exist, thus we cannot test it + } + + Context "XDG Base Directory Specification is supported on Linux" { + BeforeAll { + # Using It @ItArgs, we automatically skip on Windows for all these tests + if ($IsWindows) { + $ItArgs = @{ skip = $true } + } elseif ($env:TRAVIS_OS_NAME -eq "osx") { + $ItArgs = @{ pending = $true } + } else { + $ItArgs = @{} + } + } + + BeforeEach { + $original_PSMODULEPATH = $env:PSMODULEPATH + $original_XDG_CONFIG_HOME = $env:XDG_CONFIG_HOME + $original_XDG_CACHE_HOME = $env:XDG_CACHE_HOME + $original_XDG_DATA_HOME = $env:XDG_DATA_HOME + } + + AfterEach { + $env:PSMODULEPATH = $original_PSMODULEPATH + $env:XDG_CONFIG_HOME = $original_XDG_CONFIG_HOME + $env:XDG_CACHE_HOME = $original_XDG_CACHE_HOME + $env:XDG_DATA_HOME = $original_XDG_DATA_HOME + } + + It @ItArgs "Profile should respect XDG_CONFIG_HOME" { + $env:XDG_CONFIG_HOME = $TestDrive + $expected = [IO.Path]::Combine($TestDrive, "powershell", $profileName) + & $powershell -noprofile `$PROFILE | Should Be $expected + } + + It @ItArgs "PSMODULEPATH should respect XDG_DATA_HOME" { + $env:PSMODULEPATH = "" + $env:XDG_DATA_HOME = $TestDrive + $expected = [IO.Path]::Combine($TestDrive, "powershell", "Modules") + $actual = & $powershell -noprofile `$env:PSMODULEPATH + $actual | Should Match $expected + } + + It @ItArgs "PSReadLine history should respect XDG_DATA_HOME" { + $env:XDG_DATA_HOME = $TestDrive + $expected = [IO.Path]::Combine($TestDrive, "powershell", "PSReadLine", "ConsoleHost_history.txt") + & $powershell -noprofile { (Get-PSReadlineOption).HistorySavePath } | Should Be $expected + } + + It @ItArgs "JIT cache should respect XDG_CACHE_HOME" { + $env:XDG_CACHE_HOME = $TestDrive + $expected = [IO.Path]::Combine($TestDrive, "powershell", "StartupProfileData-NonInteractive") + Remove-Item -ErrorAction SilentlyContinue $expected + & $powershell -noprofile { exit } + $expected | Should Exist + } + } +} diff --git a/test/powershell/XDGBDS.Tests.ps1 b/test/powershell/XDGBDS.Tests.ps1 deleted file mode 100644 index 84a7c054b..000000000 --- a/test/powershell/XDGBDS.Tests.ps1 +++ /dev/null @@ -1,112 +0,0 @@ -Describe "XDG Base Directory Specification" { - - BeforeAll { - $powershell = Join-Path -Path $PsHome -ChildPath "powershell" - $profileName = "Microsoft.PowerShell_profile.ps1" - } - - BeforeEach { - $original_XDG_CONFIG_HOME = $env:XDG_CONFIG_HOME - $original_XDG_CACHE_HOME = $env:XDG_CACHE_HOME - $original_XDG_DATA_HOME = $env:XDG_DATA_HOME - - } - - AfterEach { - $env:XDG_CONFIG_HOME = $original_XDG_CONFIG_HOME - $env:XDG_CACHE_HOME = $original_XDG_CACHE_HOME - $env:XDG_DATA_HOME = $original_XDG_DATA_HOME - } - - Context "Profile" { - - It "Should not change Windows behavior" -Skip:($IsLinux -or $IsOSX) { - $expected = [IO.Path]::Combine($env:USERPROFILE, "Documents", "WindowsPowerShell", $profileName) - & $powershell -noprofile `$PROFILE | Should Be $expected - } - - It "Should start with the default profile on Linux" -Skip:($IsWindows -or ($env:TRAVIS_OS_NAME -eq "osx")) { - $expected = [IO.Path]::Combine($env:HOME, ".config", "powershell", $profileName) - & $powershell -noprofile `$PROFILE | Should Be $expected - } - - It "Should respect XDG_CONFIG_HOME on Linux" -Skip:($IsWindows -or ($env:TRAVIS_OS_NAME -eq "osx")) { - $env:XDG_CONFIG_HOME = $TestDrive - $expected = [IO.Path]::Combine($TestDrive, "powershell", $profileName) - & $powershell -noprofile `$PROFILE | Should Be $expected - } - } - - Context "Modules" { - - It "Should not change Windows behavior" -Skip:($IsLinux -or $IsOSX) { - $expected = [IO.Path]::Combine($env:USERPROFILE, "Documents", "WindowsPowerShell", "Modules") - $actual = & $powershell -noprofile `$env:PSMODULEPATH - # Windows prepends the system path, so the user path is second - $actual.split(';')[1] | Should Be $expected - } - - It "Should start with the default module path on Linux" -Skip:($IsWindows -or ($env:TRAVIS_OS_NAME -eq "osx")) { - $env:PSMODULEPATH = "" # must not be sent to child process - $expected = [IO.Path]::Combine($env:HOME, ".local", "share", "powershell", "Modules") - $actual = & $powershell -noprofile `$env:PSMODULEPATH - $actual.split(';')[0] | Should Be $expected - } - - It "Should respect XDG_DATA_HOME on Linux" -Skip:($IsWindows -or ($env:TRAVIS_OS_NAME -eq "osx")) { - $env:PSMODULEPATH = "" # must not be sent to child process - $env:XDG_DATA_HOME = $TestDrive - $expected = [IO.Path]::Combine($TestDrive, "powershell", "Modules") - $actual = & $powershell -noprofile `$env:PSMODULEPATH - $actual.split(';')[0] | Should Be $expected - } - } - - Context "PSReadLine" { - - It "Should not change Windows behavior" -Skip:($IsLinux -or $IsOSX) { - $expected = [IO.Path]::Combine($env:AppData, "Microsoft", "Windows", "PowerShell", "PSReadline", "ConsoleHost_history.txt") - & $powershell -noprofile { (Get-PSReadlineOption).HistorySavePath } | Should Be $expected - } - - It "Should start with the default history save path on Linux" -Skip:($IsWindows -or ($env:TRAVIS_OS_NAME -eq "osx")) { - $expected = [IO.Path]::Combine($env:HOME, ".local", "share", "powershell", "PSReadLine", "ConsoleHost_history.txt") - & $powershell -noprofile { (Get-PSReadlineOption).HistorySavePath } | Should Be $expected - } - - It "Should respect XDG_DATA_HOME on Linux" -Skip:($IsWindows -or ($env:TRAVIS_OS_NAME -eq "osx")) { - $env:XDG_DATA_HOME = $TestDrive - $expected = [IO.Path]::Combine($TestDrive, "powershell", "PSReadLine", "ConsoleHost_history.txt") - & $powershell -noprofile { (Get-PSReadlineOption).HistorySavePath } | Should Be $expected - } - - } - - Context "Cache" { - - It "Should not change Windows behavior" -Skip:($IsLinux -or $IsOSX) { - $expected = [IO.Path]::Combine($env:LOCALAPPDATA, "Microsoft", "Windows", "PowerShell", "StartupProfileData-NonInteractive") - Remove-Item -ErrorAction SilentlyContinue $expected - & $powershell -noprofile { exit } - $expected | Should Exist - } - - It "Should start with the default StartupProfileData on Linux" -Skip:($IsWindows -or ($env:TRAVIS_OS_NAME -eq "osx")) { - $expected = [IO.Path]::Combine($env:HOME, ".cache", "powershell", "StartupProfileData-NonInteractive") - Remove-Item -ErrorAction SilentlyContinue $expected - & $powershell -noprofile { exit } - $expected | Should Exist - } - - It "Should respect XDG_CACHE_HOME on Linux" -Skip:($IsWindows -or ($env:TRAVIS_OS_NAME -eq "osx")) { - $env:XDG_CACHE_HOME = $TestDrive - $expected = [IO.Path]::Combine($TestDrive, "powershell", "StartupProfileData-NonInteractive") - Remove-Item -ErrorAction SilentlyContinue $expected - & $powershell -noprofile { exit } - $expected | Should Exist - } - - # The ModuleAnalysisCache cannot be forced to exist, thus we cannot test it - - } -}