Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[mono] Implement Environment.GetFolderPath on iOS #34022

Merged
merged 18 commits into from
Mar 31, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Runtime.InteropServices;

internal static partial class Interop
{
internal static partial class Sys
{
[DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_SearchPath")]
internal static extern string SearchPath(int folderId);
}
}
4 changes: 3 additions & 1 deletion src/libraries/Native/Unix/System.Native/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ set(NATIVE_SOURCES
)

if (CLR_CMAKE_TARGET_IOS)
set(NATIVE_SOURCES ${NATIVE_SOURCES} pal_log.m)
set(NATIVE_SOURCES ${NATIVE_SOURCES}
pal_log.m
pal_searchpath.m)
else ()
set(NATIVE_SOURCES ${NATIVE_SOURCES} pal_console.c)
endif ()
Expand Down
10 changes: 10 additions & 0 deletions src/libraries/Native/Unix/System.Native/pal_searchpath.h
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.
// See the LICENSE file in the project root for more information.

#pragma once

#include "pal_compiler.h"
#include "pal_types.h"

PALEXPORT const char* SystemNative_SearchPath(int32_t folderId);
13 changes: 13 additions & 0 deletions src/libraries/Native/Unix/System.Native/pal_searchpath.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

#include "pal_searchpath.h"
#import <Foundation/Foundation.h>

const char* SystemNative_SearchPath(int32_t folderId)
{
NSSearchPathDirectory spd = (NSSearchPathDirectory) folderId;
NSURL* url = [[[NSFileManager defaultManager] URLsForDirectory:spd inDomains:NSUserDomainMask] lastObject];
return strdup ([[url path] UTF8String]);
}
EgorBo marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ namespace System
{
public static partial class Environment
{
#if !TARGET_IOS
akoeplinger marked this conversation as resolved.
Show resolved Hide resolved
private static Func<string, object>? s_directoryCreateDirectory;
#endif

public static bool UserInteractive => true;

Expand Down Expand Up @@ -56,7 +58,10 @@ private static string GetFolderPathCore(SpecialFolder folder, SpecialFolderOptio
// Get the path for the SpecialFolder
string path = GetFolderPathCoreWithoutValidation(folder);
Debug.Assert(path != null);

#if TARGET_IOS
// ignore verification and SpecialFolderOption.Create on iOS
return path;
#else
// If we didn't get one, or if we got one but we're not supposed to verify it,
// or if we're supposed to verify it and it passes verification, return the path.
if (path.Length == 0 ||
Expand Down Expand Up @@ -86,17 +91,21 @@ private static string GetFolderPathCore(SpecialFolder folder, SpecialFolderOptio

return path;
}
#endif
}

private static string GetFolderPathCoreWithoutValidation(SpecialFolder folder)
{
#if TARGET_IOS
return GetFolderPathiOS(folder);
#else
// First handle any paths that involve only static paths, avoiding the overheads of getting user-local paths.
// https://www.freedesktop.org/software/systemd/man/file-hierarchy.html
switch (folder)
{
case SpecialFolder.CommonApplicationData: return "/usr/share";
case SpecialFolder.CommonTemplates: return "/usr/share/templates";
#if TARGET_OSX || TARGET_IOS
#if TARGET_OSX
case SpecialFolder.ProgramFiles: return "/Applications";
case SpecialFolder.System: return "/System";
#endif
Expand Down Expand Up @@ -152,7 +161,7 @@ private static string GetFolderPathCoreWithoutValidation(SpecialFolder folder)
case SpecialFolder.MyVideos:
return ReadXdgDirectory(home, "XDG_VIDEOS_DIR", "Videos");

#if TARGET_OSX || TARGET_IOS
#if TARGET_OSX
case SpecialFolder.MyMusic:
return Path.Combine(home, "Music");
case SpecialFolder.MyPictures:
Expand All @@ -175,6 +184,7 @@ private static string GetFolderPathCoreWithoutValidation(SpecialFolder folder)

// No known path for the SpecialFolder
return string.Empty;
#endif
}

private static string GetXdgConfig(string home)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,12 @@
<Compile Include="$(BclSourcesRoot)\Internal\Resources\WindowsRuntimeResourceManagerBase.cs" />
<Compile Include="$(BclSourcesRoot)\Internal\Runtime\InteropServices\WindowsRuntime\ExceptionSupport.cs" />
</ItemGroup>
<ItemGroup Condition="'$(TargetsiOS)' == 'true'">
<Compile Include="$(BclSourcesRoot)\System\Environment.iOS.cs" />
<Compile Include="$(CommonPath)Interop\OSX\System.Native\Interop.SearchPath.cs">
<Link>Common\Interop\OSX\Interop.SearchPath.cs</Link>
</Compile>
</ItemGroup>
<ItemGroup>
<Compile Include="$(BclSourcesRoot)\Mono\RuntimeStructs.cs" />
<Compile Include="$(BclSourcesRoot)\Mono\RuntimeMarshal.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,26 @@ static string GetEnvironmentVariableCore (string variable)
{
Debug.Assert(variable != null);

variable = TrimStringOnFirstZero (variable);

// call getenv directly if s_environment is not yet initialized
if (s_environment == null) {
using (var h = RuntimeMarshal.MarshalString (variable)) {
return internalGetEnvironmentVariable_native (h.Value);
}
}

variable = TrimStringOnFirstZero (variable);
string value = "";
lock (s_environment) {
s_environment.TryGetValue (variable, out string value);
return value;
if (!s_environment.TryGetValue (variable, out value)) {
// on some platform s_environment can be empty after EnsureEnvironmentCached (), e.g. iOS
using (var h = RuntimeMarshal.MarshalString (variable)) {
EgorBo marked this conversation as resolved.
Show resolved Hide resolved
value = internalGetEnvironmentVariable_native (h.Value);
s_environment [variable] = value;
}
}
}
return value;
}

static unsafe void SetEnvironmentVariableCore (string variable, string? value)
Expand Down
115 changes: 115 additions & 0 deletions src/mono/netcore/System.Private.CoreLib/src/System/Environment.iOS.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.IO;
using System.Runtime.InteropServices;

namespace System
{
partial class Environment
EgorBo marked this conversation as resolved.
Show resolved Hide resolved
{
private const int NSDocumentDirectoryId = 9;
private const int NSLibraryDirectoryId = 5;

private static string s_document;
private static string s_library;

// TODO: fix for tvOS
EgorBo marked this conversation as resolved.
Show resolved Hide resolved
// The "normal" NSDocumentDirectory is a read-only directory on tvOS
// and that breaks a lot of assumptions in the runtime and the BCL
private static string NSDocumentDirectory => s_document ??= Interop.Sys.SearchPath(NSDocumentDirectoryId);

// Various user-visible documentation, support, and configuration files
private static string NSLibraryDirectory => s_library ??= Interop.Sys.SearchPath(NSLibraryDirectoryId);
akoeplinger marked this conversation as resolved.
Show resolved Hide resolved

private static string GetFolderPathiOS(SpecialFolder folder)
EgorBo marked this conversation as resolved.
Show resolved Hide resolved
{
switch (folder)
{
case SpecialFolder.MyComputer:
case SpecialFolder.Programs:
case SpecialFolder.SendTo:
case SpecialFolder.StartMenu:
case SpecialFolder.Startup:
case SpecialFolder.Cookies:
case SpecialFolder.History:
case SpecialFolder.Recent:
case SpecialFolder.CommonProgramFiles:
case SpecialFolder.System:
case SpecialFolder.NetworkShortcuts:
case SpecialFolder.CommonStartMenu:
case SpecialFolder.CommonPrograms:
case SpecialFolder.CommonStartup:
case SpecialFolder.CommonDesktopDirectory:
case SpecialFolder.PrinterShortcuts:
case SpecialFolder.Windows:
case SpecialFolder.SystemX86:
case SpecialFolder.ProgramFilesX86:
case SpecialFolder.CommonProgramFilesX86:
case SpecialFolder.CommonDocuments:
case SpecialFolder.CommonAdminTools:
case SpecialFolder.AdminTools:
case SpecialFolder.CommonMusic:
case SpecialFolder.CommonPictures:
case SpecialFolder.CommonVideos:
case SpecialFolder.LocalizedResources:
case SpecialFolder.CommonOemLinks:
case SpecialFolder.CDBurning:
return String.Empty;

case SpecialFolder.Personal:
case SpecialFolder.LocalApplicationData:
return NSDocumentDirectory;

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 Path.Combine(NSDocumentDirectory, ".config");

case SpecialFolder.Resources:
return NSLibraryDirectory; // older (8.2 and previous) would return String.Empty

case SpecialFolder.Desktop:
case SpecialFolder.DesktopDirectory:
return Path.Combine(NSDocumentDirectory, "Desktop");
EgorBo marked this conversation as resolved.
Show resolved Hide resolved

case SpecialFolder.MyMusic:
return Path.Combine(NSDocumentDirectory, "Music");
EgorBo marked this conversation as resolved.
Show resolved Hide resolved

case SpecialFolder.MyPictures:
return Path.Combine(NSDocumentDirectory, "Pictures");
EgorBo marked this conversation as resolved.
Show resolved Hide resolved

case SpecialFolder.Templates:
return Path.Combine(NSDocumentDirectory, "Templates");

case SpecialFolder.MyVideos:
return Path.Combine(NSDocumentDirectory, "Videos");
EgorBo marked this conversation as resolved.
Show resolved Hide resolved

case SpecialFolder.CommonTemplates:
return "/usr/share/templates";

case SpecialFolder.Fonts:
return Path.Combine(NSDocumentDirectory, ".fonts");

case SpecialFolder.Favorites:
return Path.Combine(NSLibraryDirectory, "Favorites");

case SpecialFolder.ProgramFiles:
return "/Applications";
EgorBo marked this conversation as resolved.
Show resolved Hide resolved

case SpecialFolder.InternetCache:
return Path.Combine(NSLibraryDirectory, "Caches");
EgorBo marked this conversation as resolved.
Show resolved Hide resolved

case SpecialFolder.UserProfile:
return Environment.GetEnvironmentVariable("HOME");

case SpecialFolder.CommonApplicationData:
return "/usr/share";

default:
throw new ArgumentException($"Invalid SpecialFolder '{folder}'");
EgorBo marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
}