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

Support loading ICU data from managed Interop #49406

Merged
merged 5 commits into from
Mar 13, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
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
14 changes: 14 additions & 0 deletions src/libraries/Common/src/Interop/Interop.ICU.iOS.cs
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 Globalization
{
[DllImport(Libraries.GlobalizationNative, EntryPoint = "GlobalizationNative_LoadICUData")]
internal static extern int LoadICUData(string path);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ static const Entry s_globalizationNative[] =
DllImportEntry(GlobalizationNative_IsPredefinedLocale)
DllImportEntry(GlobalizationNative_LastIndexOf)
DllImportEntry(GlobalizationNative_LoadICU)
#if defined(STATIC_ICU)
DllImportEntry(GlobalizationNative_LoadICUData)
#endif
DllImportEntry(GlobalizationNative_NormalizeString)
DllImportEntry(GlobalizationNative_StartsWith)
DllImportEntry(GlobalizationNative_ToAscii)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ PALEXPORT int32_t GlobalizationNative_GetICUVersion(void);

#if defined(STATIC_ICU)

PALEXPORT int32_t GlobalizationNative_LoadICUData(char* path);
PALEXPORT int32_t GlobalizationNative_LoadICUData(const char* path);

PALEXPORT const char* GlobalizationNative_GetICUDTName(const char* culture);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,19 @@
static int32_t isLoaded = 0;
static int32_t isDataSet = 0;

static void log_shim_error(const char* format, ...)
{
va_list args;

va_start(args, format);
vfprintf(stderr, format, args);
va_end(args);
}

static void log_icu_error(const char* name, UErrorCode status)
{
const char * statusText = u_errorName(status);
fprintf(stderr, "ICU call %s failed with error #%d '%s'.\n", name, status, statusText);
log_shim_error("ICU call %s failed with error #%d '%s'.\n", name, status, statusText);
}

static void U_CALLCONV icu_trace_data(const void* context, int32_t fnNumber, int32_t level, const char* fmt, va_list args)
Expand Down Expand Up @@ -89,45 +98,60 @@ static int32_t load_icu_data(void* pData)
}
}

int32_t GlobalizationNative_LoadICUData(char* path)
int32_t GlobalizationNative_LoadICUData(const char* path)
{
int32_t ret = -1;
char* icu_data;

FILE *fp = fopen (path, "rb");
FILE *fp = fopen(path, "rb");
if (fp == NULL) {
fprintf (stderr, "Unable to load ICU dat file '%s'.", path);
log_shim_error("Unable to load ICU dat file '%s'.", path);
return ret;
}

if (fseek (fp, 0L, SEEK_END) != 0) {
fprintf (stderr, "Unable to determine size of the dat file");
if (fseek(fp, 0L, SEEK_END) != 0) {
fclose(fp);
log_shim_error("Unable to determine size of the dat file");
return ret;
steveisok marked this conversation as resolved.
Show resolved Hide resolved
}

long bufsize = ftell (fp);
long bufsize = ftell(fp);

if (bufsize == -1) {
fprintf (stderr, "Unable to determine size of the ICU dat file.");
fclose(fp);
log_shim_error("Unable to determine size of the ICU dat file.");
return ret;
steveisok marked this conversation as resolved.
Show resolved Hide resolved
}

icu_data = malloc (sizeof (char) * (bufsize + 1));
icu_data = malloc(sizeof(char) * (bufsize + 1));
steveisok marked this conversation as resolved.
Show resolved Hide resolved

if (icu_data == NULL) {
fclose(fp);
log_shim_error("Unable to allocate enough to read the ICU dat file");
return ret;
}

if (fseek (fp, 0L, SEEK_SET) != 0) {
fprintf (stderr, "Unable to seek ICU dat file.");
if (fseek(fp, 0L, SEEK_SET) != 0) {
fclose(fp);
log_shim_error("Unable to seek ICU dat file.");
return ret;
steveisok marked this conversation as resolved.
Show resolved Hide resolved
}

fread (icu_data, sizeof (char), bufsize, fp);
if (ferror ( fp ) != 0 ) {
fprintf (stderr, "Unable to read ICU dat file");
fread(icu_data, sizeof(char), bufsize, fp);
if (ferror( fp ) != 0 ) {
fclose(fp);
log_shim_error("Unable to read ICU dat file");
return ret;
steveisok marked this conversation as resolved.
Show resolved Hide resolved
}

fclose (fp);
fclose(fp);

if (load_icu_data(icu_data) == 0) {
log_shim_error("ICU BAD EXIT %d.", ret);
return ret;
}

return load_icu_data (icu_data);
return GlobalizationNative_LoadICU();
}

const char* GlobalizationNative_GetICUDTName(const char* culture)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
<PropertyGroup>
<Nullable>enable</Nullable>
<IsOSXLike Condition="'$(TargetsOSX)' == 'true' or '$(TargetsMacCatalyst)' == 'true' or '$(TargetsiOS)' == 'true' or '$(TargetstvOS)' == 'true'">true</IsOSXLike>
<IsiOSLike Condition="'$(TargetsMacCatalyst)' == 'true' or '$(TargetsiOS)' == 'true' or '$(TargetstvOS)' == 'true'">true</IsiOSLike>
<SupportsArmIntrinsics Condition="'$(Platform)' == 'arm64'">true</SupportsArmIntrinsics>
<SupportsX86Intrinsics Condition="'$(Platform)' == 'x64' or ('$(Platform)' == 'x86' and '$(TargetsUnix)' != 'true')">true</SupportsX86Intrinsics>
<ILLinkSharedDirectory>$(MSBuildThisFileDirectory)ILLink\</ILLinkSharedDirectory>
Expand Down Expand Up @@ -1082,6 +1083,9 @@
<Compile Include="$(CommonPath)Interop\Interop.ICU.cs">
<Link>Common\Interop\Interop.ICU.cs</Link>
</Compile>
<Compile Include="$(CommonPath)Interop\Interop.ICU.iOS.cs" Condition="'$(IsiOSLike)' == 'true'">
<Link>Common\Interop\Interop.ICU.iOS.cs</Link>
</Compile>
<Compile Include="$(CommonPath)Interop\Interop.Idna.cs">
<Link>Common\Interop\Interop.Idna.cs</Link>
</Compile>
Expand Down Expand Up @@ -1831,11 +1835,13 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Environment.NoRegistry.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Environment.UnixOrBrowser.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Environment.OSVersion.OSX.cs" Condition="'$(IsOSXLike)' == 'true'" />
<Compile Include="$(MSBuildThisFileDirectory)System\Environment.GetFolderPathCore.Unix.cs" Condition="'$(TargetsMacCatalyst)' != 'true' and '$(TargetsiOS)' != 'true' and '$(TargetstvOS)' != 'true'" />
<Compile Include="$(MSBuildThisFileDirectory)System\Environment.GetFolderPathCore.Unix.cs" Condition="'$(IsiOSLike)' != 'true'" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CalendarData.Unix.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CultureData.Unix.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CultureInfo.Unix.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\GlobalizationMode.Unix.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\GlobalizationMode.LoadICU.Unix.cs" Condition="'$(IsiOSLike)' != 'true'" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\GlobalizationMode.LoadICU.iOS.cs" Condition="'$(IsiOSLike)' == 'true'" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\HijriCalendar.Unix.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Guid.Unix.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\FileStreamHelpers.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.Globalization
{
internal static partial class GlobalizationMode
{
private static int LoadICU() => Interop.Globalization.LoadICU();
}
}
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.

namespace System.Globalization
{
internal static partial class GlobalizationMode
{
private static int LoadICU()
{
object? datPath = AppContext.GetData("ICU_DAT_FILE_PATH");
return (datPath != null) ? Interop.Globalization.LoadICUData(datPath!.ToString()!) : Interop.Globalization.LoadICU();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ private static bool GetGlobalizationInvariantMode()
}
else
{
int loaded = Interop.Globalization.LoadICU();
int loaded = LoadICU();
if (loaded == 0 && !OperatingSystem.IsBrowser())
{
// This can't go into resources, because a resource lookup requires globalization, which requires ICU
Expand Down
1 change: 1 addition & 0 deletions src/mono/mono/mini/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ if(HAVE_SYS_ICU)
if(STATIC_ICU)
set(pal_icushim_sources_base
pal_icushim_static.c)
add_definitions(-DSTATIC_ICU=1)
else()
set(pal_icushim_sources_base
pal_icushim.c)
Expand Down
2 changes: 1 addition & 1 deletion src/tasks/AppleAppBuilder/AppleAppBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ public override bool Execute()

if (GenerateXcodeProject)
{
Xcode generator = new Xcode(TargetOS);
Xcode generator = new Xcode(TargetOS, Arch);
generator.EnableRuntimeLogging = EnableRuntimeLogging;

XcodeProjectPath = generator.GenerateXCode(ProjectName, MainLibraryFileName, assemblerFiles,
Expand Down
54 changes: 25 additions & 29 deletions src/tasks/AppleAppBuilder/Templates/runtime.m
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
#define MONO_ENTER_GC_UNSAFE
#define MONO_EXIT_GC_UNSAFE

#define APPLE_RUNTIME_IDENTIFIER "//%APPLE_RUNTIME_IDENTIFIER%"

const char *
get_bundle_path (void)
{
Expand Down Expand Up @@ -203,23 +205,6 @@
//%DllMap%
}

int32_t GlobalizationNative_LoadICUData(char *path);

static int32_t load_icu_data ()
{
char path [1024];
int res;

const char *dname = "icudt.dat";
const char *bundle = get_bundle_path ();

os_log_info (OS_LOG_DEFAULT, "Loading ICU data file '%s'.", dname);
res = snprintf (path, sizeof (path) - 1, "%s/%s", bundle, dname);
assert (res > 0);

return GlobalizationNative_LoadICUData(path);
}

#if FORCE_INTERPRETER || FORCE_AOT || (!TARGET_OS_SIMULATOR && !TARGET_OS_MACCATALYST)
void mono_jit_set_aot_mode (MonoAotMode mode);
void register_aot_modules (void);
Expand All @@ -237,16 +222,6 @@ static int32_t load_icu_data ()
setenv ("MONO_LOG_MASK", "all", TRUE);
#endif

#if !INVARIANT_GLOBALIZATION
int32_t ret = load_icu_data ();

if (ret == 0) {
os_log_info (OS_LOG_DEFAULT, "ICU BAD EXIT %d.", ret);
exit (ret);
return;
}
#endif

id args_array = [[NSProcessInfo processInfo] arguments];
assert ([args_array count] <= 128);
const char *managed_argv [128];
Expand All @@ -261,8 +236,29 @@ static int32_t load_icu_data ()
const char* bundle = get_bundle_path ();
chdir (bundle);

char icu_dat_path [1024];
int res;

res = snprintf (icu_dat_path, sizeof (icu_dat_path) - 1, "%s/%s", bundle, "icudt.dat");
assert (res > 0);

// TODO: set TRUSTED_PLATFORM_ASSEMBLIES, APP_PATHS and NATIVE_DLL_SEARCH_DIRECTORIES
monovm_initialize(0, NULL, NULL);
const char *appctx_keys [] = {
"RUNTIME_IDENTIFIER",
safern marked this conversation as resolved.
Show resolved Hide resolved
"APP_CONTEXT_BASE_DIRECTORY",
#ifndef INVARIANT_GLOBALIZATION
safern marked this conversation as resolved.
Show resolved Hide resolved
"ICU_DAT_FILE_PATH"
#endif
};
const char *appctx_values [] = {
APPLE_RUNTIME_IDENTIFIER,
bundle,
#ifndef INVARIANT_GLOBALIZATION
icu_dat_path
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: trailing commas are not an error.

(and they play nicer with git history when someone adds the next array entry)

#endif
};

monovm_initialize (sizeof (appctx_keys) / sizeof (appctx_keys [0]), appctx_keys, appctx_values);

#if FORCE_INTERPRETER
os_log_info (OS_LOG_DEFAULT, "INTERP Enabled");
Expand Down Expand Up @@ -300,7 +296,7 @@ static int32_t load_icu_data ()
assert (assembly);
os_log_info (OS_LOG_DEFAULT, "Executable: %{public}s", executable);

int res = mono_jit_exec (mono_domain_get (), assembly, argi, managed_argv);
res = mono_jit_exec (mono_domain_get (), assembly, argi, managed_argv);
// Print this so apps parsing logs can detect when we exited
os_log_info (OS_LOG_DEFAULT, "Exit code: %d.", res);

Expand Down
6 changes: 5 additions & 1 deletion src/tasks/AppleAppBuilder/Xcode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@

internal class Xcode
{
private string RuntimeIdentifier { get; set; }
private string SysRoot { get; set; }
private string Target { get; set; }

public Xcode(string target)
public Xcode(string target, string arch)
{
Target = target;
switch (Target)
Expand All @@ -27,6 +28,8 @@ public Xcode(string target)
SysRoot = Utils.RunProcess("xcrun", "--sdk macosx --show-sdk-path");
break;
}

RuntimeIdentifier = $"{Target}-{arch}";
}

public bool EnableRuntimeLogging { get; set; }
Expand Down Expand Up @@ -175,6 +178,7 @@ public string GenerateXCode(
File.WriteAllText(Path.Combine(binDir, "runtime.m"),
Utils.GetEmbeddedResource("runtime.m")
.Replace("//%DllMap%", dllMap.ToString())
.Replace("//%APPLE_RUNTIME_IDENTIFIER%", RuntimeIdentifier)
.Replace("%EntryPointLibName%", Path.GetFileName(entryPointLib)));

Utils.RunProcess("cmake", cmakeArgs.ToString(), workingDir: binDir);
Expand Down