Merge pull request #1060 from PowerShell/xdg

XDG Changes
I like these tests much better now - far easier to follow
This commit is contained in:
James Truher [MSFT] 2016-06-09 16:17:48 -07:00 committed by GitHub
commit eeb715bf4e
8 changed files with 270 additions and 35 deletions

View file

@ -185,15 +185,17 @@ namespace Microsoft.PowerShell
{
profileDir = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) +
@"\Microsoft\Windows\PowerShell";
} else
if (!Directory.Exists(profileDir))
{
Directory.CreateDirectory(profileDir);
}
}
else
{
profileDir = System.IO.Path.Combine(Environment.GetEnvironmentVariable("HOME"), ".powershell");
profileDir = Platform.SelectProductNameForDirectory(Platform.XDG_Type.CACHE);
}
if (!Directory.Exists(profileDir))
{
Directory.CreateDirectory(profileDir);
}
ClrFacade.SetProfileOptimizationRoot(profileDir);
}
catch
@ -268,7 +270,6 @@ namespace Microsoft.PowerShell
: "StartupProfileData-NonInteractive");
exitCode = theConsoleHost.Run(cpp, !string.IsNullOrEmpty(preStartWarning));
}
}
finally
{

View file

@ -169,24 +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.ExpandEnvironmentVariables("%AppData%"),
@"\Microsoft\Windows\PowerShell\PSReadline\",
hostName + "_history.txt");
HistorySavePath = System.IO.Path.Combine(Environment.GetEnvironmentVariable("APPDATA"),
@"Microsoft\Windows\PowerShell\PSReadline\",
historyFileName);
}
else
{
HistorySavePath = System.IO.Path.Combine(
Environment.GetEnvironmentVariable("HOME"),
".powershell",
"PSReadLine",
hostName + "_history.txt");
// 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
{
// History is data, so it goes into .local/share/powershell folder
HistorySavePath = System.IO.Path.Combine(Environment.GetEnvironmentVariable("HOME"),
".local",
"share",
"powershell",
"PSReadLine",
historyFileName);
}
}
#else
HistorySavePath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)
+ @"\Microsoft\Windows\PowerShell\PSReadline\" + hostName + "_history.txt";
+ @"\Microsoft\Windows\PowerShell\PSReadline\" + historyFileName;
#endif
CommandValidationHandler = null;
CommandsToValidateScriptBlockArguments = new HashSet<string>(StringComparer.OrdinalIgnoreCase)

View file

@ -44,6 +44,15 @@ namespace System.Management.Automation
}
}
//enum for selecting the xdgpaths
public enum XDG_Type
{
PROFILE,
MODULES,
CACHE,
DEFAULT
}
public static bool IsOSX
{
get
@ -96,8 +105,94 @@ 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 (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 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:
//the user has set XDG_CONFIG_HOME corrresponding to profile path
if (String.IsNullOrEmpty(xdgconfighome))
{
//xdg values have not been set
return xdgConfigHomeDefault;
}
else
{
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)){
//xdg values have not been set
if (!Directory.Exists(xdgModuleDefault)) //module folder not always guaranteed to exist
{
Directory.CreateDirectory(xdgModuleDefault);
}
return xdgModuleDefault;
}
else
{
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
if (!Directory.Exists(xdgCacheDefault)) //module folder not always guaranteed to exist
{
Directory.CreateDirectory(xdgCacheDefault);
}
return xdgCacheDefault;
}
else
{
if (!Directory.Exists(Path.Combine(xdgcachehome, "powershell")))
{
Directory.CreateDirectory(Path.Combine(xdgcachehome, "powershell"));
}
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))
{
try {
Directory.CreateDirectory(xdgConfigHomeDefault);
}
catch{
Console.Error.WriteLine("Failed to create default data directory: " + xdgConfigHomeDefault);
}
}
return xdgConfigHomeDefault;
}
}
// ComObjectType is null on CoreCLR for Linux since there is
// no COM support on Linux
@ -363,7 +458,7 @@ namespace System.Management.Automation
throw new InvalidOperationException("LinuxPlatform.NonWindowsHostName error: " + lastError);
}
return hostName;
}
else
{
@ -386,7 +481,7 @@ namespace System.Management.Automation
// TODO:PSL clean this up
return 0;
}
/// <summary>
/// This exception is meant to be thrown if a code path is not supported due
/// to platform restrictions
@ -570,7 +665,7 @@ namespace System.Management.Automation
int ret = Native.IsSymLink(filePath);
switch(ret)
{
case 1:
case 1:
return true;
case 0:
return false;
@ -590,7 +685,7 @@ namespace System.Management.Automation
int ret = Native.IsExecutable(filePath);
switch(ret)
{
case 1:
case 1:
return true;
case 0:
return false;
@ -685,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)]

View file

@ -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"));
}
}

View file

@ -548,13 +548,20 @@ namespace System.Management.Automation
/// <returns>personal module path</returns>
internal static string GetPersonalModulePath()
{
string personalModuleRoot = Path.Combine(
Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
Utils.ProductNameForDirectory),
Utils.ModuleDirectory);
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;
}
}
/// <summary>

View file

@ -606,7 +606,7 @@ namespace System.Management.Automation
/// Profile uses this to control profile loading.
/// </remarks>
internal static string ProductNameForDirectory =
Platform.IsWindows ? "WindowsPowerShell" : Platform.ProductNameForDirectory;
Platform.IsWindows ? "WindowsPowerShell" : Platform.SelectProductNameForDirectory(Platform.XDG_Type.PROFILE);
/// <summary>
/// The name of the subdirectory that contains packages.

View file

@ -198,7 +198,6 @@ namespace System.Management.Automation
string profileName = useTestProfile ? "profile_test.ps1" : "profile.ps1";
if (!string.IsNullOrEmpty(shellId))
{
profileName = shellId + "_" + profileName;

View file

@ -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
}
}
}