Skip to content

Commit

Permalink
Give tvOS a special value for Personal/MyDocuments (#57508)
Browse files Browse the repository at this point in the history
* Use a different Personal folder on tvOS, and a test that it works
* Set temp path to a valid location
* Add NSTemporaryDirectory interop function and supporting cast

Co-authored-by: Steve Pfister <[email protected]>
Co-authored-by: Adeel Mujahid <[email protected]>
Co-authored-by: Alexander Köplinger <[email protected]>
  • Loading branch information
4 people authored Aug 24, 2021
1 parent c1c90d3 commit 591e96b
Show file tree
Hide file tree
Showing 13 changed files with 95 additions and 11 deletions.
Original file line number Diff line number Diff line change
@@ -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();
}
}
1 change: 1 addition & 0 deletions src/libraries/Native/Unix/System.Native/entrypoints.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
6 changes: 6 additions & 0 deletions src/libraries/Native/Unix/System.Native/pal_searchpath.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,9 @@ const char* SystemNative_SearchPath(int32_t folderId)
__builtin_unreachable();
return NULL;
}

const char* SystemNative_SearchPath_TempDirectory()
{
__builtin_unreachable();
return NULL;
}
2 changes: 2 additions & 0 deletions src/libraries/Native/Unix/System.Native/pal_searchpath.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@
#include "pal_types.h"

PALEXPORT const char* SystemNative_SearchPath(int32_t folderId);

PALEXPORT const char* SystemNative_SearchPath_TempDirectory(void);
7 changes: 7 additions & 0 deletions src/libraries/Native/Unix/System.Native/pal_searchpath.m
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2112,6 +2112,8 @@
<Compile Include="$(MSBuildThisFileDirectory)System\IO\FileSystem.Exists.Unix.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\FileSystemInfo.Unix.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\Path.Unix.cs" />
<Compile Condition="'$(IsiOSLike)' == 'true'" Include="$(MSBuildThisFileDirectory)System\IO\Path.Unix.iOS.cs" />
<Compile Condition="'$(IsiOSLike)' != 'true'" Include="$(MSBuildThisFileDirectory)System\IO\Path.Unix.NoniOS.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\PathInternal.Unix.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\PersistedFiles.Names.Unix.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\RandomAccess.Unix.cs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -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/";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
@@ -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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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]
Expand Down
2 changes: 1 addition & 1 deletion src/libraries/sendtohelixhelp.proj
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@
<TestTarget>$(AppleTestTarget)</TestTarget>
</XHarnessAppBundleToTest>
<!-- Create work items for run-only apps -->
<XHarnessAppBundleToTest Condition="Exists('$(TestArchiveRoot)runonly')" Include="$([System.IO.Directory]::GetDirectories('$(TestArchiveRoot)runonly', '*.app', System.IO.SearchOption.AllDirectories))" >
<XHarnessAppBundleToTest Condition="Exists('$(TestArchiveRoot)runonly') and '$(TargetOS)' != 'tvOS'" Include="$([System.IO.Directory]::GetDirectories('$(TestArchiveRoot)runonly', '*.app', System.IO.SearchOption.AllDirectories))" >
<!-- The sample app doesn't need test runner -->
<IncludesTestRunner>false</IncludesTestRunner>
<!-- The sample's C# Main method returns 42 so it should be considered by xharness as a success -->
Expand Down
3 changes: 3 additions & 0 deletions src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,9 @@
<Compile Include="$(CommonPath)Interop\OSX\System.Native\Interop.SearchPath.cs">
<Link>Common\Interop\OSX\Interop.SearchPath.cs</Link>
</Compile>
<Compile Include="$(CommonPath)Interop\OSX\System.Native\Interop.SearchPath.iOS.cs">
<Link>Common\Interop\OSX\Interop.SearchPath.iOS.cs</Link>
</Compile>
</ItemGroup>
<ItemGroup Condition="'$(TargetsBrowser)' == 'true'">
<Compile Include="$(BclSourcesRoot)\System\Threading\TimerQueue.Browser.Mono.cs" />
Expand Down
29 changes: 21 additions & 8 deletions src/mono/System.Private.CoreLib/src/System/Environment.iOS.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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");
Expand All @@ -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");
Expand Down Expand Up @@ -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;
}
}
}
}

0 comments on commit 591e96b

Please sign in to comment.