diff --git a/src/libraries/Common/src/Interop/OSX/System.Native/Interop.SearchPath.iOS.cs b/src/libraries/Common/src/Interop/OSX/System.Native/Interop.SearchPath.iOS.cs new file mode 100644 index 0000000000000..8d73184ca4d50 --- /dev/null +++ b/src/libraries/Common/src/Interop/OSX/System.Native/Interop.SearchPath.iOS.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Sys + { + [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_SearchPath_TempDirectory")] + internal static extern string SearchPathTempDirectory(); + } +} diff --git a/src/libraries/Native/Unix/System.Native/entrypoints.c b/src/libraries/Native/Unix/System.Native/entrypoints.c index c8b678ec08340..b3ae64f96f91e 100644 --- a/src/libraries/Native/Unix/System.Native/entrypoints.c +++ b/src/libraries/Native/Unix/System.Native/entrypoints.c @@ -218,6 +218,7 @@ static const Entry s_sysNative[] = DllImportEntry(SystemNative_GetOSArchitecture) DllImportEntry(SystemNative_GetProcessArchitecture) DllImportEntry(SystemNative_SearchPath) + DllImportEntry(SystemNative_SearchPath_TempDirectory) DllImportEntry(SystemNative_RegisterForSigChld) DllImportEntry(SystemNative_SetDelayedSigChildConsoleConfigurationHandler) DllImportEntry(SystemNative_SetTerminalInvalidationHandler) diff --git a/src/libraries/Native/Unix/System.Native/pal_searchpath.c b/src/libraries/Native/Unix/System.Native/pal_searchpath.c index fbf3d903df01c..de8037dc1a295 100644 --- a/src/libraries/Native/Unix/System.Native/pal_searchpath.c +++ b/src/libraries/Native/Unix/System.Native/pal_searchpath.c @@ -10,3 +10,9 @@ const char* SystemNative_SearchPath(int32_t folderId) __builtin_unreachable(); return NULL; } + +const char* SystemNative_SearchPath_TempDirectory() +{ + __builtin_unreachable(); + return NULL; +} diff --git a/src/libraries/Native/Unix/System.Native/pal_searchpath.h b/src/libraries/Native/Unix/System.Native/pal_searchpath.h index e2de05d8c8860..cdb872914624c 100644 --- a/src/libraries/Native/Unix/System.Native/pal_searchpath.h +++ b/src/libraries/Native/Unix/System.Native/pal_searchpath.h @@ -7,3 +7,5 @@ #include "pal_types.h" PALEXPORT const char* SystemNative_SearchPath(int32_t folderId); + +PALEXPORT const char* SystemNative_SearchPath_TempDirectory(void); diff --git a/src/libraries/Native/Unix/System.Native/pal_searchpath.m b/src/libraries/Native/Unix/System.Native/pal_searchpath.m index 2e96041f3dda8..231c508c527ca 100644 --- a/src/libraries/Native/Unix/System.Native/pal_searchpath.m +++ b/src/libraries/Native/Unix/System.Native/pal_searchpath.m @@ -11,3 +11,10 @@ const char* path = [[url path] UTF8String]; return path == NULL ? NULL : strdup (path); } + +const char* SystemNative_SearchPath_TempDirectory() +{ + NSString* tempPath = NSTemporaryDirectory(); + const char *path = [tempPath UTF8String]; + return path == NULL ? NULL : strdup (path); +} diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 5a1593f032c69..e72c543d58080 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -2112,6 +2112,8 @@ + + diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Path.Unix.NoniOS.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Path.Unix.NoniOS.cs new file mode 100644 index 0000000000000..b2a9a4b06a083 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Path.Unix.NoniOS.cs @@ -0,0 +1,10 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.IO +{ + public static partial class Path + { + private static string DefaultTempPath => "/tmp/"; + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Path.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Path.Unix.cs index 61786ad757a43..f3ec19941c21b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Path.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Path.Unix.cs @@ -80,7 +80,6 @@ private static string RemoveLongPathPrefix(string path) public static string GetTempPath() { const string TempEnvVar = "TMPDIR"; - const string DefaultTempPath = "/tmp/"; // Get the temp path from the TMPDIR environment variable. // If it's not set, just return the default path. diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Path.Unix.iOS.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Path.Unix.iOS.cs new file mode 100644 index 0000000000000..c1c4a7047eec5 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Path.Unix.iOS.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace System.IO +{ + public static partial class Path + { + private static string? s_defaultTempPath; + + private static string DefaultTempPath => + s_defaultTempPath ?? (s_defaultTempPath = Interop.Sys.SearchPathTempDirectory()) ?? + throw new InvalidOperationException(); + } +} diff --git a/src/libraries/System.Runtime.Extensions/tests/System/EnvironmentTests.cs b/src/libraries/System.Runtime.Extensions/tests/System/EnvironmentTests.cs index f019b1eb11a02..f2b35eb6c2cdf 100644 --- a/src/libraries/System.Runtime.Extensions/tests/System/EnvironmentTests.cs +++ b/src/libraries/System.Runtime.Extensions/tests/System/EnvironmentTests.cs @@ -330,6 +330,13 @@ public void FailFast_ExceptionStackTrace_InnerException() } } + [Fact] + [PlatformSpecific(TestPlatforms.AnyUnix | TestPlatforms.Browser)] + public void GetFolderPath_Unix_PersonalExists() + { + Assert.True(Directory.Exists(Environment.GetFolderPath(Environment.SpecialFolder.Personal))); + } + [Fact] [PlatformSpecific(TestPlatforms.AnyUnix | TestPlatforms.Browser)] // Tests OS-specific environment public void GetFolderPath_Unix_PersonalIsHomeAndUserProfile() @@ -339,7 +346,11 @@ public void GetFolderPath_Unix_PersonalIsHomeAndUserProfile() Assert.Equal(Environment.GetEnvironmentVariable("HOME"), Environment.GetFolderPath(Environment.SpecialFolder.Personal)); Assert.Equal(Environment.GetEnvironmentVariable("HOME"), Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)); } - Assert.Equal(Environment.GetEnvironmentVariable("HOME"), Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)); + // tvOS effectively doesn't have a HOME + if (!PlatformDetection.IstvOS) + { + Assert.Equal(Environment.GetEnvironmentVariable("HOME"), Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)); + } } [Theory] diff --git a/src/libraries/sendtohelixhelp.proj b/src/libraries/sendtohelixhelp.proj index 14cf90630710d..14ea741c8c595 100644 --- a/src/libraries/sendtohelixhelp.proj +++ b/src/libraries/sendtohelixhelp.proj @@ -294,7 +294,7 @@ $(AppleTestTarget) - + false diff --git a/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj index 1878cb4ed7c05..7aa4413aad70d 100644 --- a/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -271,6 +271,9 @@ Common\Interop\OSX\Interop.SearchPath.cs + + Common\Interop\OSX\Interop.SearchPath.iOS.cs + diff --git a/src/mono/System.Private.CoreLib/src/System/Environment.iOS.cs b/src/mono/System.Private.CoreLib/src/System/Environment.iOS.cs index 1a95bdc408f1c..01ceb05bba7dc 100644 --- a/src/mono/System.Private.CoreLib/src/System/Environment.iOS.cs +++ b/src/mono/System.Private.CoreLib/src/System/Environment.iOS.cs @@ -36,18 +36,14 @@ private static string GetFolderPathCore(SpecialFolder folder, SpecialFolderOptio { switch (folder) { - // TODO: fix for tvOS (https://github.com/dotnet/runtime/issues/34007) - // The "normal" NSDocumentDirectory is a read-only directory on tvOS - // and that breaks a lot of assumptions in the runtime and the BCL - case SpecialFolder.Personal: case SpecialFolder.LocalApplicationData: - return Interop.Sys.SearchPath(NSSearchPathDirectory.NSDocumentDirectory); + return CombineDocumentDirectory(string.Empty); case SpecialFolder.ApplicationData: // note: at first glance that looked like a good place to return NSLibraryDirectory // but it would break isolated storage for existing applications - return CombineSearchPath(NSSearchPathDirectory.NSDocumentDirectory, ".config"); + return CombineDocumentDirectory(".config"); case SpecialFolder.Resources: return Interop.Sys.SearchPath(NSSearchPathDirectory.NSLibraryDirectory); // older (8.2 and previous) would return String.Empty @@ -63,7 +59,7 @@ private static string GetFolderPathCore(SpecialFolder folder, SpecialFolderOptio return Path.Combine(GetFolderPathCore(SpecialFolder.Personal, SpecialFolderOption.None), "Pictures"); case SpecialFolder.Templates: - return CombineSearchPath(NSSearchPathDirectory.NSDocumentDirectory, "Templates"); + return CombineDocumentDirectory("Templates"); case SpecialFolder.MyVideos: return Path.Combine(GetFolderPathCore(SpecialFolder.Personal, SpecialFolderOption.None), "Videos"); @@ -72,7 +68,7 @@ private static string GetFolderPathCore(SpecialFolder folder, SpecialFolderOptio return "/usr/share/templates"; case SpecialFolder.Fonts: - return CombineSearchPath(NSSearchPathDirectory.NSDocumentDirectory, ".fonts"); + return CombineDocumentDirectory(".fonts"); case SpecialFolder.Favorites: return CombineSearchPath(NSSearchPathDirectory.NSLibraryDirectory, "Favorites"); @@ -100,6 +96,23 @@ static string CombineSearchPath(NSSearchPathDirectory searchPath, string subdire Path.Combine(path, subdirectory) : string.Empty; } + + static string CombineDocumentDirectory(string subdirectory) + { +#if TARGET_TVOS + string? path = CombineSearchPath(NSSearchPathDirectory.NSLibraryDirectory, Path.Combine("Caches", "Documents", subdirectory)); + // Special version of CombineSearchPath which creates the path if needed. + // This isn't needed for "real" search paths which always exist, but on tvOS + // the base path is really a subdirectory we define rather than an OS directory. + // In order to not treat Directory.Exists(SpecialFolder.ApplicationData) differently + // on tvOS, guarantee that it exists by creating it here + if (!Directory.Exists (path)) + Directory.CreateDirectory (path); +#else + string? path = CombineSearchPath(NSSearchPathDirectory.NSDocumentDirectory, subdirectory); +#endif + return path; + } } } }